chiark / gitweb /
core: fix property changes in transient units
[elogind.git] / src / core / dbus-unit.c
index c7532c72552b4deae843c28229e6b35a05e8c625..8156fda82a51a141769672d0768f312da18b027a 100644 (file)
   Copyright 2010 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
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
   (at your option) any later version.
 
   systemd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
+  Lesser General Public License for more details.
 
-  You should have received a copy of the GNU General Public License
+  You should have received a copy of the GNU Lesser General Public License
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <errno.h>
-
-#include "dbus.h"
+#include "sd-bus.h"
 #include "log.h"
+#include "selinux-access.h"
+#include "cgroup-util.h"
+#include "strv.h"
+#include "path-util.h"
+#include "fileio.h"
 #include "dbus-unit.h"
+#include "dbus-manager.h"
 #include "bus-errors.h"
-#include "dbus-common.h"
-
-const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
-
-#define INVALIDATING_PROPERTIES                 \
-        "LoadState\0"                           \
-        "ActiveState\0"                         \
-        "SubState\0"                            \
-        "InactiveExitTimestamp\0"               \
-        "ActiveEnterTimestamp\0"                \
-        "ActiveExitTimestamp\0"                 \
-        "InactiveEnterTimestamp\0"              \
-        "Job\0"                                 \
-        "NeedDaemonReload\0"
-
-static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
-        char *t;
-        Iterator j;
-        DBusMessageIter sub;
-        Unit *u = data;
+#include "dbus-client-track.h"
+
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
+
+static int property_get_names(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Unit *u = userdata;
+        Iterator i;
+        const char *t;
+        int r;
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
-                return -ENOMEM;
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-        SET_FOREACH(t, u->names, j)
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
-                        return -ENOMEM;
+        r = sd_bus_message_open_container(reply, 'a', "s");
+        if (r < 0)
+                return r;
 
-        if (!dbus_message_iter_close_container(i, &sub))
-                return -ENOMEM;
+        SET_FOREACH(t, u->names, i) {
+                r = sd_bus_message_append(reply, "s", t);
+                if (r < 0)
+                        return r;
+        }
 
-        return 0;
+        return sd_bus_message_close_container(reply);
 }
 
-static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data, *f;
-        const char *d;
+static int property_get_following(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Unit *u = userdata, *f;
 
-        assert(i);
-        assert(property);
+        assert(bus);
+        assert(reply);
         assert(u);
 
         f = unit_following(u);
-        d = f ? f->id : "";
-
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
-                return -ENOMEM;
-
-        return 0;
+        return sd_bus_message_append(reply, "s", f ? f->id : "");
 }
 
-static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u;
+static int property_get_dependencies(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Set *s = *(Set**) userdata;
         Iterator j;
-        DBusMessageIter sub;
-        Set *s = data;
+        Unit *u;
+        int r;
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
-                return -ENOMEM;
+        assert(bus);
+        assert(reply);
 
-        SET_FOREACH(u, s, j)
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
-                        return -ENOMEM;
+        r = sd_bus_message_open_container(reply, 'a', "s");
+        if (r < 0)
+                return r;
 
-        if (!dbus_message_iter_close_container(i, &sub))
-                return -ENOMEM;
+        SET_FOREACH(u, s, j) {
+                r = sd_bus_message_append(reply, "s", u->id);
+                if (r < 0)
+                        return r;
+        }
 
-        return 0;
+        return sd_bus_message_close_container(reply);
 }
 
-static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        const char *d;
-
-        assert(i);
-        assert(property);
-        assert(u);
+static int property_get_description(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-        d = unit_description(u);
+        Unit *u = userdata;
 
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
-                return -ENOMEM;
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-        return 0;
+        return sd_bus_message_append(reply, "s", unit_description(u));
 }
 
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
+static int property_get_active_state(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        const char *state;
+        Unit *u = userdata;
 
-        assert(i);
-        assert(property);
+        assert(bus);
+        assert(reply);
         assert(u);
 
-        state = unit_active_state_to_string(unit_active_state(u));
-
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
-                return -ENOMEM;
-
-        return 0;
+        return sd_bus_message_append(reply, "s", unit_active_state_to_string(unit_active_state(u)));
 }
 
-static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        const char *state;
+static int property_get_sub_state(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-        assert(i);
-        assert(property);
-        assert(u);
-
-        state = unit_sub_state_to_string(u);
+        Unit *u = userdata;
 
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
-                return -ENOMEM;
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-        return 0;
+        return sd_bus_message_append(reply, "s", unit_sub_state_to_string(u));
 }
 
