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/>.
30 #include <sys/statfs.h>
31 #include <sys/types.h>
32 #include <sys/xattr.h>
35 #include "alloc-util.h"
36 #include "cgroup-util.h"
38 #include "dirent-util.h"
39 #include "extract-word.h"
42 #include "format-util.h"
45 #include "login-util.h"
49 #include "parse-util.h"
50 #include "path-util.h"
51 #include "proc-cmdline.h"
52 #include "process-util.h"
55 #include "stat-util.h"
56 #include "stdio-util.h"
57 #include "string-table.h"
58 #include "string-util.h"
60 #include "unit-name.h"
61 #include "user-util.h"
63 int cg_enumerate_processes(const char *controller
, const char *path
, FILE **_f
) {
64 _cleanup_free_
char *fs
= NULL
;
70 r
= cg_get_path(controller
, path
, "cgroup.procs", &fs
);
82 int cg_read_pid(FILE *f
, pid_t
*_pid
) {
85 /* Note that the cgroup.procs might contain duplicates! See
86 * cgroups.txt for details. */
92 if (fscanf(f
, "%lu", &ul
) != 1) {
97 return errno
> 0 ? -errno
: -EIO
;
108 const char *controller
,
113 _cleanup_free_
char *events
= NULL
, *content
= NULL
;
117 r
= cg_get_path(controller
, path
, "cgroup.events", &events
);
121 r
= read_full_file(events
, &content
, NULL
);
126 while ((line
= strsep(&p
, "\n"))) {
129 key
= strsep(&line
, " ");
133 if (strcmp(key
, event
))
143 bool cg_ns_supported(void) {
144 static thread_local
int enabled
= -1;
149 if (access("/proc/self/ns/cgroup", F_OK
) == 0)
157 int cg_enumerate_subgroups(const char *controller
, const char *path
, DIR **_d
) {
158 _cleanup_free_
char *fs
= NULL
;
164 /* This is not recursive! */
166 r
= cg_get_path(controller
, path
, NULL
, &fs
);
178 int cg_read_subgroup(DIR *d
, char **fn
) {
184 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
187 if (de
->d_type
!= DT_DIR
)
190 if (dot_or_dot_dot(de
->d_name
))
193 b
= strdup(de
->d_name
);
204 int cg_rmdir(const char *controller
, const char *path
) {
205 _cleanup_free_
char *p
= NULL
;
208 r
= cg_get_path(controller
, path
, NULL
, &p
);
213 if (r
< 0 && errno
!= ENOENT
)
216 r
= cg_hybrid_unified();
222 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
223 r
= cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
);
225 log_warning_errno(r
, "Failed to remove compat systemd cgroup %s: %m", path
);
232 const char *controller
,
237 cg_kill_log_func_t log_kill
,
240 _cleanup_set_free_ Set
*allocated_set
= NULL
;
247 /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send
248 * SIGCONT on SIGKILL. */
249 if (IN_SET(sig
, SIGCONT
, SIGKILL
))
250 flags
&= ~CGROUP_SIGCONT
;
252 /* This goes through the tasks list and kills them all. This
253 * is repeated until no further processes are added to the
254 * tasks list, to properly handle forking processes */
257 s
= allocated_set
= set_new(NULL
);
262 my_pid
= getpid_cached();
265 _cleanup_fclose_
FILE *f
= NULL
;
269 r
= cg_enumerate_processes(controller
, path
, &f
);
271 if (ret
>= 0 && r
!= -ENOENT
)
277 while ((r
= cg_read_pid(f
, &pid
)) > 0) {
279 if ((flags
& CGROUP_IGNORE_SELF
) && pid
== my_pid
)
282 if (set_get(s
, PID_TO_PTR(pid
)) == PID_TO_PTR(pid
))
286 log_kill(pid
, sig
, userdata
);
288 /* If we haven't killed this process yet, kill
290 if (kill(pid
, sig
) < 0) {
291 if (ret
>= 0 && errno
!= ESRCH
)
294 if (flags
& CGROUP_SIGCONT
)
295 (void) kill(pid
, SIGCONT
);
303 r
= set_put(s
, PID_TO_PTR(pid
));
319 /* To avoid racing against processes which fork
320 * quicker than we can kill them we repeat this until
321 * no new pids need to be killed. */
328 int cg_kill_recursive(
329 const char *controller
,
334 cg_kill_log_func_t log_kill
,
337 _cleanup_set_free_ Set
*allocated_set
= NULL
;
338 _cleanup_closedir_
DIR *d
= NULL
;
346 s
= allocated_set
= set_new(NULL
);
351 ret
= cg_kill(controller
, path
, sig
, flags
, s
, log_kill
, userdata
);
353 r
= cg_enumerate_subgroups(controller
, path
, &d
);
355 if (ret
>= 0 && r
!= -ENOENT
)
361 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
362 _cleanup_free_
char *p
= NULL
;
364 p
= strjoin(path
, "/", fn
);
369 r
= cg_kill_recursive(controller
, p
, sig
, flags
, s
, log_kill
, userdata
);
370 if (r
!= 0 && ret
>= 0)
373 if (ret
>= 0 && r
< 0)
376 if (flags
& CGROUP_REMOVE
) {
377 r
= cg_rmdir(controller
, path
);
378 if (r
< 0 && ret
>= 0 && !IN_SET(r
, -ENOENT
, -EBUSY
))
393 _cleanup_set_free_ Set
*s
= NULL
;
406 my_pid
= getpid_cached();
409 _cleanup_fclose_
FILE *f
= NULL
;
413 r
= cg_enumerate_processes(cfrom
, pfrom
, &f
);
415 if (ret
>= 0 && r
!= -ENOENT
)
421 while ((r
= cg_read_pid(f
, &pid
)) > 0) {
423 /* This might do weird stuff if we aren't a
424 * single-threaded program. However, we
425 * luckily know we are not */
426 if ((flags
& CGROUP_IGNORE_SELF
) && pid
== my_pid
)
429 if (set_get(s
, PID_TO_PTR(pid
)) == PID_TO_PTR(pid
))
432 /* Ignore kernel threads. Since they can only
433 * exist in the root cgroup, we only check for
436 (isempty(pfrom
) || path_equal(pfrom
, "/")) &&
437 is_kernel_thread(pid
) > 0)
440 r
= cg_attach(cto
, pto
, pid
);
442 if (ret
>= 0 && r
!= -ESRCH
)
449 r
= set_put(s
, PID_TO_PTR(pid
));
469 int cg_migrate_recursive(
476 _cleanup_closedir_
DIR *d
= NULL
;
485 ret
= cg_migrate(cfrom
, pfrom
, cto
, pto
, flags
);
487 r
= cg_enumerate_subgroups(cfrom
, pfrom
, &d
);
489 if (ret
>= 0 && r
!= -ENOENT
)
495 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
496 _cleanup_free_
char *p
= NULL
;
498 p
= strjoin(pfrom
, "/", fn
);
503 r
= cg_migrate_recursive(cfrom
, p
, cto
, pto
, flags
);
504 if (r
!= 0 && ret
>= 0)
508 if (r
< 0 && ret
>= 0)
511 if (flags
& CGROUP_REMOVE
) {
512 r
= cg_rmdir(cfrom
, pfrom
);
513 if (r
< 0 && ret
>= 0 && !IN_SET(r
, -ENOENT
, -EBUSY
))
520 int cg_migrate_recursive_fallback(
534 r
= cg_migrate_recursive(cfrom
, pfrom
, cto
, pto
, flags
);
536 char prefix
[strlen(pto
) + 1];
538 /* This didn't work? Then let's try all prefixes of the destination */
540 PATH_FOREACH_PREFIX(prefix
, pto
) {
543 q
= cg_migrate_recursive(cfrom
, pfrom
, cto
, prefix
, flags
);
552 static const char *controller_to_dirname(const char *controller
) {
557 /* Converts a controller name to the directory name below
558 * /sys/fs/cgroup/ we want to mount it to. Effectively, this
559 * just cuts off the name= prefixed used for named
560 * hierarchies, if it is specified. */
562 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
563 if (cg_hybrid_unified() > 0)
564 controller
= SYSTEMD_CGROUP_CONTROLLER_HYBRID
;
566 controller
= SYSTEMD_CGROUP_CONTROLLER_LEGACY
;
569 e
= startswith(controller
, "name=");
576 static int join_path_legacy(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
583 dn
= controller_to_dirname(controller
);
585 if (isempty(path
) && isempty(suffix
))
586 t
= strappend("/sys/fs/cgroup/", dn
);
587 else if (isempty(path
))
588 t
= strjoin("/sys/fs/cgroup/", dn
, "/", suffix
);
589 else if (isempty(suffix
))
590 t
= strjoin("/sys/fs/cgroup/", dn
, "/", path
);
592 t
= strjoin("/sys/fs/cgroup/", dn
, "/", path
, "/", suffix
);
600 static int join_path_unified(const char *path
, const char *suffix
, char **fs
) {
605 if (isempty(path
) && isempty(suffix
))
606 t
= strdup("/sys/fs/cgroup");
607 else if (isempty(path
))
608 t
= strappend("/sys/fs/cgroup/", suffix
);
609 else if (isempty(suffix
))
610 t
= strappend("/sys/fs/cgroup/", path
);
612 t
= strjoin("/sys/fs/cgroup/", path
, "/", suffix
);
620 int cg_get_path(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
628 /* If no controller is specified, we return the path
629 * *below* the controllers, without any prefix. */
631 if (!path
&& !suffix
)
639 t
= strjoin(path
, "/", suffix
);
643 *fs
= path_kill_slashes(t
);
647 if (!cg_controller_is_valid(controller
))
650 r
= cg_all_unified();
654 r
= join_path_unified(path
, suffix
, fs
);
656 r
= join_path_legacy(controller
, path
, suffix
, fs
);
660 path_kill_slashes(*fs
);
664 static int controller_is_accessible(const char *controller
) {
669 /* Checks whether a specific controller is accessible,
670 * i.e. its hierarchy mounted. In the unified hierarchy all
671 * controllers are considered accessible, except for the named
674 if (!cg_controller_is_valid(controller
))
677 r
= cg_all_unified();
681 /* We don't support named hierarchies if we are using
682 * the unified hierarchy. */
684 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
))
687 if (startswith(controller
, "name="))
693 dn
= controller_to_dirname(controller
);
694 cc
= strjoina("/sys/fs/cgroup/", dn
);
696 if (laccess(cc
, F_OK
) < 0)
703 int cg_get_path_and_check(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
709 /* Check if the specified controller is actually accessible */
710 r
= controller_is_accessible(controller
);
714 return cg_get_path(controller
, path
, suffix
, fs
);
717 static int trim_cb(const char *path
, const struct stat
*sb
, int typeflag
, struct FTW
*ftwbuf
) {
722 if (typeflag
!= FTW_DP
)
725 if (ftwbuf
->level
< 1)
732 int cg_trim(const char *controller
, const char *path
, bool delete_root
) {
733 _cleanup_free_
char *fs
= NULL
;
738 r
= cg_get_path(controller
, path
, NULL
, &fs
);
743 if (nftw(fs
, trim_cb
, 64, FTW_DEPTH
|FTW_MOUNT
|FTW_PHYS
) != 0) {
753 if (rmdir(fs
) < 0 && errno
!= ENOENT
)
757 q
= cg_hybrid_unified();
760 if (q
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
761 q
= cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, delete_root
);
763 log_warning_errno(q
, "Failed to trim compat systemd cgroup %s: %m", path
);
769 int cg_create(const char *controller
, const char *path
) {
770 _cleanup_free_
char *fs
= NULL
;
773 r
= cg_get_path_and_check(controller
, path
, NULL
, &fs
);
777 r
= mkdir_parents(fs
, 0755);
781 if (mkdir(fs
, 0755) < 0) {
789 r
= cg_hybrid_unified();
793 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
794 r
= cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
);
796 log_warning_errno(r
, "Failed to create compat systemd cgroup %s: %m", path
);
802 int cg_create_and_attach(const char *controller
, const char *path
, pid_t pid
) {
807 r
= cg_create(controller
, path
);
811 q
= cg_attach(controller
, path
, pid
);
815 /* This does not remove the cgroup on failure */
819 int cg_attach(const char *controller
, const char *path
, pid_t pid
) {
820 _cleanup_free_
char *fs
= NULL
;
821 char c
[DECIMAL_STR_MAX(pid_t
) + 2];
827 r
= cg_get_path_and_check(controller
, path
, "cgroup.procs", &fs
);
832 pid
= getpid_cached();
834 xsprintf(c
, PID_FMT
"\n", pid
);
836 r
= write_string_file(fs
, c
, 0);
840 r
= cg_hybrid_unified();
844 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
845 r
= cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, pid
);
847 log_warning_errno(r
, "Failed to attach "PID_FMT
" to compat systemd cgroup %s: %m", pid
, path
);
853 int cg_attach_fallback(const char *controller
, const char *path
, pid_t pid
) {
860 r
= cg_attach(controller
, path
, pid
);
862 char prefix
[strlen(path
) + 1];
864 /* This didn't work? Then let's try all prefixes of
867 PATH_FOREACH_PREFIX(prefix
, path
) {
870 q
= cg_attach(controller
, prefix
, pid
);
879 int cg_set_group_access(
880 const char *controller
,
886 _cleanup_free_
char *fs
= NULL
;
889 if (mode
== MODE_INVALID
&& uid
== UID_INVALID
&& gid
== GID_INVALID
)
892 if (mode
!= MODE_INVALID
)
895 r
= cg_get_path(controller
, path
, NULL
, &fs
);
899 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
903 r
= cg_hybrid_unified();
906 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
907 r
= cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, mode
, uid
, gid
);
909 log_debug_errno(r
, "Failed to set group access on compatibility systemd cgroup %s, ignoring: %m", path
);
915 int cg_set_task_access(
916 const char *controller
,
922 _cleanup_free_
char *fs
= NULL
;
927 if (mode
== MODE_INVALID
&& uid
== UID_INVALID
&& gid
== GID_INVALID
)
930 if (mode
!= MODE_INVALID
)
933 /* For both the legacy and unified hierarchies, "cgroup.procs" is the main entry point for PIDs */
934 r
= cg_get_path(controller
, path
, "cgroup.procs", &fs
);
938 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
942 r
= cg_unified_controller(controller
);
948 /* Compatibility: on cgroupsv1 always keep values for the legacy files "tasks" and
949 * "cgroup.clone_children" in sync with "cgroup.procs". Since this is legacy stuff, we don't care if
954 "cgroup.clone_children") {
958 r
= cg_get_path(controller
, path
, fn
, &fs
);
960 log_debug_errno(r
, "Failed to get path for %s of %s, ignoring: %m", fn
, path
);
962 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
964 log_debug_errno(r
, "Failed to to change ownership/access mode for %s of %s, ignoring: %m", fn
, path
);
967 /* On the unified controller, we want to permit subtree controllers too. */
970 r
= cg_get_path(controller
, path
, "cgroup.subtree_control", &fs
);
974 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
979 r
= cg_hybrid_unified();
982 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
983 /* Always propagate access mode from unified to legacy controller */
985 r
= cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, mode
, uid
, gid
);
987 log_debug_errno(r
, "Failed to set task access on compatibility systemd cgroup %s, ignoring: %m", path
);
993 int cg_set_xattr(const char *controller
, const char *path
, const char *name
, const void *value
, size_t size
, int flags
) {
994 _cleanup_free_
char *fs
= NULL
;
999 assert(value
|| size
<= 0);
1001 r
= cg_get_path(controller
, path
, NULL
, &fs
);
1005 if (setxattr(fs
, name
, value
, size
, flags
) < 0)
1011 int cg_get_xattr(const char *controller
, const char *path
, const char *name
, void *value
, size_t size
) {
1012 _cleanup_free_
char *fs
= NULL
;
1019 r
= cg_get_path(controller
, path
, NULL
, &fs
);
1023 n
= getxattr(fs
, name
, value
, size
);
1030 int cg_pid_get_path(const char *controller
, pid_t pid
, char **path
) {
1031 _cleanup_fclose_
FILE *f
= NULL
;
1032 char line
[LINE_MAX
];
1033 const char *fs
, *controller_str
;
1041 if (!cg_controller_is_valid(controller
))
1044 controller
= SYSTEMD_CGROUP_CONTROLLER
;
1046 unified
= cg_unified_controller(controller
);
1050 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
))
1051 controller_str
= SYSTEMD_CGROUP_CONTROLLER_LEGACY
;
1053 controller_str
= controller
;
1055 cs
= strlen(controller_str
);
1058 fs
= procfs_file_alloca(pid
, "cgroup");
1059 f
= fopen(fs
, "re");
1061 return errno
== ENOENT
? -ESRCH
: -errno
;
1063 FOREACH_LINE(line
, f
, return -errno
) {
1069 e
= startswith(line
, "0:");
1079 const char *word
, *state
;
1082 l
= strchr(line
, ':');
1092 FOREACH_WORD_SEPARATOR(word
, k
, l
, ",", state
) {
1093 if (k
== cs
&& memcmp(word
, controller_str
, cs
) == 0) {
1114 int cg_install_release_agent(const char *controller
, const char *agent
) {
1115 _cleanup_free_
char *fs
= NULL
, *contents
= NULL
;
1121 r
= cg_unified_controller(controller
);
1124 if (r
> 0) /* doesn't apply to unified hierarchy */
1127 r
= cg_get_path(controller
, NULL
, "release_agent", &fs
);
1131 r
= read_one_line_file(fs
, &contents
);
1135 sc
= strstrip(contents
);
1137 r
= write_string_file(fs
, agent
, 0);
1140 } else if (!path_equal(sc
, agent
))
1144 r
= cg_get_path(controller
, NULL
, "notify_on_release", &fs
);
1148 contents
= mfree(contents
);
1149 r
= read_one_line_file(fs
, &contents
);
1153 sc
= strstrip(contents
);
1154 if (streq(sc
, "0")) {
1155 r
= write_string_file(fs
, "1", 0);
1162 if (!streq(sc
, "1"))
1168 int cg_uninstall_release_agent(const char *controller
) {
1169 _cleanup_free_
char *fs
= NULL
;
1172 r
= cg_unified_controller(controller
);
1175 if (r
> 0) /* Doesn't apply to unified hierarchy */
1178 r
= cg_get_path(controller
, NULL
, "notify_on_release", &fs
);
1182 r
= write_string_file(fs
, "0", 0);
1188 r
= cg_get_path(controller
, NULL
, "release_agent", &fs
);
1192 r
= write_string_file(fs
, "", 0);
1199 int cg_is_empty(const char *controller
, const char *path
) {
1200 _cleanup_fclose_
FILE *f
= NULL
;
1206 r
= cg_enumerate_processes(controller
, path
, &f
);
1212 r
= cg_read_pid(f
, &pid
);
1219 int cg_is_empty_recursive(const char *controller
, const char *path
) {
1224 /* The root cgroup is always populated */
1225 if (controller
&& (isempty(path
) || path_equal(path
, "/")))
1228 r
= cg_unified_controller(controller
);
1232 _cleanup_free_
char *t
= NULL
;
1234 /* On the unified hierarchy we can check empty state
1235 * via the "populated" attribute of "cgroup.events". */
1237 r
= cg_read_event(controller
, path
, "populated", &t
);
1241 return streq(t
, "0");
1243 _cleanup_closedir_
DIR *d
= NULL
;
1246 r
= cg_is_empty(controller
, path
);
1250 r
= cg_enumerate_subgroups(controller
, path
, &d
);
1256 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
1257 _cleanup_free_
char *p
= NULL
;
1259 p
= strjoin(path
, "/", fn
);
1264 r
= cg_is_empty_recursive(controller
, p
);
1275 int cg_split_spec(const char *spec
, char **controller
, char **path
) {
1276 char *t
= NULL
, *u
= NULL
;
1282 if (!path_is_safe(spec
))
1290 *path
= path_kill_slashes(t
);
1299 e
= strchr(spec
, ':');
1301 if (!cg_controller_is_valid(spec
))
1318 t
= strndup(spec
, e
-spec
);
1321 if (!cg_controller_is_valid(t
)) {
1335 if (!path_is_safe(u
) ||
1336 !path_is_absolute(u
)) {
1342 path_kill_slashes(u
);
1358 int cg_mangle_path(const char *path
, char **result
) {
1359 _cleanup_free_
char *c
= NULL
, *p
= NULL
;
1366 /* First, check if it already is a filesystem path */
1367 if (path_startswith(path
, "/sys/fs/cgroup")) {
1373 *result
= path_kill_slashes(t
);
1377 /* Otherwise, treat it as cg spec */
1378 r
= cg_split_spec(path
, &c
, &p
);
1382 return cg_get_path(c
?: SYSTEMD_CGROUP_CONTROLLER
, p
?: "/", NULL
, result
);
1385 int cg_get_root_path(char **path
) {
1391 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, 1, &p
);
1395 e
= endswith(p
, "/" SPECIAL_INIT_SCOPE
);
1397 e
= endswith(p
, "/" SPECIAL_SYSTEM_SLICE
); /* legacy */
1399 e
= endswith(p
, "/system"); /* even more legacy */
1407 int cg_shift_path(const char *cgroup
, const char *root
, const char **shifted
) {
1408 _cleanup_free_
char *rt
= NULL
;
1416 /* If the root was specified let's use that, otherwise
1417 * let's determine it from PID 1 */
1419 r
= cg_get_root_path(&rt
);
1426 p
= path_startswith(cgroup
, root
);
1427 if (p
&& p
> cgroup
)
1435 int cg_pid_get_path_shifted(pid_t pid
, const char *root
, char **cgroup
) {
1436 _cleanup_free_
char *raw
= NULL
;
1443 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, pid
, &raw
);
1447 r
= cg_shift_path(raw
, root
, &c
);
1467 int cg_path_decode_unit(const char *cgroup
, char **unit
) {
1474 n
= strcspn(cgroup
, "/");
1478 c
= strndupa(cgroup
, n
);
1481 if (!unit_name_is_valid(c
, UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
))
1492 static bool valid_slice_name(const char *p
, size_t n
) {
1497 if (n
< strlen("x.slice"))
1500 if (memcmp(p
+ n
- 6, ".slice", 6) == 0) {
1506 c
= cg_unescape(buf
);
1508 return unit_name_is_valid(c
, UNIT_NAME_PLAIN
);
1514 static const char *skip_slices(const char *p
) {
1517 /* Skips over all slice assignments */
1522 p
+= strspn(p
, "/");
1524 n
= strcspn(p
, "/");
1525 if (!valid_slice_name(p
, n
))
1532 int cg_path_get_unit(const char *path
, char **ret
) {
1540 e
= skip_slices(path
);
1542 r
= cg_path_decode_unit(e
, &unit
);
1546 /* We skipped over the slices, don't accept any now */
1547 if (endswith(unit
, ".slice")) {
1556 int cg_pid_get_unit(pid_t pid
, char **unit
) {
1557 _cleanup_free_
char *cgroup
= NULL
;
1562 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1566 return cg_path_get_unit(cgroup
, unit
);
1570 * Skip session-*.scope, but require it to be there.
1572 static const char *skip_session(const char *p
) {
1578 p
+= strspn(p
, "/");
1580 n
= strcspn(p
, "/");
1581 if (n
< strlen("session-x.scope"))
1584 if (memcmp(p
, "session-", 8) == 0 && memcmp(p
+ n
- 6, ".scope", 6) == 0) {
1585 char buf
[n
- 8 - 6 + 1];
1587 memcpy(buf
, p
+ 8, n
- 8 - 6);
1590 /* Note that session scopes 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 (!session_id_valid(buf
))
1599 p
+= strspn(p
, "/");
1607 * Skip user@*.service, but require it to be there.
1609 static const char *skip_user_manager(const char *p
) {
1615 p
+= strspn(p
, "/");
1617 n
= strcspn(p
, "/");
1618 if (n
< strlen("user@x.service"))
1621 if (memcmp(p
, "user@", 5) == 0 && memcmp(p
+ n
- 8, ".service", 8) == 0) {
1622 char buf
[n
- 5 - 8 + 1];
1624 memcpy(buf
, p
+ 5, n
- 5 - 8);
1627 /* Note that user manager services never need unescaping,
1628 * since they cannot conflict with the kernel's own
1629 * names, hence we don't need to call cg_unescape()
1632 if (parse_uid(buf
, NULL
) < 0)
1636 p
+= strspn(p
, "/");
1644 static const char *skip_user_prefix(const char *path
) {
1649 /* Skip slices, if there are any */
1650 e
= skip_slices(path
);
1652 /* Skip the user manager, if it's in the path now... */
1653 t
= skip_user_manager(e
);
1657 /* Alternatively skip the user session if it is in the path... */
1658 return skip_session(e
);
1661 int cg_path_get_user_unit(const char *path
, char **ret
) {
1667 t
= skip_user_prefix(path
);
1671 /* And from here on it looks pretty much the same as for a
1672 * system unit, hence let's use the same parser from here
1674 return cg_path_get_unit(t
, ret
);
1677 int cg_pid_get_user_unit(pid_t pid
, char **unit
) {
1678 _cleanup_free_
char *cgroup
= NULL
;
1683 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1687 return cg_path_get_user_unit(cgroup
, unit
);
1690 int cg_path_get_machine_name(const char *path
, char **machine
) {
1691 _cleanup_free_
char *u
= NULL
;
1695 r
= cg_path_get_unit(path
, &u
);
1699 sl
= strjoina("/run/systemd/machines/unit:", u
);
1700 return readlink_malloc(sl
, machine
);
1703 int cg_pid_get_machine_name(pid_t pid
, char **machine
) {
1704 _cleanup_free_
char *cgroup
= NULL
;
1709 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1713 return cg_path_get_machine_name(cgroup
, machine
);
1716 int cg_path_get_session(const char *path
, char **session
) {
1717 _cleanup_free_
char *unit
= NULL
;
1723 r
= cg_path_get_unit(path
, &unit
);
1727 start
= startswith(unit
, "session-");
1730 end
= endswith(start
, ".scope");
1735 if (!session_id_valid(start
))
1751 int cg_pid_get_session(pid_t pid
, char **session
) {
1752 _cleanup_free_
char *cgroup
= NULL
;
1755 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1759 return cg_path_get_session(cgroup
, session
);
1762 int cg_path_get_owner_uid(const char *path
, uid_t
*uid
) {
1763 _cleanup_free_
char *slice
= NULL
;
1769 r
= cg_path_get_slice(path
, &slice
);
1773 start
= startswith(slice
, "user-");
1776 end
= endswith(start
, ".slice");
1781 if (parse_uid(start
, uid
) < 0)
1787 int cg_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
1788 _cleanup_free_
char *cgroup
= NULL
;
1791 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1795 return cg_path_get_owner_uid(cgroup
, uid
);
1798 int cg_path_get_slice(const char *p
, char **slice
) {
1799 const char *e
= NULL
;
1804 /* Finds the right-most slice unit from the beginning, but
1805 * stops before we come to the first non-slice unit. */
1810 p
+= strspn(p
, "/");
1812 n
= strcspn(p
, "/");
1813 if (!valid_slice_name(p
, n
)) {
1818 s
= strdup(SPECIAL_ROOT_SLICE
);
1826 return cg_path_decode_unit(e
, slice
);
1834 int cg_pid_get_slice(pid_t pid
, char **slice
) {
1835 _cleanup_free_
char *cgroup
= NULL
;
1840 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1844 return cg_path_get_slice(cgroup
, slice
);
1847 int cg_path_get_user_slice(const char *p
, char **slice
) {
1852 t
= skip_user_prefix(p
);
1856 /* And now it looks pretty much the same as for a system
1857 * slice, so let's just use the same parser from here on. */
1858 return cg_path_get_slice(t
, slice
);
1861 int cg_pid_get_user_slice(pid_t pid
, char **slice
) {
1862 _cleanup_free_
char *cgroup
= NULL
;
1867 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1871 return cg_path_get_user_slice(cgroup
, slice
);
1874 char *cg_escape(const char *p
) {
1875 bool need_prefix
= false;
1877 /* This implements very minimal escaping for names to be used
1878 * as file names in the cgroup tree: any name which might
1879 * conflict with a kernel name or is prefixed with '_' is
1880 * prefixed with a '_'. That way, when reading cgroup names it
1881 * is sufficient to remove a single prefixing underscore if
1884 /* The return value of this function (unlike cg_unescape())
1887 if (IN_SET(p
[0], 0, '_', '.') ||
1888 streq(p
, "notify_on_release") ||
1889 streq(p
, "release_agent") ||
1890 streq(p
, "tasks") ||
1891 startswith(p
, "cgroup."))
1896 dot
= strrchr(p
, '.');
1901 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
1904 n
= cgroup_controller_to_string(c
);
1909 if (memcmp(p
, n
, l
) != 0)
1919 return strappend("_", p
);
1924 char *cg_unescape(const char *p
) {
1927 /* The return value of this function (unlike cg_escape())
1928 * doesn't need free()! */
1936 #define CONTROLLER_VALID \
1940 bool cg_controller_is_valid(const char *p
) {
1946 if (streq(p
, SYSTEMD_CGROUP_CONTROLLER
))
1949 s
= startswith(p
, "name=");
1953 if (IN_SET(*p
, 0, '_'))
1956 for (t
= p
; *t
; t
++)
1957 if (!strchr(CONTROLLER_VALID
, *t
))
1960 if (t
- p
> FILENAME_MAX
)
1966 int cg_slice_to_path(const char *unit
, char **ret
) {
1967 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *e
= NULL
;
1974 if (streq(unit
, SPECIAL_ROOT_SLICE
)) {
1984 if (!unit_name_is_valid(unit
, UNIT_NAME_PLAIN
))
1987 if (!endswith(unit
, ".slice"))
1990 r
= unit_name_to_prefix(unit
, &p
);
1994 dash
= strchr(p
, '-');
1996 /* Don't allow initial dashes */
2001 _cleanup_free_
char *escaped
= NULL
;
2002 char n
[dash
- p
+ sizeof(".slice")];
2004 /* Don't allow trailing or double dashes */
2005 if (IN_SET(dash
[1], 0, '-'))
2008 strcpy(stpncpy(n
, p
, dash
- p
), ".slice");
2009 if (!unit_name_is_valid(n
, UNIT_NAME_PLAIN
))
2012 escaped
= cg_escape(n
);
2016 if (!strextend(&s
, escaped
, "/", NULL
))
2019 dash
= strchr(dash
+1, '-');
2022 e
= cg_escape(unit
);
2026 if (!strextend(&s
, e
, NULL
))
2035 int cg_set_attribute(const char *controller
, const char *path
, const char *attribute
, const char *value
) {
2036 _cleanup_free_
char *p
= NULL
;
2039 r
= cg_get_path(controller
, path
, attribute
, &p
);
2043 return write_string_file(p
, value
, 0);
2046 int cg_get_attribute(const char *controller
, const char *path
, const char *attribute
, char **ret
) {
2047 _cleanup_free_
char *p
= NULL
;
2050 r
= cg_get_path(controller
, path
, attribute
, &p
);
2054 return read_one_line_file(p
, ret
);
2057 int cg_get_keyed_attribute(const char *controller
, const char *path
, const char *attribute
, const char **keys
, char **values
) {
2058 _cleanup_free_
char *filename
= NULL
, *content
= NULL
;
2062 for (i
= 0; keys
[i
]; i
++)
2065 r
= cg_get_path(controller
, path
, attribute
, &filename
);
2069 r
= read_full_file(filename
, &content
, NULL
);
2074 while ((line
= strsep(&p
, "\n"))) {
2077 key
= strsep(&line
, " ");
2079 for (i
= 0; keys
[i
]; i
++) {
2080 if (streq(key
, keys
[i
])) {
2081 values
[i
] = strdup(line
);
2087 for (i
= 0; keys
[i
]; i
++) {
2089 for (i
= 0; keys
[i
]; i
++) {
2100 int cg_create_everywhere(CGroupMask supported
, CGroupMask mask
, const char *path
) {
2104 /* This one will create a cgroup in our private tree, but also
2105 * duplicate it in the trees specified in mask, and remove it
2108 /* First create the cgroup in our own hierarchy. */
2109 r
= cg_create(SYSTEMD_CGROUP_CONTROLLER
, path
);
2113 /* If we are in the unified hierarchy, we are done now */
2114 r
= cg_all_unified();
2120 /* Otherwise, do the same in the other hierarchies */
2121 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2122 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2125 n
= cgroup_controller_to_string(c
);
2128 (void) cg_create(n
, path
);
2129 else if (supported
& bit
)
2130 (void) cg_trim(n
, path
, true);
2136 int cg_attach_everywhere(CGroupMask supported
, const char *path
, pid_t pid
, cg_migrate_callback_t path_callback
, void *userdata
) {
2140 r
= cg_attach(SYSTEMD_CGROUP_CONTROLLER
, path
, pid
);
2144 r
= cg_all_unified();
2150 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2151 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2152 const char *p
= NULL
;
2154 if (!(supported
& bit
))
2158 p
= path_callback(bit
, userdata
);
2163 (void) cg_attach_fallback(cgroup_controller_to_string(c
), p
, pid
);
2169 int cg_attach_many_everywhere(CGroupMask supported
, const char *path
, Set
* pids
, cg_migrate_callback_t path_callback
, void *userdata
) {
2174 SET_FOREACH(pidp
, pids
, i
) {
2175 pid_t pid
= PTR_TO_PID(pidp
);
2178 q
= cg_attach_everywhere(supported
, path
, pid
, path_callback
, userdata
);
2179 if (q
< 0 && r
>= 0)
2186 int cg_migrate_everywhere(CGroupMask supported
, const char *from
, const char *to
, cg_migrate_callback_t to_callback
, void *userdata
) {
2190 if (!path_equal(from
, to
)) {
2191 r
= cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER
, from
, SYSTEMD_CGROUP_CONTROLLER
, to
, CGROUP_REMOVE
);
2196 q
= cg_all_unified();
2202 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2203 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2204 const char *p
= NULL
;
2206 if (!(supported
& bit
))
2210 p
= to_callback(bit
, userdata
);
2215 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER
, to
, cgroup_controller_to_string(c
), p
, 0);
2221 int cg_trim_everywhere(CGroupMask supported
, const char *path
, bool delete_root
) {
2225 r
= cg_trim(SYSTEMD_CGROUP_CONTROLLER
, path
, delete_root
);
2229 q
= cg_all_unified();
2235 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2236 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2238 if (!(supported
& bit
))
2241 (void) cg_trim(cgroup_controller_to_string(c
), path
, delete_root
);
2247 int cg_mask_to_string(CGroupMask mask
, char **ret
) {
2248 _cleanup_free_
char *s
= NULL
;
2249 size_t n
= 0, allocated
= 0;
2260 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2264 if (!(mask
& CGROUP_CONTROLLER_TO_MASK(c
)))
2267 k
= cgroup_controller_to_string(c
);
2270 if (!GREEDY_REALLOC(s
, allocated
, n
+ space
+ l
+ 1))
2275 memcpy(s
+ n
+ space
, k
, l
);
2290 int cg_mask_from_string(const char *value
, CGroupMask
*mask
) {
2295 _cleanup_free_
char *n
= NULL
;
2299 r
= extract_first_word(&value
, &n
, NULL
, 0);
2305 v
= cgroup_controller_from_string(n
);
2309 *mask
|= CGROUP_CONTROLLER_TO_MASK(v
);
2314 int cg_mask_supported(CGroupMask
*ret
) {
2315 CGroupMask mask
= 0;
2318 /* Determines the mask of supported cgroup controllers. Only
2319 * includes controllers we can make sense of and that are
2320 * actually accessible. */
2322 r
= cg_all_unified();
2326 _cleanup_free_
char *root
= NULL
, *controllers
= NULL
, *path
= NULL
;
2328 /* In the unified hierarchy we can read the supported
2329 * and accessible controllers from a the top-level
2330 * cgroup attribute */
2332 r
= cg_get_root_path(&root
);
2336 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, root
, "cgroup.controllers", &path
);
2340 r
= read_one_line_file(path
, &controllers
);
2344 r
= cg_mask_from_string(controllers
, &mask
);
2348 /* Currently, we support the cpu, memory, io and pids
2349 * controller in the unified hierarchy, mask
2350 * everything else off. */
2351 mask
&= CGROUP_MASK_CPU
| CGROUP_MASK_MEMORY
| CGROUP_MASK_IO
| CGROUP_MASK_PIDS
;
2356 /* In the legacy hierarchy, we check whether which
2357 * hierarchies are mounted. */
2359 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2362 n
= cgroup_controller_to_string(c
);
2363 if (controller_is_accessible(n
) >= 0)
2364 mask
|= CGROUP_CONTROLLER_TO_MASK(c
);
2372 int cg_kernel_controllers(Set
*controllers
) {
2373 _cleanup_fclose_
FILE *f
= NULL
;
2376 assert(controllers
);
2378 /* Determines the full list of kernel-known controllers. Might
2379 * include controllers we don't actually support, arbitrary
2380 * named hierarchies and controllers that aren't currently
2381 * accessible (because not mounted). */
2383 f
= fopen("/proc/cgroups", "re");
2385 if (errno
== ENOENT
)
2390 /* Ignore the header line */
2391 (void) read_line(f
, (size_t) -1, NULL
);
2398 if (fscanf(f
, "%ms %*i %*i %i", &controller
, &enabled
) != 2) {
2403 if (ferror(f
) && errno
> 0)
2414 if (!cg_controller_is_valid(controller
)) {
2419 r
= set_consume(controllers
, controller
);
2427 static thread_local CGroupUnified unified_cache
= CGROUP_UNIFIED_UNKNOWN
;
2429 /* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This
2430 * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2431 * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2432 * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
2434 * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
2435 * process management but disable the compat dual layout, we return %true on
2436 * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
2438 static thread_local
bool unified_systemd_v232
;
2440 static int cg_unified_update(void) {
2444 /* Checks if we support the unified hierarchy. Returns an
2445 * error when the cgroup hierarchies aren't mounted yet or we
2446 * have any other trouble determining if the unified hierarchy
2449 if (unified_cache
>= CGROUP_UNIFIED_NONE
)
2452 if (statfs("/sys/fs/cgroup/", &fs
) < 0)
2453 return log_debug_errno(errno
, "statfs(\"/sys/fs/cgroup/\" failed: %m");
2455 if (F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
)) {
2456 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
2457 unified_cache
= CGROUP_UNIFIED_ALL
;
2458 } else if (F_TYPE_EQUAL(fs
.f_type
, TMPFS_MAGIC
)) {
2459 if (statfs("/sys/fs/cgroup/unified/", &fs
) == 0 &&
2460 F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
)) {
2461 log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
2462 unified_cache
= CGROUP_UNIFIED_SYSTEMD
;
2463 unified_systemd_v232
= false;
2465 if (statfs("/sys/fs/cgroup/systemd/", &fs
) < 0)
2466 return log_debug_errno(errno
, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
2468 if (F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
)) {
2469 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
2470 unified_cache
= CGROUP_UNIFIED_SYSTEMD
;
2471 unified_systemd_v232
= true;
2472 } else if (F_TYPE_EQUAL(fs
.f_type
, CGROUP_SUPER_MAGIC
)) {
2473 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
2474 unified_cache
= CGROUP_UNIFIED_NONE
;
2476 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
2477 (unsigned long long) fs
.f_type
);
2478 unified_cache
= CGROUP_UNIFIED_NONE
;
2482 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2483 (unsigned long long) fs
.f_type
);
2490 int cg_unified_controller(const char *controller
) {
2493 r
= cg_unified_update();
2497 if (unified_cache
== CGROUP_UNIFIED_NONE
)
2500 if (unified_cache
>= CGROUP_UNIFIED_ALL
)
2503 return streq_ptr(controller
, SYSTEMD_CGROUP_CONTROLLER
);
2506 int cg_all_unified(void) {
2509 r
= cg_unified_update();
2513 return unified_cache
>= CGROUP_UNIFIED_ALL
;
2516 int cg_hybrid_unified(void) {
2519 r
= cg_unified_update();
2523 return unified_cache
== CGROUP_UNIFIED_SYSTEMD
&& !unified_systemd_v232
;
2526 int cg_unified_flush(void) {
2527 unified_cache
= CGROUP_UNIFIED_UNKNOWN
;
2529 return cg_unified_update();
2532 int cg_enable_everywhere(CGroupMask supported
, CGroupMask mask
, const char *p
) {
2533 _cleanup_free_
char *fs
= NULL
;
2542 r
= cg_all_unified();
2545 if (r
== 0) /* on the legacy hiearchy there's no joining of controllers defined */
2548 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, p
, "cgroup.subtree_control", &fs
);
2552 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2553 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2556 if (!(supported
& bit
))
2559 n
= cgroup_controller_to_string(c
);
2561 char s
[1 + strlen(n
) + 1];
2563 s
[0] = mask
& bit
? '+' : '-';
2566 r
= write_string_file(fs
, s
, 0);
2568 log_debug_errno(r
, "Failed to enable controller %s for %s (%s): %m", n
, p
, fs
);
2575 bool cg_is_unified_wanted(void) {
2576 static thread_local
int wanted
= -1;
2579 const bool is_default
= DEFAULT_HIERARCHY
== CGROUP_UNIFIED_ALL
;
2581 /* If we have a cached value, return that. */
2585 /* If the hierarchy is already mounted, then follow whatever
2586 * was chosen for it. */
2587 if (cg_unified_flush() >= 0)
2588 return (wanted
= unified_cache
>= CGROUP_UNIFIED_ALL
);
2590 /* Otherwise, let's see what the kernel command line has to say.
2591 * Since checking is expensive, cache a non-error result. */
2592 r
= proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b
);
2594 return (wanted
= r
> 0 ? b
: is_default
);
2597 bool cg_is_legacy_wanted(void) {
2598 static thread_local
int wanted
= -1;
2600 /* If we have a cached value, return that. */
2604 /* Check if we have cgroups2 already mounted. */
2605 if (cg_unified_flush() >= 0 &&
2606 unified_cache
== CGROUP_UNIFIED_ALL
)
2607 return (wanted
= false);
2609 /* Otherwise, assume that at least partial legacy is wanted,
2610 * since cgroups2 should already be mounted at this point. */
2611 return (wanted
= true);
2614 bool cg_is_hybrid_wanted(void) {
2615 static thread_local
int wanted
= -1;
2618 const bool is_default
= DEFAULT_HIERARCHY
>= CGROUP_UNIFIED_SYSTEMD
;
2619 /* We default to true if the default is "hybrid", obviously,
2620 * but also when the default is "unified", because if we get
2621 * called, it means that unified hierarchy was not mounted. */
2623 /* If we have a cached value, return that. */
2627 /* If the hierarchy is already mounted, then follow whatever
2628 * was chosen for it. */
2629 if (cg_unified_flush() >= 0 &&
2630 unified_cache
== CGROUP_UNIFIED_ALL
)
2631 return (wanted
= false);
2633 /* Otherwise, let's see what the kernel command line has to say.
2634 * Since checking is expensive, cache a non-error result. */
2635 r
= proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b
);
2637 /* The meaning of the kernel option is reversed wrt. to the return value
2638 * of this function, hence the negation. */
2639 return (wanted
= r
> 0 ? !b
: is_default
);
2642 int cg_weight_parse(const char *s
, uint64_t *ret
) {
2647 *ret
= CGROUP_WEIGHT_INVALID
;
2651 r
= safe_atou64(s
, &u
);
2655 if (u
< CGROUP_WEIGHT_MIN
|| u
> CGROUP_WEIGHT_MAX
)
2662 const uint64_t cgroup_io_limit_defaults
[_CGROUP_IO_LIMIT_TYPE_MAX
] = {
2663 [CGROUP_IO_RBPS_MAX
] = CGROUP_LIMIT_MAX
,
2664 [CGROUP_IO_WBPS_MAX
] = CGROUP_LIMIT_MAX
,
2665 [CGROUP_IO_RIOPS_MAX
] = CGROUP_LIMIT_MAX
,
2666 [CGROUP_IO_WIOPS_MAX
] = CGROUP_LIMIT_MAX
,
2669 static const char* const cgroup_io_limit_type_table
[_CGROUP_IO_LIMIT_TYPE_MAX
] = {
2670 [CGROUP_IO_RBPS_MAX
] = "IOReadBandwidthMax",
2671 [CGROUP_IO_WBPS_MAX
] = "IOWriteBandwidthMax",
2672 [CGROUP_IO_RIOPS_MAX
] = "IOReadIOPSMax",
2673 [CGROUP_IO_WIOPS_MAX
] = "IOWriteIOPSMax",
2676 DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type
, CGroupIOLimitType
);
2678 int cg_cpu_shares_parse(const char *s
, uint64_t *ret
) {
2683 *ret
= CGROUP_CPU_SHARES_INVALID
;
2687 r
= safe_atou64(s
, &u
);
2691 if (u
< CGROUP_CPU_SHARES_MIN
|| u
> CGROUP_CPU_SHARES_MAX
)
2698 int cg_blkio_weight_parse(const char *s
, uint64_t *ret
) {
2703 *ret
= CGROUP_BLKIO_WEIGHT_INVALID
;
2707 r
= safe_atou64(s
, &u
);
2711 if (u
< CGROUP_BLKIO_WEIGHT_MIN
|| u
> CGROUP_BLKIO_WEIGHT_MAX
)
2718 bool is_cgroup_fs(const struct statfs
*s
) {
2719 return is_fs_type(s
, CGROUP_SUPER_MAGIC
) ||
2720 is_fs_type(s
, CGROUP2_SUPER_MAGIC
);
2723 bool fd_is_cgroup_fs(int fd
) {
2726 if (fstatfs(fd
, &s
) < 0)
2729 return is_cgroup_fs(&s
);
2732 static const char *cgroup_controller_table
[_CGROUP_CONTROLLER_MAX
] = {
2733 [CGROUP_CONTROLLER_CPU
] = "cpu",
2734 [CGROUP_CONTROLLER_CPUACCT
] = "cpuacct",
2735 [CGROUP_CONTROLLER_IO
] = "io",
2736 [CGROUP_CONTROLLER_BLKIO
] = "blkio",
2737 [CGROUP_CONTROLLER_MEMORY
] = "memory",
2738 [CGROUP_CONTROLLER_DEVICES
] = "devices",
2739 [CGROUP_CONTROLLER_PIDS
] = "pids",
2742 DEFINE_STRING_TABLE_LOOKUP(cgroup_controller
, CGroupController
);