]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/dbus-unit.c
tree-wide: sd_bus_error_setf → set_bus_error_set
[thirdparty/systemd.git] / src / core / dbus-unit.c
index 7a1f5041f3f94e0fe2dc439d73e24f930886b4ad..d73161c76c07917b2be28e302a1f3e93bafe8319 100644 (file)
@@ -1,12 +1,12 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "sd-bus.h"
 
 #include "alloc-util.h"
 #include "bpf-firewall.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
 #include "bus-polkit.h"
-#include "bus-util.h"
 #include "cgroup-util.h"
 #include "condition.h"
 #include "dbus-job.h"
@@ -46,12 +46,14 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
 static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description);
 static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string);
+static BUS_DEFINE_PROPERTY_GET2(property_get_freezer_state, "s", Unit, unit_freezer_state, freezer_state_to_string);
 static BUS_DEFINE_PROPERTY_GET(property_get_sub_state, "s", Unit, unit_sub_state_to_string);
 static BUS_DEFINE_PROPERTY_GET2(property_get_unit_file_state, "s", Unit, unit_get_unit_file_state, unit_file_state_to_string);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_reload, "b", Unit, unit_can_reload);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_start, "b", Unit, unit_can_start_refuse_manual);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_stop, "b", Unit, unit_can_stop_refuse_manual);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_isolate_refuse_manual);
+static BUS_DEFINE_PROPERTY_GET(property_get_can_freeze, "b", Unit, unit_can_freeze);
 static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload);
 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0);
 
