[systemd-commits] 10 commits - .gitignore Makefile.am Makefile-man.am man/systemd-journal-remote.xml man/systemd.unit.xml man/systemd-verify.xml src/core src/shared src/systemctl src/test src/verify test/basic.target test/paths.target test/shutdown.target test/sockets.target test/sysinit.target test/timers.target

Zbigniew Jędrzejewski-Szmek zbyszek at kemper.freedesktop.org
Sun Jul 20 17:44:43 PDT 2014


 .gitignore                     |    1 
 Makefile-man.am                |    2 
 Makefile.am                    |   22 ++
 man/systemd-journal-remote.xml |   21 --
 man/systemd-verify.xml         |  174 +++++++++++++++++++
 man/systemd.unit.xml           |    7 
 src/core/cgroup.c              |   57 +++---
 src/core/main.c                |   36 +++-
 src/core/manager.c             |   38 ++--
 src/core/manager.h             |    4 
 src/core/socket.c              |    9 -
 src/core/socket.h              |    2 
 src/core/unit.c                |    5 
 src/shared/log.c               |    8 
 src/shared/pager.c             |   53 +++++
 src/shared/pager.h             |    2 
 src/shared/path-lookup.c       |   98 +++++------
 src/systemctl/systemctl.c      |   47 -----
 src/test/test-cgroup-mask.c    |   25 +-
 src/test/test-engine.c         |   24 ++
 src/test/test-sched-prio.c     |    5 
 src/test/test-unit-name.c      |    2 
 src/verify/Makefile            |    1 
 src/verify/verify.c            |  363 +++++++++++++++++++++++++++++++++++++++++
 test/basic.target              |    1 
 test/paths.target              |    1 
 test/shutdown.target           |    1 
 test/sockets.target            |    1 
 test/sysinit.target            |    1 
 test/timers.target             |    1 
 30 files changed, 824 insertions(+), 188 deletions(-)

New commits:
commit 0e8415f2e5c42e87ca3b7a96138675f64696cb7a
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jul 20 19:28:58 2014 -0400

    man: add systemd-verify(1)

diff --git a/Makefile-man.am b/Makefile-man.am
index 4339e50..5cc3081 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -92,6 +92,7 @@ MANPAGES += \
 	man/systemd-udevd.service.8 \
 	man/systemd-update-done.service.8 \
 	man/systemd-update-utmp.service.8 \
+	man/systemd-verify.1 \
 	man/systemd.1 \
 	man/systemd.automount.5 \
 	man/systemd.device.5 \
@@ -1658,6 +1659,7 @@ EXTRA_DIST += \
 	man/systemd-update-utmp.service.xml \
 	man/systemd-user-sessions.service.xml \
 	man/systemd-vconsole-setup.service.xml \
+	man/systemd-verify.xml \
 	man/systemd.automount.xml \
 	man/systemd.device.xml \
 	man/systemd.directives.xml \
diff --git a/man/systemd-journal-remote.xml b/man/systemd-journal-remote.xml
index 4065ced..b470e2c 100644
--- a/man/systemd-journal-remote.xml
+++ b/man/systemd-journal-remote.xml
@@ -21,7 +21,8 @@ You should have received a copy of the GNU Lesser General Public License
 along with systemd; If not, see <http://www.gnu.org/licenses/>.
 -->
 
-<refentry id="systemd-journal-remote" conditional='HAVE_MICROHTTPD'>
+<refentry id="systemd-journal-remote" conditional='HAVE_MICROHTTPD'
+          xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>systemd-journal-remote</title>
@@ -235,21 +236,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
 
     <variablelist>
       <varlistentry>
-        <term><option>--help</option></term>
-        <term><option>-h</option></term>
-
-        <listitem><para>Print a short help
-        text and exit.</para></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>--version</option></term>
-
-        <listitem><para>Print a short version
-        string and exit.</para></listitem>
-      </varlistentry>
-
-      <varlistentry>
         <term><option>--split-mode</option></term>
 
         <listitem><para>One of <constant>none</constant> or
@@ -293,6 +279,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
         <programlisting>--getter='wget --header="Accept: application/vnd.fdo.journal" -O- https://some.host:19531/'</programlisting>
         </listitem>
       </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
   </refsect1>
 
diff --git a/man/systemd-verify.xml b/man/systemd-verify.xml
new file mode 100644
index 0000000..128032e
--- /dev/null
+++ b/man/systemd-verify.xml
@@ -0,0 +1,174 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+This file is part of systemd.
+
+Copyright 2014 Zbigniew Jędrzejewski-Szmek
+
+systemd is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+systemd is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="systemd-verify"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-verify</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Zbigniew</firstname>
+        <surname>Jędrzejewski-Szmek</surname>
+        <email>zbyszek at in.waw.pl</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-verify</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-verify</refname>
+    <refpurpose>Check unit file validity</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>systemd-verify</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="opt" rep="repeat">FILES</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-verify</filename> will load unit files and
+    potentially print warnings if any errors are detected. Files
+    specified on the command line will be loaded, but also any other
+    units referenced by them.</para>
+
+    <para>This command works by prepending the directories for all
+    command line arguments at the beginning of the unit load path.
+    This means that all units files found in those directories will
+    be used in preference to the unit files found in the standard
+    locations, even if not listed explicitly.</para>
+
+    <para>The following errors are currently detected:</para>
+    <itemizedlist>
+      <listitem><para>unknown sections and directives,
+      </para></listitem>
+
+      <listitem><para>missing dependencies which are required to start
+      the given unit,</para></listitem>
+
+      <listitem><para>man pages listed in
+      <varname>Documentation=</varname> which are not found in the
+      system,</para></listitem>
+
+      <listitem><para>commands listed in <varname>ExecStart=</varname>
+      and similar which are not found in the system or not executable.
+      </para></listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--no-man</option></term>
+
+        <listitem><para>Do not invoke man to verify the existence
+        of man pages listen in <varname>Documentation=</varname>.
+        </para></listitem>
+      </varlistentry>
+
+      <xi:include href="user-system-options.xml" xpointer="user" />
+      <xi:include href="user-system-options.xml" xpointer="system" />
+
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Exit status</title>
+
+    <para>On success, 0 is returned, a non-zero failure
+    code otherwise.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Misspelt directives</title>
+
+      <programlisting>$ cat ./user.slice
+[Unit]
+WhatIsThis=11
+Documentation=man:systemd.nosuchfile(7)
+Requires=different.service
+
+[Service]
+Desription=x
+
+$ systemd-verify ./user.slice
+[./user.slice:9] Unknown lvalue 'WhatIsThis' in section 'Unit'
+[./user.slice:13] Unknown section 'Service'. Ignoring.
+Error: org.freedesktop.systemd1.LoadFailed: Unit different.service failed to load: No such file or directory.
+Failed to create user.slice/start: Invalid argument
+user.slice: man systemd.nosuchfile(7) command failed with code 16
+</programlisting>
+    </example>
+
+    <example>
+      <title>Missing service units</title>
+
+      <programlisting>$ tail ./a.socket ./b.socket
+==> ./a.socket <==
+[Socket]
+ListenStream=100
+
+==> ./b.socket <==
+[Socket]
+ListenStream=100
+Accept=yes
+
+$ systemd-verify ./a.socket ./b.socket
+Service a.service not loaded, a.socket cannot be started.
+Service b at 0.service not loaded, b.socket cannot be started.
+</programlisting>
+    </example>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index dd3a4a7..dc5e385 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1513,6 +1513,7 @@
                         <citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+                        <citerefentry><refentrytitle>systemd-verify</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                         <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>uname</refentrytitle><manvolnum>1</manvolnum></citerefentry>