-static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        const char *state;
-
-        assert(i);
-        assert(property);
-        assert(u);
+static int property_get_unit_file_state(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-        state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
+        Unit *u = userdata;
 
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
-                return -ENOMEM;
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-        return 0;
+        return sd_bus_message_append(reply, "s", unit_file_state_to_string(unit_get_unit_file_state(u)));
 }
 
-static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        dbus_bool_t b;
-
-        assert(i);
-        assert(property);
-        assert(u);
+static int property_get_can_start(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-        b = unit_can_start(u) &&
-                !u->refuse_manual_start;
+        Unit *u = userdata;
 
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
-                return -ENOMEM;
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-        return 0;
+        return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_start);
 }
 
-static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        dbus_bool_t b;
+static int property_get_can_stop(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-        assert(i);
-        assert(property);
+        Unit *u = userdata;
+
+        assert(bus);
+        assert(reply);
         assert(u);
 
         /* On the lower levels we assume that every unit we can start
          * we can also stop */
 
-        b = unit_can_start(u) &&
-                !u->refuse_manual_stop;
-
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
-                return -ENOMEM;
-
-        return 0;
+        return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_stop);
 }
 
-static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        dbus_bool_t b;
+static int property_get_can_reload(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Unit *u = userdata;
 
-        assert(i);
-        assert(property);
+        assert(bus);
+        assert(reply);
         assert(u);
 
-        b = unit_can_reload(u);
+        return sd_bus_message_append(reply, "b", unit_can_reload(u));
+}
 
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
-                return -ENOMEM;
+static int property_get_can_isolate(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-        return 0;
+        Unit *u = userdata;
+
+        assert(bus);
+        assert(reply);
+        assert(u);
+
+        return sd_bus_message_append(reply, "b", unit_can_isolate(u) && !u->refuse_manual_start);
 }
 
-static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        dbus_bool_t b;
+static int property_get_job(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *p = NULL;
+        Unit *u = userdata;
 
-        assert(i);
-        assert(property);
+        assert(bus);
+        assert(reply);
         assert(u);
 
-        b = unit_can_isolate(u) &&
-                !u->refuse_manual_start;
+        if (!u->job)
+                return sd_bus_message_append(reply, "(uo)", 0, "/");
 
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+        p = job_dbus_path(u->job);
+        if (!p)
                 return -ENOMEM;
 
-        return 0;
+        return sd_bus_message_append(reply, "(uo)", u->job->id, p);
 }
 
-static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        DBusMessageIter sub;
-        char *p;
+static int property_get_need_daemon_reload(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-        assert(i);
-        assert(property);
-        assert(u);
+        Unit *u = userdata;
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
-                return -ENOMEM;
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-        if (u->job) {
+        return sd_bus_message_append(reply, "b", unit_need_daemon_reload(u));
+}
 
-                if (!(p = job_dbus_path(u->job)))
-                        return -ENOMEM;
+static int property_get_conditions(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Unit *u = userdata;
+        Condition *c;
+        int r;
 
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
-                    !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
-                        free(p);
-                        return -ENOMEM;
-                }
-        } else {
-                uint32_t id = 0;
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-                /* No job, so let's fill in some placeholder
-                 * data. Since we need to fill in a valid path we
-                 * simple point to ourselves. */
+        r = sd_bus_message_open_container(reply, 'a', "(sbbsi)");
+        if (r < 0)
+                return r;
 
-                if (!(p = unit_dbus_path(u)))
-                        return -ENOMEM;
+        LIST_FOREACH(conditions, c, u->conditions) {
+                r = sd_bus_message_append(reply, "(sbbsi)",
+                                          condition_type_to_string(c->type),
+                                          c->trigger, c->negate,
+                                          c->parameter, c->state);
+                if (r < 0)
+                        return r;
 
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
-                    !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
-                        free(p);
-                        return -ENOMEM;
-                }
         }
 
-        free(p);
-
-        if (!dbus_message_iter_close_container(i, &sub))
-                return -ENOMEM;
-
-        return 0;
+        return sd_bus_message_close_container(reply);
 }
 
-static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        char *t;
-        CGroupBonding *cgb;
-        bool success;
+static int property_get_load_error(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-        assert(i);
-        assert(property);
-        assert(u);
-
-        if ((cgb = unit_get_default_cgroup(u))) {
-                if (!(t = cgroup_bonding_to_string(cgb)))
-                        return -ENOMEM;
-        } else
-                t = (char*) "";
+        _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
+        Unit *u = userdata;
 
-        success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-        if (cgb)
-                free(t);
+        if (u->load_error != 0)
+                sd_bus_error_set_errno(&e, u->load_error);
 
-        return success ? 0 : -ENOMEM;
+        return sd_bus_message_append(reply, "(ss)", e.name, e.message);
 }
 
