]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/unit.c
core: split out a few funcs into unit-serialize.[ch]
[thirdparty/systemd.git] / src / core / unit.c
index 1165d4ea8b048c783c7b76846114770cb059e311..eacebe796a0ed681c3cb066f259cbd498a2ffa61 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <errno.h>
 #include <stdlib.h>
@@ -15,6 +15,7 @@
 #include "bus-util.h"
 #include "cgroup-setup.h"
 #include "cgroup-util.h"
+#include "core-varlink.h"
 #include "dbus-unit.h"
 #include "dbus.h"
 #include "dropin.h"
@@ -25,8 +26,8 @@
 #include "fileio.h"
 #include "format-util.h"
 #include "id128-util.h"
-#include "io-util.h"
 #include "install.h"
+#include "io-util.h"
 #include "label.h"
 #include "load-dropin.h"
 #include "load-fragment.h"
 #include "macro.h"
 #include "missing_audit.h"
 #include "mkdir.h"
-#include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "rm-rf.h"
-#include "serialize.h"
 #include "set.h"
 #include "signal-util.h"
 #include "sparse-endian.h"
@@ -1165,269 +1164,6 @@ const char *unit_status_string(Unit *u) {
         return unit_description(u);
 }
 
-static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependencyMask mask, bool *space) {
-        const struct {
-                UnitDependencyMask mask;
-                const char *name;
-        } table[] = {
-                { UNIT_DEPENDENCY_FILE,               "file"               },
-                { UNIT_DEPENDENCY_IMPLICIT,           "implicit"           },
-                { UNIT_DEPENDENCY_DEFAULT,            "default"            },
-                { UNIT_DEPENDENCY_UDEV,               "udev"               },
-                { UNIT_DEPENDENCY_PATH,               "path"               },
-                { UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT, "mountinfo-implicit" },
-                { UNIT_DEPENDENCY_MOUNTINFO_DEFAULT,  "mountinfo-default"  },
-                { UNIT_DEPENDENCY_PROC_SWAP,          "proc-swap"          },
-        };
-
-        assert(f);
-        assert(kind);
-        assert(space);
-
-        for (size_t i = 0; i < ELEMENTSOF(table); i++) {
-
-                if (mask == 0)
-                        break;
-
-                if (FLAGS_SET(mask, table[i].mask)) {
-                        if (*space)
-                                fputc(' ', f);
-                        else
-                                *space = true;
-
-                        fputs(kind, f);
-                        fputs("-", f);
-                        fputs(table[i].name, f);
-
-                        mask &= ~table[i].mask;
-                }
-        }
-
-        assert(mask == 0);
-}
-
-void unit_dump(Unit *u, FILE *f, const char *prefix) {
-        char *t, **j;
-        const char *prefix2;
-        char timestamp[5][FORMAT_TIMESTAMP_MAX], timespan[FORMAT_TIMESPAN_MAX];
-        Unit *following;
-        _cleanup_set_free_ Set *following_set = NULL;
-        CGroupMask m;
-        int r;
-
-        assert(u);
-        assert(u->type >= 0);
-
-        prefix = strempty(prefix);
-        prefix2 = strjoina(prefix, "\t");
-
-        fprintf(f,
-                "%s-> Unit %s:\n",
-                prefix, u->id);
-
-        SET_FOREACH(t, u->aliases)
-                fprintf(f, "%s\tAlias: %s\n", prefix, t);
-
-        fprintf(f,
-                "%s\tDescription: %s\n"
-                "%s\tInstance: %s\n"
-                "%s\tUnit Load State: %s\n"
-                "%s\tUnit Active State: %s\n"
-                "%s\tState Change Timestamp: %s\n"
-                "%s\tInactive Exit Timestamp: %s\n"
-                "%s\tActive Enter Timestamp: %s\n"
-                "%s\tActive Exit Timestamp: %s\n"
-                "%s\tInactive Enter Timestamp: %s\n"
-                "%s\tMay GC: %s\n"
-                "%s\tNeed Daemon Reload: %s\n"
-                "%s\tTransient: %s\n"
-                "%s\tPerpetual: %s\n"
-                "%s\tGarbage Collection Mode: %s\n"
-                "%s\tSlice: %s\n"
-                "%s\tCGroup: %s\n"
-                "%s\tCGroup realized: %s\n",
-                prefix, unit_description(u),
-                prefix, strna(u->instance),
-                prefix, unit_load_state_to_string(u->load_state),
-                prefix, unit_active_state_to_string(unit_active_state(u)),
-                prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->state_change_timestamp.realtime)),
-                prefix, strna(format_timestamp(timestamp[1], sizeof(timestamp[1]), u->inactive_exit_timestamp.realtime)),
-                prefix, strna(format_timestamp(timestamp[2], sizeof(timestamp[2]), u->active_enter_timestamp.realtime)),
-                prefix, strna(format_timestamp(timestamp[3], sizeof(timestamp[3]), u->active_exit_timestamp.realtime)),
-                prefix, strna(format_timestamp(timestamp[4], sizeof(timestamp[4]), u->inactive_enter_timestamp.realtime)),
-                prefix, yes_no(unit_may_gc(u)),
-                prefix, yes_no(unit_need_daemon_reload(u)),
-                prefix, yes_no(u->transient),
-                prefix, yes_no(u->perpetual),
-                prefix, collect_mode_to_string(u->collect_mode),
-                prefix, strna(unit_slice_name(u)),
-                prefix, strna(u->cgroup_path),
-                prefix, yes_no(u->cgroup_realized));
-
-        if (u->cgroup_realized_mask != 0) {
-                _cleanup_free_ char *s = NULL;
-                (void) cg_mask_to_string(u->cgroup_realized_mask, &s);
-                fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
-        }
-
-        if (u->cgroup_enabled_mask != 0) {
-                _cleanup_free_ char *s = NULL;
-                (void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
-                fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
-        }
-
-        m = unit_get_own_mask(u);
-        if (m != 0) {
-                _cleanup_free_ char *s = NULL;
-                (void) cg_mask_to_string(m, &s);
-                fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
-        }
-
-        m = unit_get_members_mask(u);
-        if (m != 0) {
-                _cleanup_free_ char *s = NULL;
-                (void) cg_mask_to_string(m, &s);
-                fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
-        }
-
-        m = unit_get_delegate_mask(u);
-        if (m != 0) {
-                _cleanup_free_ char *s = NULL;
-                (void) cg_mask_to_string(m, &s);
-                fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
-        }
-
-        if (!sd_id128_is_null(u->invocation_id))
-                fprintf(f, "%s\tInvocation ID: " SD_ID128_FORMAT_STR "\n",
-                        prefix, SD_ID128_FORMAT_VAL(u->invocation_id));
-
-        STRV_FOREACH(j, u->documentation)
-                fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
-
-        following = unit_following(u);
-        if (following)
-                fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
-
-        r = unit_following_set(u, &following_set);
-        if (r >= 0) {
-                Unit *other;
-
-                SET_FOREACH(other, following_set)
-                        fprintf(f, "%s\tFollowing Set Member: %s\n", prefix, other->id);
-        }
-
-        if (u->fragment_path)
-                fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path);
-
-        if (u->source_path)
-                fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
-
-        STRV_FOREACH(j, u->dropin_paths)
-                fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
-
-        if (u->failure_action != EMERGENCY_ACTION_NONE)
-                fprintf(f, "%s\tFailure Action: %s\n", prefix, emergency_action_to_string(u->failure_action));
-        if (u->failure_action_exit_status >= 0)
-                fprintf(f, "%s\tFailure Action Exit Status: %i\n", prefix, u->failure_action_exit_status);
-        if (u->success_action != EMERGENCY_ACTION_NONE)
-                fprintf(f, "%s\tSuccess Action: %s\n", prefix, emergency_action_to_string(u->success_action));
-        if (u->success_action_exit_status >= 0)
-                fprintf(f, "%s\tSuccess Action Exit Status: %i\n", prefix, u->success_action_exit_status);
-
-        if (u->job_timeout != USEC_INFINITY)
-                fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
-
-        if (u->job_timeout_action != EMERGENCY_ACTION_NONE)
-                fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, emergency_action_to_string(u->job_timeout_action));
-
-        if (u->job_timeout_reboot_arg)
-                fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg);
-
-        condition_dump_list(u->conditions, f, prefix, condition_type_to_string);
-        condition_dump_list(u->asserts, f, prefix, assert_type_to_string);
-
-        if (dual_timestamp_is_set(&u->condition_timestamp))
-                fprintf(f,
-                        "%s\tCondition Timestamp: %s\n"
-                        "%s\tCondition Result: %s\n",
-                        prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->condition_timestamp.realtime)),
-                        prefix, yes_no(u->condition_result));
-
-        if (dual_timestamp_is_set(&u->assert_timestamp))
-                fprintf(f,
-                        "%s\tAssert Timestamp: %s\n"
-                        "%s\tAssert Result: %s\n",
-                        prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->assert_timestamp.realtime)),
-                        prefix, yes_no(u->assert_result));
-
-        for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
-                UnitDependencyInfo di;
-                Unit *other;
-
-                HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d]) {
-                        bool space = false;
-
-                        fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id);
-
-                        print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
-                        print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
-
-                        fputs(")\n", f);
-                }
-        }
-
-        if (!hashmap_isempty(u->requires_mounts_for)) {
-                UnitDependencyInfo di;
-                const char *path;
-
-                HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for) {
-                        bool space = false;
-
-                        fprintf(f, "%s\tRequiresMountsFor: %s (", prefix, path);
-
-                        print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
-                        print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
-
-                        fputs(")\n", f);
-                }
-        }
-
-        if (u->load_state == UNIT_LOADED) {
-
-                fprintf(f,
-                        "%s\tStopWhenUnneeded: %s\n"
-                        "%s\tRefuseManualStart: %s\n"
-                        "%s\tRefuseManualStop: %s\n"
-                        "%s\tDefaultDependencies: %s\n"
-                        "%s\tOnFailureJobMode: %s\n"
-                        "%s\tIgnoreOnIsolate: %s\n",
-                        prefix, yes_no(u->stop_when_unneeded),
-                        prefix, yes_no(u->refuse_manual_start),
-                        prefix, yes_no(u->refuse_manual_stop),
-                        prefix, yes_no(u->default_dependencies),
-                        prefix, job_mode_to_string(u->on_failure_job_mode),
-                        prefix, yes_no(u->ignore_on_isolate));
-
-                if (UNIT_VTABLE(u)->dump)
-                        UNIT_VTABLE(u)->dump(u, f, prefix2);
-
-        } else if (u->load_state == UNIT_MERGED)
-                fprintf(f,
-                        "%s\tMerged into: %s\n",
-                        prefix, u->merged_into->id);
-        else if (u->load_state == UNIT_ERROR)
-                fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror_safe(u->load_error));
-
-        for (const char *n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
-                fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
-
-        if (u->job)
-                job_dump(u->job, f, prefix2);
-
-        if (u->nop_job)
-                job_dump(u->nop_job, f, prefix2);
-}
-
 /* Common implementation for multiple backends */
 int unit_load_fragment_and_dropin(Unit *u, bool fragment_required) {
         int r;
@@ -1573,6 +1309,31 @@ static int unit_add_mount_dependencies(Unit *u) {
         return 0;
 }
 
+static int unit_add_oomd_dependencies(Unit *u) {
+        CGroupContext *c;
+        bool wants_oomd;
+        int r;
+
+        assert(u);
+
+        if (!u->default_dependencies)
+                return 0;
+
+        c = unit_get_cgroup_context(u);
+        if (!c)
+                return 0;
+
+        wants_oomd = (c->moom_swap == MANAGED_OOM_KILL || c->moom_mem_pressure == MANAGED_OOM_KILL);
+        if (!wants_oomd)
+                return 0;
+
+        r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, "systemd-oomd.service", true, UNIT_DEPENDENCY_FILE);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 static int unit_add_startup_units(Unit *u) {
         CGroupContext *c;
 
@@ -1633,13 +1394,17 @@ int unit_load(Unit *u) {
                 if (r < 0)
                         goto fail;
 
+                r = unit_add_oomd_dependencies(u);
+                if (r < 0)
+                        goto fail;
+
                 r = unit_add_startup_units(u);
                 if (r < 0)
                         goto fail;
 
                 if (u->on_failure_job_mode == JOB_ISOLATE && hashmap_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
-                        log_unit_error(u, "More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing.");
-                        r = -ENOEXEC;
+                        r = log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC),
+                                                 "More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing.");
                         goto fail;
                 }
 
@@ -1654,6 +1419,7 @@ int unit_load(Unit *u) {
 
         unit_add_to_dbus_queue(unit_follow_merge(u));
         unit_add_to_gc_queue(u);
+        (void) manager_varlink_send_managed_oom_update(u);
 
         return 0;
 
@@ -1957,6 +1723,10 @@ int unit_stop(Unit *u) {
 bool unit_can_stop(Unit *u) {
         assert(u);
 
+        /* Note: if we return true here, it does not mean that the unit may be successfully stopped.
+         * Extrinsic units follow external state and they may stop following external state changes
+         * (hence we return true here), but an attempt to do this through the manager will fail. */
+
         if (!unit_type_supported(u->type))
                 return false;
 
@@ -1987,10 +1757,8 @@ int unit_reload(Unit *u) {
         if (state == UNIT_RELOADING)
                 return -EAGAIN;
 
-        if (state != UNIT_ACTIVE) {
-                log_unit_warning(u, "Unit cannot be reloaded because it is inactive.");
-                return -ENOEXEC;
-        }
+        if (state != UNIT_ACTIVE)
+                return log_unit_warning_errno(u, SYNTHETIC_ERRNO(ENOEXEC), "Unit cannot be reloaded because it is inactive.");
 
         following = unit_following(u);
         if (following) {
@@ -2526,8 +2294,6 @@ static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags)
                         if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
                                 if (ns == UNIT_FAILED)
                                         result = JOB_FAILED;
-                                else if (FLAGS_SET(flags, UNIT_NOTIFY_SKIP_CONDITION))
-                                        result = JOB_SKIPPED;
                                 else
                                         result = JOB_DONE;
 
@@ -2592,6 +2358,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
          * the bus queue, so that any job change signal queued will force out the unit change signal first. */
         unit_add_to_dbus_queue(u);
 
+        /* Update systemd-oomd on the property/state change */
+        if (os != ns) {
+                /* Always send an update if the unit is going into an inactive state so systemd-oomd knows to stop
+                 * monitoring.
+                 * Also send an update whenever the unit goes active; this is to handle a case where an override file
+                 * sets one of the ManagedOOM*= properties to "kill", then later removes it. systemd-oomd needs to
+                 * know to stop monitoring when the unit changes from "kill" -> "auto" on daemon-reload, but we don't
+                 * have the information on the property. Thus, indiscriminately send an update. */
+                if (UNIT_IS_INACTIVE_OR_FAILED(ns) || UNIT_IS_ACTIVE_OR_RELOADING(ns))
+                        (void) manager_varlink_send_managed_oom_update(u);
+        }
+
         /* Update timestamps for state changes */
         if (!MANAGER_IS_RELOADING(m)) {
                 dual_timestamp_get(&u->state_change_timestamp);
@@ -3198,7 +2976,7 @@ char *unit_dbus_path_invocation_id(Unit *u) {
         return unit_dbus_path_from_name(u->invocation_id_string);
 }
 
-static int unit_set_invocation_id(Unit *u, sd_id128_t id) {
+int unit_set_invocation_id(Unit *u, sd_id128_t id) {
         int r;
 
         assert(u);
@@ -3302,12 +3080,17 @@ int unit_set_default_slice(Unit *u) {
                 if (MANAGER_IS_SYSTEM(u->manager))
                         slice_name = strjoina("system-", escaped, ".slice");
                 else
-                        slice_name = strjoina(escaped, ".slice");
-        } else
-                slice_name =
-                        MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE)
-                        ? SPECIAL_SYSTEM_SLICE
-                        : SPECIAL_ROOT_SLICE;
+                        slice_name = strjoina("app-", escaped, ".slice");
+
+        } else if (unit_is_extrinsic(u))
+                /* Keep all extrinsic units (e.g. perpetual units and swap and mount units in user mode) in
+                 * the root slice. They don't really belong in one of the subslices. */
+                slice_name = SPECIAL_ROOT_SLICE;
+
+        else if (MANAGER_IS_SYSTEM(u->manager))
+                slice_name = SPECIAL_SYSTEM_SLICE;
+        else
+                slice_name = SPECIAL_APP_SLICE;
 
         r = manager_load_unit(u->manager, slice_name, NULL, NULL, &slice);
         if (r < 0)
@@ -3478,527 +3261,6 @@ bool unit_can_serialize(Unit *u) {
         return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
 }
 
-static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
-        _cleanup_free_ char *s = NULL;
-        int r;
-
-        assert(f);
-        assert(key);
-
-        if (mask == 0)
-                return 0;
-
-        r = cg_mask_to_string(mask, &s);
-        if (r < 0)
-                return log_error_errno(r, "Failed to format cgroup mask: %m");
-
-        return serialize_item(f, key, s);
-}
-
-static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
-        [CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes",
-        [CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets",
-        [CGROUP_IP_EGRESS_BYTES] = "ip-accounting-egress-bytes",
-        [CGROUP_IP_EGRESS_PACKETS] = "ip-accounting-egress-packets",
-};
-
-static const char *const io_accounting_metric_field_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
-        [CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-base",
-        [CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-base",
-        [CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-base",
-        [CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-base",
-};
-
-static const char *const io_accounting_metric_field_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
-        [CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-last",
-        [CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-last",
-        [CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-last",
-        [CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last",
-};
-
-int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
-        int r;
-
-        assert(u);
-        assert(f);
-        assert(fds);
-
-        if (unit_can_serialize(u)) {
-                r = UNIT_VTABLE(u)->serialize(u, f, fds);
-                if (r < 0)
-                        return r;
-        }
-
-        (void) serialize_dual_timestamp(f, "state-change-timestamp", &u->state_change_timestamp);
-
-        (void) serialize_dual_timestamp(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
-        (void) serialize_dual_timestamp(f, "active-enter-timestamp", &u->active_enter_timestamp);
-        (void) serialize_dual_timestamp(f, "active-exit-timestamp", &u->active_exit_timestamp);
-        (void) serialize_dual_timestamp(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
-
-        (void) serialize_dual_timestamp(f, "condition-timestamp", &u->condition_timestamp);
-        (void) serialize_dual_timestamp(f, "assert-timestamp", &u->assert_timestamp);
-
-        if (dual_timestamp_is_set(&u->condition_timestamp))
-                (void) serialize_bool(f, "condition-result", u->condition_result);
-
-        if (dual_timestamp_is_set(&u->assert_timestamp))
-                (void) serialize_bool(f, "assert-result", u->assert_result);
-
-        (void) serialize_bool(f, "transient", u->transient);
-        (void) serialize_bool(f, "in-audit", u->in_audit);
-
-        (void) serialize_bool(f, "exported-invocation-id", u->exported_invocation_id);
-        (void) serialize_bool(f, "exported-log-level-max", u->exported_log_level_max);
-        (void) serialize_bool(f, "exported-log-extra-fields", u->exported_log_extra_fields);
-        (void) serialize_bool(f, "exported-log-rate-limit-interval", u->exported_log_ratelimit_interval);
-        (void) serialize_bool(f, "exported-log-rate-limit-burst", u->exported_log_ratelimit_burst);
-
-        (void) serialize_item_format(f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
-        if (u->cpu_usage_last != NSEC_INFINITY)
-                (void) serialize_item_format(f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
-
-        if (u->oom_kill_last > 0)
-                (void) serialize_item_format(f, "oom-kill-last", "%" PRIu64, u->oom_kill_last);
-
-        for (CGroupIOAccountingMetric im = 0; im < _CGROUP_IO_ACCOUNTING_METRIC_MAX; im++) {
-                (void) serialize_item_format(f, io_accounting_metric_field_base[im], "%" PRIu64, u->io_accounting_base[im]);
-
-                if (u->io_accounting_last[im] != UINT64_MAX)
-                        (void) serialize_item_format(f, io_accounting_metric_field_last[im], "%" PRIu64, u->io_accounting_last[im]);
-        }
-
-        if (u->cgroup_path)
-                (void) serialize_item(f, "cgroup", u->cgroup_path);
-
-        (void) serialize_bool(f, "cgroup-realized", u->cgroup_realized);
-        (void) serialize_cgroup_mask(f, "cgroup-realized-mask", u->cgroup_realized_mask);
-        (void) serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask);
-        (void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", u->cgroup_invalidated_mask);
-
-        if (uid_is_valid(u->ref_uid))
-                (void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
-        if (gid_is_valid(u->ref_gid))
-                (void) serialize_item_format(f, "ref-gid", GID_FMT, u->ref_gid);
-
-        if (!sd_id128_is_null(u->invocation_id))
-                (void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
-
-        (void) serialize_item_format(f, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u)));
-
-        bus_track_serialize(u->bus_track, f, "ref");
-
-        for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
-                uint64_t v;
-
-                r = unit_get_ip_accounting(u, m, &v);
-                if (r >= 0)
-                        (void) serialize_item_format(f, ip_accounting_metric_field[m], "%" PRIu64, v);
-        }
-
-        if (serialize_jobs) {
-                if (u->job) {
-                        fputs("job\n", f);
-                        job_serialize(u->job, f);
-                }
-
-                if (u->nop_job) {
-                        fputs("job\n", f);
-                        job_serialize(u->nop_job, f);
-                }
-        }
-
-        /* End marker */
-        fputc('\n', f);
-        return 0;
-}
-
-static int unit_deserialize_job(Unit *u, FILE *f) {
-        _cleanup_(job_freep) Job *j = NULL;
-        int r;
-
-        assert(u);
-        assert(f);
-
-        j = job_new_raw(u);
-        if (!j)
-                return log_oom();
-
-        r = job_deserialize(j, f);
-        if (r < 0)
-                return r;
-
-        r = job_install_deserialized(j);
-        if (r < 0)
-                return r;
-
-        TAKE_PTR(j);
-        return 0;
-}
-
-int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
-        int r;
-
-        assert(u);
-        assert(f);
-        assert(fds);
-
-        for (;;) {
-                _cleanup_free_ char *line = NULL;
-                char *l, *v;
-                ssize_t m;
-                size_t k;
-
-                r = read_line(f, LONG_LINE_MAX, &line);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to read serialization line: %m");
-                if (r == 0) /* eof */
-                        break;
-
-                l = strstrip(line);
-                if (isempty(l)) /* End marker */
-                        break;
-
-                k = strcspn(l, "=");
-
-                if (l[k] == '=') {
-                        l[k] = 0;
-                        v = l+k+1;
-                } else
-                        v = l+k;
-
-                if (streq(l, "job")) {
-                        if (v[0] == '\0') {
-                                /* New-style serialized job */
-                                r = unit_deserialize_job(u, f);
-                                if (r < 0)
-                                        return r;
-                        } else  /* Legacy for pre-44 */
-                                log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
-                        continue;
-                } else if (streq(l, "state-change-timestamp")) {
-                        (void) deserialize_dual_timestamp(v, &u->state_change_timestamp);
-                        continue;
-                } else if (streq(l, "inactive-exit-timestamp")) {
-                        (void) deserialize_dual_timestamp(v, &u->inactive_exit_timestamp);
-                        continue;
-                } else if (streq(l, "active-enter-timestamp")) {
-                        (void) deserialize_dual_timestamp(v, &u->active_enter_timestamp);
-                        continue;
-                } else if (streq(l, "active-exit-timestamp")) {
-                        (void) deserialize_dual_timestamp(v, &u->active_exit_timestamp);
-                        continue;
-                } else if (streq(l, "inactive-enter-timestamp")) {
-                        (void) deserialize_dual_timestamp(v, &u->inactive_enter_timestamp);
-                        continue;
-                } else if (streq(l, "condition-timestamp")) {
-                        (void) deserialize_dual_timestamp(v, &u->condition_timestamp);
-                        continue;
-                } else if (streq(l, "assert-timestamp")) {
-                        (void) deserialize_dual_timestamp(v, &u->assert_timestamp);
-                        continue;
-                } else if (streq(l, "condition-result")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse condition result value %s, ignoring.", v);
-                        else
-                                u->condition_result = r;
-
-                        continue;
-
-                } else if (streq(l, "assert-result")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse assert result value %s, ignoring.", v);
-                        else
-                                u->assert_result = r;
-
-                        continue;
-
-                } else if (streq(l, "transient")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse transient bool %s, ignoring.", v);
-                        else
-                                u->transient = r;
-
-                        continue;
-
-                } else if (streq(l, "in-audit")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse in-audit bool %s, ignoring.", v);
-                        else
-                                u->in_audit = r;
-
-                        continue;
-
-                } else if (streq(l, "exported-invocation-id")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse exported invocation ID bool %s, ignoring.", v);
-                        else
-                                u->exported_invocation_id = r;
-
-                        continue;
-
-                } else if (streq(l, "exported-log-level-max")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse exported log level max bool %s, ignoring.", v);
-                        else
-                                u->exported_log_level_max = r;
-
-                        continue;
-
-                } else if (streq(l, "exported-log-extra-fields")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse exported log extra fields bool %s, ignoring.", v);
-                        else
-                                u->exported_log_extra_fields = r;
-
-                        continue;
-
-                } else if (streq(l, "exported-log-rate-limit-interval")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse exported log rate limit interval %s, ignoring.", v);
-                        else
-                                u->exported_log_ratelimit_interval = r;
-
-                        continue;
-
-                } else if (streq(l, "exported-log-rate-limit-burst")) {
-
-                        r = parse_boolean(v);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse exported log rate limit burst %s, ignoring.", v);
-                        else
-                                u->exported_log_ratelimit_burst = r;
-
-                        continue;
-
-                } else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
-
-                        r = safe_atou64(v, &u->cpu_usage_base);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse CPU usage base %s, ignoring.", v);
-
-                        continue;
-
-                } else if (streq(l, "cpu-usage-last")) {
-
-                        r = safe_atou64(v, &u->cpu_usage_last);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to read CPU usage last %s, ignoring.", v);
-
-                        continue;
-
-                } else if (streq(l, "oom-kill-last")) {
-
-                        r = safe_atou64(v, &u->oom_kill_last);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to read OOM kill last %s, ignoring.", v);
-
-                        continue;
-
-                } else if (streq(l, "cgroup")) {
-
-                        r = unit_set_cgroup_path(u, v);
-                        if (r < 0)
-                                log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
-
-                        (void) unit_watch_cgroup(u);
-                        (void) unit_watch_cgroup_memory(u);
-
-                        continue;
-                } else if (streq(l, "cgroup-realized")) {
-                        int b;
-
-                        b = parse_boolean(v);
-                        if (b < 0)
-                                log_unit_debug(u, "Failed to parse cgroup-realized bool %s, ignoring.", v);
-                        else
-                                u->cgroup_realized = b;
-
-                        continue;
-
-                } else if (streq(l, "cgroup-realized-mask")) {
-
-                        r = cg_mask_from_string(v, &u->cgroup_realized_mask);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse cgroup-realized-mask %s, ignoring.", v);
-                        continue;
-
-                } else if (streq(l, "cgroup-enabled-mask")) {
-
-                        r = cg_mask_from_string(v, &u->cgroup_enabled_mask);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse cgroup-enabled-mask %s, ignoring.", v);
-                        continue;
-
-                } else if (streq(l, "cgroup-invalidated-mask")) {
-
-                        r = cg_mask_from_string(v, &u->cgroup_invalidated_mask);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse cgroup-invalidated-mask %s, ignoring.", v);
-                        continue;
-
-                } else if (streq(l, "ref-uid")) {
-                        uid_t uid;
-
-                        r = parse_uid(v, &uid);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse referenced UID %s, ignoring.", v);
-                        else
-                                unit_ref_uid_gid(u, uid, GID_INVALID);
-
-                        continue;
-
-                } else if (streq(l, "ref-gid")) {
-                        gid_t gid;
-
-                        r = parse_gid(v, &gid);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse referenced GID %s, ignoring.", v);
-                        else
-                                unit_ref_uid_gid(u, UID_INVALID, gid);
-
-                        continue;
-
-                } else if (streq(l, "ref")) {
-
-                        r = strv_extend(&u->deserialized_refs, v);
-                        if (r < 0)
-                                return log_oom();
-
-                        continue;
-                } else if (streq(l, "invocation-id")) {
-                        sd_id128_t id;
-
-                        r = sd_id128_from_string(v, &id);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse invocation id %s, ignoring.", v);
-                        else {
-                                r = unit_set_invocation_id(u, id);
-                                if (r < 0)
-                                        log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
-                        }
-
-                        continue;
-                } else if (streq(l, "freezer-state")) {
-                        FreezerState s;
-
-                        s = freezer_state_from_string(v);
-                        if (s < 0)
-                                log_unit_debug(u, "Failed to deserialize freezer-state '%s', ignoring.", v);
-                        else
-                                u->freezer_state = s;
-
-                        continue;
-                }
-
-                /* Check if this is an IP accounting metric serialization field */
-                m = string_table_lookup(ip_accounting_metric_field, ELEMENTSOF(ip_accounting_metric_field), l);
-                if (m >= 0) {
-                        uint64_t c;
-
-                        r = safe_atou64(v, &c);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse IP accounting value %s, ignoring.", v);
-                        else
-                                u->ip_accounting_extra[m] = c;
-                        continue;
-                }
-
-                m = string_table_lookup(io_accounting_metric_field_base, ELEMENTSOF(io_accounting_metric_field_base), l);
-                if (m >= 0) {
-                        uint64_t c;
-
-                        r = safe_atou64(v, &c);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse IO accounting base value %s, ignoring.", v);
-                        else
-                                u->io_accounting_base[m] = c;
-                        continue;
-                }
-
-                m = string_table_lookup(io_accounting_metric_field_last, ELEMENTSOF(io_accounting_metric_field_last), l);
-                if (m >= 0) {
-                        uint64_t c;
-
-                        r = safe_atou64(v, &c);
-                        if (r < 0)
-                                log_unit_debug(u, "Failed to parse IO accounting last value %s, ignoring.", v);
-                        else
-                                u->io_accounting_last[m] = c;
-                        continue;
-                }
-
-                if (unit_can_serialize(u)) {
-                        r = exec_runtime_deserialize_compat(u, l, v, fds);
-                        if (r < 0) {
-                                log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
-                                continue;
-                        }
-
-                        /* Returns positive if key was handled by the call */
-                        if (r > 0)
-                                continue;
-
-                        r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
-                        if (r < 0)
-                                log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l);
-                }
-        }
-
-        /* Versions before 228 did not carry a state change timestamp. In this case, take the current time. This is
-         * useful, so that timeouts based on this timestamp don't trigger too early, and is in-line with the logic from
-         * before 228 where the base for timeouts was not persistent across reboots. */
-
-        if (!dual_timestamp_is_set(&u->state_change_timestamp))
-                dual_timestamp_get(&u->state_change_timestamp);
-
-        /* Let's make sure that everything that is deserialized also gets any potential new cgroup settings applied
-         * after we are done. For that we invalidate anything already realized, so that we can realize it again. */
-        unit_invalidate_cgroup(u, _CGROUP_MASK_ALL);
-        unit_invalidate_cgroup_bpf(u);
-
-        return 0;
-}
-
-int unit_deserialize_skip(FILE *f) {
-        int r;
-        assert(f);
-
-        /* Skip serialized data for this unit. We don't know what it is. */
-
-        for (;;) {
-                _cleanup_free_ char *line = NULL;
-                char *l;
-
-                r = read_line(f, LONG_LINE_MAX, &line);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to read serialization line: %m");
-                if (r == 0)
-                        return 0;
-
-                l = strstrip(line);
-
-                /* End marker */
-                if (isempty(l))
-                        return 1;
-        }
-}
 
 int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, UnitDependencyMask mask) {
         _cleanup_free_ char *e = NULL;
@@ -4267,6 +3529,19 @@ static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) {
         return TAKE_PTR(pid_set);
 }
 
+static int kill_common_log(pid_t pid, int signo, void *userdata) {
+        _cleanup_free_ char *comm = NULL;
+        Unit *u = userdata;
+
+        assert(u);
+
+        (void) get_process_comm(pid, &comm);
+        log_unit_info(u, "Sending signal SIG%s to process " PID_FMT " (%s) on client request.",
+                      signal_to_string(signo), pid, strna(comm));
+
+        return 1;
+}
+
 int unit_kill_common(
                 Unit *u,
                 KillWho who,
@@ -4278,34 +3553,67 @@ int unit_kill_common(
         int r = 0;
         bool killed = false;
 
+        /* This is the common implementation for explicit user-requested killing of unit processes, shared by
+         * various unit types. Do not confuse with unit_kill_context(), which is what we use when we want to
+         * stop a service ourselves. */
+
         if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) {
                 if (main_pid < 0)
                         return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
-                else if (main_pid == 0)
+                if (main_pid == 0)
                         return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
         }
 
         if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) {
                 if (control_pid < 0)
                         return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
-                else if (control_pid == 0)
+                if (control_pid == 0)
                         return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
         }
 
         if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL))
                 if (control_pid > 0) {
-                        if (kill(control_pid, signo) < 0)
-                                r = -errno;
-                        else
+                        _cleanup_free_ char *comm = NULL;
+                        (void) get_process_comm(control_pid, &comm);
+
+                        if (kill(control_pid, signo) < 0) {
+                                /* Report this failure both to the logs and to the client */
+                                sd_bus_error_set_errnof(
+                                                error, errno,
+                                                "Failed to send signal SIG%s to control process " PID_FMT " (%s): %m",
+                                                signal_to_string(signo), control_pid, strna(comm));
+                                r = log_unit_warning_errno(
+                                                u, errno,
+                                                "Failed to send signal SIG%s to control process " PID_FMT " (%s) on client request: %m",
+                                                signal_to_string(signo), control_pid, strna(comm));
+                        } else {
+                                log_unit_info(u, "Sent signal SIG%s to control process " PID_FMT " (%s) on client request.",
+                                              signal_to_string(signo), control_pid, strna(comm));
                                 killed = true;
+                        }
                 }
 
         if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL))
                 if (main_pid > 0) {
-                        if (kill(main_pid, signo) < 0)
-                                r = -errno;
-                        else
+                        _cleanup_free_ char *comm = NULL;
+                        (void) get_process_comm(main_pid, &comm);
+
+                        if (kill(main_pid, signo) < 0) {
+                                if (r == 0)
+                                        sd_bus_error_set_errnof(
+                                                        error, errno,
+                                                        "Failed to send signal SIG%s to main process " PID_FMT " (%s): %m",
+                                                        signal_to_string(signo), main_pid, strna(comm));
+
+                                r = log_unit_warning_errno(
+                                                u, errno,
+                                                "Failed to send signal SIG%s to main process " PID_FMT " (%s) on client request: %m",
+                                                signal_to_string(signo), main_pid, strna(comm));
+                        } else {
+                                log_unit_info(u, "Sent signal SIG%s to main process " PID_FMT " (%s) on client request.",
+                                              signal_to_string(signo), main_pid, strna(comm));
                                 killed = true;
+                        }
                 }
 
         if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) {
@@ -4315,17 +3623,29 @@ int unit_kill_common(
                 /* Exclude the main/control pids from being killed via the cgroup */
                 pid_set = unit_pid_set(main_pid, control_pid);
                 if (!pid_set)
-                        return -ENOMEM;
+                        return log_oom();
 
-                q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, 0, pid_set, NULL, NULL);
-                if (q < 0 && !IN_SET(q, -EAGAIN, -ESRCH, -ENOENT))
-                        r = q;
-                else
+                q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, 0, pid_set, kill_common_log, u);
+                if (q < 0) {
+                        if (!IN_SET(q, -ESRCH, -ENOENT)) {
+                                if (r == 0)
+                                        sd_bus_error_set_errnof(
+                                                        error, q,
+                                                        "Failed to send signal SIG%s to auxiliary processes: %m",
+                                                        signal_to_string(signo));
+
+                                r = log_unit_warning_errno(
+                                                u, q,
+                                                "Failed to send signal SIG%s to auxiliary processes on client request: %m",
+                                                signal_to_string(signo));
+                        }
+                } else
                         killed = true;
         }
 
-        if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL))
-                return -ESRCH;
+        /* If the "fail" versions of the operation are requested, then complain if the set of processes we killed is empty */
+        if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_MAIN_FAIL))
+                return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No matching processes to kill");
 
         return r;
 }
@@ -4911,8 +4231,9 @@ int unit_kill_context(
         assert(u);
         assert(c);
 
-        /* Kill the processes belonging to this unit, in preparation for shutting the unit down.
-         * Returns > 0 if we killed something worth waiting for, 0 otherwise. */
+        /* Kill the processes belonging to this unit, in preparation for shutting the unit down.  Returns > 0
+         * if we killed something worth waiting for, 0 otherwise. Do not confuse with unit_kill_common()
+         * which is used for user-requested killing of unit processes. */
 
         if (c->kill_mode == KILL_NONE)
                 return 0;