@@ -100,20 +102,23 @@ static int property_get_names(
                 void *userdata,
                 sd_bus_error *error) {
 
-        Set **s = userdata;
-        Iterator i;
+        Unit *u = userdata;
         const char *t;
         int r;
 
         assert(bus);
         assert(reply);
-        assert(s);
+        assert(u);
 
         r = sd_bus_message_open_container(reply, 'a', "s");
         if (r < 0)
                 return r;
 
-        SET_FOREACH(t, *s, i) {
+        r = sd_bus_message_append(reply, "s", u->id);
+        if (r < 0)
+                return r;
+
+        SET_FOREACH(t, u->aliases) {
                 r = sd_bus_message_append(reply, "s", t);
                 if (r < 0)
                         return r;
@@ -151,7 +156,6 @@ static int property_get_dependencies(
                 sd_bus_error *error) {
 
         Hashmap **h = userdata;
-        Iterator j;
         Unit *u;
         void *v;
         int r;
@@ -164,7 +168,7 @@ static int property_get_dependencies(
         if (r < 0)
                 return r;
 
-        HASHMAP_FOREACH_KEY(v, u, *h, j) {
+        HASHMAP_FOREACH_KEY(v, u, *h) {
                 r = sd_bus_message_append(reply, "s", u->id);
                 if (r < 0)
                         return r;
@@ -184,7 +188,6 @@ static int property_get_requires_mounts_for(
 
         Hashmap **h = userdata;
         const char *p;
-        Iterator j;
         void *v;
         int r;
 
@@ -196,7 +199,7 @@ static int property_get_requires_mounts_for(
         if (r < 0)
                 return r;
 
-        HASHMAP_FOREACH_KEY(v, p, *h, j) {
+        HASHMAP_FOREACH_KEY(v, p, *h) {
                 r = sd_bus_message_append(reply, "s", p);
                 if (r < 0)
                         return r;
@@ -320,36 +323,37 @@ static int property_get_load_error(
         return sd_bus_message_append(reply, "(ss)", NULL, NULL);
 }
 
-static int bus_verify_manage_units_async_full(
-                Unit *u,
-                const char *verb,
-                int capability,
-                const char *polkit_message,
-                bool interactive,
-                sd_bus_message *call,
+static int property_get_markers(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
                 sd_bus_error *error) {
 
-        const char *details[9] = {
-                "unit", u->id,
-                "verb", verb,
-        };
+        unsigned *markers = userdata;
+        int r;
 
-        if (polkit_message) {
-                details[4] = "polkit.message";
-                details[5] = polkit_message;
-                details[6] = "polkit.gettext_domain";
-                details[7] = GETTEXT_PACKAGE;
-        }
+        assert(bus);
+        assert(reply);
+        assert(markers);
 
-        return bus_verify_polkit_async(
-                        call,
-                        capability,
-                        "org.freedesktop.systemd1.manage-units",
-                        details,
-                        interactive,
-                        UID_INVALID,
-                        &u->manager->polkit_registry,
-                        error);
+        r = sd_bus_message_open_container(reply, 'a', "s");
+        if (r < 0)
+                return r;
+
+        /* Make sure out values fit in the bitfield. */
+        assert_cc(_UNIT_MARKER_MAX <= sizeof(((Unit){}).markers) * 8);
+
+        for (UnitMarker m = 0; m < _UNIT_MARKER_MAX; m++)
+                if (FLAGS_SET(*markers, 1u << m)) {
+                        r = sd_bus_message_append(reply, "s", unit_marker_to_string(m));
+                        if (r < 0)
+                                return r;
+                }
+
+        return sd_bus_message_close_container(reply);
 }
 
 static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = {
@@ -523,7 +527,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
         }
 
         if (!SIGNAL_VALID(signo))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
+                return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
 
         r = bus_verify_manage_units_async_full(
                         u,
@@ -649,7 +653,7 @@ int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error
 
         r = bus_unit_track_remove_sender(u, message);
         if (r == -EUNATCH)
-                return sd_bus_error_setf(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
+                return sd_bus_error_set(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
         if (r < 0)
                 return r;
 
@@ -715,15 +719,88 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
         if (r == -EOPNOTSUPP)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not supporting cleaning.", u->id);
         if (r == -EUNATCH)
-                return sd_bus_error_setf(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found.");
+                return sd_bus_error_set(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found.");
         if (r == -EBUSY)
-                return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit is not inactive or has pending job.");
+                return sd_bus_error_set(error, BUS_ERROR_UNIT_BUSY, "Unit is not inactive or has pending job.");
         if (r < 0)
                 return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
 
+static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userdata, sd_bus_error *error, FreezerAction action) {
+        const char* perm;
+        int (*method)(Unit*);
+        Unit *u = userdata;
+        bool reply_no_delay = false;
+        int r;
+
+        assert(message);
+        assert(u);
+        assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
+
+        if (action == FREEZER_FREEZE) {
+                perm = "stop";
+                method = unit_freeze;
+        } else {
+                perm = "start";
+                method = unit_thaw;
+        }
+
+        r = mac_selinux_unit_access_check(u, message, perm, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        perm,
+                        CAP_SYS_ADMIN,
+                        N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."),
+                        true,
+                        message,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        r = method(u);
+        if (r == -EOPNOTSUPP)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support freezing.", u->id);
+        if (r == -EBUSY)
+                return sd_bus_error_set(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job.");
+        if (r == -EHOSTDOWN)
+                return sd_bus_error_set(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive.");
+        if (r == -EALREADY)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Previously requested freezer operation for unit '%s' is still in progress.", u->id);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                reply_no_delay = true;
+
+        assert(!u->pending_freezer_message);
+
+        r = sd_bus_message_new_method_return(message, &u->pending_freezer_message);
+        if (r < 0)
+                return r;
+
+        if (reply_no_delay) {
+                r = bus_unit_send_pending_freezer_message(u);
+                if (r < 0)
+                        return r;
+        }
+
+        return 1;
+}
+
+int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_THAW);
+}
+
+int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_FREEZE);
+}
+
 static int property_get_refs(
                 sd_bus *bus,
                 const char *path,
@@ -734,7 +811,6 @@ static int property_get_refs(
                 sd_bus_error *error) {
 
         Unit *u = userdata;
-        const char *i;
         int r;
 
         assert(bus);
@@ -744,15 +820,15 @@ static int property_get_refs(
         if (r < 0)
                 return r;
 
-        for (i = sd_bus_track_first(u->bus_track); i; i = sd_bus_track_next(u->bus_track)) {
-                int c, k;
+        for (const char *i = sd_bus_track_first(u->bus_track); i; i = sd_bus_track_next(u->bus_track)) {
+                int c;
 
                 c = sd_bus_track_count_name(u->bus_track, i);
                 if (c < 0)
                         return c;
 
                 /* Add the item multiple times if the ref count for each is above 1 */
-                for (k = 0; k < c; k++) {
+                for (int k = 0; k < c; k++) {
                         r = sd_bus_message_append(reply, "s", i);
                         if (r < 0)
                                 return r;
@@ -766,7 +842,7 @@ 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, offsetof(Unit, names), 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("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -793,6 +869,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         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("FreezerState", "s", property_get_freezer_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),
@@ -809,6 +886,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         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("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("CanFreeze", "b", property_get_can_freeze, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), 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),
@@ -818,6 +896,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         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("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Markers", "as", property_get_markers, offsetof(Unit, markers), 0),
         SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("JobRunningTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_running_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_emergency_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -940,6 +1019,16 @@ const sd_bus_vtable bus_unit_vtable[] = {
                                  NULL,,
                                  bus_unit_method_clean,
                                  SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Freeze",
+                      NULL,
+                      NULL,
+                      bus_unit_method_freeze,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Thaw",
+                      NULL,
+                      NULL,
+                      bus_unit_method_thaw,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
 
         /* For dependency types we don't support anymore always return an empty array */
         SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN),
@@ -980,7 +1069,7 @@ static int property_get_current_memory(
                 void *userdata,
                 sd_bus_error *error) {
 
-        uint64_t sz = (uint64_t) -1;
+        uint64_t sz = UINT64_MAX;
         Unit *u = userdata;
         int r;
 
@@ -1004,7 +1093,7 @@ static int property_get_current_tasks(
                 void *userdata,
                 sd_bus_error *error) {
 
-        uint64_t cn = (uint64_t) -1;
+        uint64_t cn = UINT64_MAX;
         Unit *u = userdata;
         int r;
 
@@ -1028,7 +1117,7 @@ static int property_get_cpu_usage(
                 void *userdata,
                 sd_bus_error *error) {
 
-        nsec_t ns = (nsec_t) -1;
+        nsec_t ns = NSEC_INFINITY;
         Unit *u = userdata;
         int r;
 
@@ -1353,10 +1442,10 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd
         }
 
         if (!unit_cgroup_delegate(u))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process migration not available on non-delegated units.");
+                return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Process migration not available on non-delegated units.");
 
         if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not active, refusing.");
+                return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not active, refusing.");
 
         r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds);
         if (r < 0)
@@ -1566,6 +1655,23 @@ void bus_unit_send_pending_change_signal(Unit *u, bool including_new) {
         bus_unit_send_change_signal(u);
 }
 
+int bus_unit_send_pending_freezer_message(Unit *u) {
+        int r;
+
+        assert(u);
+
+        if (!u->pending_freezer_message)
+                return 0;
+
+        r = sd_bus_send(NULL, u->pending_freezer_message, NULL);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send queued message, ignoring: %m");
+
+        u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+
+        return 0;
+}
+
 static int send_removed_signal(sd_bus *bus, void *userdata) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         _cleanup_free_ char *p = NULL;
@@ -1610,51 +1716,20 @@ void bus_unit_send_removed_signal(Unit *u) {
                 log_unit_debug_errno(u, r, "Failed to send unit remove signal for %s: %m", u->id);
 }
 
-int bus_unit_queue_job(
+int bus_unit_queue_job_one(
                 sd_bus_message *message,
                 Unit *u,
                 JobType type,
                 JobMode mode,
                 BusUnitQueueFlags flags,
+                sd_bus_message *reply,
                 sd_bus_error *error) {
 
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_set_free_ Set *affected = NULL;
         _cleanup_free_ char *job_path = NULL, *unit_path = NULL;
-        _cleanup_(set_freep) Set *affected = NULL;
-        Iterator i;
         Job *j, *a;
         int r;
 
-        assert(message);
-        assert(u);
-        assert(type >= 0 && type < _JOB_TYPE_MAX);
-        assert(mode >= 0 && mode < _JOB_MODE_MAX);
-
-        r = mac_selinux_unit_access_check(
-                        u, message,
-                        job_type_to_access_method(type),
-                        error);
-        if (r < 0)
-                return r;
-
-        if (FLAGS_SET(flags, BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE) && unit_can_reload(u)) {
-                if (type == JOB_RESTART)
-                        type = JOB_RELOAD_OR_START;
-                else if (type == JOB_TRY_RESTART)
-                        type = JOB_TRY_RELOAD;
-        }
-
-        if (type == JOB_STOP &&
-            IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_ERROR, UNIT_BAD_SETTING) &&
-            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) ||
-            (IN_SET(type, JOB_RESTART, JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) ||
-            (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start))
-                return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id);
-
         if (FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY)) {
                 affected = set_new(NULL);
                 if (!affected)
@@ -1678,12 +1753,9 @@ int bus_unit_queue_job(
 
         /* The classic response is just a job object path */
         if (!FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY))
-                return sd_bus_reply_method_return(message, "o", job_path);
+                return sd_bus_message_append(reply, "o", job_path);
 
         /* In verbose mode respond with the anchor job plus everything that has been affected */
-        r = sd_bus_message_new_method_return(message, &reply);
-        if (r < 0)
-                return r;
 
         unit_path = unit_dbus_path(j->unit);
         if (!unit_path)
@@ -1700,8 +1772,7 @@ int bus_unit_queue_job(
         if (r < 0)
                 return r;
 
-        SET_FOREACH(a, affected, i) {
-
+        SET_FOREACH(a, affected) {
                 if (a->id == j->id)
                         continue;
 
@@ -1725,7 +1796,55 @@ int bus_unit_queue_job(
                         return r;
         }
 
-        r = sd_bus_message_close_container(reply);
+        return sd_bus_message_close_container(reply);
+}
+
+int bus_unit_queue_job(
+                sd_bus_message *message,
+                Unit *u,
+                JobType type,
+                JobMode mode,
+                BusUnitQueueFlags flags,
+                sd_bus_error *error) {
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        int r;
+
+        assert(message);
+        assert(u);
+        assert(type >= 0 && type < _JOB_TYPE_MAX);
+        assert(mode >= 0 && mode < _JOB_MODE_MAX);
+
+        r = mac_selinux_unit_access_check(
+                        u, message,
+                        job_type_to_access_method(type),
+                        error);
+        if (r < 0)
+                return r;
+
+        if (FLAGS_SET(flags, BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE) && unit_can_reload(u)) {
+                if (type == JOB_RESTART)
+                        type = JOB_RELOAD_OR_START;
+                else if (type == JOB_TRY_RESTART)
+                        type = JOB_TRY_RELOAD;
+        }
+
+        if (type == JOB_STOP &&
+            IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_ERROR, UNIT_BAD_SETTING) &&
+            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) ||
+            (IN_SET(type, JOB_RESTART, JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) ||
+            (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start))
+                return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id);
+
+        r = sd_bus_message_new_method_return(message, &reply);
+        if (r < 0)
+                return r;
+
+        r = bus_unit_queue_job_one(message, u, type, mode, flags, reply, error);
         if (r < 0)
                 return r;
 
@@ -1745,8 +1864,8 @@ static int bus_unit_set_live_property(
         assert(name);
         assert(message);
 
-        /* Handles setting properties both "live" (i.e. at any time during runtime), and during creation (for transient
-         * units that are being created). */
+        /* Handles setting properties both "live" (i.e. at any time during runtime), and during creation (for
+         * transient units that are being created). */
 
         if (streq(name, "Description")) {
                 const char *d;
@@ -1766,6 +1885,63 @@ static int bus_unit_set_live_property(
                 return 1;
         }
 
+        /* A setting that only applies to active units. We don't actually write this to /run, this state is
+         * managed internally. "+foo" sets flag foo, "-foo" unsets flag foo, just "foo" resets flags to
+         * foo. The last type cannot be mixed with "+" or "-". */
+
+        if (streq(name, "Markers")) {
+                unsigned settings = 0, mask = 0;
+                bool some_plus_minus = false, some_absolute = false;
+
+                r = sd_bus_message_enter_container(message, 'a', "s");
+                if (r < 0)
+                        return r;
+
+                for (;;) {
+                        const char *word;
+                        bool b;
+
+                        r = sd_bus_message_read(message, "s", &word);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                break;
+
+                        if (IN_SET(word[0], '+', '-')) {
+                                b = word[0] == '+';
+                                word++;
+                                some_plus_minus = true;
+                        } else {
+                                b = true;
+                                some_absolute = true;
+                        }
+
+                        UnitMarker m = unit_marker_from_string(word);
+                        if (m < 0)
+                                return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING,
+                                                         "Unknown marker \"%s\".", word);
+
+                        SET_FLAG(settings, 1u << m, b);
+                        SET_FLAG(mask, 1u << m, true);
+                }
+
+                r = sd_bus_message_exit_container(message);
+                if (r < 0)
+                        return r;
+
+                if (some_plus_minus && some_absolute)
+                        return sd_bus_error_set(error, BUS_ERROR_BAD_UNIT_SETTING, "Bad marker syntax.");
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                        if (some_absolute)
+                                u->markers = settings;
+                        else
+                                u->markers = settings | (u->markers & ~mask);
+                }
+
+                return 1;
+        }
+
         return 0;
 }
 
@@ -1823,7 +1999,7 @@ static int bus_set_transient_exit_status(
                 return r;
 
         if (k > 255)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Exit status must be in range 0…255 or negative.");
+                return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Exit status must be in range 0…255 or negative.");
 
         if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                 *p = k < 0 ? -1 : k;
@@ -1866,14 +2042,11 @@ static int bus_set_transient_conditions(
                 if (t < 0)
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid condition type: %s", type_name);
 
-                if (t != CONDITION_NULL) {
-                        if (isempty(param))
-                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Condition parameter in %s is empty", type_name);
+                if (isempty(param))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Condition parameter in %s is empty", type_name);
 
-                        if (condition_takes_path(t) && !path_is_absolute(param))
-                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in condition %s is not absolute: %s", type_name, param);
-                } else
-                        param = NULL;
+                if (condition_takes_path(t) && !path_is_absolute(param))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in condition %s is not absolute: %s", type_name, param);
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         Condition *c;
@@ -1884,14 +2057,9 @@ static int bus_set_transient_conditions(
 
                         LIST_PREPEND(conditions, *list, c);
 
-                        if (t != CONDITION_NULL)
-                                unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name,
-                                                    "%s=%s%s%s", type_name,
-                                                    trigger ? "|" : "", negate ? "!" : "", param);
-                        else
-                                unit_write_settingf(u, flags, name,
-                                                    "%s=%s%s", type_name,
-                                                    trigger ? "|" : "", yes_no(!negate));
+                        unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name,
+                                            "%s=%s%s%s", type_name,
+                                            trigger ? "|" : "", negate ? "!" : "", param);
                 }
 
                 empty = false;
@@ -1918,15 +2086,15 @@ static int bus_unit_set_transient_property(
                 UnitWriteFlags flags,
                 sd_bus_error *error) {
 
-        UnitDependency d = _UNIT_DEPENDENCY_INVALID;
+        UnitDependency d;
         int r;
 
         assert(u);
         assert(name);
         assert(message);
 
-        /* Handles settings when transient units are created. This settings cannot be altered anymore after the unit
-         * has been created. */
+        /* Handles settings when transient units are created. This settings cannot be altered anymore after
+         * the unit has been created. */
 
         if (streq(name, "SourcePath"))
                 return bus_set_transient_path(u, name, &u->source_path, message, flags, error);
@@ -2037,11 +2205,11 @@ static int bus_unit_set_transient_property(
                 const char *s;
 
                 if (!UNIT_HAS_CGROUP_CONTEXT(u))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
+                        return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
                 if (u->type == UNIT_SLICE)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
+                        return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
                 if (unit_has_name(u, SPECIAL_INIT_SCOPE))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
+                        return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
 
                 r = sd_bus_message_read(message, "s", &s);
                 if (r < 0)
@@ -2234,7 +2402,8 @@ int bus_unit_set_properties(
                         return r;
 
                 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.");
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY,
+                                                 "Objects of this type do not support setting properties.");
 
                 r = sd_bus_message_enter_container(message, 'v', NULL);
                 if (r < 0)
@@ -2252,7 +2421,8 @@ int bus_unit_set_properties(
                         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);
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY,
+                                                 "Cannot set property %s, or unknown property.", name);
 
                 r = sd_bus_message_exit_container(message);
                 if (r < 0)
@@ -2292,7 +2462,8 @@ int bus_unit_validate_load_state(Unit *u, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, "Unit %s has a bad unit file setting.", u->id);
 
         case UNIT_ERROR: /* Only show .load_error in UNIT_ERROR state */
-                return sd_bus_error_set_errnof(error, u->load_error, "Unit %s failed to load properly: %m.", u->id);
+                return sd_bus_error_set_errnof(error, u->load_error,
+                                               "Unit %s failed to load properly, please adjust/correct and reload service manager: %m", u->id);
 
         case UNIT_MASKED:
                 return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit %s is masked.", u->id);
@@ -2370,8 +2541,8 @@ int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) {
 int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m) {
         assert(u);
 
-        /* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet, return an
-         * error */
+        /* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet,
+         * return an error */
         if (!u->bus_track)
                 return -EUNATCH;