-static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        CGroupBonding *cgb;
-        DBusMessageIter sub;
+int bus_unit_method_start_generic(sd_bus *bus, sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error) {
+        const char *smode;
+        JobMode mode;
+        int r;
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
-                return -ENOMEM;
+        assert(bus);
+        assert(message);
+        assert(u);
+        assert(job_type >= 0 && job_type < _JOB_TYPE_MAX);
 
-        LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
-                char *t;
-                bool success;
+        r = sd_bus_message_read(message, "s", &smode);
+        if (r < 0)
+                return r;
 
-                if (!(t = cgroup_bonding_to_string(cgb)))
-                        return -ENOMEM;
+        mode = job_mode_from_string(smode);
+        if (mode < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
 
-                success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
-                free(t);
+        return bus_unit_queue_job(bus, message, u, job_type, mode, reload_if_possible, error);
+}
 
-                if (!success)
-                        return -ENOMEM;
-        }
+static int method_start(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_start_generic(bus, message, userdata, JOB_START, false, error);
+}
 
-        if (!dbus_message_iter_close_container(i, &sub))
-                return -ENOMEM;
+static int method_stop(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_start_generic(bus, message, userdata, JOB_STOP, false, error);
+}
 
-        return 0;
+static int method_reload(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_start_generic(bus, message, userdata, JOB_RELOAD, false, error);
 }
 
-static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        CGroupAttribute *a;
-        DBusMessageIter sub, sub2;
+static int method_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_start_generic(bus, message, userdata, JOB_RESTART, false, error);
+}
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
-                return -ENOMEM;
+static int method_try_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_start_generic(bus, message, userdata, JOB_TRY_RESTART, false, error);
+}
 
-        LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
-                char *v = NULL;
-                bool success;
+static int method_reload_or_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_start_generic(bus, message, userdata, JOB_RESTART, true, error);
+}
 
-                if (a->map_callback)
-                        a->map_callback(a->controller, a->name, a->value, &v);
+static int method_reload_or_try_restart(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_start_generic(bus, message, userdata, JOB_TRY_RESTART, true, error);
+}
 
-                success =
-                        dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
-                        dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
-                        dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
-                        dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
-                        dbus_message_iter_close_container(&sub, &sub2);
+int bus_unit_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Unit *u = userdata;
+        const char *swho;
+        int32_t signo;
+        KillWho who;
+        int r;
 
-                free(v);
+        assert(bus);
+        assert(message);
+        assert(u);
+
+        r = sd_bus_message_read(message, "si", &swho, &signo);
+        if (r < 0)
+                return r;
 
-                if (!success)
-                        return -ENOMEM;
+        if (isempty(swho))
+                who = KILL_ALL;
+        else {
+                who = kill_who_from_string(swho);
+                if (who < 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho);
         }
 
-        if (!dbus_message_iter_close_container(i, &sub))
-                return -ENOMEM;
+        if (signo <= 0 || signo >= _NSIG)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
 
-        return 0;
+        r = selinux_unit_access_check(u, bus, message, "stop", error);
+        if (r < 0)
+                return r;
+
+        r = unit_kill(u, who, signo, error);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
 }
 
-static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        dbus_bool_t b;
+int bus_unit_method_reset_failed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Unit *u = userdata;
+        int r;
 
-        assert(i);
-        assert(property);
+        assert(bus);
+        assert(message);
         assert(u);
 
-        b = unit_need_daemon_reload(u);
+        r = selinux_unit_access_check(u, bus, message, "reload", error);
+        if (r < 0)
+                return r;
 
-        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
-                return -ENOMEM;
+        unit_reset_failed(u);
 
-        return 0;
+        return sd_bus_reply_method_return(message, NULL);
 }
 
-static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
-        Unit *u = data;
-        const char *name, *message;
-        DBusMessageIter sub;
+int bus_unit_method_set_properties(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Unit *u = userdata;
+        int runtime, r;
 
-        assert(i);
-        assert(property);
+        assert(bus);
+        assert(message);
         assert(u);
 
-        if (u->load_error != 0) {
-                name = bus_errno_to_dbus(u->load_error);
-                message = strempty(strerror(-u->load_error));
-        } else
-                name = message = "";
+        r = sd_bus_message_read(message, "b", &runtime);
+        if (r < 0)
+                return r;
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
-            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
-            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
-            !dbus_message_iter_close_container(i, &sub))
-                return -ENOMEM;
+        r = selinux_unit_access_check(u, bus, message, "start", error);
+        if (r < 0)
+                return r;
 
-        return 0;
-}
+        r = bus_unit_set_properties(u, message, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, error);
+        if (r < 0)
+                return r;
 
