1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2010 Lennart Poettering
12 #include <stdio_ext.h>
16 #include <sys/statfs.h>
17 #include <sys/types.h>
18 #include <sys/xattr.h>
21 #include "alloc-util.h"
22 #include "cgroup-util.h"
24 #include "dirent-util.h"
25 #include "extract-word.h"
28 #include "format-util.h"
31 #include "login-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "proc-cmdline.h"
38 #include "process-util.h"
41 #include "stat-util.h"
42 #include "stdio-util.h"
43 #include "string-table.h"
44 #include "string-util.h"
46 #include "unit-name.h"
47 #include "user-util.h"
49 int cg_enumerate_processes(const char *controller
, const char *path
, FILE **_f
) {
50 _cleanup_free_
char *fs
= NULL
;
56 r
= cg_get_path(controller
, path
, "cgroup.procs", &fs
);
68 int cg_read_pid(FILE *f
, pid_t
*_pid
) {
71 /* Note that the cgroup.procs might contain duplicates! See
72 * cgroups.txt for details. */
78 if (fscanf(f
, "%lu", &ul
) != 1) {
83 return errno
> 0 ? -errno
: -EIO
;
94 const char *controller
,
99 _cleanup_free_
char *events
= NULL
, *content
= NULL
;
103 r
= cg_get_path(controller
, path
, "cgroup.events", &events
);
107 r
= read_full_file(events
, &content
, NULL
);
112 while ((line
= strsep(&p
, "\n"))) {
115 key
= strsep(&line
, " ");
119 if (strcmp(key
, event
))
129 bool cg_ns_supported(void) {
130 static thread_local
int enabled
= -1;
135 if (access("/proc/self/ns/cgroup", F_OK
) == 0)
143 int cg_enumerate_subgroups(const char *controller
, const char *path
, DIR **_d
) {
144 _cleanup_free_
char *fs
= NULL
;
150 /* This is not recursive! */
152 r
= cg_get_path(controller
, path
, NULL
, &fs
);
164 int cg_read_subgroup(DIR *d
, char **fn
) {
170 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
173 if (de
->d_type
!= DT_DIR
)
176 if (dot_or_dot_dot(de
->d_name
))
179 b
= strdup(de
->d_name
);
190 int cg_rmdir(const char *controller
, const char *path
) {
191 _cleanup_free_
char *p
= NULL
;
194 r
= cg_get_path(controller
, path
, NULL
, &p
);
199 if (r
< 0 && errno
!= ENOENT
)
202 r
= cg_hybrid_unified();
208 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
209 r
= cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
);
211 log_warning_errno(r
, "Failed to remove compat systemd cgroup %s: %m", path
);
218 const char *controller
,
223 cg_kill_log_func_t log_kill
,
226 _cleanup_set_free_ Set
*allocated_set
= NULL
;
233 /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send
234 * SIGCONT on SIGKILL. */
235 if (IN_SET(sig
, SIGCONT
, SIGKILL
))
236 flags
&= ~CGROUP_SIGCONT
;
238 /* This goes through the tasks list and kills them all. This
239 * is repeated until no further processes are added to the
240 * tasks list, to properly handle forking processes */
243 s
= allocated_set
= set_new(NULL
);
248 my_pid
= getpid_cached();
251 _cleanup_fclose_
FILE *f
= NULL
;
255 r
= cg_enumerate_processes(controller
, path
, &f
);
257 if (ret
>= 0 && r
!= -ENOENT
)
263 while ((r
= cg_read_pid(f
, &pid
)) > 0) {
265 if ((flags
& CGROUP_IGNORE_SELF
) && pid
== my_pid
)
268 if (set_get(s
, PID_TO_PTR(pid
)) == PID_TO_PTR(pid
))
272 log_kill(pid
, sig
, userdata
);
274 /* If we haven't killed this process yet, kill
276 if (kill(pid
, sig
) < 0) {
277 if (ret
>= 0 && errno
!= ESRCH
)
280 if (flags
& CGROUP_SIGCONT
)
281 (void) kill(pid
, SIGCONT
);
289 r
= set_put(s
, PID_TO_PTR(pid
));
305 /* To avoid racing against processes which fork
306 * quicker than we can kill them we repeat this until
307 * no new pids need to be killed. */
314 int cg_kill_recursive(
315 const char *controller
,
320 cg_kill_log_func_t log_kill
,
323 _cleanup_set_free_ Set
*allocated_set
= NULL
;
324 _cleanup_closedir_
DIR *d
= NULL
;
332 s
= allocated_set
= set_new(NULL
);
337 ret
= cg_kill(controller
, path
, sig
, flags
, s
, log_kill
, userdata
);
339 r
= cg_enumerate_subgroups(controller
, path
, &d
);
341 if (ret
>= 0 && r
!= -ENOENT
)
347 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
348 _cleanup_free_
char *p
= NULL
;
350 p
= strjoin(path
, "/", fn
);
355 r
= cg_kill_recursive(controller
, p
, sig
, flags
, s
, log_kill
, userdata
);
356 if (r
!= 0 && ret
>= 0)
359 if (ret
>= 0 && r
< 0)
362 if (flags
& CGROUP_REMOVE
) {
363 r
= cg_rmdir(controller
, path
);
364 if (r
< 0 && ret
>= 0 && !IN_SET(r
, -ENOENT
, -EBUSY
))
379 _cleanup_set_free_ Set
*s
= NULL
;
392 my_pid
= getpid_cached();
395 _cleanup_fclose_
FILE *f
= NULL
;
399 r
= cg_enumerate_processes(cfrom
, pfrom
, &f
);
401 if (ret
>= 0 && r
!= -ENOENT
)
407 while ((r
= cg_read_pid(f
, &pid
)) > 0) {
409 /* This might do weird stuff if we aren't a
410 * single-threaded program. However, we
411 * luckily know we are not */
412 if ((flags
& CGROUP_IGNORE_SELF
) && pid
== my_pid
)
415 if (set_get(s
, PID_TO_PTR(pid
)) == PID_TO_PTR(pid
))
418 /* Ignore kernel threads. Since they can only
419 * exist in the root cgroup, we only check for
422 empty_or_root(pfrom
) &&
423 is_kernel_thread(pid
) > 0)
426 r
= cg_attach(cto
, pto
, pid
);
428 if (ret
>= 0 && r
!= -ESRCH
)
435 r
= set_put(s
, PID_TO_PTR(pid
));
455 int cg_migrate_recursive(
462 _cleanup_closedir_
DIR *d
= NULL
;
471 ret
= cg_migrate(cfrom
, pfrom
, cto
, pto
, flags
);
473 r
= cg_enumerate_subgroups(cfrom
, pfrom
, &d
);
475 if (ret
>= 0 && r
!= -ENOENT
)
481 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
482 _cleanup_free_
char *p
= NULL
;
484 p
= strjoin(pfrom
, "/", fn
);
489 r
= cg_migrate_recursive(cfrom
, p
, cto
, pto
, flags
);
490 if (r
!= 0 && ret
>= 0)
494 if (r
< 0 && ret
>= 0)
497 if (flags
& CGROUP_REMOVE
) {
498 r
= cg_rmdir(cfrom
, pfrom
);
499 if (r
< 0 && ret
>= 0 && !IN_SET(r
, -ENOENT
, -EBUSY
))
506 int cg_migrate_recursive_fallback(
520 r
= cg_migrate_recursive(cfrom
, pfrom
, cto
, pto
, flags
);
522 char prefix
[strlen(pto
) + 1];
524 /* This didn't work? Then let's try all prefixes of the destination */
526 PATH_FOREACH_PREFIX(prefix
, pto
) {
529 q
= cg_migrate_recursive(cfrom
, pfrom
, cto
, prefix
, flags
);
538 static const char *controller_to_dirname(const char *controller
) {
543 /* Converts a controller name to the directory name below
544 * /sys/fs/cgroup/ we want to mount it to. Effectively, this
545 * just cuts off the name= prefixed used for named
546 * hierarchies, if it is specified. */
548 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
549 if (cg_hybrid_unified() > 0)
550 controller
= SYSTEMD_CGROUP_CONTROLLER_HYBRID
;
552 controller
= SYSTEMD_CGROUP_CONTROLLER_LEGACY
;
555 e
= startswith(controller
, "name=");
562 static int join_path_legacy(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
569 dn
= controller_to_dirname(controller
);
571 if (isempty(path
) && isempty(suffix
))
572 t
= strappend("/sys/fs/cgroup/", dn
);
573 else if (isempty(path
))
574 t
= strjoin("/sys/fs/cgroup/", dn
, "/", suffix
);
575 else if (isempty(suffix
))
576 t
= strjoin("/sys/fs/cgroup/", dn
, "/", path
);
578 t
= strjoin("/sys/fs/cgroup/", dn
, "/", path
, "/", suffix
);
586 static int join_path_unified(const char *path
, const char *suffix
, char **fs
) {
591 if (isempty(path
) && isempty(suffix
))
592 t
= strdup("/sys/fs/cgroup");
593 else if (isempty(path
))
594 t
= strappend("/sys/fs/cgroup/", suffix
);
595 else if (isempty(suffix
))
596 t
= strappend("/sys/fs/cgroup/", path
);
598 t
= strjoin("/sys/fs/cgroup/", path
, "/", suffix
);
606 int cg_get_path(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
614 /* If no controller is specified, we return the path
615 * *below* the controllers, without any prefix. */
617 if (!path
&& !suffix
)
625 t
= strjoin(path
, "/", suffix
);
629 *fs
= path_simplify(t
, false);
633 if (!cg_controller_is_valid(controller
))
636 r
= cg_all_unified();
640 r
= join_path_unified(path
, suffix
, fs
);
642 r
= join_path_legacy(controller
, path
, suffix
, fs
);
646 path_simplify(*fs
, false);
650 static int controller_is_accessible(const char *controller
) {
655 /* Checks whether a specific controller is accessible,
656 * i.e. its hierarchy mounted. In the unified hierarchy all
657 * controllers are considered accessible, except for the named
660 if (!cg_controller_is_valid(controller
))
663 r
= cg_all_unified();
667 /* We don't support named hierarchies if we are using
668 * the unified hierarchy. */
670 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
))
673 if (startswith(controller
, "name="))
679 dn
= controller_to_dirname(controller
);
680 cc
= strjoina("/sys/fs/cgroup/", dn
);
682 if (laccess(cc
, F_OK
) < 0)
689 int cg_get_path_and_check(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
695 /* Check if the specified controller is actually accessible */
696 r
= controller_is_accessible(controller
);
700 return cg_get_path(controller
, path
, suffix
, fs
);
703 static int trim_cb(const char *path
, const struct stat
*sb
, int typeflag
, struct FTW
*ftwbuf
) {
708 if (typeflag
!= FTW_DP
)
711 if (ftwbuf
->level
< 1)
718 int cg_trim(const char *controller
, const char *path
, bool delete_root
) {
719 _cleanup_free_
char *fs
= NULL
;
724 r
= cg_get_path(controller
, path
, NULL
, &fs
);
729 if (nftw(fs
, trim_cb
, 64, FTW_DEPTH
|FTW_MOUNT
|FTW_PHYS
) != 0) {
739 if (rmdir(fs
) < 0 && errno
!= ENOENT
)
743 q
= cg_hybrid_unified();
746 if (q
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
747 q
= cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, delete_root
);
749 log_warning_errno(q
, "Failed to trim compat systemd cgroup %s: %m", path
);
755 /* Create a cgroup in the hierarchy of controller.
756 * Returns 0 if the group already existed, 1 on success, negative otherwise.
758 int cg_create(const char *controller
, const char *path
) {
759 _cleanup_free_
char *fs
= NULL
;
762 r
= cg_get_path_and_check(controller
, path
, NULL
, &fs
);
766 r
= mkdir_parents(fs
, 0755);
770 r
= mkdir_errno_wrapper(fs
, 0755);
776 r
= cg_hybrid_unified();
780 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
781 r
= cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
);
783 log_warning_errno(r
, "Failed to create compat systemd cgroup %s: %m", path
);
789 int cg_create_and_attach(const char *controller
, const char *path
, pid_t pid
) {
794 r
= cg_create(controller
, path
);
798 q
= cg_attach(controller
, path
, pid
);
802 /* This does not remove the cgroup on failure */
806 int cg_attach(const char *controller
, const char *path
, pid_t pid
) {
807 _cleanup_free_
char *fs
= NULL
;
808 char c
[DECIMAL_STR_MAX(pid_t
) + 2];
814 r
= cg_get_path_and_check(controller
, path
, "cgroup.procs", &fs
);
819 pid
= getpid_cached();
821 xsprintf(c
, PID_FMT
"\n", pid
);
823 r
= write_string_file(fs
, c
, 0);
827 r
= cg_hybrid_unified();
831 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
832 r
= cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, pid
);
834 log_warning_errno(r
, "Failed to attach "PID_FMT
" to compat systemd cgroup %s: %m", pid
, path
);
840 int cg_attach_fallback(const char *controller
, const char *path
, pid_t pid
) {
847 r
= cg_attach(controller
, path
, pid
);
849 char prefix
[strlen(path
) + 1];
851 /* This didn't work? Then let's try all prefixes of
854 PATH_FOREACH_PREFIX(prefix
, path
) {
857 q
= cg_attach(controller
, prefix
, pid
);
867 const char *controller
,
877 /* cgroupsv1, aka legacy/non-unified */
878 static const struct Attribute legacy_attributes
[] = {
879 { "cgroup.procs", true },
881 { "cgroup.clone_children", false },
885 /* cgroupsv2, aka unified */
886 static const struct Attribute unified_attributes
[] = {
887 { "cgroup.procs", true },
888 { "cgroup.subtree_control", true },
889 { "cgroup.threads", false },
893 static const struct Attribute
* const attributes
[] = {
894 [false] = legacy_attributes
,
895 [true] = unified_attributes
,
898 _cleanup_free_
char *fs
= NULL
;
899 const struct Attribute
*i
;
904 if (uid
== UID_INVALID
&& gid
== GID_INVALID
)
907 unified
= cg_unified_controller(controller
);
911 /* Configure access to the cgroup itself */
912 r
= cg_get_path(controller
, path
, NULL
, &fs
);
916 r
= chmod_and_chown(fs
, 0755, uid
, gid
);
920 /* Configure access to the cgroup's attributes */
921 for (i
= attributes
[unified
]; i
->name
; i
++) {
924 r
= cg_get_path(controller
, path
, i
->name
, &fs
);
928 r
= chmod_and_chown(fs
, 0644, uid
, gid
);
933 log_debug_errno(r
, "Failed to set access on cgroup %s, ignoring: %m", fs
);
937 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
938 r
= cg_hybrid_unified();
942 /* Always propagate access mode from unified to legacy controller */
943 r
= cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, uid
, gid
);
945 log_debug_errno(r
, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path
);
952 int cg_set_xattr(const char *controller
, const char *path
, const char *name
, const void *value
, size_t size
, int flags
) {
953 _cleanup_free_
char *fs
= NULL
;
958 assert(value
|| size
<= 0);
960 r
= cg_get_path(controller
, path
, NULL
, &fs
);
964 if (setxattr(fs
, name
, value
, size
, flags
) < 0)
970 int cg_get_xattr(const char *controller
, const char *path
, const char *name
, void *value
, size_t size
) {
971 _cleanup_free_
char *fs
= NULL
;
978 r
= cg_get_path(controller
, path
, NULL
, &fs
);
982 n
= getxattr(fs
, name
, value
, size
);
989 int cg_pid_get_path(const char *controller
, pid_t pid
, char **path
) {
990 _cleanup_fclose_
FILE *f
= NULL
;
992 const char *fs
, *controller_str
;
1000 if (!cg_controller_is_valid(controller
))
1003 controller
= SYSTEMD_CGROUP_CONTROLLER
;
1005 unified
= cg_unified_controller(controller
);
1009 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
))
1010 controller_str
= SYSTEMD_CGROUP_CONTROLLER_LEGACY
;
1012 controller_str
= controller
;
1014 cs
= strlen(controller_str
);
1017 fs
= procfs_file_alloca(pid
, "cgroup");
1018 f
= fopen(fs
, "re");
1020 return errno
== ENOENT
? -ESRCH
: -errno
;
1022 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
1024 FOREACH_LINE(line
, f
, return -errno
) {
1030 e
= startswith(line
, "0:");
1040 const char *word
, *state
;
1043 l
= strchr(line
, ':');
1053 FOREACH_WORD_SEPARATOR(word
, k
, l
, ",", state
)
1054 if (k
== cs
&& memcmp(word
, controller_str
, cs
) == 0) {
1066 /* Truncate suffix indicating the process is a zombie */
1067 e
= endswith(p
, " (deleted)");
1078 int cg_install_release_agent(const char *controller
, const char *agent
) {
1079 _cleanup_free_
char *fs
= NULL
, *contents
= NULL
;
1085 r
= cg_unified_controller(controller
);
1088 if (r
> 0) /* doesn't apply to unified hierarchy */
1091 r
= cg_get_path(controller
, NULL
, "release_agent", &fs
);
1095 r
= read_one_line_file(fs
, &contents
);
1099 sc
= strstrip(contents
);
1101 r
= write_string_file(fs
, agent
, 0);
1104 } else if (!path_equal(sc
, agent
))
1108 r
= cg_get_path(controller
, NULL
, "notify_on_release", &fs
);
1112 contents
= mfree(contents
);
1113 r
= read_one_line_file(fs
, &contents
);
1117 sc
= strstrip(contents
);
1118 if (streq(sc
, "0")) {
1119 r
= write_string_file(fs
, "1", 0);
1126 if (!streq(sc
, "1"))
1132 int cg_uninstall_release_agent(const char *controller
) {
1133 _cleanup_free_
char *fs
= NULL
;
1136 r
= cg_unified_controller(controller
);
1139 if (r
> 0) /* Doesn't apply to unified hierarchy */
1142 r
= cg_get_path(controller
, NULL
, "notify_on_release", &fs
);
1146 r
= write_string_file(fs
, "0", 0);
1152 r
= cg_get_path(controller
, NULL
, "release_agent", &fs
);
1156 r
= write_string_file(fs
, "", 0);
1163 int cg_is_empty(const char *controller
, const char *path
) {
1164 _cleanup_fclose_
FILE *f
= NULL
;
1170 r
= cg_enumerate_processes(controller
, path
, &f
);
1176 r
= cg_read_pid(f
, &pid
);
1183 int cg_is_empty_recursive(const char *controller
, const char *path
) {
1188 /* The root cgroup is always populated */
1189 if (controller
&& empty_or_root(path
))
1192 r
= cg_unified_controller(controller
);
1196 _cleanup_free_
char *t
= NULL
;
1198 /* On the unified hierarchy we can check empty state
1199 * via the "populated" attribute of "cgroup.events". */
1201 r
= cg_read_event(controller
, path
, "populated", &t
);
1205 return streq(t
, "0");
1207 _cleanup_closedir_
DIR *d
= NULL
;
1210 r
= cg_is_empty(controller
, path
);
1214 r
= cg_enumerate_subgroups(controller
, path
, &d
);
1220 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
1221 _cleanup_free_
char *p
= NULL
;
1223 p
= strjoin(path
, "/", fn
);
1228 r
= cg_is_empty_recursive(controller
, p
);
1239 int cg_split_spec(const char *spec
, char **controller
, char **path
) {
1240 char *t
= NULL
, *u
= NULL
;
1246 if (!path_is_normalized(spec
))
1254 *path
= path_simplify(t
, false);
1263 e
= strchr(spec
, ':');
1265 if (!cg_controller_is_valid(spec
))
1282 t
= strndup(spec
, e
-spec
);
1285 if (!cg_controller_is_valid(t
)) {
1299 if (!path_is_normalized(u
) ||
1300 !path_is_absolute(u
)) {
1306 path_simplify(u
, false);
1322 int cg_mangle_path(const char *path
, char **result
) {
1323 _cleanup_free_
char *c
= NULL
, *p
= NULL
;
1330 /* First, check if it already is a filesystem path */
1331 if (path_startswith(path
, "/sys/fs/cgroup")) {
1337 *result
= path_simplify(t
, false);
1341 /* Otherwise, treat it as cg spec */
1342 r
= cg_split_spec(path
, &c
, &p
);
1346 return cg_get_path(c
?: SYSTEMD_CGROUP_CONTROLLER
, p
?: "/", NULL
, result
);
1349 int cg_get_root_path(char **path
) {
1355 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, 1, &p
);
1359 e
= endswith(p
, "/" SPECIAL_INIT_SCOPE
);
1361 e
= endswith(p
, "/" SPECIAL_SYSTEM_SLICE
); /* legacy */
1363 e
= endswith(p
, "/system"); /* even more legacy */
1371 int cg_shift_path(const char *cgroup
, const char *root
, const char **shifted
) {
1372 _cleanup_free_
char *rt
= NULL
;
1380 /* If the root was specified let's use that, otherwise
1381 * let's determine it from PID 1 */
1383 r
= cg_get_root_path(&rt
);
1390 p
= path_startswith(cgroup
, root
);
1391 if (p
&& p
> cgroup
)
1399 int cg_pid_get_path_shifted(pid_t pid
, const char *root
, char **cgroup
) {
1400 _cleanup_free_
char *raw
= NULL
;
1407 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, pid
, &raw
);
1411 r
= cg_shift_path(raw
, root
, &c
);
1416 *cgroup
= TAKE_PTR(raw
);
1430 int cg_path_decode_unit(const char *cgroup
, char **unit
) {
1437 n
= strcspn(cgroup
, "/");
1441 c
= strndupa(cgroup
, n
);
1444 if (!unit_name_is_valid(c
, UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
))
1455 static bool valid_slice_name(const char *p
, size_t n
) {
1460 if (n
< STRLEN("x.slice"))
1463 if (memcmp(p
+ n
- 6, ".slice", 6) == 0) {
1469 c
= cg_unescape(buf
);
1471 return unit_name_is_valid(c
, UNIT_NAME_PLAIN
);
1477 static const char *skip_slices(const char *p
) {
1480 /* Skips over all slice assignments */
1485 p
+= strspn(p
, "/");
1487 n
= strcspn(p
, "/");
1488 if (!valid_slice_name(p
, n
))
1495 int cg_path_get_unit(const char *path
, char **ret
) {
1503 e
= skip_slices(path
);
1505 r
= cg_path_decode_unit(e
, &unit
);
1509 /* We skipped over the slices, don't accept any now */
1510 if (endswith(unit
, ".slice")) {
1519 int cg_pid_get_unit(pid_t pid
, char **unit
) {
1520 _cleanup_free_
char *cgroup
= NULL
;
1525 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1529 return cg_path_get_unit(cgroup
, unit
);
1533 * Skip session-*.scope, but require it to be there.
1535 static const char *skip_session(const char *p
) {
1541 p
+= strspn(p
, "/");
1543 n
= strcspn(p
, "/");
1544 if (n
< STRLEN("session-x.scope"))
1547 if (memcmp(p
, "session-", 8) == 0 && memcmp(p
+ n
- 6, ".scope", 6) == 0) {
1548 char buf
[n
- 8 - 6 + 1];
1550 memcpy(buf
, p
+ 8, n
- 8 - 6);
1553 /* Note that session scopes never need unescaping,
1554 * since they cannot conflict with the kernel's own
1555 * names, hence we don't need to call cg_unescape()
1558 if (!session_id_valid(buf
))
1562 p
+= strspn(p
, "/");
1570 * Skip user@*.service, but require it to be there.
1572 static const char *skip_user_manager(const char *p
) {
1578 p
+= strspn(p
, "/");
1580 n
= strcspn(p
, "/");
1581 if (n
< STRLEN("user@x.service"))
1584 if (memcmp(p
, "user@", 5) == 0 && memcmp(p
+ n
- 8, ".service", 8) == 0) {
1585 char buf
[n
- 5 - 8 + 1];
1587 memcpy(buf
, p
+ 5, n
- 5 - 8);
1590 /* Note that user manager services never need unescaping,
1591 * since they cannot conflict with the kernel's own
1592 * names, hence we don't need to call cg_unescape()
1595 if (parse_uid(buf
, NULL
) < 0)
1599 p
+= strspn(p
, "/");
1607 static const char *skip_user_prefix(const char *path
) {
1612 /* Skip slices, if there are any */
1613 e
= skip_slices(path
);
1615 /* Skip the user manager, if it's in the path now... */
1616 t
= skip_user_manager(e
);
1620 /* Alternatively skip the user session if it is in the path... */
1621 return skip_session(e
);
1624 int cg_path_get_user_unit(const char *path
, char **ret
) {
1630 t
= skip_user_prefix(path
);
1634 /* And from here on it looks pretty much the same as for a
1635 * system unit, hence let's use the same parser from here
1637 return cg_path_get_unit(t
, ret
);
1640 int cg_pid_get_user_unit(pid_t pid
, char **unit
) {
1641 _cleanup_free_
char *cgroup
= NULL
;
1646 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1650 return cg_path_get_user_unit(cgroup
, unit
);
1653 int cg_path_get_machine_name(const char *path
, char **machine
) {
1654 _cleanup_free_
char *u
= NULL
;
1658 r
= cg_path_get_unit(path
, &u
);
1662 sl
= strjoina("/run/systemd/machines/unit:", u
);
1663 return readlink_malloc(sl
, machine
);
1666 int cg_pid_get_machine_name(pid_t pid
, char **machine
) {
1667 _cleanup_free_
char *cgroup
= NULL
;
1672 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1676 return cg_path_get_machine_name(cgroup
, machine
);
1679 int cg_path_get_session(const char *path
, char **session
) {
1680 _cleanup_free_
char *unit
= NULL
;
1686 r
= cg_path_get_unit(path
, &unit
);
1690 start
= startswith(unit
, "session-");
1693 end
= endswith(start
, ".scope");
1698 if (!session_id_valid(start
))
1714 int cg_pid_get_session(pid_t pid
, char **session
) {
1715 _cleanup_free_
char *cgroup
= NULL
;
1718 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1722 return cg_path_get_session(cgroup
, session
);
1725 int cg_path_get_owner_uid(const char *path
, uid_t
*uid
) {
1726 _cleanup_free_
char *slice
= NULL
;
1732 r
= cg_path_get_slice(path
, &slice
);
1736 start
= startswith(slice
, "user-");
1739 end
= endswith(start
, ".slice");
1744 if (parse_uid(start
, uid
) < 0)
1750 int cg_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
1751 _cleanup_free_
char *cgroup
= NULL
;
1754 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1758 return cg_path_get_owner_uid(cgroup
, uid
);
1761 int cg_path_get_slice(const char *p
, char **slice
) {
1762 const char *e
= NULL
;
1767 /* Finds the right-most slice unit from the beginning, but
1768 * stops before we come to the first non-slice unit. */
1773 p
+= strspn(p
, "/");
1775 n
= strcspn(p
, "/");
1776 if (!valid_slice_name(p
, n
)) {
1781 s
= strdup(SPECIAL_ROOT_SLICE
);
1789 return cg_path_decode_unit(e
, slice
);
1797 int cg_pid_get_slice(pid_t pid
, char **slice
) {
1798 _cleanup_free_
char *cgroup
= NULL
;
1803 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1807 return cg_path_get_slice(cgroup
, slice
);
1810 int cg_path_get_user_slice(const char *p
, char **slice
) {
1815 t
= skip_user_prefix(p
);
1819 /* And now it looks pretty much the same as for a system
1820 * slice, so let's just use the same parser from here on. */
1821 return cg_path_get_slice(t
, slice
);
1824 int cg_pid_get_user_slice(pid_t pid
, char **slice
) {
1825 _cleanup_free_
char *cgroup
= NULL
;
1830 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1834 return cg_path_get_user_slice(cgroup
, slice
);
1837 char *cg_escape(const char *p
) {
1838 bool need_prefix
= false;
1840 /* This implements very minimal escaping for names to be used
1841 * as file names in the cgroup tree: any name which might
1842 * conflict with a kernel name or is prefixed with '_' is
1843 * prefixed with a '_'. That way, when reading cgroup names it
1844 * is sufficient to remove a single prefixing underscore if
1847 /* The return value of this function (unlike cg_unescape())
1850 if (IN_SET(p
[0], 0, '_', '.') ||
1851 streq(p
, "notify_on_release") ||
1852 streq(p
, "release_agent") ||
1853 streq(p
, "tasks") ||
1854 startswith(p
, "cgroup."))
1859 dot
= strrchr(p
, '.');
1864 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
1867 n
= cgroup_controller_to_string(c
);
1872 if (memcmp(p
, n
, l
) != 0)
1882 return strappend("_", p
);
1887 char *cg_unescape(const char *p
) {
1890 /* The return value of this function (unlike cg_escape())
1891 * doesn't need free()! */
1899 #define CONTROLLER_VALID \
1903 bool cg_controller_is_valid(const char *p
) {
1909 if (streq(p
, SYSTEMD_CGROUP_CONTROLLER
))
1912 s
= startswith(p
, "name=");
1916 if (IN_SET(*p
, 0, '_'))
1919 for (t
= p
; *t
; t
++)
1920 if (!strchr(CONTROLLER_VALID
, *t
))
1923 if (t
- p
> FILENAME_MAX
)
1929 int cg_slice_to_path(const char *unit
, char **ret
) {
1930 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *e
= NULL
;
1937 if (streq(unit
, SPECIAL_ROOT_SLICE
)) {
1947 if (!unit_name_is_valid(unit
, UNIT_NAME_PLAIN
))
1950 if (!endswith(unit
, ".slice"))
1953 r
= unit_name_to_prefix(unit
, &p
);
1957 dash
= strchr(p
, '-');
1959 /* Don't allow initial dashes */
1964 _cleanup_free_
char *escaped
= NULL
;
1965 char n
[dash
- p
+ sizeof(".slice")];
1967 #if HAS_FEATURE_MEMORY_SANITIZER
1968 /* msan doesn't instrument stpncpy, so it thinks
1969 * n is later used unitialized:
1970 * https://github.com/google/sanitizers/issues/926
1975 /* Don't allow trailing or double dashes */
1976 if (IN_SET(dash
[1], 0, '-'))
1979 strcpy(stpncpy(n
, p
, dash
- p
), ".slice");
1980 if (!unit_name_is_valid(n
, UNIT_NAME_PLAIN
))
1983 escaped
= cg_escape(n
);
1987 if (!strextend(&s
, escaped
, "/", NULL
))
1990 dash
= strchr(dash
+1, '-');
1993 e
= cg_escape(unit
);
1997 if (!strextend(&s
, e
, NULL
))
2005 int cg_set_attribute(const char *controller
, const char *path
, const char *attribute
, const char *value
) {
2006 _cleanup_free_
char *p
= NULL
;
2009 r
= cg_get_path(controller
, path
, attribute
, &p
);
2013 return write_string_file(p
, value
, 0);
2016 int cg_get_attribute(const char *controller
, const char *path
, const char *attribute
, char **ret
) {
2017 _cleanup_free_
char *p
= NULL
;
2020 r
= cg_get_path(controller
, path
, attribute
, &p
);
2024 return read_one_line_file(p
, ret
);
2027 int cg_get_keyed_attribute(
2028 const char *controller
,
2030 const char *attribute
,
2032 char **ret_values
) {
2034 _cleanup_free_
char *filename
= NULL
, *contents
= NULL
;
2036 size_t n
, i
, n_done
= 0;
2040 /* Reads one or more fields of a cgroupsv2 keyed attribute file. The 'keys' parameter should be an strv with
2041 * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
2042 * entries as 'keys'. On success each entry will be set to the value of the matching key.
2044 * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
2046 r
= cg_get_path(controller
, path
, attribute
, &filename
);
2050 r
= read_full_file(filename
, &contents
, NULL
);
2054 n
= strv_length(keys
);
2055 if (n
== 0) /* No keys to retrieve? That's easy, we are done then */
2058 /* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
2059 v
= newa0(char*, n
);
2061 for (p
= contents
; *p
;) {
2062 const char *w
= NULL
;
2064 for (i
= 0; i
< n
; i
++)
2066 w
= first_word(p
, keys
[i
]);
2074 l
= strcspn(w
, NEWLINE
);
2075 v
[i
] = strndup(w
, l
);
2087 p
+= strcspn(p
, NEWLINE
);
2089 p
+= strspn(p
, NEWLINE
);
2095 for (i
= 0; i
< n
; i
++)
2101 memcpy(ret_values
, v
, sizeof(char*) * n
);
2106 int cg_create_everywhere(CGroupMask supported
, CGroupMask mask
, const char *path
) {
2111 /* This one will create a cgroup in our private tree, but also
2112 * duplicate it in the trees specified in mask, and remove it
2115 * Returns 0 if the group already existed in the systemd hierarchy,
2116 * 1 on success, negative otherwise.
2119 /* First create the cgroup in our own hierarchy. */
2120 r
= cg_create(SYSTEMD_CGROUP_CONTROLLER
, path
);
2125 /* If we are in the unified hierarchy, we are done now */
2126 r
= cg_all_unified();
2132 /* Otherwise, do the same in the other hierarchies */
2133 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2134 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2137 n
= cgroup_controller_to_string(c
);
2140 (void) cg_create(n
, path
);
2141 else if (supported
& bit
)
2142 (void) cg_trim(n
, path
, true);
2148 int cg_attach_everywhere(CGroupMask supported
, const char *path
, pid_t pid
, cg_migrate_callback_t path_callback
, void *userdata
) {
2152 r
= cg_attach(SYSTEMD_CGROUP_CONTROLLER
, path
, pid
);
2156 r
= cg_all_unified();
2162 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2163 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2164 const char *p
= NULL
;
2166 if (!(supported
& bit
))
2170 p
= path_callback(bit
, userdata
);
2175 (void) cg_attach_fallback(cgroup_controller_to_string(c
), p
, pid
);
2181 int cg_attach_many_everywhere(CGroupMask supported
, const char *path
, Set
* pids
, cg_migrate_callback_t path_callback
, void *userdata
) {
2186 SET_FOREACH(pidp
, pids
, i
) {
2187 pid_t pid
= PTR_TO_PID(pidp
);
2190 q
= cg_attach_everywhere(supported
, path
, pid
, path_callback
, userdata
);
2191 if (q
< 0 && r
>= 0)
2198 int cg_migrate_everywhere(CGroupMask supported
, const char *from
, const char *to
, cg_migrate_callback_t to_callback
, void *userdata
) {
2202 if (!path_equal(from
, to
)) {
2203 r
= cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER
, from
, SYSTEMD_CGROUP_CONTROLLER
, to
, CGROUP_REMOVE
);
2208 q
= cg_all_unified();
2214 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2215 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2216 const char *p
= NULL
;
2218 if (!(supported
& bit
))
2222 p
= to_callback(bit
, userdata
);
2227 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER
, to
, cgroup_controller_to_string(c
), p
, 0);
2233 int cg_trim_everywhere(CGroupMask supported
, const char *path
, bool delete_root
) {
2237 r
= cg_trim(SYSTEMD_CGROUP_CONTROLLER
, path
, delete_root
);
2241 q
= cg_all_unified();
2247 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2248 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2250 if (!(supported
& bit
))
2253 (void) cg_trim(cgroup_controller_to_string(c
), path
, delete_root
);
2259 int cg_mask_to_string(CGroupMask mask
, char **ret
) {
2260 _cleanup_free_
char *s
= NULL
;
2261 size_t n
= 0, allocated
= 0;
2272 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2276 if (!(mask
& CGROUP_CONTROLLER_TO_MASK(c
)))
2279 k
= cgroup_controller_to_string(c
);
2282 if (!GREEDY_REALLOC(s
, allocated
, n
+ space
+ l
+ 1))
2287 memcpy(s
+ n
+ space
, k
, l
);
2301 int cg_mask_from_string(const char *value
, CGroupMask
*mask
) {
2306 _cleanup_free_
char *n
= NULL
;
2310 r
= extract_first_word(&value
, &n
, NULL
, 0);
2316 v
= cgroup_controller_from_string(n
);
2320 *mask
|= CGROUP_CONTROLLER_TO_MASK(v
);
2325 int cg_mask_supported(CGroupMask
*ret
) {
2326 CGroupMask mask
= 0;
2329 /* Determines the mask of supported cgroup controllers. Only
2330 * includes controllers we can make sense of and that are
2331 * actually accessible. */
2333 r
= cg_all_unified();
2337 _cleanup_free_
char *root
= NULL
, *controllers
= NULL
, *path
= NULL
;
2339 /* In the unified hierarchy we can read the supported
2340 * and accessible controllers from a the top-level
2341 * cgroup attribute */
2343 r
= cg_get_root_path(&root
);
2347 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, root
, "cgroup.controllers", &path
);
2351 r
= read_one_line_file(path
, &controllers
);
2355 r
= cg_mask_from_string(controllers
, &mask
);
2359 /* Currently, we support the cpu, memory, io and pids
2360 * controller in the unified hierarchy, mask
2361 * everything else off. */
2362 mask
&= CGROUP_MASK_CPU
| CGROUP_MASK_MEMORY
| CGROUP_MASK_IO
| CGROUP_MASK_PIDS
;
2367 /* In the legacy hierarchy, we check whether which
2368 * hierarchies are mounted. */
2370 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2373 n
= cgroup_controller_to_string(c
);
2374 if (controller_is_accessible(n
) >= 0)
2375 mask
|= CGROUP_CONTROLLER_TO_MASK(c
);
2383 int cg_kernel_controllers(Set
**ret
) {
2384 _cleanup_set_free_free_ Set
*controllers
= NULL
;
2385 _cleanup_fclose_
FILE *f
= NULL
;
2390 /* Determines the full list of kernel-known controllers. Might
2391 * include controllers we don't actually support, arbitrary
2392 * named hierarchies and controllers that aren't currently
2393 * accessible (because not mounted). */
2395 controllers
= set_new(&string_hash_ops
);
2399 f
= fopen("/proc/cgroups", "re");
2401 if (errno
== ENOENT
) {
2409 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
2411 /* Ignore the header line */
2412 (void) read_line(f
, (size_t) -1, NULL
);
2419 if (fscanf(f
, "%ms %*i %*i %i", &controller
, &enabled
) != 2) {
2424 if (ferror(f
) && errno
> 0)
2435 if (!cg_controller_is_valid(controller
)) {
2440 r
= set_consume(controllers
, controller
);
2445 *ret
= TAKE_PTR(controllers
);
2450 static thread_local CGroupUnified unified_cache
= CGROUP_UNIFIED_UNKNOWN
;
2452 /* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This
2453 * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2454 * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2455 * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
2457 * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
2458 * process management but disable the compat dual layout, we return %true on
2459 * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
2461 static thread_local
bool unified_systemd_v232
;
2463 static int cg_unified_update(void) {
2467 /* Checks if we support the unified hierarchy. Returns an
2468 * error when the cgroup hierarchies aren't mounted yet or we
2469 * have any other trouble determining if the unified hierarchy
2472 if (unified_cache
>= CGROUP_UNIFIED_NONE
)
2475 if (statfs("/sys/fs/cgroup/", &fs
) < 0)
2476 return log_debug_errno(errno
, "statfs(\"/sys/fs/cgroup/\") failed: %m");
2478 if (F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
)) {
2479 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
2480 unified_cache
= CGROUP_UNIFIED_ALL
;
2481 } else if (F_TYPE_EQUAL(fs
.f_type
, TMPFS_MAGIC
)) {
2482 if (statfs("/sys/fs/cgroup/unified/", &fs
) == 0 &&
2483 F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
)) {
2484 log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
2485 unified_cache
= CGROUP_UNIFIED_SYSTEMD
;
2486 unified_systemd_v232
= false;
2488 if (statfs("/sys/fs/cgroup/systemd/", &fs
) < 0)
2489 return log_debug_errno(errno
, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
2491 if (F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
)) {
2492 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
2493 unified_cache
= CGROUP_UNIFIED_SYSTEMD
;
2494 unified_systemd_v232
= true;
2495 } else if (F_TYPE_EQUAL(fs
.f_type
, CGROUP_SUPER_MAGIC
)) {
2496 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
2497 unified_cache
= CGROUP_UNIFIED_NONE
;
2499 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
2500 (unsigned long long) fs
.f_type
);
2501 unified_cache
= CGROUP_UNIFIED_NONE
;
2505 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2506 (unsigned long long) fs
.f_type
);
2513 int cg_unified_controller(const char *controller
) {
2516 r
= cg_unified_update();
2520 if (unified_cache
== CGROUP_UNIFIED_NONE
)
2523 if (unified_cache
>= CGROUP_UNIFIED_ALL
)
2526 return streq_ptr(controller
, SYSTEMD_CGROUP_CONTROLLER
);
2529 int cg_all_unified(void) {
2532 r
= cg_unified_update();
2536 return unified_cache
>= CGROUP_UNIFIED_ALL
;
2539 int cg_hybrid_unified(void) {
2542 r
= cg_unified_update();
2546 return unified_cache
== CGROUP_UNIFIED_SYSTEMD
&& !unified_systemd_v232
;
2549 int cg_unified_flush(void) {
2550 unified_cache
= CGROUP_UNIFIED_UNKNOWN
;
2552 return cg_unified_update();
2555 int cg_enable_everywhere(CGroupMask supported
, CGroupMask mask
, const char *p
) {
2556 _cleanup_fclose_
FILE *f
= NULL
;
2557 _cleanup_free_
char *fs
= NULL
;
2566 r
= cg_all_unified();
2569 if (r
== 0) /* on the legacy hiearchy there's no joining of controllers defined */
2572 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, p
, "cgroup.subtree_control", &fs
);
2576 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2577 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2580 if (!(supported
& bit
))
2583 n
= cgroup_controller_to_string(c
);
2585 char s
[1 + strlen(n
) + 1];
2587 s
[0] = mask
& bit
? '+' : '-';
2591 f
= fopen(fs
, "we");
2593 log_debug_errno(errno
, "Failed to open cgroup.subtree_control file of %s: %m", p
);
2598 r
= write_string_stream(f
, s
, 0);
2600 log_debug_errno(r
, "Failed to enable controller %s for %s (%s): %m", n
, p
, fs
);
2609 bool cg_is_unified_wanted(void) {
2610 static thread_local
int wanted
= -1;
2613 const bool is_default
= DEFAULT_HIERARCHY
== CGROUP_UNIFIED_ALL
;
2615 /* If we have a cached value, return that. */
2619 /* If the hierarchy is already mounted, then follow whatever
2620 * was chosen for it. */
2621 if (cg_unified_flush() >= 0)
2622 return (wanted
= unified_cache
>= CGROUP_UNIFIED_ALL
);
2624 /* Otherwise, let's see what the kernel command line has to say.
2625 * Since checking is expensive, cache a non-error result. */
2626 r
= proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b
);
2628 return (wanted
= r
> 0 ? b
: is_default
);
2631 bool cg_is_legacy_wanted(void) {
2632 static thread_local
int wanted
= -1;
2634 /* If we have a cached value, return that. */
2638 /* Check if we have cgroups2 already mounted. */
2639 if (cg_unified_flush() >= 0 &&
2640 unified_cache
== CGROUP_UNIFIED_ALL
)
2641 return (wanted
= false);
2643 /* Otherwise, assume that at least partial legacy is wanted,
2644 * since cgroups2 should already be mounted at this point. */
2645 return (wanted
= true);
2648 bool cg_is_hybrid_wanted(void) {
2649 static thread_local
int wanted
= -1;
2652 const bool is_default
= DEFAULT_HIERARCHY
>= CGROUP_UNIFIED_SYSTEMD
;
2653 /* We default to true if the default is "hybrid", obviously,
2654 * but also when the default is "unified", because if we get
2655 * called, it means that unified hierarchy was not mounted. */
2657 /* If we have a cached value, return that. */
2661 /* If the hierarchy is already mounted, then follow whatever
2662 * was chosen for it. */
2663 if (cg_unified_flush() >= 0 &&
2664 unified_cache
== CGROUP_UNIFIED_ALL
)
2665 return (wanted
= false);
2667 /* Otherwise, let's see what the kernel command line has to say.
2668 * Since checking is expensive, cache a non-error result. */
2669 r
= proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b
);
2671 /* The meaning of the kernel option is reversed wrt. to the return value
2672 * of this function, hence the negation. */
2673 return (wanted
= r
> 0 ? !b
: is_default
);
2676 int cg_weight_parse(const char *s
, uint64_t *ret
) {
2681 *ret
= CGROUP_WEIGHT_INVALID
;
2685 r
= safe_atou64(s
, &u
);
2689 if (u
< CGROUP_WEIGHT_MIN
|| u
> CGROUP_WEIGHT_MAX
)
2696 const uint64_t cgroup_io_limit_defaults
[_CGROUP_IO_LIMIT_TYPE_MAX
] = {
2697 [CGROUP_IO_RBPS_MAX
] = CGROUP_LIMIT_MAX
,
2698 [CGROUP_IO_WBPS_MAX
] = CGROUP_LIMIT_MAX
,
2699 [CGROUP_IO_RIOPS_MAX
] = CGROUP_LIMIT_MAX
,
2700 [CGROUP_IO_WIOPS_MAX
] = CGROUP_LIMIT_MAX
,
2703 static const char* const cgroup_io_limit_type_table
[_CGROUP_IO_LIMIT_TYPE_MAX
] = {
2704 [CGROUP_IO_RBPS_MAX
] = "IOReadBandwidthMax",
2705 [CGROUP_IO_WBPS_MAX
] = "IOWriteBandwidthMax",
2706 [CGROUP_IO_RIOPS_MAX
] = "IOReadIOPSMax",
2707 [CGROUP_IO_WIOPS_MAX
] = "IOWriteIOPSMax",
2710 DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type
, CGroupIOLimitType
);
2712 int cg_cpu_shares_parse(const char *s
, uint64_t *ret
) {
2717 *ret
= CGROUP_CPU_SHARES_INVALID
;
2721 r
= safe_atou64(s
, &u
);
2725 if (u
< CGROUP_CPU_SHARES_MIN
|| u
> CGROUP_CPU_SHARES_MAX
)
2732 int cg_blkio_weight_parse(const char *s
, uint64_t *ret
) {
2737 *ret
= CGROUP_BLKIO_WEIGHT_INVALID
;
2741 r
= safe_atou64(s
, &u
);
2745 if (u
< CGROUP_BLKIO_WEIGHT_MIN
|| u
> CGROUP_BLKIO_WEIGHT_MAX
)
2752 bool is_cgroup_fs(const struct statfs
*s
) {
2753 return is_fs_type(s
, CGROUP_SUPER_MAGIC
) ||
2754 is_fs_type(s
, CGROUP2_SUPER_MAGIC
);
2757 bool fd_is_cgroup_fs(int fd
) {
2760 if (fstatfs(fd
, &s
) < 0)
2763 return is_cgroup_fs(&s
);
2766 static const char *cgroup_controller_table
[_CGROUP_CONTROLLER_MAX
] = {
2767 [CGROUP_CONTROLLER_CPU
] = "cpu",
2768 [CGROUP_CONTROLLER_CPUACCT
] = "cpuacct",
2769 [CGROUP_CONTROLLER_IO
] = "io",
2770 [CGROUP_CONTROLLER_BLKIO
] = "blkio",
2771 [CGROUP_CONTROLLER_MEMORY
] = "memory",
2772 [CGROUP_CONTROLLER_DEVICES
] = "devices",
2773 [CGROUP_CONTROLLER_PIDS
] = "pids",
2776 DEFINE_STRING_TABLE_LOOKUP(cgroup_controller
, CGroupController
);