chiark / gitweb /
systemctl: hook up new install logic
authorLennart Poettering <lennart@poettering.net>
Mon, 25 Jul 2011 02:58:02 +0000 (04:58 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 25 Jul 2011 02:58:02 +0000 (04:58 +0200)
This adds a number of new options to systemctl, for presets, reenabling,
masking/unmask, and runtime operations.

Makefile.am
TODO
man/systemctl.xml
src/dbus-manager.c
src/hashmap.c
src/install.c
src/org.freedesktop.systemd1.conf
src/pager.c
src/spawn-agent.c [new file with mode: 0644]
src/spawn-agent.h [new file with mode: 0644]
src/systemctl.c

index 78c76d7a6b6335b69a90a392f111a1f1eee31c2e..2ebf53ce867f586fa146dd4dd83ad37168023949 100644 (file)
@@ -1203,7 +1203,9 @@ systemctl_SOURCES = \
        src/cgroup-util.c \
        src/exit-status.c \
        src/unit-name.c \
        src/cgroup-util.c \
        src/exit-status.c \
        src/unit-name.c \
-        src/pager.c
+        src/pager.c \
+        src/install.c \
+        src/spawn-agent.c
 
 systemctl_CFLAGS = \
        $(AM_CFLAGS) \
 
 systemctl_CFLAGS = \
        $(AM_CFLAGS) \
diff --git a/TODO b/TODO
index 82bc9950a6e8277d0f6d23f179e76594c2d3e380..feac225cb5983aca8f59d4bdf92f4e713fe77596 100644 (file)
--- a/TODO
+++ b/TODO
@@ -47,8 +47,6 @@ Features:
 
 * make sure systemd-ask-password-wall does not shutdown systemd-ask-password-console too early
 
 
 * make sure systemd-ask-password-wall does not shutdown systemd-ask-password-console too early
 
-* support presets
-
 * kernel: add /proc/sys file exposing CAP_LAST_CAP?
 
 * kernel: add device_type = "fb", "fbcon" to class "graphics"
 * kernel: add /proc/sys file exposing CAP_LAST_CAP?
 
 * kernel: add device_type = "fb", "fbcon" to class "graphics"
@@ -86,17 +84,6 @@ Features:
 
 * show enablement status in systemctl status
 
 
 * show enablement status in systemctl status
 
-* teach systemctl to enable unit files in arbitrary directories
-
-* In systemctl make sure both is-enabled and is-active print a string, or neither.
-
-* Implement:
-    systemctl mask <unit>
-    systemctl unmask <unit>
-  Also support --temp to make this temporary by placing mask links in /run.
-
-* perhaps add "systemctl reenable" as combination of "systemctl disable" and "systemctl enable"
-
 * add support for /bin/mount -s
 
 * GC unreferenced jobs (such as .device jobs)
 * add support for /bin/mount -s
 
 * GC unreferenced jobs (such as .device jobs)
index b60731ad569c3bed4c2569d5ef22a30a2bb57399..468141c2bde61a677a589fd34f7021e8752a45ca 100644 (file)
                         </varlistentry>
 
                         <varlistentry>
                         </varlistentry>
 
                         <varlistentry>
-                                <term><option>--defaults</option></term>
+                                <term><option>--root=</option></term>
 
                                 <listitem><para>When used with
 
                                 <listitem><para>When used with
-                                <command>disable</command>, ensures
-                                that only the symlinks created by
-                                <command>enable</command> are removed,
-                                not all symlinks pointing to the unit
-                                file that shall be
-                                disabled.</para></listitem>
+                                <command>enable</command>/<command>disable</command>/<command>is-enabled</command> (and
+                                related commands), use alternative
+                                root path when looking for unit
+                                files.</para></listitem>
                         </varlistentry>
 
                         <varlistentry>
                         </varlistentry>
 
                         <varlistentry>
-                                <term><option>--root=</option></term>
+                                <term><option>--runtime</option></term>
 
                                 <listitem><para>When used with
 
                                 <listitem><para>When used with
-                               <command>enable</command>/
-                               <command>disable</command>/
-                               <command>is-enabled</command>,
-                               use alternative root path for systemd
-                               install.</para></listitem>
+                                <command>enable</command>/<command>disable</command>/<command>is-enabled</command> (and related commands), make
+                                changes only temporarily, so that they
+                                are dropped on the next reboot. This
+                                will have the effect that changes are
+                                not made in subdirectories of
+                                <filename>/etc</filename> but in
+                                <filename>/run</filename>, with
+                                identical immediate effects, however,
+                                since the latter is lost on reboot,
+                                the changes are lost
+                                too.</para></listitem>
                         </varlistentry>
 
                         <varlistentry>
                         </varlistentry>
 
                         <varlistentry>
                                 configuration directory, and hence
                                 undoes the changes made by
                                 <command>enable</command>. Note
                                 configuration directory, and hence
                                 undoes the changes made by
                                 <command>enable</command>. Note
-                                however that this by default removes
+                                however that this removes
                                 all symlinks to the unit files
                                 (i.e. including manual additions), not
                                 just those actually created by
                                 all symlinks to the unit files
                                 (i.e. including manual additions), not
                                 just those actually created by
-                                <command>enable</command>. If only the
-                                symlinks that are suggested by default
-                                shall be removed, pass
-                                <option>--defaults</option>. This
+                                <command>enable</command>. This call
                                 implicitly reloads the systemd daemon
                                 configuration after completing the
                                 disabling of the units. Note that this
                                 implicitly reloads the systemd daemon
                                 configuration after completing the
                                 disabling of the units. Note that this
                                 (as with
                                 <command>enable</command>). Returns an
                                 exit code of 0 if at least one is
                                 (as with
                                 <command>enable</command>). Returns an
                                 exit code of 0 if at least one is
-                                enabled, non-zero
-                                otherwise.</para></listitem>
+                                enabled, non-zero otherwise. Prints
+                                the current enable status. To suppress
+                                this output use
+                                <option>--quiet</option>.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>reenable [NAME...]</command></term>
+
+                                <listitem><para>Reenable one or more
+                                unit files, as specified on the
+                                command line. This is a combination of
+                                <command>disable</command> and
+                                <command>enable</command> and is
+                                useful to reset the symlinks a unit is
+                                enabled with to the defaults
+                                configured in the
+                                <literal>[Install]</literal> section
+                                of the unit file.</para>
+                                </listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>preset [NAME...]</command></term>
+
+                                <listitem><para>Reset one or more unit
+                                files, as specified on the command
+                                line, to the defaults configured in a
+                                preset file. This has the same effect
+                                as <command>disable</command> or
+                                <command>enable</command>, depending
+                                how the unit is listed in the preset
+                                files.</para>
+                                </listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>mask [NAME...]</command></term>
+
+                                <listitem><para>Mask one or more unit
+                                files, as specified on the command
+                                line. This will link these units to
+                                <filename>/dev/null</filename>, making
+                                it impossible to start them. This is a stronger version
+                                of <command>disable</command>, since
+                                it prohibits all kinds of activation
+                                of the unit, including manual
+                                activation. Use this option with
+                                care.</para>
+                                </listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>unmask [NAME...]</command></term>
+
+                                <listitem><para>Unmask one or more
+                                unit files, as specified on the
+                                command line. This will undo the
+                                effect of
+                                <command>mask</command>.</para>
+                                </listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><command>link [NAME...]</command></term>
+
+                                <listitem><para>Link a unit file that
+                                is not in the unit file search paths
+                                into the unit file search path. This
+                                requires an absolute path to a unit
+                                file. The effect of this can be undone
+                                with <command>disable</command>. The
+                                effect of this command is that a unit
+                                file is available for
+                                <command>start</command> and other
+                                commands although it isn't installed
+                                directly in the unit search
+                                path.</para>
+                                </listitem>
                         </varlistentry>
 
                         <varlistentry>
                         </varlistentry>
 
                         <varlistentry>
index 582de4bd42e0650e5d0c00dda08fc5b68fb043df..66cf9ee67a37e77c1dce99e1a4d2c90170e8a4ea 100644 (file)
         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
+        "   <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \
         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"DisableUnitFiles\">\n"                        \
         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"DisableUnitFiles\">\n"                        \
         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
+        "   <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \
         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"LinkUnitFiles\">\n"                           \
         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"LinkUnitFiles\">\n"                           \
         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
+        "   <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \
         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"MaskUnitFiles\">\n"                           \
         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"MaskUnitFiles\">\n"                           \
@@ -422,7 +425,12 @@ static const char *message_get_sender_with_fallback(DBusMessage *m) {
         return ":no-sender";
 }
 
         return ":no-sender";
 }
 
-static DBusMessage *message_from_file_changes(DBusMessage *m, UnitFileChange *changes, unsigned n_changes) {
+static DBusMessage *message_from_file_changes(
+                DBusMessage *m,
+                UnitFileChange *changes,
+                unsigned n_changes,
+                int carries_install_info) {
+
         DBusMessageIter iter, sub, sub2;
         DBusMessage *reply;
         unsigned i;
         DBusMessageIter iter, sub, sub2;
         DBusMessage *reply;
         unsigned i;
@@ -435,6 +443,14 @@ static DBusMessage *message_from_file_changes(DBusMessage *m, UnitFileChange *ch
 
         dbus_message_iter_init_append(reply, &iter);
 
 
         dbus_message_iter_init_append(reply, &iter);
 
+        if (carries_install_info >= 0) {
+                dbus_bool_t b;
+
+                b = carries_install_info;
+                if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b))
+                        goto oom;
+        }
+
         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub))
                 goto oom;
 
         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub))
                 goto oom;
 
@@ -446,9 +462,9 @@ static DBusMessage *message_from_file_changes(DBusMessage *m, UnitFileChange *ch
                 source = strempty(changes[i].source);
 
                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
                 source = strempty(changes[i].source);
 
                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
-                    !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &type) ||
-                    !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &path) ||
-                    !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &source) ||
+                    !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
+                    !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &path) ||
+                    !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &source) ||
                     !dbus_message_iter_close_container(&sub, &sub2))
                         goto oom;
         }
                     !dbus_message_iter_close_container(&sub, &sub2))
                         goto oom;
         }
@@ -1304,6 +1320,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                 UnitFileChange *changes = NULL;
                 unsigned n_changes = 0;
                 dbus_bool_t runtime, force;
                 UnitFileChange *changes = NULL;
                 unsigned n_changes = 0;
                 dbus_bool_t runtime, force;
+                bool carries_install_info = -1;
 
                 if (!dbus_message_iter_init(message, &iter))
                         goto oom;
 
                 if (!dbus_message_iter_init(message, &iter))
                         goto oom;
@@ -1323,15 +1340,18 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                         return bus_send_error_reply(connection, message, NULL, -EIO);
                 }
 
                         return bus_send_error_reply(connection, message, NULL, -EIO);
                 }
 