-static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
-        DBusMessage *reply = NULL;
-        Manager *m = u->manager;
-        DBusError error;
-        JobType job_type = _JOB_TYPE_INVALID;
-        char *path = NULL;
-        bool reload_if_possible = false;
-
-        dbus_error_init(&error);
-
-        if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
-                job_type = JOB_START;
-        else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
-                job_type = JOB_STOP;
-        else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
-                job_type = JOB_RELOAD;
-        else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
-                job_type = JOB_RESTART;
-        else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
-                job_type = JOB_TRY_RESTART;
-        else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
-                reload_if_possible = true;
-                job_type = JOB_RESTART;
-        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
-                reload_if_possible = true;
-                job_type = JOB_TRY_RESTART;
-        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
-                const char *swho, *smode;
-                int32_t signo;
-                KillMode mode;
-                KillWho who;
-                int r;
-
-                if (!dbus_message_get_args(
-                                    message,
-                                    &error,
-                                    DBUS_TYPE_STRING, &swho,
-                                    DBUS_TYPE_STRING, &smode,
-                                    DBUS_TYPE_INT32, &signo,
-                                    DBUS_TYPE_INVALID))
-                        return bus_send_error_reply(connection, message, &error, -EINVAL);
-
-                if (isempty(swho))
-                        who = KILL_ALL;
-                else {
-                        who = kill_who_from_string(swho);
-                        if (who < 0)
-                                return bus_send_error_reply(connection, message, &error, -EINVAL);
-                }
+        return sd_bus_reply_method_return(message, NULL);
+}
 
-                if (isempty(smode))
-                        mode = KILL_CONTROL_GROUP;
-                else {
-                        mode = kill_mode_from_string(smode);
-                        if (mode < 0)
-                                return bus_send_error_reply(connection, message, &error, -EINVAL);
-                }
+const sd_bus_vtable bus_unit_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
+        SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Before", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BEFORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("After", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_AFTER]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_ON_FAILURE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DropInPaths", "as", NULL, offsetof(Unit, dropin_paths), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("UnitFileState", "s", property_get_unit_file_state, 0, 0),
+        BUS_PROPERTY_DUAL_TIMESTAMP("InactiveExitTimestamp", offsetof(Unit, inactive_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        BUS_PROPERTY_DUAL_TIMESTAMP("ActiveEnterTimestamp", offsetof(Unit, active_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        BUS_PROPERTY_DUAL_TIMESTAMP("ActiveExitTimestamp", offsetof(Unit, active_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        BUS_PROPERTY_DUAL_TIMESTAMP("InactiveEnterTimestamp", offsetof(Unit, inactive_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("CanStart", "b", property_get_can_start, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Job", "(uo)", property_get_job, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("IgnoreOnSnapshot", "b", bus_property_get_bool, offsetof(Unit, ignore_on_snapshot), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        BUS_PROPERTY_DUAL_TIMESTAMP("ConditionTimestamp", offsetof(Unit, condition_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, 0, 0),
+        SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
+
+        SD_BUS_METHOD("Start", "s", "o", method_start, 0),
+        SD_BUS_METHOD("Stop", "s", "o", method_stop, 0),
+        SD_BUS_METHOD("Reload", "s", "o", method_reload, 0),
+        SD_BUS_METHOD("Restart", "s", "o", method_restart, 0),
+        SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, 0),
+        SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, 0),
+        SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, 0),
+        SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, 0),
+        SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, 0),
+        SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, 0),
+
+        SD_BUS_VTABLE_END
+};
 
-                if (signo <= 0 || signo >= _NSIG)
-                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+static int property_get_slice(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
 
-                if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
-                        return bus_send_error_reply(connection, message, &error, r);
+        Unit *u = userdata;
 
-                if (!(reply = dbus_message_new_method_return(message)))
-                        goto oom;
+        assert(bus);
+        assert(reply);
+        assert(u);
 
-        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
+        return sd_bus_message_append(reply, "s", unit_slice_name(u));
+}
 
-                unit_reset_failed(u);
+const sd_bus_vtable bus_unit_cgroup_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+        SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
+        SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0),
+        SD_BUS_VTABLE_END
+};
 
-                if (!(reply = dbus_message_new_method_return(message)))
-                        goto oom;
+static int send_new_signal(sd_bus *bus, const char *destination, void *userdata) {
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+        _cleanup_free_ char *p = NULL;
+        Unit *u = userdata;
+        int r;
 
-        } else if (UNIT_VTABLE(u)->bus_message_handler)
-                return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
-        else
-                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        assert(bus);
+        assert(u);
 