diff --git a/src/verify/verify.c b/src/verify/verify.c
index cad79ec..c1a3da8 100644
--- a/src/verify/verify.c
+++ b/src/verify/verify.c
@@ -278,8 +278,8 @@ static void help(void) {
                "Check if unit files can be correctly loaded.\n\n"
                "  -h --help           Show this help\n"
                "     --version        Show package version\n"
-               "     --system         Connect to system manager\n"
-               "     --user           Connect to user service manager\n"
+               "     --system         Test system units\n"
+               "     --user           Test user units\n"
                "     --no-man         Do not check for existence of man pages\n"
                , program_invocation_short_name);
 }

commit 78002a678106a9a4e0e9d747f54458ba84322f1a
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jul 20 18:23:53 2014 -0400

    systemd-verify: check man pages

diff --git a/src/shared/pager.c b/src/shared/pager.c
index 002e3aa..5479094 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -150,3 +150,56 @@ void pager_close(void) {
 bool pager_have(void) {
         return pager_pid > 0;
 }
+
+int show_man_page(const char *desc, bool null_stdio) {
+        const char *args[4] = { "man", NULL, NULL, NULL };
+        char *e = NULL;
+        pid_t pid;
+        size_t k;
+        int r;
+        siginfo_t status;
+
+        k = strlen(desc);
+
+        if (desc[k-1] == ')')
+                e = strrchr(desc, '(');
+
+        if (e) {
+                char *page = NULL, *section = NULL;
+
+                page = strndupa(desc, e - desc);
+                section = strndupa(e + 1, desc + k - e - 2);
+
+                args[1] = section;
+                args[2] = page;
+        } else
+                args[1] = desc;
+
+        pid = fork();
+        if (pid < 0) {
+                log_error("Failed to fork: %m");
+                return -errno;
+        }
+
+        if (pid == 0) {
+                /* Child */
+                if (null_stdio) {
+                        r = make_null_stdio();
+                        if (r < 0) {
+                                log_error("Failed to kill stdio: %s", strerror(-r));
+                                _exit(EXIT_FAILURE);
+                        }
+                }
+
+                execvp(args[0], (char**) args);
+                log_error("Failed to execute man: %m");
+                _exit(EXIT_FAILURE);
+        }
+
+        r = wait_for_terminate(pid, &status);
+        if (r < 0)
+                return r;
+
+        log_debug("Exit code %i status %i", status.si_code, status.si_status);
+        return status.si_status;
+}
diff --git a/src/shared/pager.h b/src/shared/pager.h
index 03dca8b..6744617 100644
--- a/src/shared/pager.h
+++ b/src/shared/pager.h
@@ -28,3 +28,5 @@
 int pager_open(bool jump_to_end);
 void pager_close(void);
 bool pager_have(void) _pure_;
+
+int show_man_page(const char *page, bool null_stdio);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 96a0d05..fc32509 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3503,50 +3503,11 @@ static void show_unit_help(UnitStatusInfo *i) {
                 return;
         }
 
-        STRV_FOREACH(p, i->documentation) {
-
-                if (startswith(*p, "man:")) {
-                        const char *args[4] = { "man", NULL, NULL, NULL };
-                        _cleanup_free_ char *page = NULL, *section = NULL;
-                        char *e = NULL;
-                        pid_t pid;
-                        size_t k;
-
-                        k = strlen(*p);
-
-                        if ((*p)[k-1] == ')')
-                                e = strrchr(*p, '(');
-
-                        if (e) {
-                                page = strndup((*p) + 4, e - *p - 4);
-                                section = strndup(e + 1, *p + k - e - 2);
-                                if (!page || !section) {
-                                        log_oom();
-                                        return;
-                                }
-
-                                args[1] = section;
-                                args[2] = page;
-                        } else
-                                args[1] = *p + 4;
-
-                        pid = fork();
-                        if (pid < 0) {
-                                log_error("Failed to fork: %m");
-                                continue;
-                        }
-
-                        if (pid == 0) {
-                                /* Child */
-                                execvp(args[0], (char**) args);
-                                log_error("Failed to execute man: %m");
-                                _exit(EXIT_FAILURE);
-                        }
-
-                        wait_for_terminate(pid, NULL);
-                } else
+        STRV_FOREACH(p, i->documentation)
+                if (startswith(*p, "man:"))
+                        show_man_page(*p + 4, false);
+                else
                         log_info("Can't show: %s", *p);
-        }
 }
 
 static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *i, const char *contents) {
diff --git a/src/verify/verify.c b/src/verify/verify.c
index fc513b1..cad79ec 100644
--- a/src/verify/verify.c
+++ b/src/verify/verify.c
@@ -27,8 +27,10 @@
 #include "log.h"
 #include "strv.h"
 #include "build.h"
+#include "pager.h"
 
 SystemdRunningAs arg_running_as = SYSTEMD_SYSTEM;
+bool arg_no_man = false;
 
 static int generate_path(char **var, char **filenames) {
         char **filename;
@@ -140,6 +142,37 @@ static int verify_executables(Unit *u) {
         return r;
 }
 
+static int verify_documentation(Unit *u) {
+        char **p;
+        int r = 0, k;
+
+        if (arg_no_man)
+                return 0;
+
+        STRV_FOREACH(p, u->documentation) {
+                log_debug_unit(u->id, "%s: found documentation item %s.", u->id, *p);
+                if (startswith(*p, "man:")) {
+                        k = show_man_page(*p + 4, true);
+                        if (k != 0) {
+                                if (k < 0)
+                                        log_error_unit(u->id, "%s: can't show %s: %s",
+                                                       u->id, *p, strerror(-r));
+                                else {
+                                        log_error_unit(u->id, "%s: man %s command failed with code %d",
+                                                       u->id, *p + 4, k);
+                                        k = -ENOEXEC;
+                                }
+                                if (r == 0)
+                                        r = k;
+                        }
+                }
+        }
+
+        /* Check remote URLs? */
+
+        return r;
+}
+
 static int test_unit(Unit *u) {
         _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
         Job *j;
@@ -167,6 +200,10 @@ static int test_unit(Unit *u) {
         if (k < 0 && r == 0)
                 r = k;
 
+        k = verify_documentation(u);
+        if (k < 0 && r == 0)
+                r = k;
+
         return r;
 }
 
@@ -242,8 +279,9 @@ static void help(void) {
                "  -h --help           Show this help\n"
                "     --version        Show package version\n"
                "     --system         Connect to system manager\n"
-               "     --user           Connect to user service manager\n",
-               program_invocation_short_name);
+               "     --user           Connect to user service manager\n"
+               "     --no-man         Do not check for existence of man pages\n"
+               , program_invocation_short_name);
 }
 
 static int parse_argv(int argc, char *argv[]) {
@@ -251,6 +289,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VERSION = 0x100,
                 ARG_USER,
                 ARG_SYSTEM,
+                ARG_NO_MAN,
         };
 
         static const struct option options[] = {
@@ -288,6 +327,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_running_as = SYSTEMD_SYSTEM;
                         break;
 
+                case ARG_NO_MAN:
+                        arg_no_man = true;
+                        break;
+
                 case '?':
                         log_error("Unknown option %s.", argv[optind-1]);
                         return -EINVAL;

commit 8b835fccdad78d89f9cc64f9b02059fb75ffbab1
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jul 20 17:58:35 2014 -0400

    systemd-verify: a simple tool for offline unit verification
    
    This tool will warn about misspelt directives, unknown sections, and
    non-executable commands. It will also catch the common mistake of
    using Accept=yes with a non-template unit and vice versa.
    
    https://bugs.freedesktop.org/show_bug.cgi?id=56607

diff --git a/.gitignore b/.gitignore
index 41fff4f..81bcc16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -122,6 +122,7 @@
 /systemd-update-utmp
 /systemd-user-sessions
 /systemd-vconsole-setup
+/systemd-verify
 /tags
 /test-architecture
 /test-async
diff --git a/Makefile.am b/Makefile.am
index 91ecbe4..1e4cfb3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -350,7 +350,8 @@ bin_PROGRAMS = \
 	systemd-delta \
 	systemd-analyze \
 	systemd-run \
-	systemd-path
+	systemd-path \
+	systemd-verify
 
 dist_bin_SCRIPTS = \
 	src/kernel-install/kernel-install
@@ -1224,6 +1225,20 @@ CLEANFILES += \
 	src/core/org.freedesktop.systemd1.policy.in
 
 # ------------------------------------------------------------------------------
+
+systemd_verify_SOURCES = \
+	src/verify/verify.c
+
+systemd_verify_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(SECCOMP_CFLAGS)
+
+systemd_verify_LDADD = \
+	libsystemd-core.la \
+	$(RT_LIBS)
+
+# ------------------------------------------------------------------------------
+
 manual_tests += \
 	test-ns \
 	test-loopback \
diff --git a/src/core/socket.c b/src/core/socket.c
index c58a7f0..646887d 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -180,9 +180,8 @@ static int socket_arm_timer(Socket *s) {
                         socket_dispatch_timer, s);
 }
 
-static int socket_instantiate_service(Socket *s) {
-        _cleanup_free_ char *prefix = NULL;
-        _cleanup_free_ char *name = NULL;
+int socket_instantiate_service(Socket *s) {
+        _cleanup_free_ char *prefix = NULL, *name = NULL;
         int r;
         Unit *u;
 
@@ -193,11 +192,9 @@ static int socket_instantiate_service(Socket *s) {
          * here. For Accept=no this is mostly a NOP since the service
          * is figured out at load time anyway. */
 
-        if (UNIT_DEREF(s->service))
+        if (UNIT_DEREF(s->service) || !s->accept)
                 return 0;
 
-        assert(s->accept);
-
         prefix = unit_name_to_prefix(UNIT(s)->id);
         if (!prefix)
                 return -ENOMEM;
diff --git a/src/core/socket.h b/src/core/socket.h
index 39e00de..814a3bf 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -183,3 +183,5 @@ const char* socket_result_to_string(SocketResult i) _const_;
 SocketResult socket_result_from_string(const char *s) _pure_;
 
 const char* socket_port_type_to_string(SocketPort *p) _pure_;
+
+int socket_instantiate_service(Socket *s);
diff --git a/src/verify/Makefile b/src/verify/Makefile
new file mode 120000
index 0000000..94aaae2
--- /dev/null
+++ b/src/verify/Makefile
@@ -0,0 +1 @@
+../../Makefile
\ No newline at end of file
diff --git a/src/verify/verify.c b/src/verify/verify.c
new file mode 100644
index 0000000..fc513b1
--- /dev/null
+++ b/src/verify/verify.c
@@ -0,0 +1,320 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Zbigniew Jędrzejewski-Szmek
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <getopt.h>
+
+#include "manager.h"
+#include "bus-util.h"
+#include "log.h"
+#include "strv.h"
+#include "build.h"
+
+SystemdRunningAs arg_running_as = SYSTEMD_SYSTEM;
+
+static int generate_path(char **var, char **filenames) {
+        char **filename;
+
+        _cleanup_strv_free_ char **ans = NULL;
+        int r;
+
+        STRV_FOREACH(filename, filenames) {
+                char *t;
+
+                t = dirname_malloc(*filename);
+                if (!t)
+                        return -ENOMEM;
+
+                r = strv_consume(&ans, t);
+                if (r < 0)
+                        return r;
+        }
+
+        assert_se(strv_uniq(ans));
+
+        r = strv_extend(&ans, "");
+        if (r < 0)
+                return r;
+
+        *var = strv_join(ans, ":");
+        if (!*var)
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int verify_socket(Unit *u) {
+        int r;
+
+        assert(u);
+
+        if (u->type != UNIT_SOCKET)
+                return 0;
+
+        /* Cannot run this without the service being around */
+
+        /* This makes sure instance is created if necessary. */
+        r = socket_instantiate_service(SOCKET(u));
+        if (r < 0) {
+                log_error_unit(u->id, "Socket %s cannot be started, failed to create instance.",
+                               u->id);
+                return r;
+        }
+
+        /* This checks both type of sockets */
+        if (UNIT_ISSET(SOCKET(u)->service)) {
+                Service *service;
+
+                service = SERVICE(UNIT_DEREF(SOCKET(u)->service));
+                log_debug_unit(u->id, "%s uses %s", u->id, UNIT(service)->id);
+
+                if (UNIT(service)->load_state != UNIT_LOADED) {
+                        log_error_unit(u->id, "Service %s not loaded, %s cannot be started.",
+                                       UNIT(service)->id, u->id);
+                        return -ENOENT;
+                }
+        }
+
+        return 0;
+}
+
+static int verify_executable(Unit *u, ExecCommand *exec) {
+        if (exec == NULL)
+                return 0;
+
+        if (access(exec->path, X_OK) < 0) {
+                log_error_unit(u->id, "%s: command %s is not executable: %m",
+                               u->id, exec->path);
+                return -errno;
+        }
+
+        return 0;
+}
+
+static int verify_executables(Unit *u) {
+        ExecCommand *exec;
+        int r = 0, k;
+        unsigned i;
+
+        assert(u);
+
+        exec =  u->type == UNIT_SOCKET ? SOCKET(u)->control_command :
+                u->type == UNIT_MOUNT ? MOUNT(u)->control_command :
+                u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL;
+        k = verify_executable(u, exec);
+        if (k < 0 && r == 0)
+                r = k;
+
+        if (u->type == UNIT_SERVICE)
+                for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) {
+                        k = verify_executable(u, SERVICE(u)->exec_command[i]);
+                        if (k < 0 && r == 0)
+                                r = k;
+                }
+
+        if (u->type == UNIT_SOCKET)
+                for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) {
+                        k = verify_executable(u, SOCKET(u)->exec_command[i]);
+                        if (k < 0 && r == 0)
+                                r = k;
+                }
+
+        return r;
+}
+
+static int test_unit(Unit *u) {
+        _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
+        Job *j;
+        int r, k;
+
+        assert(u);
+
+        if (log_get_max_level() >= LOG_DEBUG)
+                unit_dump(u, stdout, "\t");
+
+        log_debug_unit(u->id, "Creating %s/start job", u->id);
+        r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, false, &err, &j);
+        if (sd_bus_error_is_set(&err))
+                log_error_unit(u->id, "Error: %s: %s",
+                               err.name, err.message);
+        if (r < 0)
+                log_error_unit(u->id, "Failed to create %s/start: %s",
+                               u->id, strerror(-r));
+
+        k = verify_socket(u);
+        if (k < 0 && r == 0)
+                r = k;
+
+        k = verify_executables(u);
+        if (k < 0 && r == 0)
+                r = k;
+
+        return r;
+}
+
+static int test_units(char **filenames) {
+        _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
+        Manager *m = NULL;
+        FILE *serial = NULL;
+        FDSet *fdset = NULL;
+
+        _cleanup_free_ char *var;
+
+        char **filename;
+        int r = 0, k;
+
+        Unit *units[strv_length(filenames)];
+        int i, count = 0;
+
+        /* set the path */
+        r = generate_path(&var, filenames);
+        if (r < 0) {
+                log_error("Failed to generate unit load path: %s", strerror(-r));
+                return r;
+        }
+
+        assert_se(set_unit_path(var) >= 0);
+
+        r = manager_new(arg_running_as, true, &m);
+        if (r < 0) {
+                log_error("Failed to initalize manager: %s", strerror(-r));
+                return r;
+        }
+
+        log_debug("Starting manager...");
+
+        r = manager_startup(m, serial, fdset);
+        if (r < 0) {
+                log_error("Failed to start manager: %s", strerror(-r));
+                goto finish;
+        }
+
+        manager_clear_jobs(m);
+
+        log_debug("Loading remaining units from the command line...");
+
+        STRV_FOREACH(filename, filenames) {
+                log_debug("Handling %s...", *filename);
+
+                k = manager_load_unit(m, NULL, *filename, &err, &units[count]);
+                if (k < 0) {
+                        log_error("Failed to load %s: %s", *filename, strerror(-r));
+                        if (r == 0)
+                                r = k;
+                }
+
+                count ++;
+        }
+
+        for (i = 0; i < count; i++) {
+                k = test_unit(units[i]);
+                if (k < 0 && r == 0)
+                        r = k;
+        }
+
+finish:
+        manager_free(m);
+
+        return r;
+}
+
+static void help(void) {
+        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+               "Check if unit files can be correctly loaded.\n\n"
+               "  -h --help           Show this help\n"
+               "     --version        Show package version\n"
+               "     --system         Connect to system manager\n"
+               "     --user           Connect to user service manager\n",
+               program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_USER,
+                ARG_SYSTEM,
+        };
+
+        static const struct option options[] = {
+                { "help",                no_argument,       NULL, 'h'                     },
+                { "version",             no_argument,       NULL, ARG_VERSION             },
+                { "user",                no_argument,       NULL, ARG_USER                },
+                { "system",              no_argument,       NULL, ARG_SYSTEM              },
+                {}
+        };
+
+        int c;
+
+        assert(argc >= 1);
+        assert(argv);
+
+        opterr = 0;
+
+        while ((c = getopt_long(argc, argv, ":h", options, NULL)) >= 0)
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+
+                case ARG_USER:
+                        arg_running_as = SYSTEMD_USER;
+                        break;
+
+                case ARG_SYSTEM:
+                        arg_running_as = SYSTEMD_SYSTEM;
+                        break;
+
+                case '?':
+                        log_error("Unknown option %s.", argv[optind-1]);
+                        return -EINVAL;
+
+                case ':':
+                        log_error("Missing argument to %s.", argv[optind-1]);
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached("Unhandled option code.");
+                }
+
+        return 1; /* work to do */
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                goto finish;
+
+        r = test_units(argv + optind);
+
+finish:
+        return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}

