***/
#include <errno.h>
-#include <string.h>
#include <stdlib.h>
-#include <unistd.h>
+#include <string.h>
#include <sys/stat.h>
+#include <unistd.h>
#include "sd-id128.h"
#include "sd-messages.h"
-#include "set.h"
-#include "unit.h"
-#include "macro.h"
-#include "strv.h"
-#include "path-util.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "unit-name.h"
-#include "dbus-unit.h"
-#include "special.h"
-#include "cgroup-util.h"
-#include "missing.h"
-#include "mkdir.h"
-#include "fileio-label.h"
+
#include "bus-common-errors.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "dbus-unit.h"
#include "dbus.h"
-#include "execute.h"
#include "dropin.h"
+#include "escape.h"
+#include "execute.h"
+#include "fileio-label.h"
#include "formats-util.h"
+#include "load-dropin.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "macro.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "path-util.h"
#include "process-util.h"
+#include "set.h"
+#include "special.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "virt.h"
+#include "unit.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
u->unit_file_state = _UNIT_FILE_STATE_INVALID;
u->unit_file_preset = -1;
u->on_failure_job_mode = JOB_REPLACE;
+ u->cgroup_inotify_wd = -1;
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
cc->cpu_accounting = u->manager->default_cpu_accounting;
cc->blockio_accounting = u->manager->default_blockio_accounting;
cc->memory_accounting = u->manager->default_memory_accounting;
+ cc->tasks_accounting = u->manager->default_tasks_accounting;
}
ec = unit_get_exec_context(u);
return;
if (u->fragment_path)
- unlink(u->fragment_path);
+ (void) unlink(u->fragment_path);
STRV_FOREACH(i, u->dropin_paths) {
_cleanup_free_ char *p = NULL;
int r;
- unlink(*i);
+ (void) unlink(*i);
r = path_get_parent(*i, &p);
if (r >= 0)
- rmdir(p);
+ (void) rmdir(p);
}
}
}
}
- strv_free(u->requires_mounts_for);
- u->requires_mounts_for = NULL;
+ u->requires_mounts_for = strv_free(u->requires_mounts_for);
}
static void unit_done(Unit *u) {
ExecContext *ec;
CGroupContext *cc;
+ int r;
assert(u);
cc = unit_get_cgroup_context(u);
if (cc)
cgroup_context_done(cc);
+
+ r = unit_remove_from_netclass_cgroup(u);
+ if (r < 0)
+ log_warning_errno(r, "Unable to remove unit from netclass group: %m");
}
void unit_free(Unit *u) {
unit_done(u);
+ sd_bus_slot_unref(u->match_bus_slot);
+
unit_free_requires_mounts_for(u);
SET_FOREACH(t, u->names, i)
if (u->in_cgroup_queue)
LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
- if (u->cgroup_path) {
- hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
- free(u->cgroup_path);
- }
+ unit_release_cgroup(u);
- manager_update_failed_units(u->manager, u, false);
+ (void) manager_update_failed_units(u->manager, u, false);
set_remove(u->manager->startup_units, u);
free(u->description);
/* The move cannot fail. The caller must have performed a reservation. */
assert_se(complete_move(&u->dependencies[d], &other->dependencies[d]) == 0);
- set_free(other->dependencies[d]);
- other->dependencies[d] = NULL;
+ other->dependencies[d] = set_free(other->dependencies[d]);
}
int unit_merge(Unit *u, Unit *other) {
static int unit_add_slice_dependencies(Unit *u) {
assert(u);
- if (!unit_get_cgroup_context(u))
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
if (UNIT_ISSET(u->slice))
- return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true);
+ return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true);
- if (streq(u->id, SPECIAL_ROOT_SLICE))
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
return 0;
- return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true);
+ return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, NULL, true);
}
static int unit_add_mount_dependencies(Unit *u) {
char prefix[strlen(*i) + 1];
PATH_FOREACH_PREFIX_MORE(prefix, *i) {
+ _cleanup_free_ char *p = NULL;
Unit *m;
- r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m);
+ r = unit_name_from_path(prefix, ".mount", &p);
if (r < 0)
return r;
- if (r == 0)
+
+ m = manager_get_unit(u->manager, p);
+ if (!m) {
+ /* Make sure to load the mount unit if
+ * it exists. If so the dependencies
+ * on this unit will be added later
+ * during the loading of the mount
+ * unit. */
+ (void) manager_load_unit_prepare(u->manager, p, NULL, NULL, &m);
continue;
+ }
if (m == u)
continue;
static int unit_add_startup_units(Unit *u) {
CGroupContext *c;
+ int r;
c = unit_get_cgroup_context(u);
if (!c)
return 0;
- if (c->startup_cpu_shares == (unsigned long) -1 &&
- c->startup_blockio_weight == (unsigned long) -1)
+ if (c->startup_cpu_shares == CGROUP_CPU_SHARES_INVALID &&
+ c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID)
return 0;
+ r = set_ensure_allocated(&u->manager->startup_units, NULL);
+ if (r < 0)
+ return r;
+
return set_put(u->manager->startup_units, u);
}
}
unit_update_cgroup_members_masks(u);
+
+ /* If we are reloading, we need to wait for the deserializer
+ * to restore the net_cls ids that have been set previously */
+ if (u->manager->n_reloading <= 0) {
+ r = unit_add_to_netclass_cgroup(u);
+ if (r < 0)
+ return r;
+ }
}
assert((u->load_state != UNIT_MERGED) == !u->merged_into);
assert(u);
+ /* Units that aren't loaded cannot be started */
if (u->load_state != UNIT_LOADED)
return -EINVAL;
return -EPROTO;
}
+ /* Units of types that aren't supported cannot be
+ * started. Note that we do this test only after the condition
+ * checks, so that we rather return condition check errors
+ * (which are usually not considered a true failure) than "not
+ * supported" errors (which are considered a failure).
+ */
+ if (!unit_supported(u))
+ return -EOPNOTSUPP;
+
/* Forward to the main object, if we aren't it. */
following = unit_following(u);
if (following) {
return unit_start(following);
}
- if (!unit_supported(u))
- return -EOPNOTSUPP;
-
/* If it is stopped, but we cannot start it, then fail */
if (!UNIT_VTABLE(u)->start)
return -EBADR;
bool unit_can_start(Unit *u) {
assert(u);
+ if (u->load_state != UNIT_LOADED)
+ return false;
+
+ if (!unit_supported(u))
+ return false;
+
return !!UNIT_VTABLE(u)->start;
}
}
/* Keep track of failed units */
- manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
+ (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
/* Make sure the cgroup is always removed when we become inactive */
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- unit_destroy_cgroup_if_empty(u);
+ unit_prune_cgroup(u);
/* Note that this doesn't apply to RemainAfterExit services exiting
* successfully, since there's no change of state in that case. Which is
if (r < 0)
return r;
- r = hashmap_put(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
+ r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u);
if (r == -EEXIST) {
r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL);
if (r < 0)
return r;
- r = hashmap_put(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
+ r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u);
}
- q = set_put(u->pids, LONG_TO_PTR(pid));
+ q = set_put(u->pids, PID_TO_PTR(pid));
if (q < 0)
return q;
assert(u);
assert(pid >= 1);
- hashmap_remove_value(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
- hashmap_remove_value(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
- set_remove(u->pids, LONG_TO_PTR(pid));
+ (void) hashmap_remove_value(u->manager->watch_pids1, PID_TO_PTR(pid), u);
+ (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u);
+ (void) set_remove(u->pids, PID_TO_PTR(pid));
}
void unit_unwatch_all_pids(Unit *u) {
assert(u);
while (!set_isempty(u->pids))
- unit_unwatch_pid(u, PTR_TO_LONG(set_first(u->pids)));
+ unit_unwatch_pid(u, PTR_TO_PID(set_first(u->pids)));
- set_free(u->pids);
- u->pids = NULL;
-}
-
-static int unit_watch_pids_in_path(Unit *u, const char *path) {
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int ret = 0, r;
-
- assert(u);
- assert(path);
-
- /* Adds all PIDs from a specific cgroup path to the set of PIDs we watch. */
-
- r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f);
- if (r >= 0) {
- pid_t pid;
-
- while ((r = cg_read_pid(f, &pid)) > 0) {
- r = unit_watch_pid(u, pid);
- if (r < 0 && ret >= 0)
- ret = r;
- }
- if (r < 0 && ret >= 0)
- ret = r;
-
- } else if (ret >= 0)
- ret = r;
-
- r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
- if (r >= 0) {
- char *fn;
-
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
-
- p = strjoin(path, "/", fn, NULL);
- free(fn);
-
- if (!p)
- return -ENOMEM;
-
- r = unit_watch_pids_in_path(u, p);
- if (r < 0 && ret >= 0)
- ret = r;
- }
- if (r < 0 && ret >= 0)
- ret = r;
-
- } else if (ret >= 0)
- ret = r;
-
- return ret;
-}
-
-int unit_watch_all_pids(Unit *u) {
- assert(u);
-
- /* Adds all PIDs from our cgroup to the set of PIDs we watch */
-
- if (!u->cgroup_path)
- return -ENOENT;
-
- return unit_watch_pids_in_path(u, u->cgroup_path);
+ u->pids = set_free(u->pids);
}
void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) {
/* Cleans dead PIDs from our list */
SET_FOREACH(e, u->pids, i) {
- pid_t pid = PTR_TO_LONG(e);
+ pid_t pid = PTR_TO_PID(e);
if (pid == except1 || pid == except2)
continue;
return unit_add_two_dependencies(u, d, e, other, add_reference);
}
-int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
- _cleanup_free_ char *buf = NULL;
- Unit *other;
- int r;
-
- assert(u);
- assert(name || path);
-
- r = resolve_template(u, name, path, &buf, &name);
- if (r < 0)
- return r;
-
- r = manager_load_unit(u->manager, name, path, NULL, &other);
- if (r < 0)
- return r;
-
- return unit_add_dependency(other, d, u, add_reference);
-}
-
-int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
- _cleanup_free_ char *buf = NULL;
- Unit *other;
- int r;
-
- assert(u);
- assert(name || path);
-
- r = resolve_template(u, name, path, &buf, &name);
- if (r < 0)
- return r;
-
- r = manager_load_unit(u->manager, name, path, NULL, &other);
- if (r < 0)
- return r;
-
- return unit_add_two_dependencies(other, d, e, u, add_reference);
-}
-
int set_unit_path(const char *p) {
/* This is mostly for debug purposes */
if (setenv("SYSTEMD_UNIT_PATH", p, 0) < 0)
return unit_dbus_path_from_name(u->id);
}
-char *unit_default_cgroup_path(Unit *u) {
- _cleanup_free_ char *escaped = NULL, *slice = NULL;
- int r;
-
+int unit_set_slice(Unit *u, Unit *slice) {
assert(u);
+ assert(slice);
- if (unit_has_name(u, SPECIAL_ROOT_SLICE))
- return strdup(u->manager->cgroup_root);
+ /* Sets the unit slice if it has not been set before. Is extra
+ * careful, to only allow this for units that actually have a
+ * cgroup context. Also, we don't allow to set this for slices
+ * (since the parent slice is derived from the name). Make
+ * sure the unit we set is actually a slice. */
- if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) {
- r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
- if (r < 0)
- return NULL;
- }
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return -EOPNOTSUPP;
- escaped = cg_escape(u->id);
- if (!escaped)
- return NULL;
+ if (u->type == UNIT_SLICE)
+ return -EINVAL;
- if (slice)
- return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
- else
- return strjoin(u->manager->cgroup_root, "/", escaped, NULL);
+ if (unit_active_state(u) != UNIT_INACTIVE)
+ return -EBUSY;
+
+ if (slice->type != UNIT_SLICE)
+ return -EINVAL;
+
+ if (unit_has_name(u, SPECIAL_INIT_SCOPE) &&
+ !unit_has_name(slice, SPECIAL_ROOT_SLICE))
+ return -EPERM;
+
+ if (UNIT_DEREF(u->slice) == slice)
+ return 0;
+
+ if (UNIT_ISSET(u->slice))
+ return -EBUSY;
+
+ unit_ref_set(&u->slice, slice);
+ return 1;
}
-int unit_add_default_slice(Unit *u, CGroupContext *c) {
+int unit_set_default_slice(Unit *u) {
_cleanup_free_ char *b = NULL;
const char *slice_name;
Unit *slice;
int r;
assert(u);
- assert(c);
if (UNIT_ISSET(u->slice))
return 0;
slice_name = b;
} else
slice_name =
- u->manager->running_as == MANAGER_SYSTEM
+ u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE)
? SPECIAL_SYSTEM_SLICE
: SPECIAL_ROOT_SLICE;
if (r < 0)
return r;
- unit_ref_set(&u->slice, slice);
- return 0;
+ return unit_set_slice(u, slice);
}
const char *unit_slice_name(Unit *u) {
return r;
}
+static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *name, *old_owner, *new_owner;
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ return 0;
+ }
+
+ if (UNIT_VTABLE(u)->bus_name_owner_change)
+ UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
+
+ return 0;
+}
+
+int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
+ const char *match;
+
+ assert(u);
+ assert(bus);
+ assert(name);
+
+ if (u->match_bus_slot)
+ return -EBUSY;
+
+ match = strjoina("type='signal',"
+ "sender='org.freedesktop.DBus',"
+ "path='/org/freedesktop/DBus',"
+ "interface='org.freedesktop.DBus',"
+ "member='NameOwnerChanged',"
+ "arg0='", name, "'",
+ NULL);
+
+ return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u);
+}
+
int unit_watch_bus_name(Unit *u, const char *name) {
+ int r;
+
assert(u);
assert(name);
/* Watch a specific name on the bus. We only support one unit
* watching each name for now. */
- return hashmap_put(u->manager->watch_bus, name, u);
+ if (u->manager->api_bus) {
+ /* If the bus is already available, install the match directly.
+ * Otherwise, just put the name in the list. bus_setup_api() will take care later. */
+ r = unit_install_bus_match(u, u->manager->api_bus, name);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m");
+ }
+
+ r = hashmap_put(u->manager->watch_bus, name, u);
+ if (r < 0) {
+ u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+ return log_warning_errno(r, "Failed to put bus name to hashmap: %m");
+ }
+
+ return 0;
}
void unit_unwatch_bus_name(Unit *u, const char *name) {
assert(name);
hashmap_remove_value(u->manager->watch_bus, name, u);
+ u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
}
bool unit_can_serialize(Unit *u) {
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized));
+ if (u->cgroup_netclass_id)
+ unit_serialize_item_format(u, f, "netclass-id", "%" PRIu32, u->cgroup_netclass_id);
+
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
return 0;
}
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
- va_list ap;
-
+int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
assert(u);
assert(f);
assert(key);
- assert(format);
+
+ if (!value)
+ return 0;
fputs(key, f);
fputc('=', f);
-
- va_start(ap, format);
- vfprintf(f, format, ap);
- va_end(ap);
-
+ fputs(value, f);
fputc('\n', f);
+
+ return 1;
}
-void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
+int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value) {
+ _cleanup_free_ char *c = NULL;
+
assert(u);
assert(f);
assert(key);
- assert(value);
- fprintf(f, "%s=%s\n", key, value);
+ if (!value)
+ return 0;
+
+ c = cescape(value);
+ if (!c)
+ return -ENOMEM;
+
+ fputs(key, f);
+ fputc('=', f);
+ fputs(c, f);
+ fputc('\n', f);
+
+ return 1;
}
-static int unit_set_cgroup_path(Unit *u, const char *path) {
- _cleanup_free_ char *p = NULL;
- int r;
+int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd) {
+ int copy;
assert(u);
+ assert(f);
+ assert(key);
- if (path) {
- p = strdup(path);
- if (!p)
- return -ENOMEM;
- } else
- p = NULL;
-
- if (streq_ptr(u->cgroup_path, p))
+ if (fd < 0)
return 0;
- if (p) {
- r = hashmap_put(u->manager->cgroup_unit, p, u);
- if (r < 0)
- return r;
- }
+ copy = fdset_put_dup(fds, fd);
+ if (copy < 0)
+ return copy;
- if (u->cgroup_path) {
- log_unit_debug(u, "Changing cgroup path from %s to %s.", u->cgroup_path, strna(p));
- hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
- free(u->cgroup_path);
- }
+ fprintf(f, "%s=%i\n", key, copy);
+ return 1;
+}
+
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
+ va_list ap;
- u->cgroup_path = p;
- p = NULL;
+ assert(u);
+ assert(f);
+ assert(key);
+ assert(format);
- return 0;
+ fputs(key, f);
+ fputc('=', f);
+
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+
+ fputc('\n', f);
}
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
+ (void) unit_watch_cgroup(u);
+
continue;
} else if (streq(l, "cgroup-realized")) {
int b;
else
u->cgroup_realized = b;
+ continue;
+ } else if (streq(l, "netclass-id")) {
+ r = safe_atou32(v, &u->cgroup_netclass_id);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse netclass ID %s, ignoring.", v);
+ else {
+ r = unit_add_to_netclass_cgroup(u);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to add unit to netclass cgroup, ignoring: %m");
+ }
+
continue;
}
/* Exclude the main/control pids from being killed via the cgroup */
if (main_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(main_pid));
+ r = set_put(pid_set, PID_TO_PTR(main_pid));
if (r < 0)
goto fail;
}
if (control_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(control_pid));
+ r = set_put(pid_set, PID_TO_PTR(control_pid));
if (r < 0)
goto fail;
}
sd_bus_error *error) {
int r = 0;
+ bool killed = false;
- if (who == KILL_MAIN && main_pid <= 0) {
+ 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
+ else if (main_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
}
- if (who == KILL_CONTROL && control_pid <= 0) {
+ 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
+ else if (control_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
}
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (control_pid > 0)
+ 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
+ killed = true;
+ }
- if (who == KILL_MAIN || who == KILL_ALL)
- if (main_pid > 0)
+ 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
+ killed = true;
+ }
- if (who == KILL_ALL && u->cgroup_path) {
+ if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) {
_cleanup_set_free_ Set *pid_set = NULL;
int q;
if (!pid_set)
return -ENOMEM;
- q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
+ q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, false, false, pid_set);
if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
r = q;
+ else
+ killed = true;
}
+ if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_ALL_FAIL))
+ return -ESRCH;
+
return r;
}
}
static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
+ assert(u);
+
if (u->manager->running_as == MANAGER_USER) {
int r;
r = user_config_home(dir);
else
r = user_runtime_dir(dir);
-
if (r == 0)
return -ENOENT;
+
return r;
}
return 0;
}
-static int unit_drop_in_file(Unit *u,
- UnitSetPropertiesMode mode, const char *name, char **p, char **q) {
- _cleanup_free_ char *dir = NULL;
- int r;
-
- assert(u);
-
- r = unit_drop_in_dir(u, mode, u->transient, &dir);
- if (r < 0)
- return r;
-
- return drop_in_file(dir, u->id, 50, name, p, q);
-}
-
int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
_cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL;
return unit_write_drop_in_private(u, mode, name, p);
}
-int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) {
- _cleanup_free_ char *p = NULL, *q = NULL;
- int r;
-
- assert(u);
-
- if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
- return 0;
-
- r = unit_drop_in_file(u, mode, name, &p, &q);
- if (r < 0)
- return r;
-
- if (unlink(q) < 0)
- r = errno == ENOENT ? 0 : -errno;
- else
- r = 1;
-
- rmdir(p);
- return r;
-}
-
int unit_make_transient(Unit *u) {
- int r;
-
assert(u);
+ if (!UNIT_VTABLE(u)->can_transient)
+ return -EOPNOTSUPP;
+
u->load_state = UNIT_STUB;
u->load_error = 0;
u->transient = true;
+ u->fragment_path = mfree(u->fragment_path);
- free(u->fragment_path);
- u->fragment_path = NULL;
-
- if (u->manager->running_as == MANAGER_USER) {
- _cleanup_free_ char *c = NULL;
-
- r = user_runtime_dir(&c);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOENT;
-
- u->fragment_path = strjoin(c, "/", u->id, NULL);
- if (!u->fragment_path)
- return -ENOMEM;
-
- mkdir_p(c, 0755);
- } else {
- u->fragment_path = strappend("/run/systemd/system/", u->id);
- if (!u->fragment_path)
- return -ENOMEM;
-
- mkdir_p("/run/systemd/system", 0755);
- }
-
- return write_string_file_atomic_label(u->fragment_path, "# Transient stub");
+ return 0;
}
int unit_kill_context(
pid_t control_pid,
bool main_pid_alien) {
- int sig, wait_for_exit = false, r;
+ bool wait_for_exit = false;
+ int sig, r;
assert(u);
assert(c);
_cleanup_free_ char *comm = NULL;
get_process_comm(main_pid, &comm);
- log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm));
+ log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm));
} else {
if (!main_pid_alien)
wait_for_exit = true;
- if (c->send_sighup && k != KILL_KILL)
- kill(main_pid, SIGHUP);
+ if (c->send_sighup && k == KILL_TERMINATE)
+ (void) kill(main_pid, SIGHUP);
}
}
_cleanup_free_ char *comm = NULL;
get_process_comm(control_pid, &comm);
- log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm));
+ log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm));
} else {
wait_for_exit = true;
- if (c->send_sighup && k != KILL_KILL)
- kill(control_pid, SIGHUP);
+ if (c->send_sighup && k == KILL_TERMINATE)
+ (void) kill(control_pid, SIGHUP);
}
}
- if ((c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL)) && u->cgroup_path) {
+ if (u->cgroup_path &&
+ (c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL))) {
_cleanup_set_free_ Set *pid_set = NULL;
/* Exclude the main/control pids from being killed via the cgroup */
if (!pid_set)
return -ENOMEM;
- r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set);
+ r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, k != KILL_TERMINATE, false, pid_set);
if (r < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_unit_warning_errno(u, r, "Failed to kill control group: %m");
- } else if (r > 0) {
+ log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path);
- /* FIXME: For now, we will not wait for the
- * cgroup members to die, simply because
- * cgroup notification is unreliable. It
- * doesn't work at all in containers, and
- * outside of containers it can be confused
- * easily by leaving directories in the
- * cgroup. */
+ } else if (r > 0) {
- /* wait_for_exit = true; */
+ /* FIXME: For now, on the legacy hierarchy, we
+ * will not wait for the cgroup members to die
+ * if we are running in a container or if this
+ * is a delegation unit, simply because cgroup
+ * notification is unreliable in these
+ * cases. It doesn't work at all in
+ * containers, and outside of containers it
+ * can be confused easily by left-over
+ * directories in the cgroup -- which however
+ * should not exist in non-delegated units. On
+ * the unified hierarchy that's different,
+ * there we get proper events. Hence rely on
+ * them.*/
+
+ if (cg_unified() > 0 ||
+ (detect_container() == 0 && !unit_cgroup_delegate(u)))
+ wait_for_exit = true;
if (c->send_sighup && k != KILL_KILL) {
set_free(pid_set);
return -ELOOP;
}
-
-static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
- [UNIT_ACTIVE] = "active",
- [UNIT_RELOADING] = "reloading",
- [UNIT_INACTIVE] = "inactive",
- [UNIT_FAILED] = "failed",
- [UNIT_ACTIVATING] = "activating",
- [UNIT_DEACTIVATING] = "deactivating"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);