-        if (job_type != _JOB_TYPE_INVALID) {
-                const char *smode;
-                JobMode mode;
-                Job *j;
-                int r;
+        p = unit_dbus_path(u);
+        if (!u)
+                return -ENOMEM;
 
-                if ((job_type == JOB_START && u->refuse_manual_start) ||
-                    (job_type == JOB_STOP && u->refuse_manual_stop) ||
-                    ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
-                     (u->refuse_manual_start || u->refuse_manual_stop))) {
-                        dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
-                        return bus_send_error_reply(connection, message, &error, -EPERM);
-                }
+        r = sd_bus_message_new_signal(
+                        bus,
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "UnitNew",
+                        &m);
+        if (r < 0)
+                return r;
 
-                if (!dbus_message_get_args(
-                                    message,
-                                    &error,
-                                    DBUS_TYPE_STRING, &smode,
-                                    DBUS_TYPE_INVALID))
-                        return bus_send_error_reply(connection, message, &error, -EINVAL);
-
-                if (reload_if_possible && unit_can_reload(u)) {
-                        if (job_type == JOB_RESTART)
-                                job_type = JOB_RELOAD_OR_START;
-                        else if (job_type == JOB_TRY_RESTART)
-                                job_type = JOB_RELOAD;
-                }
+        r = sd_bus_message_append(m, "so", u->id, p);
+        if (r < 0)
+                return r;
 
-                if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
-                        dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
-                        return bus_send_error_reply(connection, message, &error, -EINVAL);
-                }
+        return sd_bus_send_to(bus, m, destination, NULL);
+}
 
-                if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
-                        return bus_send_error_reply(connection, message, &error, r);
+static int send_changed_signal(sd_bus *bus, const char *destination, void *userdata) {
+        _cleanup_free_ char *p = NULL;
+        Unit *u = userdata;
+        int r;
 
-                if (!(reply = dbus_message_new_method_return(message)))
-                        goto oom;
+        assert(bus);
+        assert(u);
 
-                if (!(path = job_dbus_path(j)))
-                        goto oom;
+        p = unit_dbus_path(u);
+        if (!p)
+                return -ENOMEM;
 
-                if (!dbus_message_append_args(
-                                    reply,
-                                    DBUS_TYPE_OBJECT_PATH, &path,
-                                    DBUS_TYPE_INVALID))
-                        goto oom;
+        /* Send a properties changed signal. First for the specific
+         * type, then for the generic unit. The clients may rely on
+         * this order to get atomic behavior if needed. */
+
+        r = sd_bus_emit_properties_changed_strv(
+                        bus, p,
+                        UNIT_VTABLE(u)->bus_interface,
+                        NULL);
+        if (r < 0) {
+                log_warning("Failed to send out specific PropertiesChanged signal for %s: %s", u->id, strerror(-r));
+                return r;
         }
 
-        if (reply) {
-                if (!dbus_connection_send(connection, reply, NULL))
-                        goto oom;
-
-                dbus_message_unref(reply);
+        r = sd_bus_emit_properties_changed_strv(
+                        bus, p,
+                        "org.freedesktop.systemd1.Unit",
+                        NULL);
+        if (r < 0) {
+                log_warning("Failed to send out generic PropertiesChanged signal for %s: %s", u->id, strerror(-r));
+                return r;
         }
 
-        free(path);
+        return 0;
+}
 
-        return DBUS_HANDLER_RESULT_HANDLED;
+void bus_unit_send_change_signal(Unit *u) {
+        int r;
+        assert(u);
 
-oom:
-        free(path);
+        if (u->in_dbus_queue) {
+                LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
+                u->in_dbus_queue = false;
+        }
 
-        if (reply)
-                dbus_message_unref(reply);
+        if (!u->id)
+                return;
 
-        dbus_error_free(&error);
+        r = bus_manager_foreach_client(u->manager, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u);
+        if (r < 0)
+                log_debug("Failed to send unit change signal for %s: %s", u->id, strerror(-r));
 
-        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+        u->sent_dbus_new_signal = true;
 }
 
-static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
-        Manager *m = data;
-        Unit *u;
+static int send_removed_signal(sd_bus *bus, const char *destination, void *userdata) {
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+        _cleanup_free_ char *p = NULL;
+        Unit *u = userdata;
         int r;
-        DBusMessage *reply;
 
-        assert(connection);
-        assert(message);
-        assert(m);
-
-        if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
-                /* Be nice to gdbus and return introspection data for our mid-level paths */
+        assert(bus);
+        assert(u);
 
-                if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
-                        char *introspection = NULL;
-                        FILE *f;
-                        Iterator i;
-                        const char *k;
-                        size_t size;
+        p = unit_dbus_path(u);
+        if (!u)
+                return -ENOMEM;
 
-                        if (!(reply = dbus_message_new_method_return(message)))
-                                goto oom;
+        r = sd_bus_message_new_signal(
+                        bus,
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "UnitRemoved",
+                        &m);
+        if (r < 0)
+                return r;
 
-                        /* We roll our own introspection code here, instead of
-                         * relying on bus_default_message_handler() because we
-                         * need to generate our introspection string
-                         * dynamically. */
+        r = sd_bus_message_append(m, "so", u->id, p);
+        if (r < 0)
+                return r;
 
-                        if (!(f = open_memstream(&introspection, &size)))
-                                goto oom;
+        return sd_bus_send_to(bus, m, destination, NULL);
+}
 
-                        fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
-                              "<node>\n", f);
+void bus_unit_send_removed_signal(Unit *u) {
+        int r;
+        assert(u);
 
-                        fputs(BUS_INTROSPECTABLE_INTERFACE, f);
-                        fputs(BUS_PEER_INTERFACE, f);
+        if (!u->sent_dbus_new_signal)
+                bus_unit_send_change_signal(u);
 
-                        HASHMAP_FOREACH_KEY(u, k, m->units, i) {
-                                char *p;
+        if (!u->id)
+                return;
 
-                                if (k != u->id)
-                                        continue;
+        r = bus_manager_foreach_client(u->manager, send_removed_signal, u);
+        if (r < 0)
+                log_debug("Failed to send unit remove signal for %s: %s", u->id, strerror(-r));
+}
 
-                                if (!(p = bus_path_escape(k))) {
-                                        fclose(f);
-                                        free(introspection);
-                                        goto oom;
-                                }
+int bus_unit_queue_job(
+                sd_bus *bus,
+                sd_bus_message *message,
+                Unit *u,
+                JobType type,
+                JobMode mode,
+                bool reload_if_possible,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *path = NULL;
+        Job *j;
+        int r;
 
-                                fprintf(f, "<node name=\"%s\"/>", p);
-                                free(p);
-                        }
+        assert(bus);
+        assert(message);
+        assert(u);
+        assert(type >= 0 && type < _JOB_TYPE_MAX);
+        assert(mode >= 0 && mode < _JOB_MODE_MAX);
+
+        if (reload_if_possible && unit_can_reload(u)) {
+                if (type == JOB_RESTART)
+                        type = JOB_RELOAD_OR_START;
+                else if (type == JOB_TRY_RESTART)
+                        type = JOB_RELOAD;
+        }
 
-                        fputs("</node>\n", f);
+        r = selinux_unit_access_check(
+                        u, bus, message,
+                        (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
+                        type == JOB_STOP ? "stop" : "reload", error);
+        if (r < 0)
+                return r;
+
+        if (type == JOB_STOP &&
+            (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) &&
+            unit_active_state(u) == UNIT_INACTIVE)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
+
+        if ((type == JOB_START && u->refuse_manual_start) ||
+            (type == JOB_STOP && u->refuse_manual_stop) ||
+            ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)))
+                return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id);
+
+        r = manager_add_job(u->manager, type, u, mode, true, error, &j);
+        if (r < 0)
+                return r;
+
+        r = bus_client_track(&j->subscribed, bus, sd_bus_message_get_sender(message));
+        if (r < 0)
+                return r;
+
+        path = job_dbus_path(j);
+        if (!path)
+                return -ENOMEM;
 