commit cf7d80a5fe549d4db11800015e02220dccec3096
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jul 20 17:56:57 2014 -0400

    path-lookup: make SYSTEMD_UNIT_PATH more flexible
    
    It can now contain more than one directory, and can be used
    to only prepend, not totally override, the normal load path.

diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 86a8cbb..dd3a4a7 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -275,8 +275,10 @@
                 (<option>--user</option>) and the variable
                 <varname>$SYSTEMD_UNIT_PATH</varname> is set, this
                 contents of this variable overrides the unit load
-                path.
-                </para>
+                path. If <varname>$SYSTEMD_UNIT_PATH</varname> ends
+                with an empty component (<literal>:</literal>), the
+                usual unit load path will be appended to the contents
+                of the variable.</para>
 
                 <table>
                   <title>
diff --git a/src/core/unit.c b/src/core/unit.c
index 0e4ebfd..b68796a 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2165,11 +2165,8 @@ int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDep
 }
 
 int set_unit_path(const char *p) {
-        _cleanup_free_ char *c = NULL;
-
         /* This is mostly for debug purposes */
-        c = path_make_absolute_cwd(p);
-        if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0)
+        if (setenv("SYSTEMD_UNIT_PATH", p, 0) < 0)
                 return -errno;
 
         return 0;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 46cfc12..7a715b7 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -187,6 +187,7 @@ int lookup_paths_init(
                 const char *generator_late) {
 
         const char *e;
+        bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
 
         assert(p);
 
@@ -194,69 +195,76 @@ int lookup_paths_init(
          * vars */
         e = getenv("SYSTEMD_UNIT_PATH");
         if (e) {
+                if (endswith(e, ":")) {
+                        e = strndupa(e, strlen(e) - 1);
+                        append = true;
+                }
+
+                /* FIXME: empty components in other places should be
+                 * rejected. */
+
                 p->unit_path = path_split_and_make_absolute(e);
                 if (!p->unit_path)
                         return -ENOMEM;
         } else
                 p->unit_path = NULL;
 
-        if (strv_isempty(p->unit_path)) {
-                /* Nothing is set, so let's figure something out. */
-                strv_free(p->unit_path);
+        if (!p->unit_path || append) {
+                /* Let's figure something out. */
+
+                char **unit_path;
+                int r;
 
                 /* For the user units we include share/ in the search
-                 * path in order to comply with the XDG basedir
-                 * spec. For the system stuff we avoid such
-                 * nonsense. OTOH we include /lib in the search path
-                 * for the system stuff but avoid it for user
-                 * stuff. */
+                 * path in order to comply with the XDG basedir spec.
+                 * For the system stuff we avoid such nonsense. OTOH
+                 * we include /lib in the search path for the system
+                 * stuff but avoid it for user stuff. */
 
                 if (running_as == SYSTEMD_USER) {
-
                         if (personal)
-                                p->unit_path = user_dirs(generator, generator_early, generator_late);
+                                unit_path = user_dirs(generator, generator_early, generator_late);
                         else
-                                p->unit_path = strv_new(
-                                                /* If you modify this you also want to modify
-                                                 * systemduserunitpath= in systemd.pc.in, and
-                                                 * the arrays in user_dirs() above! */
-                                                STRV_IFNOTNULL(generator_early),
-                                                USER_CONFIG_UNIT_PATH,
-                                                "/etc/systemd/user",
-                                                "/run/systemd/user",
-                                                STRV_IFNOTNULL(generator),
-                                                "/usr/local/lib/systemd/user",
-                                                "/usr/local/share/systemd/user",
-                                                USER_DATA_UNIT_PATH,
-                                                "/usr/lib/systemd/user",
-                                                "/usr/share/systemd/user",
-                                                STRV_IFNOTNULL(generator_late),
-                                                NULL);
-
-                        if (!p->unit_path)
-                                return -ENOMEM;
-
-                } else {
-                        p->unit_path = strv_new(
+                                unit_path = strv_new(
                                         /* If you modify this you also want to modify
-                                         * systemdsystemunitpath= in systemd.pc.in! */
+                                         * systemduserunitpath= in systemd.pc.in, and
+                                         * the arrays in user_dirs() above! */
                                         STRV_IFNOTNULL(generator_early),
-                                        SYSTEM_CONFIG_UNIT_PATH,
-                                        "/etc/systemd/system",
-                                        "/run/systemd/system",
+                                        USER_CONFIG_UNIT_PATH,
+                                        "/etc/systemd/user",
+                                        "/run/systemd/user",
                                         STRV_IFNOTNULL(generator),
-                                        "/usr/local/lib/systemd/system",
-                                        SYSTEM_DATA_UNIT_PATH,
-                                        "/usr/lib/systemd/system",
-#ifdef HAVE_SPLIT_USR
-                                        "/lib/systemd/system",
-#endif
+                                        "/usr/local/lib/systemd/user",
+                                        "/usr/local/share/systemd/user",
+                                        USER_DATA_UNIT_PATH,
+                                        "/usr/lib/systemd/user",
+                                        "/usr/share/systemd/user",
                                         STRV_IFNOTNULL(generator_late),
                                         NULL);
+                } else
+                        unit_path = strv_new(
+                                /* If you modify this you also want to modify
+                                 * systemdsystemunitpath= in systemd.pc.in! */
+                                STRV_IFNOTNULL(generator_early),
+                                SYSTEM_CONFIG_UNIT_PATH,
+                                "/etc/systemd/system",
+                                "/run/systemd/system",
+                                STRV_IFNOTNULL(generator),
+                                "/usr/local/lib/systemd/system",
+                                SYSTEM_DATA_UNIT_PATH,
+                                "/usr/lib/systemd/system",
+#ifdef HAVE_SPLIT_USR
+                                "/lib/systemd/system",
+#endif
+                                STRV_IFNOTNULL(generator_late),
+                                NULL);
 
-                        if (!p->unit_path)
-                                return -ENOMEM;
-                }
+                if (!unit_path)
+                        return -ENOMEM;
+
+                r = strv_extend_strv(&p->unit_path, unit_path);
+                if (r < 0)
+                        return r;
         }
 
         if (!path_strv_resolve_uniq(p->unit_path, root_dir))

commit 8328d8c63332319965fd288634a9a709e9b96425
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Fri Jul 18 17:50:04 2014 -0400

    test-cgroup-mask: fix masks in test and enable by default
    
    Commit 637f421e5c6a ("cgroups: always propagate controller membership
    to siblings") changed the mask propagation logic, but the test wasn't
    updated.
    
    Move to normal tests from manual tests, it should not touch the system
    anymore.

diff --git a/Makefile.am b/Makefile.am
index 5857863..91ecbe4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1230,7 +1230,6 @@ manual_tests += \
 	test-hostname \
 	test-daemon \
 	test-cgroup \
-	test-cgroup-mask \
 	test-install \
 	test-watchdog \
 	test-log \
@@ -1243,6 +1242,7 @@ endif
 
 tests += \
 	test-engine \
+	test-cgroup-mask \
 	test-job-type \
 	test-env-replace \
 	test-strbuf \
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index 5c82363..f98e1bb 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -82,16 +82,16 @@ static int test_cgroup_mask(void) {
         assert(unit_get_members_mask(root) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY));
 
         /* Verify aggregation of sibling masks. */
-        assert(unit_get_siblings_mask(son) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
-        assert(unit_get_siblings_mask(daughter) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
+        assert(unit_get_siblings_mask(son) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
+        assert(unit_get_siblings_mask(daughter) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
         assert(unit_get_siblings_mask(grandchild) == 0);
-        assert(unit_get_siblings_mask(parent_deep) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
-        assert(unit_get_siblings_mask(parent) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO) & m->cgroup_supported));
-        assert(unit_get_siblings_mask(root) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO) & m->cgroup_supported));
+        assert(unit_get_siblings_mask(parent_deep) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
+        assert(unit_get_siblings_mask(parent) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported));
+        assert(unit_get_siblings_mask(root) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported));
 
         /* Verify aggregation of target masks. */
-        assert(unit_get_target_mask(son) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
-        assert(unit_get_target_mask(daughter) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
+        assert(unit_get_target_mask(son) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
+        assert(unit_get_target_mask(daughter) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
         assert(unit_get_target_mask(grandchild) == 0);
         assert(unit_get_target_mask(parent_deep) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
         assert(unit_get_target_mask(parent) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported));

commit c2ef6f8427031e17dd2ea3fe232959687c77ea82
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Tue Feb 18 23:28:15 2014 -0500

    test-cgroup-mask: pass on kernels without memory controller
    
    It seems that unit_get_siblings_mask returns the controllers
    filtered by what is available, but get_members_mask and
    get_cgroup_mask do not. This just fixes the test following the
    symptoms.

diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index a230c97..5c82363 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -82,20 +82,20 @@ static int test_cgroup_mask(void) {
         assert(unit_get_members_mask(root) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY));
 
         /* Verify aggregation of sibling masks. */
-        assert(unit_get_siblings_mask(son) == (CGROUP_CPU | CGROUP_CPUACCT));
-        assert(unit_get_siblings_mask(daughter) == (CGROUP_CPU | CGROUP_CPUACCT));
+        assert(unit_get_siblings_mask(son) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
+        assert(unit_get_siblings_mask(daughter) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
         assert(unit_get_siblings_mask(grandchild) == 0);
-        assert(unit_get_siblings_mask(parent_deep) == (CGROUP_CPU | CGROUP_CPUACCT));
-        assert(unit_get_siblings_mask(parent) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO));
-        assert(unit_get_siblings_mask(root) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO));
+        assert(unit_get_siblings_mask(parent_deep) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
+        assert(unit_get_siblings_mask(parent) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO) & m->cgroup_supported));
+        assert(unit_get_siblings_mask(root) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO) & m->cgroup_supported));
 
         /* Verify aggregation of target masks. */
-        assert(unit_get_target_mask(son) == (CGROUP_CPU | CGROUP_CPUACCT));
-        assert(unit_get_target_mask(daughter) == (CGROUP_CPU | CGROUP_CPUACCT));
+        assert(unit_get_target_mask(son) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
+        assert(unit_get_target_mask(daughter) == ((CGROUP_CPU | CGROUP_CPUACCT) & m->cgroup_supported));
         assert(unit_get_target_mask(grandchild) == 0);
-        assert(unit_get_target_mask(parent_deep) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY));
-        assert(unit_get_target_mask(parent) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY));
-        assert(unit_get_target_mask(root) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY));
+        assert(unit_get_target_mask(parent_deep) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported));
+        assert(unit_get_target_mask(parent) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported));
+        assert(unit_get_target_mask(root) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported));
 
         manager_free(m);
 