-                if (streq(member, "EnableUnitFiles"))
+                if (streq(member, "EnableUnitFiles")) {
                         r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
                         r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
-                else if (streq(member, "ReenableUnitFiles"))
+                        carries_install_info = r;
+                } else if (streq(member, "ReenableUnitFiles")) {
                         r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
                         r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
-                else if (streq(member, "LinkUnitFiles"))
+                        carries_install_info = r;
+                } else if (streq(member, "LinkUnitFiles"))
                         r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
                         r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
-                else if (streq(member, "PresetUnitFiles"))
+                else if (streq(member, "PresetUnitFiles")) {
                         r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
                         r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
-                else if (streq(member, "MaskUnitFiles"))
+                        carries_install_info = r;
+                } else if (streq(member, "MaskUnitFiles"))
                         r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
                 else
                         assert_not_reached("Uh? Wrong method");
                         r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
                 else
                         assert_not_reached("Uh? Wrong method");
@@ -1344,7 +1364,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                         return bus_send_error_reply(connection, message, NULL, r);
                 }
 
                         return bus_send_error_reply(connection, message, NULL, r);
                 }
 
-                reply = message_from_file_changes(message, changes, n_changes);
+                reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
                 unit_file_changes_free(changes, n_changes);
 
                 if (!reply)
                 unit_file_changes_free(changes, n_changes);
 
                 if (!reply)
@@ -1392,7 +1412,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                         return bus_send_error_reply(connection, message, NULL, r);
                 }
 
                         return bus_send_error_reply(connection, message, NULL, r);
                 }
 
-                reply = message_from_file_changes(message, changes, n_changes);
+                reply = message_from_file_changes(message, changes, n_changes, -1);
                 unit_file_changes_free(changes, n_changes);
 
                 if (!reply)
                 unit_file_changes_free(changes, n_changes);
 
                 if (!reply)