-                        if (ferror(f)) {
-                                fclose(f);
-                                free(introspection);
-                                goto oom;
-                        }
+        return sd_bus_reply_method_return(message, "o", path);
+}
 
-                        fclose(f);
+static int bus_unit_set_transient_property(
+                Unit *u,
+                const char *name,
+                sd_bus_message *message,
+                UnitSetPropertiesMode mode,
+                sd_bus_error *error) {
 
-                        if (!introspection)
-                                goto oom;
+        int r;
 
-                        if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
-                                free(introspection);
-                                goto oom;
-                        }
+        assert(u);
+        assert(name);
+        assert(message);
 
-                        free(introspection);
+        if (streq(name, "Description")) {
+                const char *d;
 
-                        if (!dbus_connection_send(connection, reply, NULL))
-                                goto oom;
+                r = sd_bus_message_read(message, "s", &d);
+                if (r < 0)
+                        return r;
 
-                        dbus_message_unref(reply);
+                if (mode != UNIT_CHECK) {
+                        r = unit_set_description(u, d);
+                        if (r < 0)
+                                return r;
 
-                        return DBUS_HANDLER_RESULT_HANDLED;
+                        unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", d);
                 }
 
-                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        }
-
-        if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
+                return 1;
 
-                if (r == -ENOMEM)
-                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+        } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
+                const char *s;
 