commit 1cd974edfd7cd91dcdf321e7202bd220bac50a2d
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Fri Jul 18 17:05:18 2014 -0400

    systemd: return the first error from manager_startup()

diff --git a/src/core/manager.c b/src/core/manager.c
index 2e63c5e..0653d7e 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1005,11 +1005,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
         dual_timestamp_get(&m->units_load_finish_timestamp);
 
         /* Second, deserialize if there is something to deserialize */
-        if (serialization) {
-                q = manager_deserialize(m, serialization, fds);
-                if (q < 0)
-                        r = q;
-        }
+        if (serialization)
+                r = manager_deserialize(m, serialization, fds);
 
         /* Any fds left? Find some unit which wants them. This is
          * useful to allow container managers to pass some file
@@ -1017,13 +1014,15 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
          * socket-based activation of entire containers. */
         if (fdset_size(fds) > 0) {
                 q = manager_distribute_fds(m, fds);
-                if (q < 0)
+                if (q < 0 && r == 0)
                         r = q;
         }
 
         /* We might have deserialized the notify fd, but if we didn't
          * then let's create the bus now */
-        manager_setup_notify(m);
+        q = manager_setup_notify(m);
+        if (q < 0 && r == 0)
+                r = q;
 
         /* We might have deserialized the kdbus control fd, but if we
          * didn't, then let's create the bus now. */