index 53502576ad8b9773d08a528853ce7c27948c865c..ca83e9338552626db3f7d7770a10110cbaacc510 100644 (file)
@@ -596,16 +596,16 @@ Hashmap *hashmap_copy(Hashmap *h) {
 char **hashmap_get_strv(Hashmap *h) {
         char **sv;
         Iterator it;
 char **hashmap_get_strv(Hashmap *h) {
         char **sv;
         Iterator it;
-        char *path;
+        char *item;
         int n;
 
         int n;
 
-        sv = malloc((h->n_entries+1) * sizeof(char *));
-        if (sv == NULL)
+        sv = new(char*, h->n_entries+1);
+        if (!sv)
                 return NULL;
 
         n = 0;
                 return NULL;
 
         n = 0;
-        HASHMAP_FOREACH(path, h, it)
-                sv[n++] = path;
+        HASHMAP_FOREACH(item, h, it)
+                sv[n++] = item;
         sv[n] = NULL;
 
         return sv;
         sv[n] = NULL;
 
         return sv;
index b37bbbdc5acd64bbf1faf62516178098f7468a03..b843ee156b5e9843f1a34023339170b00b1d7a33 100644 (file)
@@ -126,8 +126,6 @@ static int add_file_change(
         UnitFileChange *c;
         unsigned i;
 
         UnitFileChange *c;
         unsigned i;
 
-        assert(type >= 0);
-        assert(type < _UNIT_FILE_CHANGE_TYPE_MAX);
         assert(path);
         assert(!changes == !n_changes);
 
         assert(path);
         assert(!changes == !n_changes);
 
@@ -1414,6 +1412,10 @@ int unit_file_enable(
                         goto finish;
         }
 
                         goto finish;
         }
 
+        /* This will return the number of symlink rules that were
+        supposed to be created, not the ones actually created. This is
+        useful to determine whether the passed files hat any
+        installation data at all. */
         r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
 
 finish:
         r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
 
 finish:
@@ -1514,6 +1516,7 @@ int unit_file_reenable(
 
         r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
 
 
         r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
 
+        /* Returns number of symlinks that where supposed to be installed. */
         q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
         if (r == 0)
                 r = q;
         q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
         if (r == 0)
                 r = q;
@@ -1763,6 +1766,7 @@ int unit_file_preset(
         if (r == 0)
                 r = q;
 
         if (r == 0)
                 r = q;
 
+        /* Returns number of symlinks that where supposed to be installed. */
         q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
         if (r == 0)
                 r = q;
         q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
         if (r == 0)
                 r = q;
index 8008f0f4bab5eaf3e35fdf3d4a387f40ed9fa1b3..201afe65bef5a320ec18b2b9f91b404bf4f8bc4b 100644 (file)
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="ListUnits"/>
 
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="ListUnits"/>
 
+                <allow send_destination="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
+                       send_member="ListUnitFiles"/>
+
+                <allow send_destination="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
+                       send_member="GetUnitFileState"/>
+
                 <allow send_destination="org.freedesktop.systemd1"
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="ListJobs"/>
                 <allow send_destination="org.freedesktop.systemd1"
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="ListJobs"/>
index 6ea25ada65f93f2d6ee2123eedd012edb34dee84..be284da9622b7a66776fc168928553748d325c7c 100644 (file)
@@ -39,13 +39,13 @@ void pager_open(void) {
         if (pager_pid > 0)
                 return;
 
         if (pager_pid > 0)
                 return;
 
-        if (isatty(STDOUT_FILENO) <= 0)
-                return;
-
         if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER")))
                 if (!*pager || streq(pager, "cat"))
                         return;
 
         if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER")))
                 if (!*pager || streq(pager, "cat"))
                         return;
 
+        if (isatty(STDOUT_FILENO) <= 0)
+                return;
+
         /* Determine and cache number of columns before we spawn the
          * pager so that we get the value from the actual tty */
         columns();
         /* Determine and cache number of columns before we spawn the
          * pager so that we get the value from the actual tty */
         columns();
diff --git a/src/spawn-agent.c b/src/spawn-agent.c
new file mode 100644 (file)
index 0000000..2de2530
--- /dev/null
@@ -0,0 +1,120 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "log.h"
+#include "util.h"
+#include "spawn-agent.h"
+
+static pid_t agent_pid = 0;
+
+void agent_open(void) {
+        pid_t parent_pid;
+
+        if (agent_pid > 0)
+                return;
+
+        /* We check STDIN here, not STDOUT, since this is about input,
+         * not output */
+        if (!isatty(STDIN_FILENO))
+                return;
+
+        parent_pid = getpid();
+
+        /* Spawns a temporary TTY agent, making sure it goes away when
+         * we go away */
+
+        agent_pid = fork();
+        if (agent_pid < 0) {
+                log_error("Failed to fork agent: %m");
+                return;
+        }
+
+        if (agent_pid == 0) {
+                /* In the child */
+
+                int fd;
+                bool stdout_is_tty, stderr_is_tty;
+
+                /* Make sure the agent goes away when the parent dies */
+                if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+                        _exit(EXIT_FAILURE);
+
+                /* Check whether our parent died before we were able
+                 * to set the death signal */
+                if (getppid() != parent_pid)
+                        _exit(EXIT_SUCCESS);
+
+                /* Don't leak fds to the agent */
+                close_all_fds(NULL, 0);
+
+                stdout_is_tty = isatty(STDOUT_FILENO);
+                stderr_is_tty = isatty(STDERR_FILENO);
+
+                if (!stdout_is_tty || !stderr_is_tty) {
+                        /* Detach from stdout/stderr. and reopen
+                         * /dev/tty for them. This is important to
+                         * ensure that when systemctl is started via
+                         * popen() or a similar call that expects to
+                         * read EOF we actually do generate EOF and
+                         * not delay this indefinitely by because we
+                         * keep an unused copy of stdin around. */
+                        fd = open("/dev/tty", O_WRONLY);
+                        if (fd < 0) {
+                                log_error("Failed to open /dev/tty: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        if (!stdout_is_tty)
+                                dup2(fd, STDOUT_FILENO);
+
+                        if (!stderr_is_tty)
+                                dup2(fd, STDERR_FILENO);
+
+                        if (fd > 2)
+                                close(fd);
+                }
+
+                execl(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
+
+                log_error("Unable to execute agent: %m");
+                _exit(EXIT_FAILURE);
+        }
+}
+
+void agent_close(void) {
+
+        if (agent_pid <= 0)
+                return;
+
+        /* Inform agent that we are done */
+        kill(agent_pid, SIGTERM);
+        kill(agent_pid, SIGCONT);
+        wait_for_terminate(agent_pid, NULL);
+        agent_pid = 0;
+}
diff --git a/src/spawn-agent.h b/src/spawn-agent.h
new file mode 100644 (file)
index 0000000..fd0a910
--- /dev/null
@@ -0,0 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foospawnagenthfoo
+#define foospawnagenthfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+void agent_open(void);
+void agent_close(void);
+
+#endif
index 9f5ba8a202e9c73b62e12f2d16e1f79edca6f0c1..620b91ff639460e06a8cabfd53007bca3e1bde4f 100644 (file)
 #include "build.h"
 #include "unit-name.h"
 #include "pager.h"
 #include "build.h"
 #include "unit-name.h"
 #include "pager.h"
+#include "spawn-agent.h"
+#include "install.h"
 
 static const char *arg_type = NULL;
 static char **arg_property = NULL;
 static bool arg_all = false;
 static const char *arg_job_mode = "replace";
 
 static const char *arg_type = NULL;
 static char **arg_property = NULL;
 static bool arg_all = false;
 static const char *arg_job_mode = "replace";
-static bool arg_user = false;
-static bool arg_global = false;
+static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
 static bool arg_immediate = false;
 static bool arg_no_block = false;
 static bool arg_no_pager = false;
 static bool arg_immediate = false;
 static bool arg_no_block = false;
 static bool arg_no_pager = false;
@@ -74,9 +75,9 @@ static bool arg_dry = false;
 static bool arg_quiet = false;
 static bool arg_full = false;
 static bool arg_force = false;
 static bool arg_quiet = false;
 static bool arg_full = false;
 static bool arg_force = false;
-static bool arg_defaults = false;
 static bool arg_ask_password = false;
 static bool arg_failed = false;
 static bool arg_ask_password = false;
 static bool arg_failed = false;
+static bool arg_runtime = false;
 static char **arg_wall = NULL;
 static const char *arg_kill_who = NULL;
 static const char *arg_kill_mode = NULL;
 static char **arg_wall = NULL;
 static const char *arg_kill_who = NULL;
 static const char *arg_kill_mode = NULL;
@@ -118,9 +119,7 @@ static const char *arg_host = NULL;
 
 static bool private_bus = false;
 
 
 static bool private_bus = false;
 
-static pid_t agent_pid = 0;
-
-static int daemon_reload(DBusConnection *bus, char **args, unsigned n);
+static int daemon_reload(DBusConnection *bus, char **args);
 
 static bool on_tty(void) {
         static int t = -1;
 
 static bool on_tty(void) {
         static int t = -1;
@@ -140,84 +139,23 @@ static bool on_tty(void) {
 static void pager_open_if_enabled(void) {
         on_tty();
 
 static void pager_open_if_enabled(void) {
         on_tty();
 
-        if (!arg_no_pager)
-                pager_open();
-}
+        if (arg_no_pager)
+                return;
 
 
-static void spawn_ask_password_agent(void) {
-        pid_t parent;
+        pager_open();
+}
 
 
-        if (agent_pid > 0)
-                return;
+static void agent_open_if_enabled(void) {
 
 
-        /* We check STDIN here, not STDOUT, since this is about input,
-         * not output */
-        if (!isatty(STDIN_FILENO))
-                return;
+        /* Open the password agent as a child process if necessary */
 
         if (!arg_ask_password)
                 return;
 
 
         if (!arg_ask_password)
                 return;
 
-        if (arg_user)
-                return;
-
-        parent = getpid();
-
-        /* Spawns a temporary TTY agent, making sure it goes away when
-         * we go away */
-
-        if ((agent_pid = fork()) < 0)
+        if (arg_scope != UNIT_FILE_SYSTEM)
                 return;
 
                 return;
 
-        if (agent_pid == 0) {
-                /* In the child */
-
-                int fd;
-                bool stdout_is_tty, stderr_is_tty;
-
-                /* Make sure the agent goes away when the parent dies */
-                if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
-                        _exit(EXIT_FAILURE);
-
-                /* Check whether our parent died before we were able
-                 * to set the death signal */
-                if (getppid() != parent)
-                        _exit(EXIT_SUCCESS);
-
-                /* Don't leak fds to the agent */
-                close_all_fds(NULL, 0);
-
-                stdout_is_tty = isatty(STDOUT_FILENO);
-                stderr_is_tty = isatty(STDERR_FILENO);
-
-                if (!stdout_is_tty || !stderr_is_tty) {
-                        /* Detach from stdout/stderr. and reopen
-                         * /dev/tty for them. This is important to
-                         * ensure that when systemctl is started via
-                         * popen() or a similar call that expects to
-                         * read EOF we actually do generate EOF and
-                         * not delay this indefinitely by because we
-                         * keep an unused copy of stdin around. */
-                        if ((fd = open("/dev/tty", O_WRONLY)) < 0) {
-                                log_error("Failed to open /dev/tty: %m");
-                                _exit(EXIT_FAILURE);
-                        }
-
-                        if (!stdout_is_tty)
-                                dup2(fd, STDOUT_FILENO);
-
-                        if (!stderr_is_tty)
-                                dup2(fd, STDERR_FILENO);
-
-                        if (fd > 2)
-                                close(fd);
-                }
-
-                execl(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
-
-                log_error("Unable to execute agent: %m");
-                _exit(EXIT_FAILURE);
-        }
+        agent_open();
 }
 
 static const char *ansi_highlight(bool b) {
 }
 
 static const char *ansi_highlight(bool b) {
@@ -315,6 +253,23 @@ static void warn_wall(enum action action) {
         utmp_wall(table[action], NULL);
 }
 
         utmp_wall(table[action], NULL);
 }
 
+static bool avoid_bus(void) {
+
+        if (running_in_chroot() > 0)
+                return true;
+
+        if (sd_booted() <= 0)
+                return true;
+
+        if (!isempty(arg_root))
+                return true;
+
+        if (arg_scope == UNIT_FILE_GLOBAL)
+                return true;
+
+        return false;
+}
+
 struct unit_info {
         const char *id;
         const char *description;
 struct unit_info {
         const char *id;
         const char *description;
@@ -452,7 +407,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
         }
 }
 
         }
 }
 
-static int list_units(DBusConnection *bus, char **args, unsigned n) {
+static int list_units(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -559,6 +514,210 @@ finish:
         return r;
 }
 
         return r;
 }
 
+static int compare_unit_file_list(const void *a, const void *b) {
+        const char *d1, *d2;
+        const UnitFileList *u = a, *v = b;
+
+        d1 = strrchr(u->path, '.');
+        d2 = strrchr(v->path, '.');
+
+        if (d1 && d2) {
+                int r;
+
+                r = strcasecmp(d1, d2);
+                if (r != 0)
+                        return r;
+        }
+
+        return strcasecmp(file_name_from_path(u->path), file_name_from_path(v->path));
+}
+
+static bool output_show_unit_file(const UnitFileList *u) {
+        const char *dot;
+
+        return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
+}
+
+static void output_unit_file_list(const UnitFileList *units, unsigned c) {
+        unsigned n_shown = 0;
+        const UnitFileList *u;
+
+        if (on_tty())
+                printf("%-25s %-6s\n", "UNIT FILE", "STATE");
+
+        for (u = units; u < units + c; u++) {
+                char *e;
+                const char *on, *off;
+                const char *id;
+
+                if (!output_show_unit_file(u))
+                        continue;
+
+                n_shown++;
+
+                if (u->state == UNIT_FILE_MASKED ||
+                    u->state == UNIT_FILE_MASKED_RUNTIME ||
+                    u->state == UNIT_FILE_DISABLED) {
+                        on  = ansi_highlight(true);
+                        off = ansi_highlight(false);
+                } else if (u->state == UNIT_FILE_ENABLED) {
+                        on  = ansi_highlight_green(true);
+                        off = ansi_highlight_green(false);
+                } else
+                        on = off = "";
+
+                id = file_name_from_path(u->path);
+
+                e = arg_full ? NULL : ellipsize(id, 25, 33);
+
+                printf("%-25s %s%-6s%s\n",
+                       e ? e : id,
+                       on, unit_file_state_to_string(u->state), off);
+
+                free(e);
+        }
+
+        if (on_tty())
+                printf("\n%u unit files listed.\n", n_shown);
+}
+
+static int list_unit_files(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        DBusMessageIter iter, sub, sub2;
+        unsigned c = 0, n_units = 0;
+        UnitFileList *units = NULL;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+
+        pager_open_if_enabled();
+
+        if (avoid_bus()) {
+                Hashmap *h;
+                UnitFileList *u;
+                Iterator i;
+
+                h = hashmap_new(string_hash_func, string_compare_func);
+                if (!h) {
+                        log_error("Out of memory");
+                        return -ENOMEM;
+                }
+
+                r = unit_file_get_list(arg_scope, arg_root, h);
+                if (r < 0) {
+                        log_error("Failed to get unit file list: %s", strerror(-r));
+                        return r;
+                }
+
+                n_units = hashmap_size(h);
+                units = new(UnitFileList, n_units);
+                if (!units) {
+                        unit_file_list_free(h);
+                        log_error("Out of memory");
+                        return -ENOMEM;
+                }
+
+                HASHMAP_FOREACH(u, h, i) {
+                        memcpy(units + c++, u, sizeof(UnitFileList));
+                        free(u);
+                }
+
+                hashmap_free(h);
+        } else {
+                m = dbus_message_new_method_call(
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "ListUnitFiles");
+                if (!m) {
+                        log_error("Could not allocate message.");
+                        return -ENOMEM;
+                }
+
+                reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+                if (!reply) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (!dbus_message_iter_init(reply, &iter) ||
+                    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+                    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&iter, &sub);
+
+                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                        UnitFileList *u;
+                        const char *state;
+
+                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        if (c >= n_units) {
+                                UnitFileList *w;
+
+                                n_units = MAX(2*c, 16);
+                                w = realloc(units, sizeof(struct UnitFileList) * n_units);
+
+                                if (!w) {
+                                        log_error("Failed to allocate unit array.");
+                                        r = -ENOMEM;
+                                        goto finish;
+                                }
+
+                                units = w;
+                        }
+
+                        u = units+c;
+
+                        dbus_message_iter_recurse(&sub, &sub2);
+
+                        if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 ||
+                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        u->state = unit_file_state_from_string(state);
+
+                        dbus_message_iter_next(&sub);
+                        c++;
+                }
+        }
+
+        if (c > 0) {
+                qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
+                output_unit_file_list(units, c);
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        free(units);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
 static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
         static const char * const colors[] = {
                 "Requires",              "[color=\"black\"]",
 static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
         static const char * const colors[] = {
                 "Requires",              "[color=\"black\"]",
@@ -711,7 +870,7 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int dot(DBusConnection *bus, char **args, unsigned n) {
+static int dot(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -803,7 +962,7 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
+static int list_jobs(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -894,18 +1053,18 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int load_unit(DBusConnection *bus, char **args, unsigned n) {
+static int load_unit(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
-        unsigned i;
+        char **name;
 
         dbus_error_init(&error);
 
         assert(bus);
         assert(args);
 
 
         dbus_error_init(&error);
 
         assert(bus);
         assert(args);
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
@@ -918,7 +1077,7 @@ static int load_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
@@ -951,21 +1110,21 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
+static int cancel_job(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
-        unsigned i;
+        char **name;
 
         dbus_error_init(&error);
 
         assert(bus);
         assert(args);
 
 
         dbus_error_init(&error);
 
         assert(bus);
         assert(args);
 
-        if (n <= 1)
-                return daemon_reload(bus, args, n);
+        if (strv_length(args) <= 1)
+                return daemon_reload(bus, args);
 
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
                 unsigned id;
                 const char *path;
 
                 unsigned id;
                 const char *path;
 
@@ -979,7 +1138,7 @@ static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
                         goto finish;
                 }
 
                         goto finish;
                 }
 
-                if ((r = safe_atou(args[i], &id)) < 0) {
+                if ((r = safe_atou(*name, &id)) < 0) {
                         log_error("Failed to parse job id: %s", strerror(-r));
                         goto finish;
                 }
                         log_error("Failed to parse job id: %s", strerror(-r));
                         goto finish;
                 }
@@ -1327,8 +1486,8 @@ static int start_unit_one(
         }
 
         if (need_daemon_reload(bus, name))
         }
 
         if (need_daemon_reload(bus, name))
-                log_warning("Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
-                            arg_user ? "--user" : "--system");
+                log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
+                            arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
 
         if (!arg_no_block) {
                 char *p;
 
         if (!arg_no_block) {
                 char *p;
@@ -1379,7 +1538,7 @@ static enum action verb_to_action(const char *verb) {
                 return ACTION_INVALID;
 }
 
                 return ACTION_INVALID;
 }
 
-static int start_unit(DBusConnection *bus, char **args, unsigned n) {
+static int start_unit(DBusConnection *bus, char **args) {
 
         static const char * const table[_ACTION_MAX] = {
                 [ACTION_HALT] = SPECIAL_HALT_TARGET,
 
         static const char * const table[_ACTION_MAX] = {
                 [ACTION_HALT] = SPECIAL_HALT_TARGET,
@@ -1397,16 +1556,16 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
         };
 
         int r, ret = 0;
         };
 
         int r, ret = 0;
-        unsigned i;
         const char *method, *mode, *one_name;
         Set *s = NULL;
         DBusError error;
         const char *method, *mode, *one_name;
         Set *s = NULL;
         DBusError error;
+        char **name;
 
         dbus_error_init(&error);
 
         assert(bus);
 
 
         dbus_error_init(&error);
 
         assert(bus);
 
-        spawn_ask_password_agent();
+        agent_open_if_enabled();
 
         if (arg_action == ACTION_SYSTEMCTL) {
                 method =
 
         if (arg_action == ACTION_SYSTEMCTL) {
                 method =
@@ -1466,8 +1625,8 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                 if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
                         goto finish;
         } else {
                 if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
                         goto finish;
         } else {
-                for (i = 1; i < n; i++)
-                        if ((r = start_unit_one(bus, method, args[i], mode, &error, s)) != 0) {
+                STRV_FOREACH(name, args+1)
+                        if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) {
                                 ret = translate_bus_error_to_exit_status(r, &error);
                                 dbus_error_free(&error);
                         }
                                 ret = translate_bus_error_to_exit_status(r, &error);
                                 dbus_error_free(&error);
                         }
@@ -1488,7 +1647,7 @@ finish:
         return ret;
 }
 
         return ret;
 }
 
-static int start_special(DBusConnection *bus, char **args, unsigned n) {
+static int start_special(DBusConnection *bus, char **args) {
         int r;
 
         assert(bus);
         int r;
 
         assert(bus);
@@ -1500,9 +1659,9 @@ static int start_special(DBusConnection *bus, char **args, unsigned n) {
              streq(args[0], "reboot") ||
              streq(args[0], "kexec") ||
              streq(args[0], "exit")))
              streq(args[0], "reboot") ||
              streq(args[0], "kexec") ||
              streq(args[0], "exit")))
-                return daemon_reload(bus, args, n);
+                return daemon_reload(bus, args);
 
 
-        r = start_unit(bus, args, n);
+        r = start_unit(bus, args);
 
         if (r >= 0)
                 warn_wall(verb_to_action(args[0]));
 
         if (r >= 0)
                 warn_wall(verb_to_action(args[0]));
@@ -1510,21 +1669,21 @@ static int start_special(DBusConnection *bus, char **args, unsigned n) {
         return r;
 }
 
         return r;
 }
 
-static int check_unit(DBusConnection *bus, char **args, unsigned n) {
+static int check_unit(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         const char
                 *interface = "org.freedesktop.systemd1.Unit",
                 *property = "ActiveState";
         int r = 3; /* According to LSB: "program is not running" */
         DBusError error;
         DBusMessage *m = NULL, *reply = NULL;
         const char
                 *interface = "org.freedesktop.systemd1.Unit",
                 *property = "ActiveState";
         int r = 3; /* According to LSB: "program is not running" */
         DBusError error;
-        unsigned i;
+        char **name;
 
         assert(bus);
         assert(args);
 
         dbus_error_init(&error);
 
 
         assert(bus);
         assert(args);
 
         dbus_error_init(&error);
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
                 const char *path = NULL;
                 const char *state;
                 DBusMessageIter iter, sub;
                 const char *path = NULL;
                 const char *state;
                 DBusMessageIter iter, sub;
@@ -1540,7 +1699,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
@@ -1634,11 +1793,11 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
+static int kill_unit(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         int r = 0;
         DBusError error;
         DBusMessage *m = NULL, *reply = NULL;
         int r = 0;
         DBusError error;
-        unsigned i;
+        char **name;
 
         assert(bus);
         assert(args);
 
         assert(bus);
         assert(args);
@@ -1651,7 +1810,7 @@ static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
         if (!arg_kill_mode)
                 arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
 
         if (!arg_kill_mode)
                 arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
@@ -1664,7 +1823,7 @@ static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_STRING, &arg_kill_who,
                                               DBUS_TYPE_STRING, &arg_kill_mode,
                                               DBUS_TYPE_INT32, &arg_signal,
                                               DBUS_TYPE_STRING, &arg_kill_who,
                                               DBUS_TYPE_STRING, &arg_kill_mode,
                                               DBUS_TYPE_INT32, &arg_signal,
@@ -2075,7 +2234,7 @@ static void print_status_info(UnitStatusInfo *i) {
                 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
                        ansi_highlight(true),
                        ansi_highlight(false),
                 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
                        ansi_highlight(true),
                        ansi_highlight(false),
-                       arg_user ? "--user" : "--system");
+                       arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
 }
 
 static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
 }
 
 static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
@@ -2517,12 +2676,12 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int show(DBusConnection *bus, char **args, unsigned n) {
+static int show(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         int r, ret = 0;
         DBusError error;
         DBusMessage *m = NULL, *reply = NULL;
         int r, ret = 0;
         DBusError error;
-        unsigned i;
         bool show_properties, new_line = false;
         bool show_properties, new_line = false;
+        char **name;
 
         assert(bus);
         assert(args);
 
         assert(bus);
         assert(args);
@@ -2534,7 +2693,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
         if (show_properties)
                 pager_open_if_enabled();
 
         if (show_properties)
                 pager_open_if_enabled();
 
-        if (show_properties && n <= 1) {
+        if (show_properties && strv_length(args) <= 1) {
                 /* If not argument is specified inspect the manager
                  * itself */
 
                 /* If not argument is specified inspect the manager
                  * itself */
 
@@ -2542,11 +2701,11 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                 goto finish;
         }
 
                 goto finish;
         }
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
                 const char *path = NULL;
                 uint32_t id;
 
                 const char *path = NULL;
                 uint32_t id;
 
-                if (safe_atou32(args[i], &id) < 0) {
+                if (safe_atou32(*name, &id) < 0) {
 
                         /* Interpret as unit name */
 
 
                         /* Interpret as unit name */
 
@@ -2561,7 +2720,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                         }
 
                         if (!dbus_message_append_args(m,
                         }
 
                         if (!dbus_message_append_args(m,
-                                                      DBUS_TYPE_STRING, &args[i],
+                                                      DBUS_TYPE_STRING, name,
                                                       DBUS_TYPE_INVALID)) {
                                 log_error("Could not append arguments to message.");
                                 ret = -ENOMEM;
                                                       DBUS_TYPE_INVALID)) {
                                 log_error("Could not append arguments to message.");
                                 ret = -ENOMEM;
@@ -2590,7 +2749,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                 }
 
                                 if (!dbus_message_append_args(m,
                                 }
 
                                 if (!dbus_message_append_args(m,
-                                                              DBUS_TYPE_STRING, &args[i],
+                                                              DBUS_TYPE_STRING, name,
                                                               DBUS_TYPE_INVALID)) {
                                         log_error("Could not append arguments to message.");
                                         ret = -ENOMEM;
                                                               DBUS_TYPE_INVALID)) {
                                         log_error("Could not append arguments to message.");
                                         ret = -ENOMEM;
@@ -2692,7 +2851,7 @@ finish:
         return ret;
 }
 
         return ret;
 }
 
-static int dump(DBusConnection *bus, char **args, unsigned n) {
+static int dump(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -2741,7 +2900,7 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int snapshot(DBusConnection *bus, char **args, unsigned n) {
+static int snapshot(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -2763,7 +2922,7 @@ static int snapshot(DBusConnection *bus, char **args, unsigned n) {
                 return -ENOMEM;
         }
 
                 return -ENOMEM;
         }
 
-        if (n > 1)
+        if (strv_length(args) > 1)
                 name = args[1];
 
         if (!dbus_message_append_args(m,
                 name = args[1];
 
         if (!dbus_message_append_args(m,
@@ -2848,18 +3007,18 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
+static int delete_snapshot(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         int r;
         DBusError error;
         DBusMessage *m = NULL, *reply = NULL;
         int r;
         DBusError error;
-        unsigned i;
+        char **name;
 
         assert(bus);
         assert(args);
 
         dbus_error_init(&error);
 
 
         assert(bus);
         assert(args);
 
         dbus_error_init(&error);
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
                 const char *path = NULL;
 
                 if (!(m = dbus_message_new_method_call(
                 const char *path = NULL;
 
                 if (!(m = dbus_message_new_method_call(
@@ -2873,7 +3032,7 @@ static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
@@ -2931,7 +3090,7 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int daemon_reload(DBusConnection *bus, char **args, unsigned n) {
+static int daemon_reload(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -3003,19 +3162,19 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int reset_failed(DBusConnection *bus, char **args, unsigned n) {
+static int reset_failed(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusMessage *m = NULL, *reply = NULL;
-        unsigned i;
         int r;
         DBusError error;
         int r;
         DBusError error;
+        char **name;
 
         assert(bus);
         dbus_error_init(&error);
 
 
         assert(bus);
         dbus_error_init(&error);
 
-        if (n <= 1)
-                return daemon_reload(bus, args, n);
+        if (strv_length(args) <= 1)
+                return daemon_reload(bus, args);
 
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
@@ -3028,7 +3187,7 @@ static int reset_failed(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, args + i,
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
@@ -3060,7 +3219,7 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
+static int show_enviroment(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         DBusMessageIter iter, sub, sub2;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         DBusMessageIter iter, sub, sub2;
@@ -3144,13 +3303,13 @@ finish:
         return r;
 }
 
         return r;
 }
 
-static int set_environment(DBusConnection *bus, char **args, unsigned n) {
+static int set_environment(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         const char *method;
         DBusMessageIter iter, sub;
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         const char *method;
         DBusMessageIter iter, sub;
-        unsigned i;
+        char **name;
 
         dbus_error_init(&error);
 
 
         dbus_error_init(&error);
 
@@ -3176,8 +3335,8 @@ static int set_environment(DBusConnection *bus, char **args, unsigned n) {
                 goto finish;
         }
 
                 goto finish;
         }
 
-        for (i = 1; i < n; i++)
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
+        STRV_FOREACH(name, args+1)
+                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
                         goto finish;
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
                         goto finish;
@@ -3209,801 +3368,479 @@ finish:
         return r;
 }
 
         return r;
 }
 
-typedef struct {
-        char *name;
-        char *path;
-
-        char **aliases;
-        char **wanted_by;
-} InstallInfo;
+static int enable_sysv_units(char **args) {
+        int r = 0;
 
 
-static Hashmap *will_install = NULL, *have_installed = NULL;
-static Set *remove_symlinks_to = NULL;
-static unsigned n_symlinks = 0;
+#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX))
+        const char *verb = args[0];
+        unsigned f = 1, t = 1;
+        LookupPaths paths;
 
 
-static void install_info_free(InstallInfo *i) {
-        assert(i);
+        if (arg_scope != UNIT_FILE_SYSTEM)
+                return 0;
 
 
-        free(i->name);
-        free(i->path);
-        strv_free(i->aliases);
-        strv_free(i->wanted_by);
-        free(i);
-}
+        if (!streq(verb, "enable") &&
+            !streq(verb, "disable") &&
+            !streq(verb, "is-enabled"))
+                return 0;
 
 
-static void install_info_hashmap_free(Hashmap *m) {
-        InstallInfo *i;
+        /* Processes all SysV units, and reshuffles the array so that
+         * afterwards only the native units remain */
 
 
-        while ((i = hashmap_steal_first(m)))
-                install_info_free(i);
+        zero(paths);
+        r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
+        if (r < 0)
+                return r;
 
 
-        hashmap_free(m);
-}
+        r = 0;
 
 
-static int install_info_add(const char *name) {
-        InstallInfo *i;
-        int r;
+        for (f = 1; args[f]; f++) {
+                const char *name;
+                char *p;
+                bool found_native = false, found_sysv;
+                unsigned c = 1;
+                const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
+                char **k, *l, *q = NULL;
+                int j;
+                pid_t pid;
+                siginfo_t status;
 
 
-        assert(will_install);
+                name = args[f];
 
 
-        if (!unit_name_is_valid_no_type(name, true)) {
-                log_warning("Unit name %s is not a valid unit name.", name);
-                return -EINVAL;
-        }
+                if (!endswith(name, ".service"))
+                        continue;
 
 
-        if (hashmap_get(have_installed, name) ||
-            hashmap_get(will_install, name))
-                return 0;
+                if (path_is_absolute(name))
+                        continue;
 
 
-        if (!(i = new0(InstallInfo, 1))) {
-                r = -ENOMEM;
-                goto fail;
-        }
+                STRV_FOREACH(k, paths.unit_path) {
+                        p = NULL;
 
 
-        if (!(i->name = strdup(name))) {
-                r = -ENOMEM;
-                goto fail;
-        }
+                        if (!isempty(arg_root))
+                                asprintf(&p, "%s/%s/%s", arg_root, *k, name);
+                        else
+                                asprintf(&p, "%s/%s", *k, name);
 
 
-        if ((r = hashmap_put(will_install, i->name, i)) < 0)
-                goto fail;
+                        if (!p) {
+                                log_error("No memory");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
 
 
-        return 0;
+                        found_native = access(p, F_OK) >= 0;
+                        free(p);
 
 
-fail:
-        if (i)
-                install_info_free(i);
+                        if (found_native)
+                                break;
+                }
 
 
-        return r;
-}
+                if (found_native)
+                        continue;
 
 
-static int config_parse_also(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char *w;
-        size_t l;
-        char *state;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                char *n;
-                int r;
+                p = NULL;
+                if (!isempty(arg_root))
+                        asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
+                else
+                        asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
+                if (!p) {
+                        log_error("No memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
 
 
-                if (!(n = strndup(w, l)))
-                        return -ENOMEM;
+                p[strlen(p) - sizeof(".service") + 1] = 0;
+                found_sysv = access(p, F_OK) >= 0;
 
 
-                if ((r = install_info_add(n)) < 0) {
-                        log_warning("Cannot install unit %s: %s", n, strerror(-r));
-                        free(n);
-                        return r;
+                if (!found_sysv) {
+                        free(p);
+                        continue;
                 }
 
                 }
 
-                free(n);
-        }
+                /* Mark this entry, so that we don't try enabling it as native unit */
+                args[f] = (char*) "";
 
 
-        return 0;
-}
+                log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
 
 
-static int mark_symlink_for_removal(const char *p) {
-        char *n;
-        int r;
+                if (!isempty(arg_root))
+                        argv[c++] = q = strappend("--root=", arg_root);
 
 
-        assert(p);
-        assert(path_is_absolute(p));
+                argv[c++] = file_name_from_path(p);
+                argv[c++] =
+                        streq(verb, "enable") ? "on" :
+                        streq(verb, "disable") ? "off" : "--level=5";
+                argv[c] = NULL;
 
 
-        if (!remove_symlinks_to)
-                return 0;
+                l = strv_join((char**)argv, " ");
+                if (!l) {
+                        log_error("No memory.");
+                        free(q);
+                        free(p);
+                        r = -ENOMEM;
+                        goto finish;
+                }
 
 
-        if (!(n = strdup(p)))
-                return -ENOMEM;
+                log_info("Executing %s", l);
+                free(l);
 
 
-        path_kill_slashes(n);
+                pid = fork();
+                if (pid < 0) {
+                        log_error("Failed to fork: %m");
+                        free(p);
+                        free(q);
+                        r = -errno;
+                        goto finish;
+                } else if (pid == 0) {
+                        /* Child */
 
 
-        if ((r = set_put(remove_symlinks_to, n)) < 0) {
-                free(n);
-                return r == -EEXIST ? 0 : r;
-        }
+                        execv(argv[0], (char**) argv);
+                        _exit(EXIT_FAILURE);
+                }
 
 
-        return 0;
-}
+                free(p);
+                free(q);
 
 
-static int remove_marked_symlinks_fd(int fd, const char *config_path, const char *root, bool *deleted) {
-        int r = 0;
-        DIR *d;
-        struct dirent *de;
+                j = wait_for_terminate(pid, &status);
+                if (j < 0) {
+                        log_error("Failed to wait for child: %s", strerror(-r));
+                        r = j;
+                        goto finish;
+                }
 
 
-        assert(fd >= 0);
-        assert(root);
-        assert(deleted);
+                if (status.si_code == CLD_EXITED) {
+                        if (streq(verb, "is-enabled")) {
+                                if (status.si_status == 0) {
+                                        if (!arg_quiet)
+                                                puts("enabled");
+                                        r = 1;
+                                } else {
+                                        if (!arg_quiet)
+                                                puts("disabled");
+                                }
 
 
-        if (!(d = fdopendir(fd))) {
-                close_nointr_nofail(fd);
-                return -errno;
+                        } else if (status.si_status != 0) {
+                                r = -EINVAL;
+                                goto finish;
+                        }
+                } else {
+                        r = -EPROTO;
+                        goto finish;
+                }
         }
 
         }
 
-        rewinddir(d);
+finish:
+        lookup_paths_free(&paths);
 
 
-        while ((de = readdir(d))) {
-                bool is_dir = false, is_link = false;
+        /* Drop all SysV units */
+        for (f = 1, t = 1; args[f]; f++) {
 
 
-                if (ignore_file(de->d_name))
+                if (isempty(args[f]))
                         continue;
 
                         continue;
 
-                if (de->d_type == DT_LNK)
-                        is_link = true;
-                else if (de->d_type == DT_DIR)
-                        is_dir = true;
-                else if (de->d_type == DT_UNKNOWN) {
-                        struct stat st;
+                args[t++] = args[f];
+        }
 
 
-                        if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                                log_error("Failed to stat %s/%s: %m", root, de->d_name);
+        args[t] = NULL;
 
 
-                                if (r == 0)
-                                        r = -errno;
-                                continue;
-                        }
+#endif
+        return r;
+}
 
 
-                        is_link = S_ISLNK(st.st_mode);
-                        is_dir = S_ISDIR(st.st_mode);
-                } else
-                        continue;
+static int enable_unit(DBusConnection *bus, char **args) {
+        const char *verb = args[0];
+        UnitFileChange *changes = NULL;
+        unsigned n_changes = 0, i;
+        int carries_install_info = -1;
+        DBusMessage *m = NULL, *reply = NULL;
+        int r;
+        DBusError error;
 
 
-                if (is_dir) {
-                        int nfd, q;
-                        char *p;
-
-                        if ((nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0) {
-                                log_error("Failed to open %s/%s: %m", root, de->d_name);
-
-                                if (r == 0)
-                                        r = -errno;
-                                continue;
-                        }
-
-                        if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
-                                log_error("Failed to allocate directory string.");
-                                close_nointr_nofail(nfd);
-                                r = -ENOMEM;
-                                break;
-                        }
-
-                        /* This will close nfd, regardless whether it succeeds or not */
-                        q = remove_marked_symlinks_fd(nfd, config_path, p, deleted);
-                        free(p);
-
-                        if (r == 0)
-                                r = q;
-
-                } else if (is_link) {
-                        char *p, *dest, *c;
-                        int q;
-
-                        if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
-                                log_error("Failed to allocate symlink string.");
-                                r = -ENOMEM;
-                                break;
-                        }
-
-                        if ((q = readlink_and_make_absolute(p, &dest)) < 0) {
-                                log_error("Cannot read symlink %s: %s", p, strerror(-q));
-                                free(p);
-
-                                if (r == 0)
-                                        r = q;
-                                continue;
-                        }
-
-                        if ((c = canonicalize_file_name(dest))) {
-                                /* This might fail if the destination
-                                 * is already removed */
-
-                                free(dest);
-                                dest = c;
-                        }
-
-                        path_kill_slashes(dest);
-                        if (set_get(remove_symlinks_to, dest)) {
-
-                                if (!arg_quiet)
-                                        log_info("rm '%s'", p);
-
-                                if (unlink(p) < 0) {
-                                        log_error("Cannot unlink symlink %s: %m", p);
-
-                                        if (r == 0)
-                                                r = -errno;
-                                } else {
-                                        rmdir_parents(p, config_path);
-                                        path_kill_slashes(p);
-
-                                        if (!set_get(remove_symlinks_to, p)) {
-
-                                                if ((r = mark_symlink_for_removal(p)) < 0) {
-                                                        if (r == 0)
-                                                                r = q;
-                                                } else
-                                                        *deleted = true;
-                                        }
-                                }
-                        }
-
-                        free(p);
-                        free(dest);
-                }
-        }
-
-        closedir(d);
-
-        return r;
-}
-
-static int remove_marked_symlinks(const char *config_path) {
-        int fd, r = 0;
-        bool deleted;
-
-        assert(config_path);
-
-        if (set_size(remove_symlinks_to) <= 0)
-                return 0;
-
-        if ((fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0)
-                return -errno;
-
-        do {
-                int q, cfd;
-                deleted = false;
-
-                if ((cfd = dup(fd)) < 0) {
-                        r = -errno;
-                        break;
-                }
-
-                /* This takes possession of cfd and closes it */
-                if ((q = remove_marked_symlinks_fd(cfd, config_path, config_path, &deleted)) < 0) {
-                        if (r == 0)
-                                r = q;
-                }
-        } while (deleted);
-
-        close_nointr_nofail(fd);
-
-        return r;
-}
-
-static int create_symlink(const char *verb, const char *orig_old_path, const char *new_path) {
-        int r;
-        const char *old_path;
-
-        if (arg_root)
-                old_path = orig_old_path+strlen(arg_root);
-        else
-                old_path = orig_old_path;
-
-        assert(old_path);
-        assert(new_path);
-        assert(verb);
-
-        if (streq(verb, "enable")) {
-                char *dest;
-
-                mkdir_parents(new_path, 0755);
-
-                if (symlink(old_path, new_path) >= 0) {
-
-                        if (!arg_quiet)
-                                log_info("ln -s '%s' '%s'", old_path, new_path);
-
-                        return 0;
-                }
-
-                if (errno != EEXIST) {
-                        log_error("Cannot link %s to %s: %m", old_path, new_path);
-                        return -errno;
-                }
-
-                if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
-
-                        if (errno == EINVAL) {
-                                log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
-                                return -EEXIST;
-                        }
-
-                        log_error("readlink() failed: %s", strerror(-r));
-                        return r;
-                }
-
-                if (streq(dest, old_path)) {
-                        free(dest);
-                        return 0;
-                }
-
-                if (!arg_force) {
-                        log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
-                        free(dest);
-                        return -EEXIST;
-                }
-
-                free(dest);
-                unlink(new_path);
-
-                if (!arg_quiet)
-                        log_info("ln -s '%s' '%s'", old_path, new_path);
-
-                if (symlink(old_path, new_path) >= 0)
-                        return 0;
-
-                log_error("Cannot link %s to %s: %m", old_path, new_path);
-                return -errno;
-
-        } else if (streq(verb, "disable")) {
-                char *dest;
-
-                if ((r = mark_symlink_for_removal(old_path)) < 0)
-                        return r;
-
-                if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
-                        if (errno == ENOENT)
-                                return 0;
-
-                        if (errno == EINVAL) {
-                                log_warning("File %s not a symlink, ignoring.", old_path);
-                                return 0;
-                        }
-
-                        log_error("readlink() failed: %s", strerror(-r));
-                        return r;
-                }
-
-                if (!streq(dest, old_path)) {
-                        log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
-                        free(dest);
-                        return 0;
-                }
-
-                free(dest);
-
-                if ((r = mark_symlink_for_removal(new_path)) < 0)
-                        return r;
-
-                if (!arg_quiet)
-                        log_info("rm '%s'", new_path);
-
-                if (unlink(new_path) >= 0)
-                        return 0;
-
-                log_error("Cannot unlink %s: %m", new_path);
-                return -errno;
-
-        } else if (streq(verb, "is-enabled")) {
-                char *dest;
+        dbus_error_init(&error);
 
 
-                if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
+        r = enable_sysv_units(args);
+        if (r < 0)
+                return r;
 
 
-                        if (errno == ENOENT || errno == EINVAL)
-                                return 0;
+        if (!bus || avoid_bus()) {
+                if (streq(verb, "enable")) {
+                        r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                        carries_install_info = r;
+                } else if (streq(verb, "disable"))
+                        r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
+                else if (streq(verb, "reenable")) {
+                        r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                        carries_install_info = r;
+                } else if (streq(verb, "link"))
+                        r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                else if (streq(verb, "preset")) {
+                        r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                        carries_install_info = r;
+                } else if (streq(verb, "mask"))
+                        r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                else if (streq(verb, "unmask"))
+                        r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
+                else
+                        assert_not_reached("Unknown verb");
 
 
-                        log_error("readlink() failed: %s", strerror(-r));
-                        return r;
+                if (r < 0) {
+                        log_error("Operation failed: %s", strerror(-r));
+                        goto finish;
                 }
 
                 }
 
-                if (streq(dest, old_path)) {
-                        free(dest);
-                        return 1;
+                for (i = 0; i < n_changes; i++) {
+                        if (changes[i].type == UNIT_FILE_SYMLINK)
+                                log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
+                        else
+                                log_info("rm '%s'", changes[i].path);
                 }
 
                 }
 
-                free(dest);
-                return 0;
-        }
-
-        assert_not_reached("Unknown action.");
-}
-
-static int install_info_symlink_alias(const char *verb, InstallInfo *i, const char *config_path) {
-        char **s;
-        char *alias_path = NULL;
-        int r;
-
-        assert(verb);
-        assert(i);
-        assert(config_path);
-
-        STRV_FOREACH(s, i->aliases) {
-
-                free(alias_path);
-                if (!(alias_path = path_make_absolute(*s, config_path))) {
+        } else {
+                const char *method;
+                bool send_force = true, expect_carries_install_info = false;
+                dbus_bool_t a, b;
+                DBusMessageIter iter, sub, sub2;
+
+                if (streq(verb, "enable")) {
+                        method = "EnableUnitFiles";
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "disable")) {
+                        method = "DisableUnitFiles";
+                        send_force = false;
+                } else if (streq(verb, "reenable")) {
+                        method = "ReenableUnitFiles";
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "link"))
+                        method = "LinkUnitFiles";
+                else if (streq(verb, "preset")) {
+                        method = "PresetUnitFiles";
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "mask"))
+                        method = "MaskUnitFiles";
+                else if (streq(verb, "unmask")) {
+                        method = "UnmaskUnitFiles";
+                        send_force = false;
+                } else
+                        assert_not_reached("Unknown verb");
+
+                m = dbus_message_new_method_call(
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                method);
+                if (!m) {
                         log_error("Out of memory");
                         r = -ENOMEM;
                         goto finish;
                 }
 
                         log_error("Out of memory");
                         r = -ENOMEM;
                         goto finish;
                 }
 
-                if ((r = create_symlink(verb, i->path, alias_path)) != 0)
-                        goto finish;
-
-                if (streq(verb, "disable"))
-                        rmdir_parents(alias_path, config_path);
-        }
-        r = 0;
-
-finish:
-        free(alias_path);
-
-        return r;
-}
-
-static int install_info_symlink_wants(const char *verb, InstallInfo *i, const char *config_path) {
-        char **s;
-        char *alias_path = NULL;
-        int r;
+                dbus_message_iter_init_append(m, &iter);
 
 
-        assert(verb);
-        assert(i);
-        assert(config_path);
-
-        STRV_FOREACH(s, i->wanted_by) {
-                if (!unit_name_is_valid_no_type(*s, true)) {
-                        log_error("Invalid name %s.", *s);
-                        r = -EINVAL;
+                r = bus_append_strv_iter(&iter, args+1);
+                if (r < 0) {
+                        log_error("Failed to append unit files.");
                         goto finish;
                 }
 
                         goto finish;
                 }
 
-                free(alias_path);
-                alias_path = NULL;
-
-                if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
-                        log_error("Out of memory");
+                a = arg_runtime;
+                if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &a)) {
+                        log_error("Failed to append runtime boolean.");
                         r = -ENOMEM;
                         goto finish;
                 }
 
                         r = -ENOMEM;
                         goto finish;
                 }
 
-                if ((r = create_symlink(verb, i->path, alias_path)) != 0)
-                        goto finish;
-
-                if (streq(verb, "disable"))
-                        rmdir_parents(alias_path, config_path);
-        }
-
-        r = 0;
-
-finish:
-        free(alias_path);
-
-        return r;
-}
-
-static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo *i, const char *config_path) {
-
-        const ConfigItem items[] = {
-                { "Alias",    config_parse_strv, 0, &i->aliases,   "Install" },
-                { "WantedBy", config_parse_strv, 0, &i->wanted_by, "Install" },
-                { "Also",     config_parse_also, 0, NULL,          "Install" },
-
-                { NULL, NULL, 0, NULL, NULL }
-        };
-
-        char **p;
-        char *filename = NULL;
-        FILE *f = NULL;
-        int r;
-
-        assert(paths);
-        assert(i);
-
-        STRV_FOREACH(p, paths->unit_path) {
-                int fd;
-                char *path, *should_free;
+                if (send_force) {
+                        b = arg_force;
 
 
-                if (arg_root)
-                        should_free = path = strappend(arg_root, *p);
-                else {
-                        should_free = NULL;
-                        path = *p;
+                        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
+                                log_error("Failed to append force boolean.");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
                 }
 
                 }
 
-                if (!(filename = path_make_absolute(i->name, path))) {
-                        log_error("Out of memory");
-                        return -ENOMEM;
+                reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+                if (!reply) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
                 }
 
                 }
 
-                if (should_free)
-                        free(should_free);
+                if (!dbus_message_iter_init(reply, &iter)) {
+                        log_error("Failed to initialize iterator.");
+                        goto finish;
+                }
 
 
-                /* Ensure that we don't follow symlinks */
-                if ((fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOCTTY)) >= 0)
-                        if ((f = fdopen(fd, "re")))
-                                break;
+                if (expect_carries_install_info) {
+                        r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &b, true);
+                        if (r < 0) {
+                                log_error("Failed to parse reply.");
+                                goto finish;
+                        }
 
 
-                if (errno == ELOOP) {
-                        log_error("Refusing to operate on symlinks, please pass unit names or absolute paths to unit files.");
-                        free(filename);
-                        return -errno;
+                        carries_install_info = b;
                 }
 
                 }
 
-                if (errno != ENOENT) {
-                        log_error("Failed to open %s: %m", filename);
-                        free(filename);
-                        return -errno;
+                if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+                    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
                 }
 
                 }
 
-                free(filename);
-                filename = NULL;
-        }
-
-        if (!f) {
-#if (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX)) && defined (HAVE_SYSV_COMPAT)
-
-                if (endswith(i->name, ".service")) {
-                        char *sysv;
-                        bool exists;
+                dbus_message_iter_recurse(&iter, &sub);
+                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                        const char *type, *path, *source;
 
 
-                        if (asprintf(&sysv, SYSTEM_SYSVINIT_PATH "/%s", i->name) < 0) {
-                                log_error("Out of memory");
-                                return -ENOMEM;
+                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
                         }
 
                         }
 
-                        sysv[strlen(sysv) - sizeof(".service") + 1] = 0;
-                        if (arg_root) {
-                                char *tmp_path;
-
-                                tmp_path = strappend (arg_root, sysv);
-                                exists = access (tmp_path, F_OK) >= 0;
-                                free (tmp_path);
-                        } else
-                                exists = access(sysv, F_OK) >= 0;
-
-                        if (exists) {
-                                pid_t pid;
-                                siginfo_t status;
-
-                                const char *argv[] = {
-                                        "/sbin/chkconfig",
-                                        NULL,
-                                        NULL,
-                                        NULL,
-                                        NULL
-                                };
-
-                                log_info("%s is not a native service, redirecting to /sbin/chkconfig.", i->name);
-
-                                argv[1] = file_name_from_path(sysv);
-                                argv[2] =
-                                        streq(verb, "enable") ? "on" :
-                                        streq(verb, "disable") ? "off" : "--level=5";
-                                if (arg_root)
-                                        argv[3] = strappend("--root=", arg_root);
-
-                                log_info("Executing %s %s %s %s", argv[0], argv[1], strempty(argv[2]), strempty(argv[3]));
-
-                                if ((pid = fork()) < 0) {
-                                        log_error("Failed to fork: %m");
-                                        free(sysv);
-                                        return -errno;
-                                } else if (pid == 0) {
-                                        execv(argv[0], (char**) argv);
-                                        _exit(EXIT_FAILURE);
-                                }
-
-                                free(sysv);
+                        dbus_message_iter_recurse(&sub, &sub2);
 
 
-                                if ((r = wait_for_terminate(pid, &status)) < 0)
-                                        return r;
-
-                                if (status.si_code == CLD_EXITED) {
-
-                                        if (streq(verb, "is-enabled"))
-                                                return status.si_status == 0 ? 1 : 0;
-
-                                        if (status.si_status == 0)
-                                                n_symlinks ++;
-
-                                        return status.si_status == 0 ? 0 : -EINVAL;
-
-                                } else
-                                        return -EPROTO;
+                        if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
+                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
+                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &source, false) < 0) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
                         }
 
                         }
 
-                        free(sysv);
-                }
-
-#endif
-
-                log_error("Couldn't find %s.", i->name);
-                return -ENOENT;
-        }
-
-        i->path = filename;
+                        if (streq(type, "symlink"))
+                                log_info("ln -s '%s' '%s'", source, path);
+                        else
+                                log_info("rm '%s'", path);
 
 
-        if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
-                fclose(f);
-                return r;
-        }
+                        dbus_message_iter_next(&sub);
+                }
 
 
-        /* Consider unit files stored in /lib and /usr always enabled
-         * if they have no [Install] data. */
-        if (streq(verb, "is-enabled") &&
-            strv_isempty(i->aliases) &&
-            strv_isempty(i->wanted_by) &&
-            !path_startswith(filename, "/etc")) {
-                fclose(f);
-                return 1;
+                /* Try to reload if enabeld */
+                if (!arg_no_reload)
+                        r = daemon_reload(bus, args);
         }
 
         }
 
-        n_symlinks += strv_length(i->aliases);
-        n_symlinks += strv_length(i->wanted_by);
-
-        fclose(f);
-
-        if ((r = install_info_symlink_alias(verb, i, config_path)) != 0)
-                return r;
-
-        if ((r = install_info_symlink_wants(verb, i, config_path)) != 0)
-                return r;
-
-        if ((r = mark_symlink_for_removal(filename)) < 0)
-                return r;
-
-        if ((r = remove_marked_symlinks(config_path)) < 0)
-                return r;
-
-        return 0;
-}
+        if (carries_install_info == 0)
+                log_warning("Warning: unit files do not carry install information. No operation executed.");
 
 
-static char *get_config_path(void) {
-        char *ret;
+finish:
+        if (m)
+                dbus_message_unref(m);
 
 
-        if (arg_user && arg_global)
-                ret = strdup(USER_CONFIG_UNIT_PATH);
+        if (reply)
+                dbus_message_unref(reply);
 
 
-        if (arg_user) {
-                if (user_config_home(&ret) < 0)
-                        return NULL;
-        }
+        unit_file_changes_free(changes, n_changes);
 
 
-        ret = strdup(SYSTEM_CONFIG_UNIT_PATH);
-        if (arg_root) {
-                char *p;
-                p = strappend (arg_root, ret);
-                free (ret);
-                return p;
-        } else
-                return ret;
+        dbus_error_free(&error);
+        return r;
 }
 
 }
 
-static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
+static int unit_is_enabled(DBusConnection *bus, char **args) {
         DBusError error;
         int r;
         DBusError error;
         int r;
-        LookupPaths paths;
-        char *config_path = NULL;
-        unsigned j;
-        InstallInfo *i;
-        const char *verb = args[0];
+        DBusMessage *m = NULL, *reply = NULL;
+        bool enabled;
+        char **name;
 
         dbus_error_init(&error);
 
 
         dbus_error_init(&error);
 
-        zero(paths);
-        if ((r = lookup_paths_init(&paths, arg_user ? MANAGER_USER : MANAGER_SYSTEM, true)) < 0) {
-                log_error("Failed to determine lookup paths: %s", strerror(-r));
-                goto finish;
-        }
+        r = enable_sysv_units(args);
+        if (r < 0)
+                return r;
 
 
-        if (!(config_path = get_config_path())) {
-                log_error("Failed to determine config path");
-                r = -ENOMEM;
-                goto finish;
-        }
+        enabled = r > 0;
 
 
-        will_install = hashmap_new(string_hash_func, string_compare_func);
-        have_installed = hashmap_new(string_hash_func, string_compare_func);
+        if (!bus || avoid_bus()) {
 
 
-        if (!will_install || !have_installed) {
-                log_error("Failed to allocate unit sets.");
-                r = -ENOMEM;
-                goto finish;
-        }
+                STRV_FOREACH(name, args+1) {
+                        UnitFileState state;
 
 
-        if (!arg_defaults && streq(verb, "disable"))
-                if (!(remove_symlinks_to = set_new(string_hash_func, string_compare_func))) {
-                        log_error("Failed to allocate symlink sets.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                        state = unit_file_get_state(arg_scope, arg_root, *name);
+                        if (state < 0) {
+                                r = state;
+                                goto finish;
+                        }
 
 
-        for (j = 1; j < n; j++)
-                if ((r = install_info_add(args[j])) < 0) {
-                        log_warning("Cannot install unit %s: %s", args[j], strerror(-r));
-                        goto finish;
+                        if (state == UNIT_FILE_ENABLED ||
+                            state == UNIT_FILE_ENABLED_RUNTIME ||
+                            state == UNIT_FILE_STATIC)
+                                enabled = true;
+
+                        if (!arg_quiet)
+                                puts(unit_file_state_to_string(state));
                 }
 
                 }
 
-        r = 0;
+        } else {
+                STRV_FOREACH(name, args+1) {
+                        const char *s;
 
 
-        while ((i = hashmap_first(will_install))) {
-                int q;
+                        m = dbus_message_new_method_call(
+                                        "org.freedesktop.systemd1",
+                                        "/org/freedesktop/systemd1",
+                                        "org.freedesktop.systemd1.Manager",
+                                        "GetUnitFileState");
+                        if (!m) {
+                                log_error("Out of memory");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
 
 
-                assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
+                        if (!dbus_message_append_args(m,
+                                                      DBUS_TYPE_STRING, name,
+                                                      DBUS_TYPE_INVALID)) {
+                                log_error("Could not append arguments to message.");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
 
 
-                if ((q = install_info_apply(verb, &paths, i, config_path)) != 0) {
+                        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+                        if (!reply) {
+                                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                                r = -EIO;
+                                goto finish;
+                        }
 
 
-                        if (q < 0) {
-                                if (r == 0)
-                                        r = q;
+                        if (!dbus_message_get_args(reply, &error,
+                                                   DBUS_TYPE_STRING, &s,
+                                                   DBUS_TYPE_INVALID)) {
+                                log_error("Failed to parse reply: %s", bus_error_message(&error));
+                                r = -EIO;
                                 goto finish;
                         }
 
                                 goto finish;
                         }
 
-                        /* In test mode and found something */
-                        r = 1;
-                        break;
-                }
-        }
+                        dbus_message_unref(m);
+                        dbus_message_unref(reply);
+                        m = reply = NULL;
 
 
-        if (streq(verb, "is-enabled"))
-                r = r > 0 ? 0 : -ENOENT;
-        else {
-                if (n_symlinks <= 0)
-                        log_warning("Unit files contain no applicable installation information. Ignoring.");
-
-                if (bus &&
-                    /* Don't try to reload anything if the user asked us to not do this */
-                    !arg_no_reload &&
-                    /* Don't try to reload anything when updating a unit globally */
-                    !arg_global &&
-                    /* Don't try to reload anything if we are called for system changes but the system wasn't booted with systemd */
-                    (arg_user || sd_booted() > 0) &&
-                    /* Don't try to reload anything if we are running in a chroot environment */
-                    (arg_user || running_in_chroot() <= 0 || arg_root) ) {
-                        int q;
-
-                        if ((q = daemon_reload(bus, args, n)) < 0)
-                                r = q;
+                        if (streq(s, "enabled") ||
+                            streq(s, "enabled-runtime") ||
+                            streq(s, "static"))
+                                enabled = true;
+
+                        if (!arg_quiet)
+                                puts(s);
                 }
         }
 
                 }
         }
 
-finish:
-        install_info_hashmap_free(will_install);
-        install_info_hashmap_free(have_installed);
+        r = enabled ? 0 : 1;
 
 
-        set_free_free(remove_symlinks_to);
-
-        lookup_paths_free(&paths);
+finish:
+        if (m)
+                dbus_message_unref(m);
 
 
-        free(config_path);
+        if (reply)
+                dbus_message_unref(reply);
 
 
+        dbus_error_free(&error);
         return r;
 }
 
 static int systemctl_help(void) {
 
         return r;
 }
 
 static int systemctl_help(void) {
 
+        pager_open_if_enabled();
+
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
-               "Send control commands to or query the systemd manager.\n\n"
+               "Query or send control commands to the systemd manager.\n\n"
                "  -h --help           Show this help\n"
                "     --version        Show package version\n"
                "  -t --type=TYPE      List only units of a particular type\n"
                "  -h --help           Show this help\n"
                "     --version        Show package version\n"
                "  -t --type=TYPE      List only units of a particular type\n"
@@ -4030,15 +3867,15 @@ static int systemctl_help(void) {
                "                      Do not ask for system passwords\n"
                "     --order          When generating graph for dot, show only order\n"
                "     --require        When generating graph for dot, show only requirement\n"
                "                      Do not ask for system passwords\n"
                "     --order          When generating graph for dot, show only order\n"
                "     --require        When generating graph for dot, show only requirement\n"
-               "     --root=path      Use <root> as the root file system\n"
                "     --system         Connect to system manager\n"
                "     --user           Connect to user service manager\n"
                "     --global         Enable/disable unit files globally\n"
                "  -f --force          When enabling unit files, override existing symlinks\n"
                "                      When shutting down, execute action immediately\n"
                "     --system         Connect to system manager\n"
                "     --user           Connect to user service manager\n"
                "     --global         Enable/disable unit files globally\n"
                "  -f --force          When enabling unit files, override existing symlinks\n"
                "                      When shutting down, execute action immediately\n"
-               "     --defaults       When disabling unit files, remove default symlinks only\n\n"
+               "     --root=PATH      Enable unit files in the specified root directory\n"
+               "     --runtime        Enable unit files only temporarily until next reboot\n\n"
                "Unit Commands:\n"
                "Unit Commands:\n"
-               "  list-units                      List units\n"
+               "  list-units                      List loaded units\n"
                "  start [NAME...]                 Start (activate) one or more units\n"
                "  stop [NAME...]                  Stop (deactivate) one or more units\n"
                "  reload [NAME...]                Reload one or more units\n"
                "  start [NAME...]                 Start (activate) one or more units\n"
                "  stop [NAME...]                  Stop (deactivate) one or more units\n"
                "  reload [NAME...]                Reload one or more units\n"
@@ -4058,8 +3895,16 @@ static int systemctl_help(void) {
                "                                  units\n"
                "  load [NAME...]                  Load one or more units\n\n"
                "Unit File Commands:\n"
                "                                  units\n"
                "  load [NAME...]                  Load one or more units\n\n"
                "Unit File Commands:\n"
+               "  list-unit-files                 List installed unit files\n"
                "  enable [NAME...]                Enable one or more unit files\n"
                "  disable [NAME...]               Disable one or more unit files\n"
                "  enable [NAME...]                Enable one or more unit files\n"
                "  disable [NAME...]               Disable one or more unit files\n"
+               "  reenable [NAME...]              Reenable one or more unit files\n"
+               "  preset [NAME...]                Enable/disable one or more unit files\n"
+               "                                  based on preset configuration\n"
+               "  mask [NAME...]                  Mask one or more units\n"
+               "  unmask [NAME...]                Unmask one or more units\n"
+               "  link [PATH...]                  Link one or more units files into\n"
+               "                                  the search path\n"
                "  is-enabled [NAME...]            Check whether unit files are enabled\n\n"
                "Job Commands:\n"
                "  list-jobs                       List jobs\n"
                "  is-enabled [NAME...]            Check whether unit files are enabled\n\n"
                "Job Commands:\n"
                "  list-jobs                       List jobs\n"
@@ -4174,11 +4019,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_ROOT,
                 ARG_FULL,
                 ARG_NO_RELOAD,
                 ARG_ROOT,
                 ARG_FULL,
                 ARG_NO_RELOAD,
-                ARG_DEFAULTS,
                 ARG_KILL_MODE,
                 ARG_KILL_WHO,
                 ARG_NO_ASK_PASSWORD,
                 ARG_KILL_MODE,
                 ARG_KILL_WHO,
                 ARG_NO_ASK_PASSWORD,
-                ARG_FAILED
+                ARG_FAILED,
+                ARG_RUNTIME
         };
 
         static const struct option options[] = {
         };
 
         static const struct option options[] = {
@@ -4203,13 +4048,13 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "root",      required_argument, NULL, ARG_ROOT      },
                 { "force",     no_argument,       NULL, 'f'           },
                 { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
                 { "root",      required_argument, NULL, ARG_ROOT      },
                 { "force",     no_argument,       NULL, 'f'           },
                 { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
-                { "defaults",  no_argument,       NULL, ARG_DEFAULTS  },
                 { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
                 { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
                 { "signal",    required_argument, NULL, 's'           },
                 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
                 { "host",      required_argument, NULL, 'H'           },
                 { "privileged",no_argument,       NULL, 'P'           },
                 { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
                 { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
                 { "signal",    required_argument, NULL, 's'           },
                 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
                 { "host",      required_argument, NULL, 'H'           },
                 { "privileged",no_argument,       NULL, 'P'           },
+                { "runtime",   no_argument,       NULL, ARG_RUNTIME   },
                 { NULL,        0,                 NULL, 0             }
         };
 
                 { NULL,        0,                 NULL, 0             }
         };
 
@@ -4268,11 +4113,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_USER:
                         break;
 
                 case ARG_USER:
-                        arg_user = true;
+                        arg_scope = UNIT_FILE_USER;
                         break;
 
                 case ARG_SYSTEM:
                         break;
 
                 case ARG_SYSTEM:
-                        arg_user = false;
+                        arg_scope = UNIT_FILE_SYSTEM;
+                        break;
+
+                case ARG_GLOBAL:
+                        arg_scope = UNIT_FILE_GLOBAL;
                         break;
 
                 case ARG_NO_BLOCK:
                         break;
 
                 case ARG_NO_BLOCK:
@@ -4319,15 +4168,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_no_reload = true;
                         break;
 
                         arg_no_reload = true;
                         break;
 
-                case ARG_GLOBAL:
-                        arg_global = true;
-                        arg_user = true;
-                        break;
-
-                case ARG_DEFAULTS:
-                        arg_defaults = true;
-                        break;
-
                 case ARG_KILL_WHO:
                         arg_kill_who = optarg;
                         break;
                 case ARG_KILL_WHO:
                         arg_kill_who = optarg;
                         break;
@@ -4356,6 +4196,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_host = optarg;
                         break;
 
                         arg_host = optarg;
                         break;
 
+                case ARG_RUNTIME:
+                        arg_runtime = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
                 case '?':
                         return -EINVAL;
 
@@ -4365,7 +4209,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 }
         }
 
                 }
         }
 
-        if (arg_transport != TRANSPORT_NORMAL && arg_user) {
+        if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
                 log_error("Cannot access user instance remotely.");
                 return -EINVAL;
         }
                 log_error("Cannot access user instance remotely.");
                 return -EINVAL;
         }
@@ -4955,9 +4799,10 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                         EQUAL
                 } argc_cmp;
                 const int argc;
                         EQUAL
                 } argc_cmp;
                 const int argc;
-                int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
+                int (* const dispatch)(DBusConnection *bus, char **args);
         } verbs[] = {
                 { "list-units",            LESS,  1, list_units        },
         } verbs[] = {
                 { "list-units",            LESS,  1, list_units        },
+                { "list-unit-files",       EQUAL, 1, list_unit_files   },
                 { "list-jobs",             EQUAL, 1, list_jobs         },
                 { "clear-jobs",            EQUAL, 1, daemon_reload     },
                 { "load",                  MORE,  2, load_unit         },
                 { "list-jobs",             EQUAL, 1, list_jobs         },
                 { "clear-jobs",            EQUAL, 1, daemon_reload     },
                 { "load",                  MORE,  2, load_unit         },
@@ -4999,7 +4844,12 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "reset-failed",          MORE,  1, reset_failed      },
                 { "enable",                MORE,  2, enable_unit       },
                 { "disable",               MORE,  2, enable_unit       },
                 { "reset-failed",          MORE,  1, reset_failed      },
                 { "enable",                MORE,  2, enable_unit       },
                 { "disable",               MORE,  2, enable_unit       },
-                { "is-enabled",            MORE,  2, enable_unit       }
+                { "is-enabled",            MORE,  2, unit_is_enabled   },
+                { "reenable",              MORE,  2, enable_unit       },
+                { "preset",                MORE,  2, enable_unit       },
+                { "mask",                  MORE,  2, enable_unit       },
+                { "unmask",                MORE,  2, enable_unit       },
+                { "link",                  MORE,  2, enable_unit       }
         };
 
         int left;
         };
 
         int left;
@@ -5062,20 +4912,27 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
 
         /* Require a bus connection for all operations but
          * enable/disable */
 
         /* Require a bus connection for all operations but
          * enable/disable */
-        if (!streq(verbs[i].verb, "enable") && !streq(verbs[i].verb, "disable")) {
+        if (!streq(verbs[i].verb, "enable") &&
+            !streq(verbs[i].verb, "disable") &&
+            !streq(verbs[i].verb, "is-enable") &&
+            !streq(verbs[i].verb, "reenable") &&
+            !streq(verbs[i].verb, "preset") &&
+            !streq(verbs[i].verb, "mask") &&
+            !streq(verbs[i].verb, "unmask") &&
+            !streq(verbs[i].verb, "link")) {
 
                 if (running_in_chroot() > 0) {
                         log_info("Running in chroot, ignoring request.");
                         return 0;
                 }
 
 
                 if (running_in_chroot() > 0) {
                         log_info("Running in chroot, ignoring request.");
                         return 0;
                 }
 
-                if (!bus) {
+                if (!bus && !avoid_bus()) {
                         log_error("Failed to get D-Bus connection: %s", error->message);
                         return -EIO;
                 }
         }
 
                         log_error("Failed to get D-Bus connection: %s", error->message);
                         return -EIO;
                 }
         }
 
-        return verbs[i].dispatch(bus, argv + optind, left);
+        return verbs[i].dispatch(bus, argv + optind);
 }
 
 static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
 }
 
 static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
@@ -5126,7 +4983,7 @@ static int reload_with_fallback(DBusConnection *bus) {
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
-                if (daemon_reload(bus, NULL, 0) > 0)
+                if (daemon_reload(bus, NULL) > 0)
                         return 0;
         }
 
                         return 0;
         }
 
@@ -5145,7 +5002,7 @@ static int start_with_fallback(DBusConnection *bus) {
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
-                if (start_unit(bus, NULL, 0) >= 0)
+                if (start_unit(bus, NULL) >= 0)
                         goto done;
         }
 
                         goto done;
         }
 
@@ -5246,8 +5103,9 @@ static int halt_main(DBusConnection *bus) {
 static int runlevel_main(void) {
         int r, runlevel, previous;
 
 static int runlevel_main(void) {
         int r, runlevel, previous;
 
-        if ((r = utmp_get_runlevel(&runlevel, &previous)) < 0) {
-                printf("unknown\n");
+        r = utmp_get_runlevel(&runlevel, &previous);
+        if (r < 0) {
+                puts("unknown");
                 return r;
         }
 
                 return r;
         }
 
@@ -5258,19 +5116,6 @@ static int runlevel_main(void) {
         return 0;
 }
 
         return 0;
 }
 
-static void agent_close(void) {
-        siginfo_t dummy;
-
-        if (agent_pid <= 0)
-                return;
-
-        /* Inform agent that we are done */
-        kill(agent_pid, SIGTERM);
-        kill(agent_pid, SIGCONT);
-        wait_for_terminate(agent_pid, &dummy);
-        agent_pid = 0;
-}
-
 int main(int argc, char*argv[]) {
         int r, retval = EXIT_FAILURE;
         DBusConnection *bus = NULL;
 int main(int argc, char*argv[]) {
         int r, retval = EXIT_FAILURE;
         DBusConnection *bus = NULL;
@@ -5302,16 +5147,18 @@ int main(int argc, char*argv[]) {
                 goto finish;
         }
 
                 goto finish;
         }
 
-        if (arg_transport == TRANSPORT_NORMAL)
-                bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error);
-        else if (arg_transport == TRANSPORT_POLKIT) {
-                bus_connect_system_polkit(&bus, &error);
-                private_bus = false;
-        } else if (arg_transport == TRANSPORT_SSH) {
-                bus_connect_system_ssh(NULL, arg_host, &bus, &error);
-                private_bus = false;
-        } else
-                assert_not_reached("Uh, invalid transport...");
+        if (!avoid_bus()) {
+                if (arg_transport == TRANSPORT_NORMAL)
+                        bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
+                else if (arg_transport == TRANSPORT_POLKIT) {
+                        bus_connect_system_polkit(&bus, &error);
+                        private_bus = false;
+                } else if (arg_transport == TRANSPORT_SSH) {
+                        bus_connect_system_ssh(NULL, arg_host, &bus, &error);
+                        private_bus = false;
+                } else
+                        assert_not_reached("Uh, invalid transport...");
+        }
 
         switch (arg_action) {
 
 
         switch (arg_action) {
 
@@ -5354,7 +5201,6 @@ int main(int argc, char*argv[]) {
         retval = r < 0 ? EXIT_FAILURE : r;
 
 finish:
         retval = r < 0 ? EXIT_FAILURE : r;
 
 finish:
-
         if (bus) {
                 dbus_connection_flush(bus);
                 dbus_connection_close(bus);
         if (bus) {
                 dbus_connection_flush(bus);
                 dbus_connection_close(bus);