-                if (r == -ENOENT) {
-                        DBusError e;
+                r = sd_bus_message_read(message, "s", &s);
+                if (r < 0)
+                        return r;
 
-                        dbus_error_init(&e);
-                        dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown unit");
-                        return bus_send_error_reply(connection, message, &e, r);
-                }
+                if (!unit_name_is_valid(s, TEMPLATE_INVALID) || !endswith(s, ".slice"))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s);
 
-                return bus_send_error_reply(connection, message, NULL, r);
-        }
+                if (isempty(s)) {
+                        if (mode != UNIT_CHECK) {
+                                unit_ref_unset(&u->slice);
+                                unit_remove_drop_in(u, mode, name);
+                        }
+                } else {
+                        Unit *slice;
 
-        return bus_unit_message_dispatch(u, connection, message);
+                        r = manager_load_unit(u->manager, s, NULL, error, &slice);
+                        if (r < 0)
+                                return r;
 
-oom:
-        if (reply)
-                dbus_message_unref(reply);
+                        if (slice->type != UNIT_SLICE)
+                                return -EINVAL;
 
-        return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
+                        if (mode != UNIT_CHECK) {
+                                unit_ref_set(&u->slice, slice);
+                                unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
+                        }
+                }
 
-const DBusObjectPathVTable bus_unit_vtable = {
-        .message_function = bus_unit_message_handler
-};
+                return 1;
+
+        } else if (streq(name, "Requires") ||
+                   streq(name, "RequiresOverridable") ||
+                   streq(name, "Requisite") ||
+                   streq(name, "RequisiteOverridable") ||
+                   streq(name, "Wants") ||
+                   streq(name, "BindsTo") ||
+                   streq(name, "Conflicts") ||
+                   streq(name, "Before") ||
+                   streq(name, "After") ||
+                   streq(name, "OnFailure") ||
+                   streq(name, "PropagatesReloadTo") ||
+                   streq(name, "ReloadPropagatedFrom") ||
+                   streq(name, "PartOf")) {
+
+                UnitDependency d;
+                const char *other;
+
+                d = unit_dependency_from_string(name);
+                if (d < 0)
+                        return -EINVAL;
+
+                r = sd_bus_message_enter_container(message, 'a', "s");
+                if (r < 0)
+                        return r;
+
+                while ((r = sd_bus_message_read(message, "s", &other)) > 0) {
+                        if (!unit_name_is_valid(other, TEMPLATE_INVALID))
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name %s", other);
+
+                        if (mode != UNIT_CHECK) {
+                                _cleanup_free_ char *label = NULL;
+
+                                r = unit_add_dependency_by_name(u, d, other, NULL, true);
+                                if (r < 0)
+                                        return r;
+
+                                label = strjoin(name, "-", other, NULL);
+                                if (!label)
+                                        return -ENOMEM;
+
+                                unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
+                        }
 
-void bus_unit_send_change_signal(Unit *u) {
-        char *p = NULL;
-        DBusMessage *m = NULL;
+                }
+                if (r < 0)
+                        return r;
 
-        assert(u);
+                r = sd_bus_message_exit_container(message);
+                if (r < 0)
+                        return r;
 
-        if (u->in_dbus_queue) {
-                LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
-                u->in_dbus_queue = false;
+                return 1;
         }
 
-        if (!u->id)
-                return;
-
-        if (!bus_has_subscriber(u->manager)) {
-                u->sent_dbus_new_signal = true;
-                return;
-        }
-
-        if (!(p = unit_dbus_path(u)))
-                goto oom;
-
-        if (u->sent_dbus_new_signal) {
-                /* Send a properties changed signal. First for the
-                 * specific type, then for the generic unit. The
-                 * clients may rely on this order to get atomic
-                 * behaviour if needed. */
-
-                if (UNIT_VTABLE(u)->bus_invalidating_properties) {
-
-                        if (!(m = bus_properties_changed_new(p,
-                                                             UNIT_VTABLE(u)->bus_interface,
-                                                             UNIT_VTABLE(u)->bus_invalidating_properties)))
-                                goto oom;
+        return 0;
+}
 
-                        if (bus_broadcast(u->manager, m) < 0)
-                                goto oom;
+int bus_unit_set_properties(
+                Unit *u,
+                sd_bus_message *message,
+                UnitSetPropertiesMode mode,
+                bool commit,
+                sd_bus_error *error) {
 
-                        dbus_message_unref(m);
-                }
+        bool for_real = false;
+        unsigned n = 0;
+        int r;
 
-                if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
-                        goto oom;
+        assert(u);
+        assert(message);
 
-        } else {
-                /* Send a new signal */
+        /* We iterate through the array twice. First run we just check
+         * if all passed data is valid, second run actually applies
+         * it. This is to implement transaction-like behaviour without
+         * actually providing full transactions. */
 
-                if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
-                        goto oom;
+        r = sd_bus_message_enter_container(message, 'a', "(sv)");
+        if (r < 0)
+                return r;
 
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &u->id,
-                                              DBUS_TYPE_OBJECT_PATH, &p,
-                                              DBUS_TYPE_INVALID))
-                        goto oom;
-        }
+        for (;;) {
+                const char *name;
 
-        if (bus_broadcast(u->manager, m) < 0)
-                goto oom;
+                r = sd_bus_message_enter_container(message, 'r', "sv");
+                if (r < 0)
+                        return r;
+                if (r == 0) {
+                        if (for_real || mode == UNIT_CHECK)
+                                break;
 
-        free(p);
-        dbus_message_unref(m);
+                        /* Reached EOF. Let's try again, and this time for realz... */
+                        r = sd_bus_message_rewind(message, false);
+                        if (r < 0)
+                                return r;
 
-        u->sent_dbus_new_signal = true;
+                        for_real = true;
+                        continue;
+                }
 
-        return;
+                r = sd_bus_message_read(message, "s", &name);
+                if (r < 0)
+                        return r;
 
-oom:
-        free(p);
+                if (!UNIT_VTABLE(u)->bus_set_property)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
 
-        if (m)
-                dbus_message_unref(m);
+                r = sd_bus_message_enter_container(message, 'v', NULL);
+                if (r < 0)
+                        return r;
 
-        log_error("Failed to allocate unit change/new signal.");
-}
+                r = UNIT_VTABLE(u)->bus_set_property(u, name, message, for_real ? mode : UNIT_CHECK, error);
+                if (r == 0 && u->transient && u->load_state == UNIT_STUB)
+                        r = bus_unit_set_transient_property(u, name, message, for_real ? mode : UNIT_CHECK, error);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
 