@@ -1033,7 +1032,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
 
         /* Third, fire things up! */
         q = manager_coldplug(m);
-        if (q < 0)
+        if (q < 0 && r == 0)
                 r = q;
 
         if (serialization) {

commit 0d8c31ff7237149b505290652864b4e7e866b2a7
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Tue Jan 7 08:41:24 2014 -0500

    test-engine: fix access to unit load path
    
    Also add a bit of debugging output to help diagnose problems,
    add missing units, and simplify cppflags.
    
    Move test-engine to normal tests from manual tests, it should now
    work without destroying the system.

diff --git a/Makefile.am b/Makefile.am
index c6853e4..5857863 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -189,6 +189,7 @@ AM_CPPFLAGS = \
 	-DKEXEC=\"$(KEXEC)\" \
 	-DLIBDIR=\"$(libdir)\" \
 	-DROOTLIBDIR=\"$(rootlibdir)\" \
+	-DTEST_DIR=\"$(abs_top_srcdir)/test\" \
 	-I $(top_srcdir)/src \
 	-I $(top_builddir)/src/shared \
 	-I $(top_srcdir)/src/shared \
@@ -1224,7 +1225,6 @@ CLEANFILES += \
 
 # ------------------------------------------------------------------------------
 manual_tests += \
-	test-engine \
 	test-ns \
 	test-loopback \
 	test-hostname \
@@ -1242,6 +1242,7 @@ manual_tests += \
 endif
 
 tests += \
+	test-engine \
 	test-job-type \
 	test-env-replace \
 	test-strbuf \
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index cd67963..db8b4d7 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -832,7 +832,6 @@ pid_t unit_search_main_pid(Unit *u) {
 
 int manager_setup_cgroup(Manager *m) {
         _cleanup_free_ char *path = NULL;
-        char *e;
         int r;
 
         assert(m);
@@ -851,6 +850,8 @@ int manager_setup_cgroup(Manager *m) {
          * off. This is to support live upgrades from older systemd
          * versions where PID 1 was moved there. */
         if (m->running_as == SYSTEMD_SYSTEM) {
+                char *e;
+
                 e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
                 if (!e)
                         e = endswith(m->cgroup_root, "/system");
@@ -872,40 +873,42 @@ int manager_setup_cgroup(Manager *m) {
         }
 
         log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
+        if (!m->test_run) {
 
-        /* 3. Install agent */
-        if (m->running_as == SYSTEMD_SYSTEM) {
-                r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
-                if (r < 0)
-                        log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
-                else if (r > 0)
-                        log_debug("Installed release agent.");
-                else
-                        log_debug("Release agent already installed.");
-        }
+                /* 3. Install agent */
+                if (m->running_as == SYSTEMD_SYSTEM) {
+                        r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
+                        if (r < 0)
+                                log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
+                        else if (r > 0)
+                                log_debug("Installed release agent.");
+                        else
+                                log_debug("Release agent already installed.");
+                }
 
-        /* 4. Make sure we are in the root cgroup */
-        r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
-        if (r < 0) {
-                log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
-                return r;
-        }
+                /* 4. Make sure we are in the root cgroup */
+                r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
+                if (r < 0) {
+                        log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
+                        return r;
+                }
 
-        /* 5. And pin it, so that it cannot be unmounted */
-        safe_close(m->pin_cgroupfs_fd);
+                /* 5. And pin it, so that it cannot be unmounted */
+                safe_close(m->pin_cgroupfs_fd);
 
-        m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
-        if (m->pin_cgroupfs_fd < 0) {
-                log_error("Failed to open pin file: %m");
-                return -errno;
+                m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
+                if (m->pin_cgroupfs_fd < 0) {
+                        log_error("Failed to open pin file: %m");
+                        return -errno;
+                }
+
+                /* 6.  Always enable hierarchial support if it exists... */
+                cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
         }
 
-        /* 6. Figure out which controllers are supported */
+        /* 7. Figure out which controllers are supported */
         m->cgroup_supported = cg_mask_supported();
 
-        /* 7.  Always enable hierarchial support if it exists... */
-        cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
-
         return 0;
 }
 
diff --git a/src/core/main.c b/src/core/main.c
index ae38b43..29f9736 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1473,6 +1473,9 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
+        if (arg_action == ACTION_TEST)
+                skip_setup = true;
+
         pager_open_if_enabled();
 
         if (arg_action == ACTION_HELP) {
@@ -1543,7 +1546,8 @@ int main(int argc, char *argv[]) {
         if (arg_running_as == SYSTEMD_SYSTEM) {
                 const char *virtualization = NULL;
 
-                log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES ")");
+                log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")",
+                         arg_action == ACTION_TEST ? "test " : "" );
 
                 detect_virtualization(&virtualization);
                 if (virtualization)
@@ -1571,7 +1575,8 @@ int main(int argc, char *argv[]) {
                 _cleanup_free_ char *t;
 
                 t = uid_to_name(getuid());
-                log_debug(PACKAGE_STRING " running in user mode for user "UID_FMT"/%s. (" SYSTEMD_FEATURES ")", getuid(), strna(t));
+                log_debug(PACKAGE_STRING " running in %suser mode for user "UID_FMT"/%s. (" SYSTEMD_FEATURES ")",
+                          arg_action == ACTION_TEST ? " test" : "", getuid(), t);
         }
 
         if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) {
@@ -1636,7 +1641,7 @@ int main(int argc, char *argv[]) {
                 }
         }
 
-        r = manager_new(arg_running_as, &m);
+        r = manager_new(arg_running_as, arg_action == ACTION_TEST, &m);
         if (r < 0) {
                 log_error("Failed to allocate manager object: %s", strerror(-r));
                 goto finish;
diff --git a/src/core/manager.c b/src/core/manager.c
index 9d9a713..2e63c5e 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -241,6 +241,9 @@ static int manager_setup_time_change(Manager *m) {
         assert(m);
         assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
 
+        if (m->test_run)
+                return 0;
+
         /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
          * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */
 
@@ -302,6 +305,9 @@ static int manager_setup_signals(Manager *m) {
 
         assert(m);
 
+        if (m->test_run)
+                return 0;
+
         /* We are not interested in SIGSTOP and friends. */
         assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
 
@@ -409,7 +415,7 @@ static int manager_default_environment(Manager *m) {
         return 0;
 }
 
-int manager_new(SystemdRunningAs running_as, Manager **_m) {
+int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) {
         Manager *m;
         int r;
 
@@ -435,6 +441,8 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
         m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = -1;
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
 
+        m->test_run = test_run;
+
         r = manager_default_environment(m);
         if (r < 0)
                 goto fail;
@@ -514,6 +522,9 @@ fail:
 static int manager_setup_notify(Manager *m) {
         int r;
 
+        if (m->test_run)
+                return 0;
+
         if (m->notify_fd < 0) {
                 _cleanup_close_ int fd = -1;
                 union {
@@ -592,12 +603,10 @@ static int manager_setup_notify(Manager *m) {
 static int manager_setup_kdbus(Manager *m) {
 #ifdef ENABLE_KDBUS
         _cleanup_free_ char *p = NULL;
-#endif
 
-#ifdef ENABLE_KDBUS
         assert(m);
 
-        if (m->kdbus_fd >= 0)
+        if (m->test_run || m->kdbus_fd >= 0)
                 return 0;
 
         m->kdbus_fd = bus_kernel_create_bus(m->running_as == SYSTEMD_SYSTEM ? "system" : "user", m->running_as == SYSTEMD_SYSTEM, &p);
@@ -624,6 +633,9 @@ static int manager_connect_bus(Manager *m, bool reexecuting) {
 
         assert(m);
 
+        if (m->test_run)
+                return 0;
+
         try_bus_connect =
                 m->kdbus_fd >= 0 ||
                 reexecuting ||
@@ -2636,6 +2648,9 @@ void manager_run_generators(Manager *m) {
 
         assert(m);
 
+        if (m->test_run)
+                return;
+
         generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
         d = opendir(generator_path);
         if (!d) {
diff --git a/src/core/manager.h b/src/core/manager.h
index 718c29f..aa6c76f 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -230,6 +230,8 @@ struct Manager {
         bool taint_usr:1;
         bool first_boot:1;
 
+        bool test_run:1;
+
         ShowStatus show_status;
         bool confirm_spawn;
         bool no_console_output;
@@ -276,7 +278,7 @@ struct Manager {
         int kdbus_fd;
 };
 
-int manager_new(SystemdRunningAs running_as, Manager **m);
+int manager_new(SystemdRunningAs running_as, bool test_run, Manager **m);
 void manager_free(Manager *m);
 
 int manager_enumerate(Manager *m);
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index 4d60859..a230c97 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -37,11 +37,10 @@ static int test_cgroup_mask(void) {
         FILE *serial = NULL;
         FDSet *fdset = NULL;
         int r;
-        const char *dir = TEST_DIR;
 
         /* Prepare the manager. */
-        assert_se(set_unit_path(dir) >= 0);
-        r = manager_new(SYSTEMD_USER, &m);
+        assert_se(set_unit_path(TEST_DIR) >= 0);
+        r = manager_new(SYSTEMD_USER, true, &m);
         if (r == -EPERM || r == -EACCES) {
                 puts("manager_new: Permission denied. Skipping test.");
                 return EXIT_TEST_SKIP;
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index 0f38622..6e39a58 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -25,15 +25,26 @@
 #include <unistd.h>
 
 #include "manager.h"
+#include "bus-util.h"
 
 int main(int argc, char *argv[]) {
+        _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
         Manager *m = NULL;
         Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL;
+        FILE *serial = NULL;
+        FDSet *fdset = NULL;
         Job *j;
-
-        assert_se(set_unit_path("test") >= 0);
-
-        assert_se(manager_new(SYSTEMD_SYSTEM, &m) >= 0);
+        int r;
+
+        /* prepare the test */
+        assert_se(set_unit_path(TEST_DIR) >= 0);
+        r = manager_new(SYSTEMD_USER, true, &m);
+        if (r == -EPERM || r == -EACCES || r == -EADDRINUSE || r == -EHOSTDOWN) {
+                printf("Skipping test: manager_new: %s", strerror(-r));
+                return EXIT_TEST_SKIP;
+        }
+        assert(r >= 0);
+        assert_se(manager_startup(m, serial, fdset) >= 0);
 
         printf("Load1:\n");
         assert_se(manager_load_unit(m, "a.service", NULL, NULL, &a) >= 0);
@@ -42,7 +53,10 @@ int main(int argc, char *argv[]) {
         manager_dump_units(m, stdout, "\t");
 
         printf("Test1: (Trivial)\n");
-        assert_se(manager_add_job(m, JOB_START, c, JOB_REPLACE, false, NULL, &j) == 0);
+        r = manager_add_job(m, JOB_START, c, JOB_REPLACE, false, &err, &j);
+        if (sd_bus_error_is_set(&err))
+                log_error("error: %s: %s", err.name, err.message);
+        assert(r == 0);
         manager_dump_jobs(m, stdout, "\t");
 
         printf("Load2:\n");
diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c
index e984ee4..8fdca0e 100644
--- a/src/test/test-sched-prio.c
+++ b/src/test/test-sched-prio.c
@@ -31,11 +31,10 @@ int main(int argc, char *argv[]) {
         FILE *serial = NULL;
         FDSet *fdset = NULL;
         int r;
-        const char *dir = TEST_DIR;
 
         /* prepare the test */
-        assert_se(set_unit_path(dir) >= 0);
-        r = manager_new(SYSTEMD_USER, &m);
+        assert_se(set_unit_path(TEST_DIR) >= 0);
+        r = manager_new(SYSTEMD_USER, true, &m);
         if (r == -EPERM || r == -EACCES || r == -EADDRINUSE || r == -EHOSTDOWN) {
                 printf("Skipping test: manager_new: %s", strerror(-r));
                 return EXIT_TEST_SKIP;
diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c
index db728bc..b733173 100644
--- a/src/test/test-unit-name.c
+++ b/src/test/test-unit-name.c
@@ -125,7 +125,7 @@ static int test_unit_printf(void) {
         assert_se((root = getpwnam("root")));
         assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0);
 
-        r = manager_new(SYSTEMD_USER, &m);
+        r = manager_new(SYSTEMD_USER, true, &m);
         if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) {
                 puts("manager_new: Permission denied. Skipping test.");
                 return EXIT_TEST_SKIP;
diff --git a/test/basic.target b/test/basic.target
new file mode 120000
index 0000000..0612934
--- /dev/null
+++ b/test/basic.target
@@ -0,0 +1 @@
+../units/basic.target
\ No newline at end of file
diff --git a/test/paths.target b/test/paths.target
new file mode 120000
index 0000000..e9939c9
--- /dev/null
+++ b/test/paths.target
@@ -0,0 +1 @@
+../units/paths.target
\ No newline at end of file
diff --git a/test/shutdown.target b/test/shutdown.target
new file mode 120000
index 0000000..1a3c2ee
--- /dev/null
+++ b/test/shutdown.target
@@ -0,0 +1 @@
+../units/shutdown.target
\ No newline at end of file
diff --git a/test/sockets.target b/test/sockets.target
new file mode 120000
index 0000000..8ff86a0
--- /dev/null
+++ b/test/sockets.target
@@ -0,0 +1 @@
+../units/sockets.target
\ No newline at end of file
diff --git a/test/sysinit.target b/test/sysinit.target
new file mode 120000
index 0000000..3301338
--- /dev/null
+++ b/test/sysinit.target
@@ -0,0 +1 @@
+../units/sysinit.target
\ No newline at end of file
diff --git a/test/timers.target b/test/timers.target
new file mode 120000
index 0000000..576d47f
--- /dev/null
+++ b/test/timers.target
@@ -0,0 +1 @@
+../units/timers.target
\ No newline at end of file

commit f0ea29eaeb3449822bfbdfa839b00e323dfc523e
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Sun Jul 20 19:47:42 2014 -0400

    Add quotes to warning message
    
    The message for SYSTEMD_LOG_LEVEL= looked a bit strange.

diff --git a/src/shared/log.c b/src/shared/log.c
index 9039db3..3941e3e 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -884,19 +884,19 @@ void log_parse_environment(void) {
 
         e = secure_getenv("SYSTEMD_LOG_TARGET");
         if (e && log_set_target_from_string(e) < 0)
-                log_warning("Failed to parse log target %s. Ignoring.", e);
+                log_warning("Failed to parse log target '%s'. Ignoring.", e);
 
         e = secure_getenv("SYSTEMD_LOG_LEVEL");
         if (e && log_set_max_level_from_string(e) < 0)
-                log_warning("Failed to parse log level %s. Ignoring.", e);
+                log_warning("Failed to parse log level '%s'. Ignoring.", e);
 
         e = secure_getenv("SYSTEMD_LOG_COLOR");
         if (e && log_show_color_from_string(e) < 0)
-                log_warning("Failed to parse bool %s. Ignoring.", e);
+                log_warning("Failed to parse bool '%s'. Ignoring.", e);
 
         e = secure_getenv("SYSTEMD_LOG_LOCATION");
         if (e && log_show_location_from_string(e) < 0)
-                log_warning("Failed to parse bool %s. Ignoring.", e);
+                log_warning("Failed to parse bool '%s'. Ignoring.", e);
 }
 
 LogTarget log_get_target(void) {

commit b87c2aa6bf1247c298c9bd9f56b9b56a87836b2d
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Tue Jan 7 00:00:05 2014 -0500

    systemd: use pager for --test and --help

diff --git a/src/core/main.c b/src/core/main.c
index f9ee297..ae38b43 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -50,6 +50,7 @@
 #include "conf-parser.h"
 #include "missing.h"
 #include "label.h"
+#include "pager.h"
 #include "build.h"
 #include "strv.h"
 #include "def.h"
@@ -94,6 +95,7 @@ static int arg_crash_chvt = -1;
 static bool arg_confirm_spawn = false;
 static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
 static bool arg_switched_root = false;
+static int arg_no_pager = -1;
 static char ***arg_join_controllers = NULL;
 static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL;
 static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
@@ -117,6 +119,14 @@ static bool arg_default_memory_accounting = false;
 
 static void nop_handler(int sig) {}
 
+static void pager_open_if_enabled(void) {
+
+        if (arg_no_pager <= 0)
+                return;
+
+        pager_open(false);
+}
+
 noreturn static void crash(int sig) {
 
         if (getpid() != 1)
@@ -704,6 +714,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SYSTEM,
                 ARG_USER,
                 ARG_TEST,
+                ARG_NO_PAGER,
                 ARG_VERSION,
                 ARG_DUMP_CONFIGURATION_ITEMS,
                 ARG_DUMP_CORE,
@@ -725,6 +736,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "system",                   no_argument,       NULL, ARG_SYSTEM                   },
                 { "user",                     no_argument,       NULL, ARG_USER                     },
                 { "test",                     no_argument,       NULL, ARG_TEST                     },
+                { "no-pager",                 no_argument,       NULL, ARG_NO_PAGER                 },
                 { "help",                     no_argument,       NULL, 'h'                          },
                 { "version",                  no_argument,       NULL, ARG_VERSION                  },
                 { "dump-configuration-items", no_argument,       NULL, ARG_DUMP_CONFIGURATION_ITEMS },
@@ -832,6 +844,12 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case ARG_TEST:
                         arg_action = ACTION_TEST;
+                        if (arg_no_pager < 0)
+                                arg_no_pager = true;
+                        break;
+
+                case ARG_NO_PAGER:
+                        arg_no_pager = true;
                         break;
 
                 case ARG_VERSION:
@@ -912,6 +930,8 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case 'h':
                         arg_action = ACTION_HELP;
+                        if (arg_no_pager < 0)
+                                arg_no_pager = true;
                         break;
 
                 case 'D':
@@ -984,6 +1004,7 @@ static int help(void) {
                "Starts up and maintains the system or user services.\n\n"
                "  -h --help                      Show this help\n"
                "     --test                      Determine startup sequence, dump it and exit\n"
+               "     --no-pager                  Do not pipe output into a pager\n"
                "     --dump-configuration-items  Dump understood unit configuration items\n"
                "     --unit=UNIT                 Set default unit\n"
                "     --system                    Run a system instance, even if PID != 1\n"
@@ -1452,6 +1473,8 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
+        pager_open_if_enabled();
+
         if (arg_action == ACTION_HELP) {
                 retval = help();
                 goto finish;
@@ -1798,6 +1821,8 @@ int main(int argc, char *argv[]) {
         }
 
 finish:
+        pager_close();
+
         if (m) {
                 manager_free(m);
                 m = NULL;



More information about the systemd-commits mailing list