diff options
Diffstat (limited to 'src')
32 files changed, 342 insertions, 157 deletions
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 6b3162a35f..98adace55a 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -29,7 +29,6 @@ #include <sys/types.h> #include <ftw.h> -#include "cgroup-util.h" #include "set.h" #include "macro.h" #include "util.h" @@ -41,6 +40,7 @@ #include "special.h" #include "mkdir.h" #include "login-util.h" +#include "cgroup-util.h" int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { _cleanup_free_ char *fs = NULL; @@ -113,7 +113,7 @@ int cg_read_subgroup(DIR *d, char **fn) { assert(d); assert(fn); - FOREACH_DIRENT(de, d, return -errno) { + FOREACH_DIRENT_ALL(de, d, return -errno) { char *b; if (de->d_type != DT_DIR) @@ -197,7 +197,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo ret = -errno; } else { if (sigcont && sig != SIGKILL) - kill(pid, SIGCONT); + (void) kill(pid, SIGCONT); if (ret == 0) ret = 1; @@ -233,7 +233,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) { _cleanup_set_free_ Set *allocated_set = NULL; _cleanup_closedir_ DIR *d = NULL; - int r, ret = 0; + int r, ret; char *fn; assert(path); @@ -264,7 +264,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si return -ENOMEM; r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s); - if (ret >= 0 && r != 0) + if (r != 0 && ret >= 0) ret = r; } @@ -321,6 +321,14 @@ int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid)) continue; + /* Ignore kernel threads. Since they can only + * exist in the root cgroup, we only check for + * them there. */ + if (cfrom && + (isempty(pfrom) || path_equal(pfrom, "/")) && + is_kernel_thread(pid) > 0) + continue; + r = cg_attach(cto, pto, pid); if (r < 0) { if (ret >= 0 && r != -ESRCH) @@ -382,12 +390,8 @@ int cg_migrate_recursive( p = strjoin(pfrom, "/", fn, NULL); free(fn); - if (!p) { - if (ret >= 0) - return -ENOMEM; - - return ret; - } + if (!p) + return -ENOMEM; r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem); if (r != 0 && ret >= 0) @@ -428,13 +432,15 @@ int cg_migrate_recursive_fallback( /* This didn't work? Then let's try all prefixes of the destination */ PATH_FOREACH_PREFIX(prefix, pto) { - r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem); - if (r >= 0) - break; + int q; + + q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem); + if (q >= 0) + return q; } } - return 0; + return r; } static const char *normalize_controller(const char *controller) { @@ -549,7 +555,7 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct if (ftwbuf->level < 1) return 0; - rmdir(path); + (void) rmdir(path); return 0; } @@ -564,8 +570,14 @@ int cg_trim(const char *controller, const char *path, bool delete_root) { return r; errno = 0; - if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) - r = errno ? -errno : -EIO; + if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) { + if (errno == ENOENT) + r = 0; + else if (errno != 0) + r = -errno; + else + r = -EIO; + } if (delete_root) { if (rmdir(fs) < 0 && errno != ENOENT) @@ -664,13 +676,15 @@ int cg_attach_fallback(const char *controller, const char *path, pid_t pid) { * the destination */ PATH_FOREACH_PREFIX(prefix, path) { - r = cg_attach(controller, prefix, pid); - if (r >= 0) - break; + int q; + + q = cg_attach(controller, prefix, pid); + if (q >= 0) + return q; } } - return 0; + return r; } int cg_set_group_access( @@ -683,7 +697,8 @@ int cg_set_group_access( _cleanup_free_ char *fs = NULL; int r; - assert(path); + if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) + return 0; if (mode != MODE_INVALID) mode &= 0777; @@ -819,7 +834,7 @@ int cg_install_release_agent(const char *controller, const char *agent) { return r; sc = strstrip(contents); - if (sc[0] == 0) { + if (isempty(sc)) { r = write_string_file(fs, agent, 0); if (r < 0) return r; @@ -876,49 +891,46 @@ int cg_uninstall_release_agent(const char *controller) { return 0; } -int cg_is_empty(const char *controller, const char *path, bool ignore_self) { +int cg_is_empty(const char *controller, const char *path) { _cleanup_fclose_ FILE *f = NULL; - pid_t pid = 0, self_pid; - bool found = false; + pid_t pid = 0; int r; assert(path); r = cg_enumerate_processes(controller, path, &f); + if (r == -ENOENT) + return 1; if (r < 0) - return r == -ENOENT ? 1 : r; - - self_pid = getpid(); - - while ((r = cg_read_pid(f, &pid)) > 0) { - - if (ignore_self && pid == self_pid) - continue; - - found = true; - break; - } + return r; + r = cg_read_pid(f, &pid); if (r < 0) return r; - return !found; + return r == 0; } -int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) { +int cg_is_empty_recursive(const char *controller, const char *path) { _cleanup_closedir_ DIR *d = NULL; char *fn; int r; assert(path); - r = cg_is_empty(controller, path, ignore_self); + /* The root cgroup is always populated */ + if (controller && (isempty(path) || path_equal(path, "/"))) + return 0; + + r = cg_is_empty(controller, path); if (r <= 0) return r; r = cg_enumerate_subgroups(controller, path, &d); + if (r == -ENOENT) + return 1; if (r < 0) - return r == -ENOENT ? 1 : r; + return r; while ((r = cg_read_subgroup(d, &fn)) > 0) { _cleanup_free_ char *p = NULL; @@ -928,7 +940,7 @@ int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_ if (!p) return -ENOMEM; - r = cg_is_empty_recursive(controller, p, ignore_self); + r = cg_is_empty_recursive(controller, p); if (r <= 0) return r; } @@ -1869,6 +1881,11 @@ int cg_kernel_controllers(Set *controllers) { assert(controllers); + /* Determines the full list of kernel-known controllers. Might + * include controllers we don't actually support, arbitrary + * named hierarchies and controllers that aren't currently + * accessible (because not mounted). */ + f = fopen("/proc/cgroups", "re"); if (!f) { if (errno == ENOENT) @@ -1889,7 +1906,7 @@ int cg_kernel_controllers(Set *controllers) { if (feof(f)) break; - if (ferror(f) && errno) + if (ferror(f) && errno != 0) return -errno; return -EBADMSG; diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index fd72e9e5c5..1c86581eb5 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -93,8 +93,8 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui int cg_install_release_agent(const char *controller, const char *agent); int cg_uninstall_release_agent(const char *controller); -int cg_is_empty(const char *controller, const char *path, bool ignore_self); -int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self); +int cg_is_empty(const char *controller, const char *path); +int cg_is_empty_recursive(const char *controller, const char *path); int cg_get_root_path(char **path); diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index 7c58985cd2..a39a0f775a 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -199,11 +199,11 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { if (!mac_selinux_use()) return -EOPNOTSUPP; - r = getcon(&mycon); + r = getcon_raw(&mycon); if (r < 0) return -errno; - r = getfilecon(exe, &fcon); + r = getfilecon_raw(exe, &fcon); if (r < 0) return -errno; @@ -225,7 +225,7 @@ int mac_selinux_get_our_label(char **label) { if (!mac_selinux_use()) return -EOPNOTSUPP; - r = getcon(label); + r = getcon_raw(label); if (r < 0) return -errno; #endif @@ -249,7 +249,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * if (!mac_selinux_use()) return -EOPNOTSUPP; - r = getcon(&mycon); + r = getcon_raw(&mycon); if (r < 0) return -errno; @@ -260,7 +260,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * if (!exec_label) { /* If there is no context set for next exec let's use context of target executable */ - r = getfilecon(exe, &fcon); + r = getfilecon_raw(exe, &fcon); if (r < 0) return -errno; } diff --git a/src/basic/set.h b/src/basic/set.h index 51e40d3a6c..4554ef2d49 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -28,12 +28,14 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) -static inline void set_free(Set *s) { +static inline Set *set_free(Set *s) { internal_hashmap_free(HASHMAP_BASE(s)); + return NULL; } -static inline void set_free_free(Set *s) { +static inline Set *set_free_free(Set *s) { internal_hashmap_free_free(HASHMAP_BASE(s)); + return NULL; } /* no set_free_free_free */ diff --git a/src/basic/time-util.c b/src/basic/time-util.c index afc6a6eb24..531931f6e1 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1046,7 +1046,7 @@ clockid_t clock_boottime_or_monotonic(void) { return clock; } -int get_timezone(char **timezone) { +int get_timezone(char **tz) { _cleanup_free_ char *t = NULL; const char *e; char *z; @@ -1069,6 +1069,6 @@ int get_timezone(char **timezone) { if (!z) return -ENOMEM; - *timezone = z; + *tz = z; return 0; } diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 06a43d15e4..ae562ba135 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -560,15 +560,17 @@ static void display(Hashmap *a) { path_columns = maxtpath; for (j = 0; j < n; j++) { - _cleanup_free_ char *p = NULL; + _cleanup_free_ char *ellipsized = NULL; + const char *path; if (on_tty() && j + 5 > rows) break; g = array[j]; - p = ellipsize(g->path, path_columns, 33); - printf("%-*s", path_columns, p ?: g->path); + path = isempty(g->path) ? "/" : g->path; + ellipsized = ellipsize(path, path_columns, 33); + printf("%-*s", path_columns, ellipsized ?: path); if (g->n_tasks_valid) printf(" %7u", g->n_tasks); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index c26807ba2b..e92d2cc850 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1005,6 +1005,7 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { _cleanup_free_ char *cgroup = NULL; + Unit *u; int r; assert(m); @@ -1012,6 +1013,14 @@ Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { if (pid <= 1) return NULL; + u = hashmap_get(m->watch_pids1, LONG_TO_PTR(pid)); + if (u) + return u; + + u = hashmap_get(m->watch_pids2, LONG_TO_PTR(pid)); + if (u) + return u; + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); if (r < 0) return NULL; @@ -1030,7 +1039,7 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { if (!u) return 0; - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); if (r <= 0) return r; @@ -1124,6 +1133,18 @@ int unit_reset_cpu_usage(Unit *u) { return 0; } +bool unit_cgroup_delegate(Unit *u) { + CGroupContext *c; + + assert(u); + + c = unit_get_cgroup_context(u); + if (!c) + return false; + + return c->delegate; +} + static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { [CGROUP_AUTO] = "auto", [CGROUP_CLOSED] = "closed", diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 869ddae8c4..7b38d210fb 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -130,5 +130,7 @@ int unit_get_memory_current(Unit *u, uint64_t *ret); int unit_get_cpu_usage(Unit *u, nsec_t *ret); int unit_reset_cpu_usage(Unit *u); +bool unit_cgroup_delegate(Unit *u); + const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_; CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; diff --git a/src/core/main.c b/src/core/main.c index 8878ddc85d..4cd2b08c38 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -537,9 +537,8 @@ static int config_parse_join_controllers(const char *unit, void *data, void *userdata) { + const char *whole_rvalue = rvalue; unsigned n = 0; - const char *word, *state; - size_t length; assert(filename); assert(lvalue); @@ -547,16 +546,22 @@ static int config_parse_join_controllers(const char *unit, free_join_controllers(); - FOREACH_WORD_QUOTED(word, length, rvalue, state) { - char *s, **l; - - s = strndup(word, length); - if (!s) - return log_oom(); + for (;;) { + _cleanup_free_ char *word = NULL; + char **l; + int r; - l = strv_split(s, ","); - free(s); + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue); + return r; + } + if (r == 0) + break; + l = strv_split(word, ","); + if (!l) + log_oom(); strv_uniq(l); if (strv_length(l) <= 1) { @@ -616,7 +621,7 @@ static int config_parse_join_controllers(const char *unit, arg_join_controllers = t; } } - if (!isempty(state)) + if (!isempty(rvalue)) log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring."); @@ -2042,7 +2047,7 @@ finish: * kernel; at this point, we will not listen to the * signals anyway */ if (detect_container(NULL) <= 0) - cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER); + (void) cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER); execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block); log_error_errno(errno, "Failed to execute shutdown binary, %s: %m", diff --git a/src/core/manager.c b/src/core/manager.c index ede2a9910d..14f069ba97 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -573,6 +573,7 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { m->ask_password_inotify_fd = -1; m->have_ask_password = -EINVAL; /* we don't know */ + m->first_boot = -1; m->test_run = test_run; @@ -2998,12 +2999,14 @@ void manager_set_first_boot(Manager *m, bool b) { if (m->running_as != MANAGER_SYSTEM) return; - m->first_boot = b; + if (m->first_boot != (int) b) { + if (b) + (void) touch("/run/systemd/first-boot"); + else + (void) unlink("/run/systemd/first-boot"); + } - if (m->first_boot) - touch("/run/systemd/first-boot"); - else - unlink("/run/systemd/first-boot"); + m->first_boot = b; } void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) { diff --git a/src/core/manager.h b/src/core/manager.h index 1e01f2bdef..3f7fa24e58 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -233,7 +233,6 @@ struct Manager { bool dispatching_dbus_queue:1; bool taint_usr:1; - bool first_boot:1; bool test_run:1; @@ -295,6 +294,8 @@ struct Manager { const char *unit_log_field; const char *unit_log_format_string; + + int first_boot; }; int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); diff --git a/src/core/scope.c b/src/core/scope.c index c594ab5294..1e94d63561 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -396,7 +396,7 @@ static bool scope_check_gc(Unit *u) { if (u->cgroup_path) { int r; - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); if (r <= 0) return true; } diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index f920c2e2cd..40ca0c6166 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -246,7 +246,7 @@ int mac_selinux_generic_access_check( if (path) { /* Get the file context of the unit file */ - r = getfilecon(path, &fcon); + r = getfilecon_raw(path, &fcon); if (r < 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path); goto finish; @@ -254,7 +254,7 @@ int mac_selinux_generic_access_check( tclass = "service"; } else { - r = getcon(&fcon); + r = getcon_raw(&fcon); if (r < 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context."); goto finish; diff --git a/src/core/service.c b/src/core/service.c index 3c4232417d..5a0a3aa867 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1269,7 +1269,7 @@ static int cgroup_good(Service *s) { if (!UNIT(s)->cgroup_path) return 0; - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true); + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path); if (r < 0) return r; @@ -1520,18 +1520,33 @@ fail: service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); } +static bool service_good(Service *s) { + int main_pid_ok; + assert(s); + + if (s->type == SERVICE_DBUS && !s->bus_name_good) + return false; + + main_pid_ok = main_pid_good(s); + if (main_pid_ok > 0) /* It's alive */ + return true; + if (main_pid_ok == 0) /* It's dead */ + return false; + + /* OK, we don't know anything about the main PID, maybe + * because there is none. Let's check the control group + * instead. */ + + return cgroup_good(s) != 0; +} + static void service_enter_running(Service *s, ServiceResult f) { - int main_pid_ok, cgroup_ok; assert(s); if (f != SERVICE_SUCCESS) s->result = f; - main_pid_ok = main_pid_good(s); - cgroup_ok = cgroup_good(s); - - if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) && - (s->bus_name_good || s->type != SERVICE_DBUS)) { + if (service_good(s)) { /* If there are any queued up sd_notify() * notifications, process them now */ diff --git a/src/core/unit.c b/src/core/unit.c index 5f602bdf5f..34d3adcd3b 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -28,27 +28,28 @@ #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 "dbus.h" -#include "execute.h" -#include "dropin.h" #include "formats-util.h" #include "process-util.h" +#include "virt.h" +#include "bus-common-errors.h" #include "bus-util.h" +#include "dropin.h" +#include "unit-name.h" +#include "special.h" +#include "unit.h" +#include "load-fragment.h" +#include "load-dropin.h" +#include "dbus.h" +#include "dbus-unit.h" +#include "execute.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -2016,9 +2017,9 @@ void unit_unwatch_pid(Unit *u, pid_t pid) { 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, LONG_TO_PTR(pid), u); + (void) hashmap_remove_value(u->manager->watch_pids2, LONG_TO_PTR(pid), u); + (void) set_remove(u->pids, LONG_TO_PTR(pid)); } void unit_unwatch_all_pids(Unit *u) { @@ -2440,6 +2441,9 @@ int unit_set_slice(Unit *u, Unit *slice) { if (u->type == UNIT_SLICE) return -EINVAL; + if (unit_active_state(u) != UNIT_INACTIVE) + return -EBUSY; + if (slice->type != UNIT_SLICE) return -EINVAL; @@ -3168,7 +3172,7 @@ int unit_kill_common( 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; } @@ -3524,7 +3528,8 @@ 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); @@ -3553,13 +3558,13 @@ int unit_kill_context( _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); } } @@ -3570,16 +3575,17 @@ int unit_kill_context( _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 */ @@ -3587,21 +3593,26 @@ int unit_kill_context( 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"); + log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path); + } else if (r > 0) { /* 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. */ - - /* wait_for_exit = true; */ + * 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. */ + + if (detect_container(NULL) == 0 && !unit_cgroup_delegate(u)) + wait_for_exit = true; if (c->send_sighup && k != KILL_KILL) { set_free(pid_set); diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 6551e7c94c..aa07846693 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -1121,13 +1121,13 @@ int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t return 0; } -int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone) { +int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) { assert_return(lease, -EINVAL); - assert_return(timezone, -EINVAL); + assert_return(tz, -EINVAL); if (!lease->timezone) return -ENODATA; - *timezone = lease->timezone; + *tz = lease->timezone; return 0; } diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 7a8b298b51..1f167485e3 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -1062,16 +1062,16 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { return r; } -int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone) { +int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) { int r; assert_return(server, -EINVAL); - assert_return(timezone_is_valid(timezone), -EINVAL); + assert_return(timezone_is_valid(tz), -EINVAL); - if (streq_ptr(timezone, server->timezone)) + if (streq_ptr(tz, server->timezone)) return 0; - r = free_and_strdup(&server->timezone, timezone); + r = free_and_strdup(&server->timezone, tz); if (r < 0) return r; diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 0eadc8c747..7d6a4b78cf 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -237,11 +237,13 @@ _public_ int sd_uid_get_display(uid_t uid, char **session) { return r; r = parse_env_file(p, NEWLINE, "DISPLAY", &s, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; if (isempty(s)) - return -ENOENT; + return -ENXIO; *session = s; s = NULL; @@ -465,7 +467,7 @@ static int session_get_string(const char *session, const char *field, char **val return r; if (isempty(s)) - return -ENOENT; + return -ENXIO; *value = s; s = NULL; diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c index 05affa442d..ddea7ffa14 100644 --- a/src/libsystemd/sd-login/test-login.c +++ b/src/libsystemd/sd-login/test-login.c @@ -33,7 +33,7 @@ static void test_login(void) { _cleanup_free_ char *pp = NULL, *qq = NULL; int r, k; uid_t u, u2; - char *seat, *type, *class, *display, *remote_user, *remote_host; + char *seat, *type, *class, *display, *remote_user, *remote_host, *display_session; char *session; char *state; char *session2; @@ -50,6 +50,12 @@ static void test_login(void) { assert_se(sd_pid_get_owner_uid(0, &u2) == 0); printf("user = "UID_FMT"\n", u2); + display_session = NULL; + r = sd_uid_get_display(u2, &display_session); + assert_se(r >= 0 || r == -ENXIO); + printf("user's display session = %s\n", strna(display_session)); + free(display_session); + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); sd_peer_get_session(pair[0], &pp); sd_peer_get_session(pair[1], &qq); @@ -100,16 +106,22 @@ static void test_login(void) { printf("class = %s\n", class); free(class); - assert_se(sd_session_get_display(session, &display) >= 0); - printf("display = %s\n", display); + display = NULL; + r = sd_session_get_display(session, &display); + assert_se(r >= 0 || r == -ENXIO); + printf("display = %s\n", strna(display)); free(display); - assert_se(sd_session_get_remote_user(session, &remote_user) >= 0); - printf("remote_user = %s\n", remote_user); + remote_user = NULL; + r = sd_session_get_remote_user(session, &remote_user); + assert_se(r >= 0 || r == -ENXIO); + printf("remote_user = %s\n", strna(remote_user)); free(remote_user); - assert_se(sd_session_get_remote_host(session, &remote_host) >= 0); - printf("remote_host = %s\n", remote_host); + remote_host = NULL; + r = sd_session_get_remote_host(session, &remote_host); + assert_se(r >= 0 || r == -ENXIO); + printf("remote_host = %s\n", strna(remote_host)); free(remote_host); assert_se(sd_session_get_seat(session, &seat) >= 0); diff --git a/src/login/70-power-switch.rules b/src/login/70-power-switch.rules index 71f9fe6c72..36d2a3eb40 100644 --- a/src/login/70-power-switch.rules +++ b/src/login/70-power-switch.rules @@ -11,6 +11,7 @@ SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", \ SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="116", TAG+="power-switch" diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 5fa98e069f..a7e64071cf 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -263,7 +263,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit if (isempty(cgroup)) return 0; - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) return 0; c = columns(); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 8bd0ed756b..bb8c5ac64b 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -375,7 +375,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { if (r < 0) return bus_log_parse_error(r); - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) return 0; c = columns(); diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 2281d4b718..786579def0 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -497,7 +497,7 @@ static int link_status_one( sd_hwdb *hwdb, const char *name) { _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL; - _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *timezone = NULL; + _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL; _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL; _cleanup_device_unref_ sd_device *d = NULL; char devid[2 + DECIMAL_STR_MAX(int)]; @@ -662,9 +662,9 @@ static int link_status_one( if (!strv_isempty(carrier_bound_by)) dump_list("Carrier Bound By: ", carrier_bound_by); - (void) sd_network_link_get_timezone(ifindex, &timezone); - if (timezone) - printf(" Time Zone: %s", timezone); + (void) sd_network_link_get_timezone(ifindex, &tz); + if (tz) + printf(" Time Zone: %s", tz); return 0; } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 979f3115f6..1dc9db0fca 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -967,14 +967,14 @@ static int set_timezone_handler(sd_bus_message *m, void *userdata, sd_bus_error return 1; } -int link_set_timezone(Link *link, const char *timezone) { +int link_set_timezone(Link *link, const char *tz) { int r; assert(link); assert(link->manager); - assert(timezone); + assert(tz); - log_link_debug(link, "Setting system timezone: '%s'", timezone); + log_link_debug(link, "Setting system timezone: '%s'", tz); if (!link->manager->bus) { log_link_info(link, "Not connected to system bus, ignoring timezone."); @@ -991,7 +991,7 @@ int link_set_timezone(Link *link, const char *timezone) { set_timezone_handler, link, "sb", - timezone, + tz, false); if (r < 0) return log_link_error_errno(link, r, "Could not set timezone: %m"); diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 9469160eba..e0bd0e024a 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -39,6 +39,7 @@ Tunnel.DiscoverPathMTU, config_parse_bool, 0, Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) +Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c index 265e67b7e3..a906e473b6 100644 --- a/src/network/networkd-netdev-tunnel.c +++ b/src/network/networkd-netdev-tunnel.c @@ -284,6 +284,12 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl if (t->copy_dscp) t->flags |= IP6_TNL_F_RCV_DSCP_COPY; + if (t->encap_limit != IPV6_DEFAULT_TNL_ENCAP_LIMIT) { + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_LIMIT attribute: %m"); + } + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m"); @@ -442,6 +448,45 @@ int config_parse_ipv6_flowlabel(const char* unit, return 0; } +int config_parse_encap_limit(const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Tunnel *t = userdata; + int k = 0; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (streq(rvalue, "none")) + t->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; + else { + r = safe_atoi(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue); + return 0; + } + + if (k > 255 || k < 0) + log_syntax(unit, LOG_ERR, filename, line, k, "Invalid Tunnel Encapsulation value, ignoring: %d", k); + else { + t->encap_limit = k; + t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; + } + } + + return 0; +} + static void ipip_init(NetDev *n) { Tunnel *t = IPIP(n); diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h index e4fa74aef4..fa7decce18 100644 --- a/src/network/networkd-netdev-tunnel.h +++ b/src/network/networkd-netdev-tunnel.h @@ -95,3 +95,9 @@ int config_parse_ipv6_flowlabel(const char *unit, const char *filename, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +int config_parse_encap_limit(const char *unit, const char *filename, + unsigned line, const char *section, + unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, + void *userdata); diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 2a77242013..ee14401982 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -786,7 +786,7 @@ int config_parse_timezone( void *data, void *userdata) { - char **timezone = data, *tz = NULL; + char **datap = data, *tz = NULL; int r; assert(filename); @@ -803,8 +803,8 @@ int config_parse_timezone( return 0; } - free(*timezone); - *timezone = tz; + free(*datap); + *datap = tz; return 0; } diff --git a/src/run/run.c b/src/run/run.c index 3dd97022de..a69560208c 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -36,7 +36,9 @@ #include "ptyfwd.h" #include "formats-util.h" #include "signal-util.h" +#include "spawn-polkit-agent.h" +static bool arg_ask_password = true; static bool arg_scope = false; static bool arg_remain_after_exit = false; static bool arg_no_block = false; @@ -64,6 +66,18 @@ static char *arg_on_calendar = NULL; static char **arg_timer_property = NULL; static bool arg_quiet = false; +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + static void help(void) { printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" "Run the specified command in a transient scope or service or timer\n" @@ -71,6 +85,7 @@ static void help(void) { "specified with --unit option then command can be omitted.\n\n" " -h --help Show this help\n" " --version Show package version\n" + " --no-ask-password Do not prompt for password\n" " --user Run as user unit\n" " -H --host=[USER@]HOST Operate on remote host\n" " -M --machine=CONTAINER Operate on local container\n" @@ -108,6 +123,7 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_NO_ASK_PASSWORD, ARG_USER, ARG_SYSTEM, ARG_SCOPE, @@ -160,6 +176,7 @@ static int parse_argv(int argc, char *argv[]) { { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, {}, }; @@ -177,6 +194,10 @@ static int parse_argv(int argc, char *argv[]) { help(); return 0; + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + case ARG_VERSION: puts(PACKAGE_STRING); puts(SYSTEMD_FEATURES); @@ -745,6 +766,10 @@ static int start_transient_service( if (r < 0) return bus_log_create_error(r); + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + /* Name and mode */ r = sd_bus_message_append(m, "ss", service, "fail"); if (r < 0) @@ -768,6 +793,8 @@ static int start_transient_service( if (r < 0) return bus_log_create_error(r); + polkit_agent_open_if_enabled(); + r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { log_error("Failed to start transient service unit: %s", bus_error_message(&error, -r)); @@ -860,6 +887,10 @@ static int start_transient_scope( if (r < 0) return bus_log_create_error(r); + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + /* Name and Mode */ r = sd_bus_message_append(m, "ss", scope, "fail"); if (r < 0) @@ -883,6 +914,8 @@ static int start_transient_scope( if (r < 0) return bus_log_create_error(r); + polkit_agent_open_if_enabled(); + r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r)); @@ -1025,6 +1058,10 @@ static int start_transient_timer( if (r < 0) return bus_log_create_error(r); + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + /* Name and Mode */ r = sd_bus_message_append(m, "ss", timer, "fail"); if (r < 0) @@ -1077,6 +1114,8 @@ static int start_transient_timer( if (r < 0) return bus_log_create_error(r); + polkit_agent_open_if_enabled(); + r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r)); diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 3abccdb49a..31b4f6c684 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -152,7 +152,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns if (!k) return -ENOMEM; - if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k, false) > 0) + if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0) continue; if (!shown_pids) { diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 3cb5f61868..8d80aae182 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3557,7 +3557,7 @@ static void print_status_info( if (i->control_group && (i->main_pid > 0 || i->control_pid > 0 || - ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) { + ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) { unsigned c; printf(" CGroup: %s\n", i->control_group); diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c index 4be69a408d..8b0302cfe6 100644 --- a/src/test/test-cgroup.c +++ b/src/test/test-cgroup.c @@ -56,18 +56,18 @@ int main(int argc, char*argv[]) { assert_se(path_equal(path, "/sys/fs/cgroup/systemd/test-b/test-d")); free(path); - assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0); - assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) == 0); + assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0); + assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) == 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) > 0); assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", false, false) > 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) == 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) > 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) == 0); |