-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <stdlib.h>
#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"
#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"
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;
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;
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;
}
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;
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;
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) {
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;
* 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);
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);
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)
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;
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,
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) {
/* 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;
}
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;