/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-***/
#include <errno.h>
#include <stdlib.h>
u->in_dbus_queue = true;
}
+void unit_submit_to_stop_when_unneeded_queue(Unit *u) {
+ assert(u);
+
+ if (u->in_stop_when_unneeded_queue)
+ return;
+
+ if (!u->stop_when_unneeded)
+ return;
+
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
+ return;
+
+ LIST_PREPEND(stop_when_unneeded_queue, u->manager->stop_when_unneeded_queue, u);
+ u->in_stop_when_unneeded_queue = true;
+}
+
static void bidi_set_free(Unit *u, Hashmap *h) {
Unit *other;
Iterator i;
if (u->in_target_deps_queue)
LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
+ if (u->in_stop_when_unneeded_queue)
+ LIST_REMOVE(stop_when_unneeded_queue, u->manager->stop_when_unneeded_queue, u);
+
safe_close(u->ip_accounting_ingress_map_fd);
safe_close(u->ip_accounting_egress_map_fd);
return r;
}
- r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
/* If syslog or kernel logging is requested, make sure our own
* logging daemon is run first. */
- r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
if (unit_has_name(u, SPECIAL_ROOT_SLICE))
return 0;
- return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, NULL, true, mask);
+ return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, true, mask);
}
static int unit_add_mount_dependencies(Unit *u) {
return UNIT_VTABLE(u)->reload;
}
-static void unit_check_unneeded(Unit *u) {
-
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-
- static const UnitDependency needed_dependencies[] = {
+bool unit_is_unneeded(Unit *u) {
+ static const UnitDependency deps[] = {
UNIT_REQUIRED_BY,
UNIT_REQUISITE_OF,
UNIT_WANTED_BY,
UNIT_BOUND_BY,
};
-
- unsigned j;
- int r;
+ size_t j;
assert(u);
- /* If this service shall be shut down when unneeded then do
- * so. */
-
if (!u->stop_when_unneeded)
- return;
+ return false;
- if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
- return;
+ /* Don't clean up while the unit is transitioning or is even inactive. */
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
+ return false;
+ if (u->job)
+ return false;
- for (j = 0; j < ELEMENTSOF(needed_dependencies); j++) {
+ for (j = 0; j < ELEMENTSOF(deps); j++) {
Unit *other;
Iterator i;
void *v;
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[needed_dependencies[j]], i)
- if (unit_active_or_pending(other) || unit_will_restart(other))
- return;
- }
+ /* If a dependent unit has a job queued, is active or transitioning, or is marked for
+ * restart, then don't clean this one up. */
- /* If stopping a unit fails continuously we might enter a stop
- * loop here, hence stop acting on the service being
- * unnecessary after a while. */
- if (!ratelimit_below(&u->auto_stop_ratelimit)) {
- log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently.");
- return;
+ HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]], i) {
+ if (u->job)
+ return false;
+
+ if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
+ return false;
+
+ if (unit_will_restart(other))
+ return false;
+ }
}
- log_unit_info(u, "Unit not needed anymore. Stopping.");
+ return true;
+}
- /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL);
- if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
+static void check_unneeded_dependencies(Unit *u) {
+
+ static const UnitDependency deps[] = {
+ UNIT_REQUIRES,
+ UNIT_REQUISITE,
+ UNIT_WANTS,
+ UNIT_BINDS_TO,
+ };
+ size_t j;
+
+ assert(u);
+
+ /* Add all units this unit depends on to the queue that processes StopWhenUnneeded= behaviour. */
+
+ for (j = 0; j < ELEMENTSOF(deps); j++) {
+ Unit *other;
+ Iterator i;
+ void *v;
+
+ HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]], i)
+ unit_submit_to_stop_when_unneeded_queue(other);
+ }
}
static void unit_check_binds_to(Unit *u) {
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
}
-static void check_unneeded_dependencies(Unit *u) {
- Unit *other;
- Iterator i;
- void *v;
-
- assert(u);
- assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
-
- /* Garbage collect services that might not be needed anymore, if enabled */
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUISITE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
-}
-
void unit_start_on_failure(Unit *u) {
Unit *other;
Iterator i;
}
/* stop unneeded units regardless if going down was expected or not */
- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
check_unneeded_dependencies(u);
if (ns != os && ns == UNIT_FAILED) {
if (!MANAGER_IS_RELOADING(u->manager)) {
/* Maybe we finished startup and are now ready for being stopped because unneeded? */
- unit_check_unneeded(u);
+ unit_submit_to_stop_when_unneeded_queue(u);
/* Maybe we finished startup, but something we needed has vanished? Let's die then. (This happens when
* something BindsTo= to a Type=oneshot unit, as these units go directly from starting to inactive,
return unit_add_dependency(u, e, other, add_reference, mask);
}
-static int resolve_template(Unit *u, const char *name, const char*path, char **buf, const char **ret) {
+static int resolve_template(Unit *u, const char *name, char **buf, const char **ret) {
int r;
assert(u);
- assert(name || path);
+ assert(name);
assert(buf);
assert(ret);
- if (!name)
- name = basename(path);
-
if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
*buf = NULL;
*ret = name;
return 0;
}
-int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference, UnitDependencyMask mask) {
+int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, bool add_reference, UnitDependencyMask mask) {
_cleanup_free_ char *buf = NULL;
Unit *other;
int r;
assert(u);
- assert(name || path);
+ assert(name);
- r = resolve_template(u, name, path, &buf, &name);
+ r = resolve_template(u, name, &buf, &name);
if (r < 0)
return r;
- r = manager_load_unit(u->manager, name, path, NULL, &other);
+ r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0)
return r;
return unit_add_dependency(u, d, other, add_reference, mask);
}
-int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference, UnitDependencyMask mask) {
+int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, bool add_reference, UnitDependencyMask mask) {
_cleanup_free_ char *buf = NULL;
Unit *other;
int r;
assert(u);
- assert(name || path);
+ assert(name);
- r = resolve_template(u, name, path, &buf, &name);
+ r = resolve_template(u, name, &buf, &name);
if (r < 0)
return r;
- r = manager_load_unit(u->manager, name, path, NULL, &other);
+ r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0)
return r;
}
cc = unit_get_cgroup_context(u);
- if (cc) {
+ if (cc && ec) {
- if (ec &&
- ec->private_devices &&
+ if (ec->private_devices &&
cc->device_policy == CGROUP_AUTO)
cc->device_policy = CGROUP_CLOSED;
+
+ if (ec->root_image &&
+ (cc->device_policy != CGROUP_AUTO || cc->device_allow)) {
+
+ /* When RootImage= is specified, the following devices are touched. */
+ r = cgroup_add_device_allow(cc, "/dev/loop-control", "rw");
+ if (r < 0)
+ return r;
+
+ r = cgroup_add_device_allow(cc, "block-loop", "rwm");
+ if (r < 0)
+ return r;
+
+ r = cgroup_add_device_allow(cc, "block-blkext", "rwm");
+ if (r < 0)
+ return r;
+ }
}
return 0;
return c->kill_signal;
case KILL_KILL:
- return SIGKILL;
+ return c->final_kill_signal;
- case KILL_ABORT:
- return SIGABRT;
+ case KILL_WATCHDOG:
+ return c->watchdog_signal;
default:
assert_not_reached("KillOperation unknown");
/* Some extra safety check */
if (pid == 1 || pid == getpid_cached())
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process " PID_FMT " is a manager processs, refusing.", pid);
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process " PID_FMT " is a manager process, refusing.", pid);
/* Don't even begin to bother with kernel threads */
r = is_kernel_thread(pid);