1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <stdio_ext.h>
31 //#include <sys/statfs.h>
32 #include <sys/types.h>
33 #include <sys/xattr.h>
36 #include "alloc-util.h"
37 #include "cgroup-util.h"
39 #include "dirent-util.h"
40 #include "extract-word.h"
43 #include "format-util.h"
46 #include "login-util.h"
48 //#include "missing.h"
50 #include "parse-util.h"
51 #include "path-util.h"
52 #include "proc-cmdline.h"
53 #include "process-util.h"
55 //#include "special.h"
56 #include "stat-util.h"
57 #include "stdio-util.h"
58 #include "string-table.h"
59 #include "string-util.h"
61 #include "unit-name.h"
62 #include "user-util.h"
64 int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
65 _cleanup_free_ char *fs = NULL;
71 r = cg_get_path(controller, path, "cgroup.procs", &fs);
83 int cg_read_pid(FILE *f, pid_t *_pid) {
86 /* Note that the cgroup.procs might contain duplicates! See
87 * cgroups.txt for details. */
93 if (fscanf(f, "%lu", &ul) != 1) {
98 return errno > 0 ? -errno : -EIO;
109 const char *controller,
114 _cleanup_free_ char *events = NULL, *content = NULL;
118 r = cg_get_path(controller, path, "cgroup.events", &events);
122 r = read_full_file(events, &content, NULL);
127 while ((line = strsep(&p, "\n"))) {
130 key = strsep(&line, " ");
134 if (strcmp(key, event))
144 #if 0 /// UNNEEDED by elogind
145 bool cg_ns_supported(void) {
146 static thread_local int enabled = -1;
151 if (access("/proc/self/ns/cgroup", F_OK) == 0)
160 int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
161 _cleanup_free_ char *fs = NULL;
167 /* This is not recursive! */
169 r = cg_get_path(controller, path, NULL, &fs);
181 int cg_read_subgroup(DIR *d, char **fn) {
187 FOREACH_DIRENT_ALL(de, d, return -errno) {
190 if (de->d_type != DT_DIR)
193 if (dot_or_dot_dot(de->d_name))
196 b = strdup(de->d_name);
207 int cg_rmdir(const char *controller, const char *path) {
208 _cleanup_free_ char *p = NULL;
211 r = cg_get_path(controller, path, NULL, &p);
216 if (r < 0 && errno != ENOENT)
219 r = cg_hybrid_unified();
225 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
226 r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
228 log_warning_errno(r, "Failed to remove compat systemd cgroup %s: %m", path);
235 const char *controller,
240 cg_kill_log_func_t log_kill,
243 _cleanup_set_free_ Set *allocated_set = NULL;
250 /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send
251 * SIGCONT on SIGKILL. */
252 if (IN_SET(sig, SIGCONT, SIGKILL))
253 flags &= ~CGROUP_SIGCONT;
255 /* This goes through the tasks list and kills them all. This
256 * is repeated until no further processes are added to the
257 * tasks list, to properly handle forking processes */
260 s = allocated_set = set_new(NULL);
265 my_pid = getpid_cached();
268 _cleanup_fclose_ FILE *f = NULL;
272 r = cg_enumerate_processes(controller, path, &f);
274 if (ret >= 0 && r != -ENOENT)
280 while ((r = cg_read_pid(f, &pid)) > 0) {
282 if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
285 if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
289 log_kill(pid, sig, userdata);
291 /* If we haven't killed this process yet, kill
293 if (kill(pid, sig) < 0) {
294 if (ret >= 0 && errno != ESRCH)
297 if (flags & CGROUP_SIGCONT)
298 (void) kill(pid, SIGCONT);
306 r = set_put(s, PID_TO_PTR(pid));
322 /* To avoid racing against processes which fork
323 * quicker than we can kill them we repeat this until
324 * no new pids need to be killed. */
331 int cg_kill_recursive(
332 const char *controller,
337 cg_kill_log_func_t log_kill,
340 _cleanup_set_free_ Set *allocated_set = NULL;
341 _cleanup_closedir_ DIR *d = NULL;
349 s = allocated_set = set_new(NULL);
354 ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
356 r = cg_enumerate_subgroups(controller, path, &d);
358 if (ret >= 0 && r != -ENOENT)
364 while ((r = cg_read_subgroup(d, &fn)) > 0) {
365 _cleanup_free_ char *p = NULL;
367 p = strjoin(path, "/", fn);
372 r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
373 if (r != 0 && ret >= 0)
376 if (ret >= 0 && r < 0)
379 if (flags & CGROUP_REMOVE) {
380 r = cg_rmdir(controller, path);
381 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
396 _cleanup_set_free_ Set *s = NULL;
409 my_pid = getpid_cached();
411 log_debug_elogind("Migrating \"%s\"/\"%s\" to \"%s\"/\"%s\" (%s)",
412 cfrom, pfrom, cto, pto,
413 (flags & CGROUP_IGNORE_SELF)
414 ? "ignoring self" : "watching self");
416 _cleanup_fclose_ FILE *f = NULL;
420 r = cg_enumerate_processes(cfrom, pfrom, &f);
422 if (ret >= 0 && r != -ENOENT)
428 while ((r = cg_read_pid(f, &pid)) > 0) {
430 /* This might do weird stuff if we aren't a
431 * single-threaded program. However, we
432 * luckily know we are not */
433 if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
436 if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
439 /* Ignore kernel threads. Since they can only
440 * exist in the root cgroup, we only check for
443 (isempty(pfrom) || path_equal(pfrom, "/")) &&
444 is_kernel_thread(pid) > 0)
447 r = cg_attach(cto, pto, pid);
449 if (ret >= 0 && r != -ESRCH)
456 r = set_put(s, PID_TO_PTR(pid));
476 int cg_migrate_recursive(
483 _cleanup_closedir_ DIR *d = NULL;
492 ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
494 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
496 if (ret >= 0 && r != -ENOENT)
502 while ((r = cg_read_subgroup(d, &fn)) > 0) {
503 _cleanup_free_ char *p = NULL;
505 p = strjoin(pfrom, "/", fn);
510 r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
511 if (r != 0 && ret >= 0)
515 if (r < 0 && ret >= 0)
518 if (flags & CGROUP_REMOVE) {
519 r = cg_rmdir(cfrom, pfrom);
520 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
527 int cg_migrate_recursive_fallback(
541 r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
543 char prefix[strlen(pto) + 1];
545 /* This didn't work? Then let's try all prefixes of the destination */
547 PATH_FOREACH_PREFIX(prefix, pto) {
550 q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
559 static const char *controller_to_dirname(const char *controller) {
564 /* Converts a controller name to the directory name below
565 * /sys/fs/cgroup/ we want to mount it to. Effectively, this
566 * just cuts off the name= prefixed used for named
567 * hierarchies, if it is specified. */
569 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
570 if (cg_hybrid_unified() > 0)
571 controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID;
573 controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
576 e = startswith(controller, "name=");
583 static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) {
590 dn = controller_to_dirname(controller);
592 if (isempty(path) && isempty(suffix))
593 t = strappend("/sys/fs/cgroup/", dn);
594 else if (isempty(path))
595 t = strjoin("/sys/fs/cgroup/", dn, "/", suffix);
596 else if (isempty(suffix))
597 t = strjoin("/sys/fs/cgroup/", dn, "/", path);
599 t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix);
607 static int join_path_unified(const char *path, const char *suffix, char **fs) {
612 if (isempty(path) && isempty(suffix))
613 t = strdup("/sys/fs/cgroup");
614 else if (isempty(path))
615 t = strappend("/sys/fs/cgroup/", suffix);
616 else if (isempty(suffix))
617 t = strappend("/sys/fs/cgroup/", path);
619 t = strjoin("/sys/fs/cgroup/", path, "/", suffix);
627 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
635 /* If no controller is specified, we return the path
636 * *below* the controllers, without any prefix. */
638 if (!path && !suffix)
646 t = strjoin(path, "/", suffix);
650 *fs = path_kill_slashes(t);
654 if (!cg_controller_is_valid(controller))
657 r = cg_all_unified();
661 r = join_path_unified(path, suffix, fs);
663 r = join_path_legacy(controller, path, suffix, fs);
667 path_kill_slashes(*fs);
671 static int controller_is_accessible(const char *controller) {
676 /* Checks whether a specific controller is accessible,
677 * i.e. its hierarchy mounted. In the unified hierarchy all
678 * controllers are considered accessible, except for the named
681 if (!cg_controller_is_valid(controller))
684 r = cg_all_unified();
688 /* We don't support named hierarchies if we are using
689 * the unified hierarchy. */
691 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
694 if (startswith(controller, "name="))
700 dn = controller_to_dirname(controller);
701 cc = strjoina("/sys/fs/cgroup/", dn);
703 if (laccess(cc, F_OK) < 0)
710 int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
716 /* Check if the specified controller is actually accessible */
717 r = controller_is_accessible(controller);
721 return cg_get_path(controller, path, suffix, fs);
724 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
729 if (typeflag != FTW_DP)
732 if (ftwbuf->level < 1)
739 int cg_trim(const char *controller, const char *path, bool delete_root) {
740 _cleanup_free_ char *fs = NULL;
745 r = cg_get_path(controller, path, NULL, &fs);
750 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
760 if (rmdir(fs) < 0 && errno != ENOENT)
764 q = cg_hybrid_unified();
767 if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
768 q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
770 log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
776 int cg_create(const char *controller, const char *path) {
777 _cleanup_free_ char *fs = NULL;
780 r = cg_get_path_and_check(controller, path, NULL, &fs);
784 r = mkdir_parents(fs, 0755);
788 if (mkdir(fs, 0755) < 0) {
796 r = cg_hybrid_unified();
800 if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
801 r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
803 log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
809 int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
814 r = cg_create(controller, path);
818 q = cg_attach(controller, path, pid);
822 /* This does not remove the cgroup on failure */
826 int cg_attach(const char *controller, const char *path, pid_t pid) {
827 _cleanup_free_ char *fs = NULL;
828 char c[DECIMAL_STR_MAX(pid_t) + 2];
834 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
839 pid = getpid_cached();
841 xsprintf(c, PID_FMT "\n", pid);
843 r = write_string_file(fs, c, 0);
847 r = cg_hybrid_unified();
851 if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
852 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
854 log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
860 int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
867 r = cg_attach(controller, path, pid);
869 char prefix[strlen(path) + 1];
871 /* This didn't work? Then let's try all prefixes of
874 PATH_FOREACH_PREFIX(prefix, path) {
877 q = cg_attach(controller, prefix, pid);
886 #if 0 /// UNNEEDED by elogind
888 const char *controller,
898 /* cgroupsv1, aka legacy/non-unified */
899 static const struct Attribute legacy_attributes[] = {
900 { "cgroup.procs", true },
902 { "cgroup.clone_children", false },
906 /* cgroupsv2, aka unified */
907 static const struct Attribute unified_attributes[] = {
908 { "cgroup.procs", true },
909 { "cgroup.subtree_control", true },
910 { "cgroup.threads", false },
914 static const struct Attribute* const attributes[] = {
915 [false] = legacy_attributes,
916 [true] = unified_attributes,
919 _cleanup_free_ char *fs = NULL;
920 const struct Attribute *i;
925 if (uid == UID_INVALID && gid == GID_INVALID)
928 unified = cg_unified_controller(controller);
932 /* Configure access to the cgroup itself */
933 r = cg_get_path(controller, path, NULL, &fs);
937 r = chmod_and_chown(fs, 0755, uid, gid);
941 /* Configure access to the cgroup's attributes */
942 for (i = attributes[unified]; i->name; i++) {
945 r = cg_get_path(controller, path, i->name, &fs);
949 r = chmod_and_chown(fs, 0644, uid, gid);
954 log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
958 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
959 r = cg_hybrid_unified();
963 /* Always propagate access mode from unified to legacy controller */
964 r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
966 log_debug_errno(r, "Failed to set access on compatibility elogind cgroup %s, ignoring: %m", path);
973 int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
974 _cleanup_free_ char *fs = NULL;
979 assert(value || size <= 0);
981 r = cg_get_path(controller, path, NULL, &fs);
985 if (setxattr(fs, name, value, size, flags) < 0)
991 int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
992 _cleanup_free_ char *fs = NULL;
999 r = cg_get_path(controller, path, NULL, &fs);
1003 n = getxattr(fs, name, value, size);
1011 int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
1012 _cleanup_fclose_ FILE *f = NULL;
1013 char line[LINE_MAX];
1014 #if 0 /// At elogind we do not want that (false alarm) "maybe uninitialized" warning
1015 const char *fs, *controller_str;
1017 const char *fs, *controller_str = NULL;
1026 if (!cg_controller_is_valid(controller))
1029 controller = SYSTEMD_CGROUP_CONTROLLER;
1031 unified = cg_unified_controller(controller);
1035 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1036 controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
1038 controller_str = controller;
1040 cs = strlen(controller_str);
1043 fs = procfs_file_alloca(pid, "cgroup");
1044 log_debug_elogind("Searching for PID %u in \"%s\" (controller \"%s\")",
1045 pid, fs, controller);
1046 f = fopen(fs, "re");
1048 return errno == ENOENT ? -ESRCH : -errno;
1050 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
1052 FOREACH_LINE(line, f, return -errno) {
1058 e = startswith(line, "0:");
1068 const char *word, *state;
1071 l = strchr(line, ':');
1081 FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
1082 if (k == cs && memcmp(word, controller_str, cs) == 0) {
1092 log_debug_elogind("Found %s:%s", line, e+1);
1097 /* Truncate suffix indicating the process is a zombie */
1098 e = endswith(p, " (deleted)");
1109 #if 0 /// UNNEEDED by elogind
1110 int cg_install_release_agent(const char *controller, const char *agent) {
1111 _cleanup_free_ char *fs = NULL, *contents = NULL;
1117 r = cg_unified_controller(controller);
1120 if (r > 0) /* doesn't apply to unified hierarchy */
1123 r = cg_get_path(controller, NULL, "release_agent", &fs);
1127 r = read_one_line_file(fs, &contents);
1131 sc = strstrip(contents);
1133 r = write_string_file(fs, agent, 0);
1136 } else if (!path_equal(sc, agent))
1140 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
1144 contents = mfree(contents);
1145 r = read_one_line_file(fs, &contents);
1149 sc = strstrip(contents);
1150 if (streq(sc, "0")) {
1151 r = write_string_file(fs, "1", 0);
1158 if (!streq(sc, "1"))
1164 int cg_uninstall_release_agent(const char *controller) {
1165 _cleanup_free_ char *fs = NULL;
1168 r = cg_unified_controller(controller);
1171 if (r > 0) /* Doesn't apply to unified hierarchy */
1174 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
1178 r = write_string_file(fs, "0", 0);
1184 r = cg_get_path(controller, NULL, "release_agent", &fs);
1188 r = write_string_file(fs, "", 0);
1196 int cg_is_empty(const char *controller, const char *path) {
1197 _cleanup_fclose_ FILE *f = NULL;
1203 r = cg_enumerate_processes(controller, path, &f);
1209 r = cg_read_pid(f, &pid);
1216 int cg_is_empty_recursive(const char *controller, const char *path) {
1221 /* The root cgroup is always populated */
1222 if (controller && (isempty(path) || path_equal(path, "/")))
1225 r = cg_unified_controller(controller);
1229 _cleanup_free_ char *t = NULL;
1231 /* On the unified hierarchy we can check empty state
1232 * via the "populated" attribute of "cgroup.events". */
1234 r = cg_read_event(controller, path, "populated", &t);
1238 return streq(t, "0");
1240 _cleanup_closedir_ DIR *d = NULL;
1243 r = cg_is_empty(controller, path);
1247 r = cg_enumerate_subgroups(controller, path, &d);
1253 while ((r = cg_read_subgroup(d, &fn)) > 0) {
1254 _cleanup_free_ char *p = NULL;
1256 p = strjoin(path, "/", fn);
1261 r = cg_is_empty_recursive(controller, p);
1272 int cg_split_spec(const char *spec, char **controller, char **path) {
1273 char *t = NULL, *u = NULL;
1279 if (!path_is_normalized(spec))
1287 *path = path_kill_slashes(t);
1296 e = strchr(spec, ':');
1298 if (!cg_controller_is_valid(spec))
1315 t = strndup(spec, e-spec);
1318 if (!cg_controller_is_valid(t)) {
1332 if (!path_is_normalized(u) ||
1333 !path_is_absolute(u)) {
1339 path_kill_slashes(u);
1355 int cg_mangle_path(const char *path, char **result) {
1356 _cleanup_free_ char *c = NULL, *p = NULL;
1363 /* First, check if it already is a filesystem path */
1364 if (path_startswith(path, "/sys/fs/cgroup")) {
1370 *result = path_kill_slashes(t);
1374 /* Otherwise, treat it as cg spec */
1375 r = cg_split_spec(path, &c, &p);
1379 return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result);
1382 int cg_get_root_path(char **path) {
1388 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
1392 #if 0 /// elogind does not support systemd scopes and slices
1393 e = endswith(p, "/" SPECIAL_INIT_SCOPE);
1395 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
1397 e = endswith(p, "/system"); /* even more legacy */
1399 e = endswith(p, "/elogind");
1408 int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1409 _cleanup_free_ char *rt = NULL;
1417 /* If the root was specified let's use that, otherwise
1418 * let's determine it from PID 1 */
1420 r = cg_get_root_path(&rt);
1425 log_debug_elogind("Determined root path: \"%s\"", root);
1428 p = path_startswith(cgroup, root);
1429 #if 0 /// With other controllers, elogind might end up in /elogind, and *p is 0
1430 if (p && p > cgroup)
1432 if (p && p[0] && (p > cgroup))
1441 int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1442 _cleanup_free_ char *raw = NULL;
1449 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
1453 log_debug_elogind("Shifting path: \"%s\" (PID %u, root: \"%s\")",
1454 raw, pid, root ? root : "NULL");
1455 r = cg_shift_path(raw, root, &c);
1471 log_debug_elogind("Resulting cgroup:\"%s\"", *cgroup);
1476 #if 0 /// UNNEEDED by elogind
1477 int cg_path_decode_unit(const char *cgroup, char **unit) {
1484 n = strcspn(cgroup, "/");
1488 c = strndupa(cgroup, n);
1491 if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
1502 static bool valid_slice_name(const char *p, size_t n) {
1507 if (n < STRLEN("x.slice"))
1510 if (memcmp(p + n - 6, ".slice", 6) == 0) {
1516 c = cg_unescape(buf);
1518 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
1524 static const char *skip_slices(const char *p) {
1527 /* Skips over all slice assignments */
1532 p += strspn(p, "/");
1534 n = strcspn(p, "/");
1535 if (!valid_slice_name(p, n))
1542 int cg_path_get_unit(const char *path, char **ret) {
1550 e = skip_slices(path);
1552 r = cg_path_decode_unit(e, &unit);
1556 /* We skipped over the slices, don't accept any now */
1557 if (endswith(unit, ".slice")) {
1566 int cg_pid_get_unit(pid_t pid, char **unit) {
1567 _cleanup_free_ char *cgroup = NULL;
1572 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1576 return cg_path_get_unit(cgroup, unit);
1580 * Skip session-*.scope, but require it to be there.
1582 static const char *skip_session(const char *p) {
1588 p += strspn(p, "/");
1590 n = strcspn(p, "/");
1591 if (n < STRLEN("session-x.scope"))
1594 if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1595 char buf[n - 8 - 6 + 1];
1597 memcpy(buf, p + 8, n - 8 - 6);
1600 /* Note that session scopes never need unescaping,
1601 * since they cannot conflict with the kernel's own
1602 * names, hence we don't need to call cg_unescape()
1605 if (!session_id_valid(buf))
1609 p += strspn(p, "/");
1617 * Skip user@*.service, but require it to be there.
1619 static const char *skip_user_manager(const char *p) {
1625 p += strspn(p, "/");
1627 n = strcspn(p, "/");
1628 if (n < STRLEN("user@x.service"))
1631 if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1632 char buf[n - 5 - 8 + 1];
1634 memcpy(buf, p + 5, n - 5 - 8);
1637 /* Note that user manager services never need unescaping,
1638 * since they cannot conflict with the kernel's own
1639 * names, hence we don't need to call cg_unescape()
1642 if (parse_uid(buf, NULL) < 0)
1646 p += strspn(p, "/");
1654 static const char *skip_user_prefix(const char *path) {
1659 /* Skip slices, if there are any */
1660 e = skip_slices(path);
1662 /* Skip the user manager, if it's in the path now... */
1663 t = skip_user_manager(e);
1667 /* Alternatively skip the user session if it is in the path... */
1668 return skip_session(e);
1671 int cg_path_get_user_unit(const char *path, char **ret) {
1677 t = skip_user_prefix(path);
1681 /* And from here on it looks pretty much the same as for a
1682 * system unit, hence let's use the same parser from here
1684 return cg_path_get_unit(t, ret);
1687 int cg_pid_get_user_unit(pid_t pid, char **unit) {
1688 _cleanup_free_ char *cgroup = NULL;
1693 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1697 return cg_path_get_user_unit(cgroup, unit);
1700 int cg_path_get_machine_name(const char *path, char **machine) {
1701 _cleanup_free_ char *u = NULL;
1705 r = cg_path_get_unit(path, &u);
1709 sl = strjoina("/run/systemd/machines/unit:", u);
1710 return readlink_malloc(sl, machine);
1713 int cg_pid_get_machine_name(pid_t pid, char **machine) {
1714 _cleanup_free_ char *cgroup = NULL;
1719 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1723 return cg_path_get_machine_name(cgroup, machine);
1727 int cg_path_get_session(const char *path, char **session) {
1728 #if 0 /// UNNEEDED by elogind
1729 _cleanup_free_ char *unit = NULL;
1735 r = cg_path_get_unit(path, &unit);
1739 start = startswith(unit, "session-");
1742 end = endswith(start, ".scope");
1747 if (!session_id_valid(start))
1750 /* Elogind uses a flat hierarchy, just "/SESSION". The only
1751 wrinkle is that SESSION might be escaped. */
1752 const char *e, *n, *start;
1755 log_debug_elogind("path is \"%s\"", path);
1756 assert(path[0] == '/');
1759 n = strchrnul(e, '/');
1763 start = strndupa(e, n - e);
1764 start = cg_unescape(start);
1773 log_debug_elogind("found session: \"%s\"", start);
1784 int cg_pid_get_session(pid_t pid, char **session) {
1785 _cleanup_free_ char *cgroup = NULL;
1788 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1792 return cg_path_get_session(cgroup, session);
1795 int cg_path_get_owner_uid(const char *path, uid_t *uid) {
1796 #if 0 /// elogind needs one more value
1797 _cleanup_free_ char *slice = NULL;
1800 _cleanup_free_ char *slice = NULL, *p = NULL, *s = NULL;
1806 r = cg_path_get_slice(path, &slice);
1810 #if 0 /// elogind does not support systemd slices
1811 start = startswith(slice, "user-");
1814 end = endswith(start, ".slice");
1819 if (parse_uid(start, uid) < 0)
1822 p = strappend("/run/systemd/sessions/", slice);
1824 r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
1832 if (parse_uid(s, uid) < 0)
1839 int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1840 _cleanup_free_ char *cgroup = NULL;
1843 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1847 return cg_path_get_owner_uid(cgroup, uid);
1850 int cg_path_get_slice(const char *p, char **slice) {
1851 const char *e = NULL;
1856 #if 0 /// elogind does not support systemd slices
1857 /* Finds the right-most slice unit from the beginning, but
1858 * stops before we come to the first non-slice unit. */
1863 p += strspn(p, "/");
1865 n = strcspn(p, "/");
1866 if (!valid_slice_name(p, n)) {
1871 s = strdup(SPECIAL_ROOT_SLICE);
1879 return cg_path_decode_unit(e, slice);
1886 /* In elogind, what is reported here, is the location of
1887 * the session. This is derived from /proc/<self|PID>/cgroup.
1888 * In there we look at the controller, which will look something
1889 * like "1:name=openrc:/3".
1890 * The last part gets extracted (and is now p), which is "/3" in
1891 * this case. The three is the session id, and that can be mapped.
1893 e = startswith(p, "/");
1904 int cg_pid_get_slice(pid_t pid, char **slice) {
1905 _cleanup_free_ char *cgroup = NULL;
1910 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1911 log_debug_elogind("Found cgroup %s for pid %u (result %d)",
1916 return cg_path_get_slice(cgroup, slice);
1919 int cg_path_get_user_slice(const char *p, char **slice) {
1920 #if 0 /// UNNEEDED by elogind
1926 #if 0 /// nothing to skip in elogind
1927 t = skip_user_prefix(p);
1932 #if 0 /// UNNEEDED by elogind
1933 /* And now it looks pretty much the same as for a system
1934 * slice, so let's just use the same parser from here on. */
1935 return cg_path_get_slice(t, slice);
1937 /* In elogind there is nothing to skip, we can use the path
1938 * directly. Generally speaking this is always a session id
1939 * to user mapping. */
1940 return cg_path_get_slice(p, slice);
1944 int cg_pid_get_user_slice(pid_t pid, char **slice) {
1945 _cleanup_free_ char *cgroup = NULL;
1950 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1954 return cg_path_get_user_slice(cgroup, slice);
1957 char *cg_escape(const char *p) {
1958 bool need_prefix = false;
1960 /* This implements very minimal escaping for names to be used
1961 * as file names in the cgroup tree: any name which might
1962 * conflict with a kernel name or is prefixed with '_' is
1963 * prefixed with a '_'. That way, when reading cgroup names it
1964 * is sufficient to remove a single prefixing underscore if
1967 /* The return value of this function (unlike cg_unescape())
1970 if (IN_SET(p[0], 0, '_', '.') ||
1971 streq(p, "notify_on_release") ||
1972 streq(p, "release_agent") ||
1973 streq(p, "tasks") ||
1974 startswith(p, "cgroup."))
1979 dot = strrchr(p, '.');
1984 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1987 n = cgroup_controller_to_string(c);
1992 if (memcmp(p, n, l) != 0)
2002 return strappend("_", p);
2007 char *cg_unescape(const char *p) {
2010 /* The return value of this function (unlike cg_escape())
2011 * doesn't need free()! */
2019 #define CONTROLLER_VALID \
2023 bool cg_controller_is_valid(const char *p) {
2029 if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
2032 s = startswith(p, "name=");
2036 if (IN_SET(*p, 0, '_'))
2039 for (t = p; *t; t++)
2040 if (!strchr(CONTROLLER_VALID, *t))
2043 if (t - p > FILENAME_MAX)
2049 #if 0 /// UNNEEDED by elogind
2050 int cg_slice_to_path(const char *unit, char **ret) {
2051 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
2058 if (streq(unit, SPECIAL_ROOT_SLICE)) {
2068 if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
2071 if (!endswith(unit, ".slice"))
2074 r = unit_name_to_prefix(unit, &p);
2078 dash = strchr(p, '-');
2080 /* Don't allow initial dashes */
2085 _cleanup_free_ char *escaped = NULL;
2086 char n[dash - p + sizeof(".slice")];
2088 /* Don't allow trailing or double dashes */
2089 if (IN_SET(dash[1], 0, '-'))
2092 strcpy(stpncpy(n, p, dash - p), ".slice");
2093 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
2096 escaped = cg_escape(n);
2100 if (!strextend(&s, escaped, "/", NULL))
2103 dash = strchr(dash+1, '-');
2106 e = cg_escape(unit);
2110 if (!strextend(&s, e, NULL))
2120 int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
2121 _cleanup_free_ char *p = NULL;
2124 r = cg_get_path(controller, path, attribute, &p);
2128 return write_string_file(p, value, 0);
2131 int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
2132 _cleanup_free_ char *p = NULL;
2135 r = cg_get_path(controller, path, attribute, &p);
2139 return read_one_line_file(p, ret);
2142 #if 0 /// UNNEEDED by elogind
2143 int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values) {
2144 _cleanup_free_ char *filename = NULL, *content = NULL;
2148 for (i = 0; keys[i]; i++)
2151 r = cg_get_path(controller, path, attribute, &filename);
2155 r = read_full_file(filename, &content, NULL);
2160 while ((line = strsep(&p, "\n"))) {
2163 key = strsep(&line, " ");
2165 for (i = 0; keys[i]; i++) {
2166 if (streq(key, keys[i])) {
2167 values[i] = strdup(line);
2173 for (i = 0; keys[i]; i++) {
2175 for (i = 0; keys[i]; i++) {
2176 values[i] = mfree(values[i]);
2185 int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
2189 /* This one will create a cgroup in our private tree, but also
2190 * duplicate it in the trees specified in mask, and remove it
2193 /* First create the cgroup in our own hierarchy. */
2194 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
2198 /* If we are in the unified hierarchy, we are done now */
2199 r = cg_all_unified();
2205 /* Otherwise, do the same in the other hierarchies */
2206 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2207 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2210 n = cgroup_controller_to_string(c);
2213 (void) cg_create(n, path);
2214 else if (supported & bit)
2215 (void) cg_trim(n, path, true);
2221 int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
2225 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
2229 r = cg_all_unified();
2235 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2236 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2237 const char *p = NULL;
2239 if (!(supported & bit))
2243 p = path_callback(bit, userdata);
2248 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
2254 int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
2259 SET_FOREACH(pidp, pids, i) {
2260 pid_t pid = PTR_TO_PID(pidp);
2263 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
2264 if (q < 0 && r >= 0)
2271 int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
2275 if (!path_equal(from, to)) {
2276 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
2281 q = cg_all_unified();
2287 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2288 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2289 const char *p = NULL;
2291 if (!(supported & bit))
2295 p = to_callback(bit, userdata);
2300 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
2306 int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
2310 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
2314 q = cg_all_unified();
2320 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2321 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2323 if (!(supported & bit))
2326 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
2333 int cg_mask_to_string(CGroupMask mask, char **ret) {
2334 _cleanup_free_ char *s = NULL;
2335 size_t n = 0, allocated = 0;
2346 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2350 if (!(mask & CGROUP_CONTROLLER_TO_MASK(c)))
2353 k = cgroup_controller_to_string(c);
2356 if (!GREEDY_REALLOC(s, allocated, n + space + l + 1))
2361 memcpy(s + n + space, k, l);
2376 int cg_mask_from_string(const char *value, CGroupMask *mask) {
2381 _cleanup_free_ char *n = NULL;
2385 r = extract_first_word(&value, &n, NULL, 0);
2391 v = cgroup_controller_from_string(n);
2395 *mask |= CGROUP_CONTROLLER_TO_MASK(v);
2400 int cg_mask_supported(CGroupMask *ret) {
2401 CGroupMask mask = 0;
2404 /* Determines the mask of supported cgroup controllers. Only
2405 * includes controllers we can make sense of and that are
2406 * actually accessible. */
2408 r = cg_all_unified();
2412 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
2414 /* In the unified hierarchy we can read the supported
2415 * and accessible controllers from a the top-level
2416 * cgroup attribute */
2418 r = cg_get_root_path(&root);
2422 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
2426 r = read_one_line_file(path, &controllers);
2430 r = cg_mask_from_string(controllers, &mask);
2434 /* Currently, we support the cpu, memory, io and pids
2435 * controller in the unified hierarchy, mask
2436 * everything else off. */
2437 mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
2442 /* In the legacy hierarchy, we check whether which
2443 * hierarchies are mounted. */
2445 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2448 n = cgroup_controller_to_string(c);
2449 if (controller_is_accessible(n) >= 0)
2450 mask |= CGROUP_CONTROLLER_TO_MASK(c);
2458 #if 0 /// UNNEEDED by elogind
2459 int cg_kernel_controllers(Set **ret) {
2460 _cleanup_set_free_free_ Set *controllers = NULL;
2461 _cleanup_fclose_ FILE *f = NULL;
2466 /* Determines the full list of kernel-known controllers. Might
2467 * include controllers we don't actually support, arbitrary
2468 * named hierarchies and controllers that aren't currently
2469 * accessible (because not mounted). */
2471 controllers = set_new(&string_hash_ops);
2475 f = fopen("/proc/cgroups", "re");
2477 if (errno == ENOENT) {
2485 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
2487 /* Ignore the header line */
2488 (void) read_line(f, (size_t) -1, NULL);
2495 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2500 if (ferror(f) && errno > 0)
2511 if (!cg_controller_is_valid(controller)) {
2516 r = set_consume(controllers, controller);
2528 static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
2530 /* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This
2531 * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2532 * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2533 * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
2535 * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
2536 * process management but disable the compat dual layout, we return %true on
2537 * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
2539 static thread_local bool unified_systemd_v232;
2541 static int cg_unified_update(void) {
2545 /* Checks if we support the unified hierarchy. Returns an
2546 * error when the cgroup hierarchies aren't mounted yet or we
2547 * have any other trouble determining if the unified hierarchy
2550 if (unified_cache >= CGROUP_UNIFIED_NONE)
2553 if (statfs("/sys/fs/cgroup/", &fs) < 0)
2554 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\" failed: %m");
2556 if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2557 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
2558 unified_cache = CGROUP_UNIFIED_ALL;
2559 #if 0 /// The handling of cgroups is a bit different with elogind
2560 } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2562 } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)
2563 || F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2565 if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
2566 F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2567 log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for elogind controller");
2568 unified_cache = CGROUP_UNIFIED_SYSTEMD;
2569 unified_systemd_v232 = false;
2571 #if 0 /// There is no sub-grouping within elogind
2572 if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
2573 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
2575 if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2576 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
2577 unified_cache = CGROUP_UNIFIED_SYSTEMD;
2578 unified_systemd_v232 = true;
2579 } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) {
2580 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
2581 unified_cache = CGROUP_UNIFIED_NONE;
2583 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
2584 (unsigned long long) fs.f_type);
2585 unified_cache = CGROUP_UNIFIED_NONE;
2588 unified_cache = CGROUP_UNIFIED_NONE;
2592 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2593 (unsigned long long) fs.f_type);
2600 int cg_unified_controller(const char *controller) {
2603 r = cg_unified_update();
2607 if (unified_cache == CGROUP_UNIFIED_NONE)
2610 if (unified_cache >= CGROUP_UNIFIED_ALL)
2613 #if 0 /// only if elogind is the controller we can use cgroups2 in hybrid mode
2614 return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
2616 return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID);
2620 int cg_all_unified(void) {
2623 r = cg_unified_update();
2627 return unified_cache >= CGROUP_UNIFIED_ALL;
2630 int cg_hybrid_unified(void) {
2633 r = cg_unified_update();
2637 return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
2640 int cg_unified_flush(void) {
2641 unified_cache = CGROUP_UNIFIED_UNKNOWN;
2643 return cg_unified_update();
2646 #if 0 /// UNNEEDED by elogind
2647 int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
2648 _cleanup_fclose_ FILE *f = NULL;
2649 _cleanup_free_ char *fs = NULL;
2658 r = cg_all_unified();
2661 if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
2664 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
2668 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2669 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2672 if (!(supported & bit))
2675 n = cgroup_controller_to_string(c);
2677 char s[1 + strlen(n) + 1];
2679 s[0] = mask & bit ? '+' : '-';
2683 f = fopen(fs, "we");
2685 log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
2690 r = write_string_stream(f, s, 0);
2692 log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
2700 bool cg_is_unified_wanted(void) {
2701 static thread_local int wanted = -1;
2704 const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
2706 /* If we have a cached value, return that. */
2710 /* If the hierarchy is already mounted, then follow whatever
2711 * was chosen for it. */
2712 if (cg_unified_flush() >= 0)
2713 return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
2715 #if 0 /// elogind is not init and has no business with kernel command line
2716 /* Otherwise, let's see what the kernel command line has to say.
2717 * Since checking is expensive, cache a non-error result. */
2718 r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
2721 return (wanted = r > 0 ? b : is_default);
2724 bool cg_is_legacy_wanted(void) {
2725 static thread_local int wanted = -1;
2727 /* If we have a cached value, return that. */
2731 /* Check if we have cgroups2 already mounted. */
2732 if (cg_unified_flush() >= 0 &&
2733 unified_cache == CGROUP_UNIFIED_ALL)
2734 return (wanted = false);
2736 /* Otherwise, assume that at least partial legacy is wanted,
2737 * since cgroups2 should already be mounted at this point. */
2738 return (wanted = true);
2741 bool cg_is_hybrid_wanted(void) {
2742 static thread_local int wanted = -1;
2745 const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
2746 /* We default to true if the default is "hybrid", obviously,
2747 * but also when the default is "unified", because if we get
2748 * called, it means that unified hierarchy was not mounted. */
2750 /* If we have a cached value, return that. */
2754 /* If the hierarchy is already mounted, then follow whatever
2755 * was chosen for it. */
2756 if (cg_unified_flush() >= 0 &&
2757 unified_cache == CGROUP_UNIFIED_ALL)
2758 return (wanted = false);
2760 #if 0 /// elogind is not init and has no business with kernel command line
2761 /* Otherwise, let's see what the kernel command line has to say.
2762 * Since checking is expensive, cache a non-error result. */
2763 r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
2766 /* The meaning of the kernel option is reversed wrt. to the return value
2767 * of this function, hence the negation. */
2768 return (wanted = r > 0 ? !b : is_default);
2771 #if 0 /// UNNEEDED by elogind
2772 int cg_weight_parse(const char *s, uint64_t *ret) {
2777 *ret = CGROUP_WEIGHT_INVALID;
2781 r = safe_atou64(s, &u);
2785 if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
2792 const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2793 [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX,
2794 [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX,
2795 [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX,
2796 [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX,
2799 static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2800 [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax",
2801 [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax",
2802 [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax",
2803 [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax",
2806 DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
2808 int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
2813 *ret = CGROUP_CPU_SHARES_INVALID;
2817 r = safe_atou64(s, &u);
2821 if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
2828 int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
2833 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
2837 r = safe_atou64(s, &u);
2841 if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
2849 bool is_cgroup_fs(const struct statfs *s) {
2850 return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
2851 is_fs_type(s, CGROUP2_SUPER_MAGIC);
2854 bool fd_is_cgroup_fs(int fd) {
2857 if (fstatfs(fd, &s) < 0)
2860 return is_cgroup_fs(&s);
2863 static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
2864 [CGROUP_CONTROLLER_CPU] = "cpu",
2865 [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
2866 [CGROUP_CONTROLLER_IO] = "io",
2867 [CGROUP_CONTROLLER_BLKIO] = "blkio",
2868 [CGROUP_CONTROLLER_MEMORY] = "memory",
2869 [CGROUP_CONTROLLER_DEVICES] = "devices",
2870 [CGROUP_CONTROLLER_PIDS] = "pids",
2873 DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);