diff options
Diffstat (limited to 'src/basic/cgroup-util.c')
-rw-r--r-- | src/basic/cgroup-util.c | 254 |
1 files changed, 140 insertions, 114 deletions
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index d51c3efd22..f599d0f0f1 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -23,6 +24,7 @@ #include <limits.h> #include <signal.h> #include <stddef.h> +#include <stdio_ext.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> @@ -875,115 +877,87 @@ int cg_attach_fallback(const char *controller, const char *path, pid_t pid) { return r; } -int cg_set_group_access( +int cg_set_access( const char *controller, const char *path, - mode_t mode, uid_t uid, gid_t gid) { - _cleanup_free_ char *fs = NULL; - int r; - - if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) - return 0; - - if (mode != MODE_INVALID) - mode &= 0777; - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - r = chmod_and_chown(fs, mode, uid, gid); - if (r < 0) - return r; - - r = cg_hybrid_unified(); - if (r < 0) - return r; - if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { - r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid); - if (r < 0) - log_debug_errno(r, "Failed to set group access on compatibility systemd cgroup %s, ignoring: %m", path); - } - - return 0; -} - -int cg_set_task_access( - const char *controller, - const char *path, - mode_t mode, - uid_t uid, - gid_t gid) { + struct Attribute { + const char *name; + bool fatal; + }; + + /* cgroupsv1, aka legacy/non-unified */ + static const struct Attribute legacy_attributes[] = { + { "cgroup.procs", true }, + { "tasks", false }, + { "cgroup.clone_children", false }, + {}, + }; + + /* cgroupsv2, aka unified */ + static const struct Attribute unified_attributes[] = { + { "cgroup.procs", true }, + { "cgroup.subtree_control", true }, + { "cgroup.threads", false }, + {}, + }; + + static const struct Attribute* const attributes[] = { + [false] = legacy_attributes, + [true] = unified_attributes, + }; _cleanup_free_ char *fs = NULL; - int r; + const struct Attribute *i; + int r, unified; assert(path); - if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) + if (uid == UID_INVALID && gid == GID_INVALID) return 0; - if (mode != MODE_INVALID) - mode &= 0666; + unified = cg_unified_controller(controller); + if (unified < 0) + return unified; - /* For both the legacy and unified hierarchies, "cgroup.procs" is the main entry point for PIDs */ - r = cg_get_path(controller, path, "cgroup.procs", &fs); + /* Configure access to the cgroup itself */ + r = cg_get_path(controller, path, NULL, &fs); if (r < 0) return r; - r = chmod_and_chown(fs, mode, uid, gid); + r = chmod_and_chown(fs, 0755, uid, gid); if (r < 0) return r; - r = cg_unified_controller(controller); - if (r < 0) - return r; - if (r == 0) { - const char *fn; - - /* Compatibility: on cgroupsv1 always keep values for the legacy files "tasks" and - * "cgroup.clone_children" in sync with "cgroup.procs". Since this is legacy stuff, we don't care if - * this fails. */ - - FOREACH_STRING(fn, - "tasks", - "cgroup.clone_children") { - - fs = mfree(fs); - - r = cg_get_path(controller, path, fn, &fs); - if (r < 0) - log_debug_errno(r, "Failed to get path for %s of %s, ignoring: %m", fn, path); - - r = chmod_and_chown(fs, mode, uid, gid); - if (r < 0) - log_debug_errno(r, "Failed to to change ownership/access mode for %s of %s, ignoring: %m", fn, path); - } - } else { - /* On the unified controller, we want to permit subtree controllers too. */ - + /* Configure access to the cgroup's attributes */ + for (i = attributes[unified]; i->name; i++) { fs = mfree(fs); - r = cg_get_path(controller, path, "cgroup.subtree_control", &fs); - if (r < 0) - return r; - r = chmod_and_chown(fs, mode, uid, gid); + r = cg_get_path(controller, path, i->name, &fs); if (r < 0) return r; - } - r = cg_hybrid_unified(); - if (r < 0) - return r; - if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { - /* Always propagate access mode from unified to legacy controller */ + r = chmod_and_chown(fs, 0644, uid, gid); + if (r < 0) { + if (i->fatal) + return r; + + log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs); + } + } - r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid); + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + r = cg_hybrid_unified(); if (r < 0) - log_debug_errno(r, "Failed to set task access on compatibility systemd cgroup %s, ignoring: %m", path); + return r; + if (r > 0) { + /* Always propagate access mode from unified to legacy controller */ + r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid); + if (r < 0) + log_debug_errno(r, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path); + } } return 0; @@ -1059,6 +1033,8 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { if (!f) return errno == ENOENT ? -ESRCH : -errno; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + FOREACH_LINE(line, f, return -errno) { char *e, *p; @@ -1103,6 +1079,11 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { if (!p) return -ENOMEM; + /* Truncate suffix indicating the process is a zombie */ + e = endswith(p, " (deleted)"); + if (e) + *e = 0; + *path = p; return 0; } @@ -1278,7 +1259,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) { assert(spec); if (*spec == '/') { - if (!path_is_safe(spec)) + if (!path_is_normalized(spec)) return -EINVAL; if (path) { @@ -1331,7 +1312,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return -ENOMEM; } - if (!path_is_safe(u) || + if (!path_is_normalized(u) || !path_is_absolute(u)) { free(t); free(u); @@ -1493,7 +1474,7 @@ static bool valid_slice_name(const char *p, size_t n) { if (!p) return false; - if (n < strlen("x.slice")) + if (n < STRLEN("x.slice")) return false; if (memcmp(p + n - 6, ".slice", 6) == 0) { @@ -1577,7 +1558,7 @@ static const char *skip_session(const char *p) { p += strspn(p, "/"); n = strcspn(p, "/"); - if (n < strlen("session-x.scope")) + if (n < STRLEN("session-x.scope")) return NULL; if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) { @@ -1614,7 +1595,7 @@ static const char *skip_user_manager(const char *p) { p += strspn(p, "/"); n = strcspn(p, "/"); - if (n < strlen("user@x.service")) + if (n < STRLEN("user@x.service")) return NULL; if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) { @@ -2086,8 +2067,7 @@ int cg_get_keyed_attribute(const char *controller, const char *path, const char for (i = 0; keys[i]; i++) { if (!values[i]) { for (i = 0; keys[i]; i++) { - free(values[i]); - values[i] = NULL; + values[i] = mfree(values[i]); } return -ENOENT; } @@ -2244,10 +2224,10 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) } int cg_mask_to_string(CGroupMask mask, char **ret) { - const char *controllers[_CGROUP_CONTROLLER_MAX + 1]; + _cleanup_free_ char *s = NULL; + size_t n = 0, allocated = 0; + bool space = false; CGroupController c; - int i = 0; - char *s; assert(ret); @@ -2257,19 +2237,32 @@ int cg_mask_to_string(CGroupMask mask, char **ret) { } for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + const char *k; + size_t l; if (!(mask & CGROUP_CONTROLLER_TO_MASK(c))) continue; - controllers[i++] = cgroup_controller_to_string(c); - controllers[i] = NULL; + k = cgroup_controller_to_string(c); + l = strlen(k); + + if (!GREEDY_REALLOC(s, allocated, n + space + l + 1)) + return -ENOMEM; + + if (space) + s[n] = ' '; + memcpy(s + n + space, k, l); + n += space + l; + + space = true; } - s = strv_join((char **)controllers, NULL); - if (!s) - return -ENOMEM; + assert(s); + s[n] = 0; *ret = s; + s = NULL; + return 0; } @@ -2355,24 +2348,34 @@ int cg_mask_supported(CGroupMask *ret) { return 0; } -int cg_kernel_controllers(Set *controllers) { +int cg_kernel_controllers(Set **ret) { + _cleanup_set_free_free_ Set *controllers = NULL; _cleanup_fclose_ FILE *f = NULL; int r; - assert(controllers); + assert(ret); /* 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). */ + controllers = set_new(&string_hash_ops); + if (!controllers) + return -ENOMEM; + f = fopen("/proc/cgroups", "re"); if (!f) { - if (errno == ENOENT) + if (errno == ENOENT) { + *ret = NULL; return 0; + } + return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + /* Ignore the header line */ (void) read_line(f, (size_t) -1, NULL); @@ -2407,6 +2410,9 @@ int cg_kernel_controllers(Set *controllers) { return r; } + *ret = controllers; + controllers = NULL; + return 0; } @@ -2436,28 +2442,39 @@ static int cg_unified_update(void) { return 0; if (statfs("/sys/fs/cgroup/", &fs) < 0) - return -errno; + return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\" failed: %m"); - if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) + if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) { + log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy"); unified_cache = CGROUP_UNIFIED_ALL; - else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) { + } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) { if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 && F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) { + log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller"); unified_cache = CGROUP_UNIFIED_SYSTEMD; unified_systemd_v232 = false; - } else if (statfs("/sys/fs/cgroup/systemd/", &fs) == 0 && - F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) { - unified_cache = CGROUP_UNIFIED_SYSTEMD; - unified_systemd_v232 = true; } else { if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0) - return -errno; - if (!F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) - return -ENOMEDIUM; - unified_cache = CGROUP_UNIFIED_NONE; + return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m"); + + if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) { + log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)"); + unified_cache = CGROUP_UNIFIED_SYSTEMD; + unified_systemd_v232 = true; + } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) { + log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy"); + unified_cache = CGROUP_UNIFIED_NONE; + } else { + log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy", + (unsigned long long) fs.f_type); + unified_cache = CGROUP_UNIFIED_NONE; + } } - } else + } else { + log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.", + (unsigned long long) fs.f_type); return -ENOMEDIUM; + } return 0; } @@ -2505,6 +2522,7 @@ int cg_unified_flush(void) { } int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { + _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *fs = NULL; CGroupController c; int r; @@ -2538,7 +2556,15 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { s[0] = mask & bit ? '+' : '-'; strcpy(s + 1, n); - r = write_string_file(fs, s, 0); + if (!f) { + f = fopen(fs, "we"); + if (!f) { + log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p); + break; + } + } + + r = write_string_stream(f, s, 0); if (r < 0) log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs); } |