-void bus_unit_send_removed_signal(Unit *u) {
-        char *p = NULL;
-        DBusMessage *m = NULL;
+                r = sd_bus_message_exit_container(message);
+                if (r < 0)
+                        return r;
 
-        assert(u);
+                r = sd_bus_message_exit_container(message);
+                if (r < 0)
+                        return r;
 
-        if (!bus_has_subscriber(u->manager))
-                return;
+                n += for_real;
+        }
 
-        if (!u->sent_dbus_new_signal)
-                bus_unit_send_change_signal(u);
+        r = sd_bus_message_exit_container(message);
+        if (r < 0)
+                return r;
 
-        if (!u->id)
-                return;
+        if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
+                UNIT_VTABLE(u)->bus_commit_properties(u);
 
-        if (!(p = unit_dbus_path(u)))
-                goto oom;
-
-        if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
-                goto oom;
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &u->id,
-                                      DBUS_TYPE_OBJECT_PATH, &p,
-                                      DBUS_TYPE_INVALID))
-                goto oom;
-
-        if (bus_broadcast(u->manager, m) < 0)
-                goto oom;
-
-        free(p);
-        dbus_message_unref(m);
-
-        return;
-
-oom:
-        free(p);
-
-        if (m)
-                dbus_message_unref(m);
-
-        log_error("Failed to allocate unit remove signal.");
-}
-
-const BusProperty bus_unit_properties[] = {
-        { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
-        { "Names",                bus_unit_append_names,             "as", 0 },
-        { "Following",            bus_unit_append_following,          "s", 0 },
-        { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
-        { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
-        { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
-        { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
-        { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
-        { "BindTo",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BIND_TO]),                 true },
-        { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
-        { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
-        { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
-        { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
-        { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
-        { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
-        { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
-        { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
-        { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
-        { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
-        { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
-        { "PropagateReloadTo",    bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]),     true },
-        { "PropagateReloadFrom",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]),   true },
-        { "Description",          bus_unit_append_description,        "s", 0 },
-        { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
-        { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
-        { "SubState",             bus_unit_append_sub_state,          "s", 0 },
-        { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
-        { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
-        { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
-        { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
-        { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
-        { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
-        { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
-        { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
-        { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
-        { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
-        { "CanStart",             bus_unit_append_can_start,          "b", 0 },
-        { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
-        { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
-        { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
-        { "Job",                  bus_unit_append_job,             "(uo)", 0 },
-        { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
-        { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
-        { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
-        { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
-        { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
-        { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
-        { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
-        { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
-        { "DefaultControlGroup",  bus_unit_append_default_cgroup,     "s", 0 },
-        { "ControlGroup",         bus_unit_append_cgroups,           "as", 0 },
-        { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
-        { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
-        { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
-        { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
-        { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
-        { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
-        { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
-        { NULL, }
-};
+        return n;
+}