2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include <sys/statfs.h>
30 #include <sys/types.h>
31 #include <sys/xattr.h>
34 #include "alloc-util.h"
35 #include "cgroup-util.h"
37 #include "dirent-util.h"
38 #include "extract-word.h"
41 #include "format-util.h"
44 #include "login-util.h"
48 #include "parse-util.h"
49 #include "path-util.h"
50 #include "proc-cmdline.h"
51 #include "process-util.h"
54 #include "stat-util.h"
55 #include "stdio-util.h"
56 #include "string-table.h"
57 #include "string-util.h"
59 #include "unit-name.h"
60 #include "user-util.h"
62 int cg_enumerate_processes(const char *controller
, const char *path
, FILE **_f
) {
63 _cleanup_free_
char *fs
= NULL
;
69 r
= cg_get_path(controller
, path
, "cgroup.procs", &fs
);
81 int cg_read_pid(FILE *f
, pid_t
*_pid
) {
84 /* Note that the cgroup.procs might contain duplicates! See
85 * cgroups.txt for details. */
91 if (fscanf(f
, "%lu", &ul
) != 1) {
96 return errno
> 0 ? -errno
: -EIO
;
107 const char *controller
,
112 _cleanup_free_
char *events
= NULL
, *content
= NULL
;
116 r
= cg_get_path(controller
, path
, "cgroup.events", &events
);
120 r
= read_full_file(events
, &content
, NULL
);
125 while ((line
= strsep(&p
, "\n"))) {
128 key
= strsep(&line
, " ");
132 if (strcmp(key
, event
))
142 bool cg_ns_supported(void) {
143 static thread_local
int enabled
= -1;
148 if (access("/proc/self/ns/cgroup", F_OK
) == 0)
156 int cg_enumerate_subgroups(const char *controller
, const char *path
, DIR **_d
) {
157 _cleanup_free_
char *fs
= NULL
;
163 /* This is not recursive! */
165 r
= cg_get_path(controller
, path
, NULL
, &fs
);
177 int cg_read_subgroup(DIR *d
, char **fn
) {
183 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
186 if (de
->d_type
!= DT_DIR
)
189 if (dot_or_dot_dot(de
->d_name
))
192 b
= strdup(de
->d_name
);
203 int cg_rmdir(const char *controller
, const char *path
) {
204 _cleanup_free_
char *p
= NULL
;
207 r
= cg_get_path(controller
, path
, NULL
, &p
);
212 if (r
< 0 && errno
!= ENOENT
)
215 r
= cg_hybrid_unified();
221 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
222 r
= cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
);
224 log_warning_errno(r
, "Failed to remove compat systemd cgroup %s: %m", path
);
231 const char *controller
,
236 cg_kill_log_func_t log_kill
,
239 _cleanup_set_free_ Set
*allocated_set
= NULL
;
246 /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send
247 * SIGCONT on SIGKILL. */
248 if (IN_SET(sig
, SIGCONT
, SIGKILL
))
249 flags
&= ~CGROUP_SIGCONT
;
251 /* This goes through the tasks list and kills them all. This
252 * is repeated until no further processes are added to the
253 * tasks list, to properly handle forking processes */
256 s
= allocated_set
= set_new(NULL
);
261 my_pid
= getpid_cached();
264 _cleanup_fclose_
FILE *f
= NULL
;
268 r
= cg_enumerate_processes(controller
, path
, &f
);
270 if (ret
>= 0 && r
!= -ENOENT
)
276 while ((r
= cg_read_pid(f
, &pid
)) > 0) {
278 if ((flags
& CGROUP_IGNORE_SELF
) && pid
== my_pid
)
281 if (set_get(s
, PID_TO_PTR(pid
)) == PID_TO_PTR(pid
))
285 log_kill(pid
, sig
, userdata
);
287 /* If we haven't killed this process yet, kill
289 if (kill(pid
, sig
) < 0) {
290 if (ret
>= 0 && errno
!= ESRCH
)
293 if (flags
& CGROUP_SIGCONT
)
294 (void) kill(pid
, SIGCONT
);
302 r
= set_put(s
, PID_TO_PTR(pid
));
318 /* To avoid racing against processes which fork
319 * quicker than we can kill them we repeat this until
320 * no new pids need to be killed. */
327 int cg_kill_recursive(
328 const char *controller
,
333 cg_kill_log_func_t log_kill
,
336 _cleanup_set_free_ Set
*allocated_set
= NULL
;
337 _cleanup_closedir_
DIR *d
= NULL
;
345 s
= allocated_set
= set_new(NULL
);
350 ret
= cg_kill(controller
, path
, sig
, flags
, s
, log_kill
, userdata
);
352 r
= cg_enumerate_subgroups(controller
, path
, &d
);
354 if (ret
>= 0 && r
!= -ENOENT
)
360 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
361 _cleanup_free_
char *p
= NULL
;
363 p
= strjoin(path
, "/", fn
);
368 r
= cg_kill_recursive(controller
, p
, sig
, flags
, s
, log_kill
, userdata
);
369 if (r
!= 0 && ret
>= 0)
372 if (ret
>= 0 && r
< 0)
375 if (flags
& CGROUP_REMOVE
) {
376 r
= cg_rmdir(controller
, path
);
377 if (r
< 0 && ret
>= 0 && !IN_SET(r
, -ENOENT
, -EBUSY
))
392 _cleanup_set_free_ Set
*s
= NULL
;
405 my_pid
= getpid_cached();
408 _cleanup_fclose_
FILE *f
= NULL
;
412 r
= cg_enumerate_processes(cfrom
, pfrom
, &f
);
414 if (ret
>= 0 && r
!= -ENOENT
)
420 while ((r
= cg_read_pid(f
, &pid
)) > 0) {
422 /* This might do weird stuff if we aren't a
423 * single-threaded program. However, we
424 * luckily know we are not */
425 if ((flags
& CGROUP_IGNORE_SELF
) && pid
== my_pid
)
428 if (set_get(s
, PID_TO_PTR(pid
)) == PID_TO_PTR(pid
))
431 /* Ignore kernel threads. Since they can only
432 * exist in the root cgroup, we only check for
435 (isempty(pfrom
) || path_equal(pfrom
, "/")) &&
436 is_kernel_thread(pid
) > 0)
439 r
= cg_attach(cto
, pto
, pid
);
441 if (ret
>= 0 && r
!= -ESRCH
)
448 r
= set_put(s
, PID_TO_PTR(pid
));
468 int cg_migrate_recursive(
475 _cleanup_closedir_
DIR *d
= NULL
;
484 ret
= cg_migrate(cfrom
, pfrom
, cto
, pto
, flags
);
486 r
= cg_enumerate_subgroups(cfrom
, pfrom
, &d
);
488 if (ret
>= 0 && r
!= -ENOENT
)
494 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
495 _cleanup_free_
char *p
= NULL
;
497 p
= strjoin(pfrom
, "/", fn
);
502 r
= cg_migrate_recursive(cfrom
, p
, cto
, pto
, flags
);
503 if (r
!= 0 && ret
>= 0)
507 if (r
< 0 && ret
>= 0)
510 if (flags
& CGROUP_REMOVE
) {
511 r
= cg_rmdir(cfrom
, pfrom
);
512 if (r
< 0 && ret
>= 0 && !IN_SET(r
, -ENOENT
, -EBUSY
))
519 int cg_migrate_recursive_fallback(
533 r
= cg_migrate_recursive(cfrom
, pfrom
, cto
, pto
, flags
);
535 char prefix
[strlen(pto
) + 1];
537 /* This didn't work? Then let's try all prefixes of the destination */
539 PATH_FOREACH_PREFIX(prefix
, pto
) {
542 q
= cg_migrate_recursive(cfrom
, pfrom
, cto
, prefix
, flags
);
551 static const char *controller_to_dirname(const char *controller
) {
556 /* Converts a controller name to the directory name below
557 * /sys/fs/cgroup/ we want to mount it to. Effectively, this
558 * just cuts off the name= prefixed used for named
559 * hierarchies, if it is specified. */
561 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
562 if (cg_hybrid_unified() > 0)
563 controller
= SYSTEMD_CGROUP_CONTROLLER_HYBRID
;
565 controller
= SYSTEMD_CGROUP_CONTROLLER_LEGACY
;
568 e
= startswith(controller
, "name=");
575 static int join_path_legacy(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
582 dn
= controller_to_dirname(controller
);
584 if (isempty(path
) && isempty(suffix
))
585 t
= strappend("/sys/fs/cgroup/", dn
);
586 else if (isempty(path
))
587 t
= strjoin("/sys/fs/cgroup/", dn
, "/", suffix
);
588 else if (isempty(suffix
))
589 t
= strjoin("/sys/fs/cgroup/", dn
, "/", path
);
591 t
= strjoin("/sys/fs/cgroup/", dn
, "/", path
, "/", suffix
);
599 static int join_path_unified(const char *path
, const char *suffix
, char **fs
) {
604 if (isempty(path
) && isempty(suffix
))
605 t
= strdup("/sys/fs/cgroup");
606 else if (isempty(path
))
607 t
= strappend("/sys/fs/cgroup/", suffix
);
608 else if (isempty(suffix
))
609 t
= strappend("/sys/fs/cgroup/", path
);
611 t
= strjoin("/sys/fs/cgroup/", path
, "/", suffix
);
619 int cg_get_path(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
627 /* If no controller is specified, we return the path
628 * *below* the controllers, without any prefix. */
630 if (!path
&& !suffix
)
638 t
= strjoin(path
, "/", suffix
);
642 *fs
= path_kill_slashes(t
);
646 if (!cg_controller_is_valid(controller
))
649 r
= cg_all_unified();
653 r
= join_path_unified(path
, suffix
, fs
);
655 r
= join_path_legacy(controller
, path
, suffix
, fs
);
659 path_kill_slashes(*fs
);
663 static int controller_is_accessible(const char *controller
) {
668 /* Checks whether a specific controller is accessible,
669 * i.e. its hierarchy mounted. In the unified hierarchy all
670 * controllers are considered accessible, except for the named
673 if (!cg_controller_is_valid(controller
))
676 r
= cg_all_unified();
680 /* We don't support named hierarchies if we are using
681 * the unified hierarchy. */
683 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
))
686 if (startswith(controller
, "name="))
692 dn
= controller_to_dirname(controller
);
693 cc
= strjoina("/sys/fs/cgroup/", dn
);
695 if (laccess(cc
, F_OK
) < 0)
702 int cg_get_path_and_check(const char *controller
, const char *path
, const char *suffix
, char **fs
) {
708 /* Check if the specified controller is actually accessible */
709 r
= controller_is_accessible(controller
);
713 return cg_get_path(controller
, path
, suffix
, fs
);
716 static int trim_cb(const char *path
, const struct stat
*sb
, int typeflag
, struct FTW
*ftwbuf
) {
721 if (typeflag
!= FTW_DP
)
724 if (ftwbuf
->level
< 1)
731 int cg_trim(const char *controller
, const char *path
, bool delete_root
) {
732 _cleanup_free_
char *fs
= NULL
;
737 r
= cg_get_path(controller
, path
, NULL
, &fs
);
742 if (nftw(fs
, trim_cb
, 64, FTW_DEPTH
|FTW_MOUNT
|FTW_PHYS
) != 0) {
752 if (rmdir(fs
) < 0 && errno
!= ENOENT
)
756 q
= cg_hybrid_unified();
759 if (q
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
760 q
= cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, delete_root
);
762 log_warning_errno(q
, "Failed to trim compat systemd cgroup %s: %m", path
);
768 int cg_create(const char *controller
, const char *path
) {
769 _cleanup_free_
char *fs
= NULL
;
772 r
= cg_get_path_and_check(controller
, path
, NULL
, &fs
);
776 r
= mkdir_parents(fs
, 0755);
780 if (mkdir(fs
, 0755) < 0) {
788 r
= cg_hybrid_unified();
792 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
793 r
= cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
);
795 log_warning_errno(r
, "Failed to create compat systemd cgroup %s: %m", path
);
801 int cg_create_and_attach(const char *controller
, const char *path
, pid_t pid
) {
806 r
= cg_create(controller
, path
);
810 q
= cg_attach(controller
, path
, pid
);
814 /* This does not remove the cgroup on failure */
818 int cg_attach(const char *controller
, const char *path
, pid_t pid
) {
819 _cleanup_free_
char *fs
= NULL
;
820 char c
[DECIMAL_STR_MAX(pid_t
) + 2];
826 r
= cg_get_path_and_check(controller
, path
, "cgroup.procs", &fs
);
831 pid
= getpid_cached();
833 xsprintf(c
, PID_FMT
"\n", pid
);
835 r
= write_string_file(fs
, c
, 0);
839 r
= cg_hybrid_unified();
843 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
844 r
= cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, pid
);
846 log_warning_errno(r
, "Failed to attach "PID_FMT
" to compat systemd cgroup %s: %m", pid
, path
);
852 int cg_attach_fallback(const char *controller
, const char *path
, pid_t pid
) {
859 r
= cg_attach(controller
, path
, pid
);
861 char prefix
[strlen(path
) + 1];
863 /* This didn't work? Then let's try all prefixes of
866 PATH_FOREACH_PREFIX(prefix
, path
) {
869 q
= cg_attach(controller
, prefix
, pid
);
878 int cg_set_group_access(
879 const char *controller
,
885 _cleanup_free_
char *fs
= NULL
;
888 if (mode
== MODE_INVALID
&& uid
== UID_INVALID
&& gid
== GID_INVALID
)
891 if (mode
!= MODE_INVALID
)
894 r
= cg_get_path(controller
, path
, NULL
, &fs
);
898 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
902 r
= cg_hybrid_unified();
905 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
906 r
= cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, mode
, uid
, gid
);
908 log_debug_errno(r
, "Failed to set group access on compatibility systemd cgroup %s, ignoring: %m", path
);
914 int cg_set_task_access(
915 const char *controller
,
921 _cleanup_free_
char *fs
= NULL
;
926 if (mode
== MODE_INVALID
&& uid
== UID_INVALID
&& gid
== GID_INVALID
)
929 if (mode
!= MODE_INVALID
)
932 /* For both the legacy and unified hierarchies, "cgroup.procs" is the main entry point for PIDs */
933 r
= cg_get_path(controller
, path
, "cgroup.procs", &fs
);
937 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
941 r
= cg_unified_controller(controller
);
947 /* Compatibility: on cgroupsv1 always keep values for the legacy files "tasks" and
948 * "cgroup.clone_children" in sync with "cgroup.procs". Since this is legacy stuff, we don't care if
953 "cgroup.clone_children") {
957 r
= cg_get_path(controller
, path
, fn
, &fs
);
959 log_debug_errno(r
, "Failed to get path for %s of %s, ignoring: %m", fn
, path
);
961 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
963 log_debug_errno(r
, "Failed to to change ownership/access mode for %s of %s, ignoring: %m", fn
, path
);
966 /* On the unified controller, we want to permit subtree controllers too. */
969 r
= cg_get_path(controller
, path
, "cgroup.subtree_control", &fs
);
973 r
= chmod_and_chown(fs
, mode
, uid
, gid
);
978 r
= cg_hybrid_unified();
981 if (r
> 0 && streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
982 /* Always propagate access mode from unified to legacy controller */
984 r
= cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY
, path
, mode
, uid
, gid
);
986 log_debug_errno(r
, "Failed to set task access on compatibility systemd cgroup %s, ignoring: %m", path
);
992 int cg_set_xattr(const char *controller
, const char *path
, const char *name
, const void *value
, size_t size
, int flags
) {
993 _cleanup_free_
char *fs
= NULL
;
998 assert(value
|| size
<= 0);
1000 r
= cg_get_path(controller
, path
, NULL
, &fs
);
1004 if (setxattr(fs
, name
, value
, size
, flags
) < 0)
1010 int cg_get_xattr(const char *controller
, const char *path
, const char *name
, void *value
, size_t size
) {
1011 _cleanup_free_
char *fs
= NULL
;
1018 r
= cg_get_path(controller
, path
, NULL
, &fs
);
1022 n
= getxattr(fs
, name
, value
, size
);
1029 int cg_pid_get_path(const char *controller
, pid_t pid
, char **path
) {
1030 _cleanup_fclose_
FILE *f
= NULL
;
1031 char line
[LINE_MAX
];
1032 const char *fs
, *controller_str
;
1040 if (!cg_controller_is_valid(controller
))
1043 controller
= SYSTEMD_CGROUP_CONTROLLER
;
1045 unified
= cg_unified_controller(controller
);
1049 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
))
1050 controller_str
= SYSTEMD_CGROUP_CONTROLLER_LEGACY
;
1052 controller_str
= controller
;
1054 cs
= strlen(controller_str
);
1057 fs
= procfs_file_alloca(pid
, "cgroup");
1058 f
= fopen(fs
, "re");
1060 return errno
== ENOENT
? -ESRCH
: -errno
;
1062 FOREACH_LINE(line
, f
, return -errno
) {
1068 e
= startswith(line
, "0:");
1078 const char *word
, *state
;
1081 l
= strchr(line
, ':');
1091 FOREACH_WORD_SEPARATOR(word
, k
, l
, ",", state
) {
1092 if (k
== cs
&& memcmp(word
, controller_str
, cs
) == 0) {
1113 int cg_install_release_agent(const char *controller
, const char *agent
) {
1114 _cleanup_free_
char *fs
= NULL
, *contents
= NULL
;
1120 r
= cg_unified_controller(controller
);
1123 if (r
> 0) /* doesn't apply to unified hierarchy */
1126 r
= cg_get_path(controller
, NULL
, "release_agent", &fs
);
1130 r
= read_one_line_file(fs
, &contents
);
1134 sc
= strstrip(contents
);
1136 r
= write_string_file(fs
, agent
, 0);
1139 } else if (!path_equal(sc
, agent
))
1143 r
= cg_get_path(controller
, NULL
, "notify_on_release", &fs
);
1147 contents
= mfree(contents
);
1148 r
= read_one_line_file(fs
, &contents
);
1152 sc
= strstrip(contents
);
1153 if (streq(sc
, "0")) {
1154 r
= write_string_file(fs
, "1", 0);
1161 if (!streq(sc
, "1"))
1167 int cg_uninstall_release_agent(const char *controller
) {
1168 _cleanup_free_
char *fs
= NULL
;
1171 r
= cg_unified_controller(controller
);
1174 if (r
> 0) /* Doesn't apply to unified hierarchy */
1177 r
= cg_get_path(controller
, NULL
, "notify_on_release", &fs
);
1181 r
= write_string_file(fs
, "0", 0);
1187 r
= cg_get_path(controller
, NULL
, "release_agent", &fs
);
1191 r
= write_string_file(fs
, "", 0);
1198 int cg_is_empty(const char *controller
, const char *path
) {
1199 _cleanup_fclose_
FILE *f
= NULL
;
1205 r
= cg_enumerate_processes(controller
, path
, &f
);
1211 r
= cg_read_pid(f
, &pid
);
1218 int cg_is_empty_recursive(const char *controller
, const char *path
) {
1223 /* The root cgroup is always populated */
1224 if (controller
&& (isempty(path
) || path_equal(path
, "/")))
1227 r
= cg_unified_controller(controller
);
1231 _cleanup_free_
char *t
= NULL
;
1233 /* On the unified hierarchy we can check empty state
1234 * via the "populated" attribute of "cgroup.events". */
1236 r
= cg_read_event(controller
, path
, "populated", &t
);
1240 return streq(t
, "0");
1242 _cleanup_closedir_
DIR *d
= NULL
;
1245 r
= cg_is_empty(controller
, path
);
1249 r
= cg_enumerate_subgroups(controller
, path
, &d
);
1255 while ((r
= cg_read_subgroup(d
, &fn
)) > 0) {
1256 _cleanup_free_
char *p
= NULL
;
1258 p
= strjoin(path
, "/", fn
);
1263 r
= cg_is_empty_recursive(controller
, p
);
1274 int cg_split_spec(const char *spec
, char **controller
, char **path
) {
1275 char *t
= NULL
, *u
= NULL
;
1281 if (!path_is_safe(spec
))
1289 *path
= path_kill_slashes(t
);
1298 e
= strchr(spec
, ':');
1300 if (!cg_controller_is_valid(spec
))
1317 t
= strndup(spec
, e
-spec
);
1320 if (!cg_controller_is_valid(t
)) {
1334 if (!path_is_safe(u
) ||
1335 !path_is_absolute(u
)) {
1341 path_kill_slashes(u
);
1357 int cg_mangle_path(const char *path
, char **result
) {
1358 _cleanup_free_
char *c
= NULL
, *p
= NULL
;
1365 /* First, check if it already is a filesystem path */
1366 if (path_startswith(path
, "/sys/fs/cgroup")) {
1372 *result
= path_kill_slashes(t
);
1376 /* Otherwise, treat it as cg spec */
1377 r
= cg_split_spec(path
, &c
, &p
);
1381 return cg_get_path(c
?: SYSTEMD_CGROUP_CONTROLLER
, p
?: "/", NULL
, result
);
1384 int cg_get_root_path(char **path
) {
1390 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, 1, &p
);
1394 e
= endswith(p
, "/" SPECIAL_INIT_SCOPE
);
1396 e
= endswith(p
, "/" SPECIAL_SYSTEM_SLICE
); /* legacy */
1398 e
= endswith(p
, "/system"); /* even more legacy */
1406 int cg_shift_path(const char *cgroup
, const char *root
, const char **shifted
) {
1407 _cleanup_free_
char *rt
= NULL
;
1415 /* If the root was specified let's use that, otherwise
1416 * let's determine it from PID 1 */
1418 r
= cg_get_root_path(&rt
);
1425 p
= path_startswith(cgroup
, root
);
1426 if (p
&& p
> cgroup
)
1434 int cg_pid_get_path_shifted(pid_t pid
, const char *root
, char **cgroup
) {
1435 _cleanup_free_
char *raw
= NULL
;
1442 r
= cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
, pid
, &raw
);
1446 r
= cg_shift_path(raw
, root
, &c
);
1466 int cg_path_decode_unit(const char *cgroup
, char **unit
) {
1473 n
= strcspn(cgroup
, "/");
1477 c
= strndupa(cgroup
, n
);
1480 if (!unit_name_is_valid(c
, UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
))
1491 static bool valid_slice_name(const char *p
, size_t n
) {
1496 if (n
< strlen("x.slice"))
1499 if (memcmp(p
+ n
- 6, ".slice", 6) == 0) {
1505 c
= cg_unescape(buf
);
1507 return unit_name_is_valid(c
, UNIT_NAME_PLAIN
);
1513 static const char *skip_slices(const char *p
) {
1516 /* Skips over all slice assignments */
1521 p
+= strspn(p
, "/");
1523 n
= strcspn(p
, "/");
1524 if (!valid_slice_name(p
, n
))
1531 int cg_path_get_unit(const char *path
, char **ret
) {
1539 e
= skip_slices(path
);
1541 r
= cg_path_decode_unit(e
, &unit
);
1545 /* We skipped over the slices, don't accept any now */
1546 if (endswith(unit
, ".slice")) {
1555 int cg_pid_get_unit(pid_t pid
, char **unit
) {
1556 _cleanup_free_
char *cgroup
= NULL
;
1561 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1565 return cg_path_get_unit(cgroup
, unit
);
1569 * Skip session-*.scope, but require it to be there.
1571 static const char *skip_session(const char *p
) {
1577 p
+= strspn(p
, "/");
1579 n
= strcspn(p
, "/");
1580 if (n
< strlen("session-x.scope"))
1583 if (memcmp(p
, "session-", 8) == 0 && memcmp(p
+ n
- 6, ".scope", 6) == 0) {
1584 char buf
[n
- 8 - 6 + 1];
1586 memcpy(buf
, p
+ 8, n
- 8 - 6);
1589 /* Note that session scopes never need unescaping,
1590 * since they cannot conflict with the kernel's own
1591 * names, hence we don't need to call cg_unescape()
1594 if (!session_id_valid(buf
))
1598 p
+= strspn(p
, "/");
1606 * Skip user@*.service, but require it to be there.
1608 static const char *skip_user_manager(const char *p
) {
1614 p
+= strspn(p
, "/");
1616 n
= strcspn(p
, "/");
1617 if (n
< strlen("user@x.service"))
1620 if (memcmp(p
, "user@", 5) == 0 && memcmp(p
+ n
- 8, ".service", 8) == 0) {
1621 char buf
[n
- 5 - 8 + 1];
1623 memcpy(buf
, p
+ 5, n
- 5 - 8);
1626 /* Note that user manager services never need unescaping,
1627 * since they cannot conflict with the kernel's own
1628 * names, hence we don't need to call cg_unescape()
1631 if (parse_uid(buf
, NULL
) < 0)
1635 p
+= strspn(p
, "/");
1643 static const char *skip_user_prefix(const char *path
) {
1648 /* Skip slices, if there are any */
1649 e
= skip_slices(path
);
1651 /* Skip the user manager, if it's in the path now... */
1652 t
= skip_user_manager(e
);
1656 /* Alternatively skip the user session if it is in the path... */
1657 return skip_session(e
);
1660 int cg_path_get_user_unit(const char *path
, char **ret
) {
1666 t
= skip_user_prefix(path
);
1670 /* And from here on it looks pretty much the same as for a
1671 * system unit, hence let's use the same parser from here
1673 return cg_path_get_unit(t
, ret
);
1676 int cg_pid_get_user_unit(pid_t pid
, char **unit
) {
1677 _cleanup_free_
char *cgroup
= NULL
;
1682 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1686 return cg_path_get_user_unit(cgroup
, unit
);
1689 int cg_path_get_machine_name(const char *path
, char **machine
) {
1690 _cleanup_free_
char *u
= NULL
;
1694 r
= cg_path_get_unit(path
, &u
);
1698 sl
= strjoina("/run/systemd/machines/unit:", u
);
1699 return readlink_malloc(sl
, machine
);
1702 int cg_pid_get_machine_name(pid_t pid
, char **machine
) {
1703 _cleanup_free_
char *cgroup
= NULL
;
1708 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1712 return cg_path_get_machine_name(cgroup
, machine
);
1715 int cg_path_get_session(const char *path
, char **session
) {
1716 _cleanup_free_
char *unit
= NULL
;
1722 r
= cg_path_get_unit(path
, &unit
);
1726 start
= startswith(unit
, "session-");
1729 end
= endswith(start
, ".scope");
1734 if (!session_id_valid(start
))
1750 int cg_pid_get_session(pid_t pid
, char **session
) {
1751 _cleanup_free_
char *cgroup
= NULL
;
1754 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1758 return cg_path_get_session(cgroup
, session
);
1761 int cg_path_get_owner_uid(const char *path
, uid_t
*uid
) {
1762 _cleanup_free_
char *slice
= NULL
;
1768 r
= cg_path_get_slice(path
, &slice
);
1772 start
= startswith(slice
, "user-");
1775 end
= endswith(start
, ".slice");
1780 if (parse_uid(start
, uid
) < 0)
1786 int cg_pid_get_owner_uid(pid_t pid
, uid_t
*uid
) {
1787 _cleanup_free_
char *cgroup
= NULL
;
1790 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1794 return cg_path_get_owner_uid(cgroup
, uid
);
1797 int cg_path_get_slice(const char *p
, char **slice
) {
1798 const char *e
= NULL
;
1803 /* Finds the right-most slice unit from the beginning, but
1804 * stops before we come to the first non-slice unit. */
1809 p
+= strspn(p
, "/");
1811 n
= strcspn(p
, "/");
1812 if (!valid_slice_name(p
, n
)) {
1817 s
= strdup(SPECIAL_ROOT_SLICE
);
1825 return cg_path_decode_unit(e
, slice
);
1833 int cg_pid_get_slice(pid_t pid
, char **slice
) {
1834 _cleanup_free_
char *cgroup
= NULL
;
1839 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1843 return cg_path_get_slice(cgroup
, slice
);
1846 int cg_path_get_user_slice(const char *p
, char **slice
) {
1851 t
= skip_user_prefix(p
);
1855 /* And now it looks pretty much the same as for a system
1856 * slice, so let's just use the same parser from here on. */
1857 return cg_path_get_slice(t
, slice
);
1860 int cg_pid_get_user_slice(pid_t pid
, char **slice
) {
1861 _cleanup_free_
char *cgroup
= NULL
;
1866 r
= cg_pid_get_path_shifted(pid
, NULL
, &cgroup
);
1870 return cg_path_get_user_slice(cgroup
, slice
);
1873 char *cg_escape(const char *p
) {
1874 bool need_prefix
= false;
1876 /* This implements very minimal escaping for names to be used
1877 * as file names in the cgroup tree: any name which might
1878 * conflict with a kernel name or is prefixed with '_' is
1879 * prefixed with a '_'. That way, when reading cgroup names it
1880 * is sufficient to remove a single prefixing underscore if
1883 /* The return value of this function (unlike cg_unescape())
1886 if (IN_SET(p
[0], 0, '_', '.') ||
1887 streq(p
, "notify_on_release") ||
1888 streq(p
, "release_agent") ||
1889 streq(p
, "tasks") ||
1890 startswith(p
, "cgroup."))
1895 dot
= strrchr(p
, '.');
1900 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
1903 n
= cgroup_controller_to_string(c
);
1908 if (memcmp(p
, n
, l
) != 0)
1918 return strappend("_", p
);
1923 char *cg_unescape(const char *p
) {
1926 /* The return value of this function (unlike cg_escape())
1927 * doesn't need free()! */
1935 #define CONTROLLER_VALID \
1939 bool cg_controller_is_valid(const char *p
) {
1945 if (streq(p
, SYSTEMD_CGROUP_CONTROLLER
))
1948 s
= startswith(p
, "name=");
1952 if (IN_SET(*p
, 0, '_'))
1955 for (t
= p
; *t
; t
++)
1956 if (!strchr(CONTROLLER_VALID
, *t
))
1959 if (t
- p
> FILENAME_MAX
)
1965 int cg_slice_to_path(const char *unit
, char **ret
) {
1966 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *e
= NULL
;
1973 if (streq(unit
, SPECIAL_ROOT_SLICE
)) {
1983 if (!unit_name_is_valid(unit
, UNIT_NAME_PLAIN
))
1986 if (!endswith(unit
, ".slice"))
1989 r
= unit_name_to_prefix(unit
, &p
);
1993 dash
= strchr(p
, '-');
1995 /* Don't allow initial dashes */
2000 _cleanup_free_
char *escaped
= NULL
;
2001 char n
[dash
- p
+ sizeof(".slice")];
2003 /* Don't allow trailing or double dashes */
2004 if (IN_SET(dash
[1], 0, '-'))
2007 strcpy(stpncpy(n
, p
, dash
- p
), ".slice");
2008 if (!unit_name_is_valid(n
, UNIT_NAME_PLAIN
))
2011 escaped
= cg_escape(n
);
2015 if (!strextend(&s
, escaped
, "/", NULL
))
2018 dash
= strchr(dash
+1, '-');
2021 e
= cg_escape(unit
);
2025 if (!strextend(&s
, e
, NULL
))
2034 int cg_set_attribute(const char *controller
, const char *path
, const char *attribute
, const char *value
) {
2035 _cleanup_free_
char *p
= NULL
;
2038 r
= cg_get_path(controller
, path
, attribute
, &p
);
2042 return write_string_file(p
, value
, 0);
2045 int cg_get_attribute(const char *controller
, const char *path
, const char *attribute
, char **ret
) {
2046 _cleanup_free_
char *p
= NULL
;
2049 r
= cg_get_path(controller
, path
, attribute
, &p
);
2053 return read_one_line_file(p
, ret
);
2056 int cg_get_keyed_attribute(const char *controller
, const char *path
, const char *attribute
, const char **keys
, char **values
) {
2057 _cleanup_free_
char *filename
= NULL
, *content
= NULL
;
2061 for (i
= 0; keys
[i
]; i
++)
2064 r
= cg_get_path(controller
, path
, attribute
, &filename
);
2068 r
= read_full_file(filename
, &content
, NULL
);
2073 while ((line
= strsep(&p
, "\n"))) {
2076 key
= strsep(&line
, " ");
2078 for (i
= 0; keys
[i
]; i
++) {
2079 if (streq(key
, keys
[i
])) {
2080 values
[i
] = strdup(line
);
2086 for (i
= 0; keys
[i
]; i
++) {
2088 for (i
= 0; keys
[i
]; i
++) {
2099 int cg_create_everywhere(CGroupMask supported
, CGroupMask mask
, const char *path
) {
2103 /* This one will create a cgroup in our private tree, but also
2104 * duplicate it in the trees specified in mask, and remove it
2107 /* First create the cgroup in our own hierarchy. */
2108 r
= cg_create(SYSTEMD_CGROUP_CONTROLLER
, path
);
2112 /* If we are in the unified hierarchy, we are done now */
2113 r
= cg_all_unified();
2119 /* Otherwise, do the same in the other hierarchies */
2120 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2121 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2124 n
= cgroup_controller_to_string(c
);
2127 (void) cg_create(n
, path
);
2128 else if (supported
& bit
)
2129 (void) cg_trim(n
, path
, true);
2135 int cg_attach_everywhere(CGroupMask supported
, const char *path
, pid_t pid
, cg_migrate_callback_t path_callback
, void *userdata
) {
2139 r
= cg_attach(SYSTEMD_CGROUP_CONTROLLER
, path
, pid
);
2143 r
= cg_all_unified();
2149 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2150 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2151 const char *p
= NULL
;
2153 if (!(supported
& bit
))
2157 p
= path_callback(bit
, userdata
);
2162 (void) cg_attach_fallback(cgroup_controller_to_string(c
), p
, pid
);
2168 int cg_attach_many_everywhere(CGroupMask supported
, const char *path
, Set
* pids
, cg_migrate_callback_t path_callback
, void *userdata
) {
2173 SET_FOREACH(pidp
, pids
, i
) {
2174 pid_t pid
= PTR_TO_PID(pidp
);
2177 q
= cg_attach_everywhere(supported
, path
, pid
, path_callback
, userdata
);
2178 if (q
< 0 && r
>= 0)
2185 int cg_migrate_everywhere(CGroupMask supported
, const char *from
, const char *to
, cg_migrate_callback_t to_callback
, void *userdata
) {
2189 if (!path_equal(from
, to
)) {
2190 r
= cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER
, from
, SYSTEMD_CGROUP_CONTROLLER
, to
, CGROUP_REMOVE
);
2195 q
= cg_all_unified();
2201 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2202 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2203 const char *p
= NULL
;
2205 if (!(supported
& bit
))
2209 p
= to_callback(bit
, userdata
);
2214 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER
, to
, cgroup_controller_to_string(c
), p
, 0);
2220 int cg_trim_everywhere(CGroupMask supported
, const char *path
, bool delete_root
) {
2224 r
= cg_trim(SYSTEMD_CGROUP_CONTROLLER
, path
, delete_root
);
2228 q
= cg_all_unified();
2234 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2235 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2237 if (!(supported
& bit
))
2240 (void) cg_trim(cgroup_controller_to_string(c
), path
, delete_root
);
2246 int cg_mask_to_string(CGroupMask mask
, char **ret
) {
2247 _cleanup_free_
char *s
= NULL
;
2248 size_t n
= 0, allocated
= 0;
2259 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2263 if (!(mask
& CGROUP_CONTROLLER_TO_MASK(c
)))
2266 k
= cgroup_controller_to_string(c
);
2269 if (!GREEDY_REALLOC(s
, allocated
, n
+ space
+ l
+ 1))
2274 memcpy(s
+ n
+ space
, k
, l
);
2289 int cg_mask_from_string(const char *value
, CGroupMask
*mask
) {
2294 _cleanup_free_
char *n
= NULL
;
2298 r
= extract_first_word(&value
, &n
, NULL
, 0);
2304 v
= cgroup_controller_from_string(n
);
2308 *mask
|= CGROUP_CONTROLLER_TO_MASK(v
);
2313 int cg_mask_supported(CGroupMask
*ret
) {
2314 CGroupMask mask
= 0;
2317 /* Determines the mask of supported cgroup controllers. Only
2318 * includes controllers we can make sense of and that are
2319 * actually accessible. */
2321 r
= cg_all_unified();
2325 _cleanup_free_
char *root
= NULL
, *controllers
= NULL
, *path
= NULL
;
2327 /* In the unified hierarchy we can read the supported
2328 * and accessible controllers from a the top-level
2329 * cgroup attribute */
2331 r
= cg_get_root_path(&root
);
2335 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, root
, "cgroup.controllers", &path
);
2339 r
= read_one_line_file(path
, &controllers
);
2343 r
= cg_mask_from_string(controllers
, &mask
);
2347 /* Currently, we support the cpu, memory, io and pids
2348 * controller in the unified hierarchy, mask
2349 * everything else off. */
2350 mask
&= CGROUP_MASK_CPU
| CGROUP_MASK_MEMORY
| CGROUP_MASK_IO
| CGROUP_MASK_PIDS
;
2355 /* In the legacy hierarchy, we check whether which
2356 * hierarchies are mounted. */
2358 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2361 n
= cgroup_controller_to_string(c
);
2362 if (controller_is_accessible(n
) >= 0)
2363 mask
|= CGROUP_CONTROLLER_TO_MASK(c
);
2371 int cg_kernel_controllers(Set
*controllers
) {
2372 _cleanup_fclose_
FILE *f
= NULL
;
2375 assert(controllers
);
2377 /* Determines the full list of kernel-known controllers. Might
2378 * include controllers we don't actually support, arbitrary
2379 * named hierarchies and controllers that aren't currently
2380 * accessible (because not mounted). */
2382 f
= fopen("/proc/cgroups", "re");
2384 if (errno
== ENOENT
)
2389 /* Ignore the header line */
2390 (void) read_line(f
, (size_t) -1, NULL
);
2397 if (fscanf(f
, "%ms %*i %*i %i", &controller
, &enabled
) != 2) {
2402 if (ferror(f
) && errno
> 0)
2413 if (!cg_controller_is_valid(controller
)) {
2418 r
= set_consume(controllers
, controller
);
2426 static thread_local CGroupUnified unified_cache
= CGROUP_UNIFIED_UNKNOWN
;
2428 /* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This
2429 * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2430 * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2431 * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
2433 * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
2434 * process management but disable the compat dual layout, we return %true on
2435 * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
2437 static thread_local
bool unified_systemd_v232
;
2439 static int cg_unified_update(void) {
2443 /* Checks if we support the unified hierarchy. Returns an
2444 * error when the cgroup hierarchies aren't mounted yet or we
2445 * have any other trouble determining if the unified hierarchy
2448 if (unified_cache
>= CGROUP_UNIFIED_NONE
)
2451 if (statfs("/sys/fs/cgroup/", &fs
) < 0)
2454 if (F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
))
2455 unified_cache
= CGROUP_UNIFIED_ALL
;
2456 else if (F_TYPE_EQUAL(fs
.f_type
, TMPFS_MAGIC
)) {
2457 if (statfs("/sys/fs/cgroup/unified/", &fs
) == 0 &&
2458 F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
)) {
2459 unified_cache
= CGROUP_UNIFIED_SYSTEMD
;
2460 unified_systemd_v232
= false;
2461 } else if (statfs("/sys/fs/cgroup/systemd/", &fs
) == 0 &&
2462 F_TYPE_EQUAL(fs
.f_type
, CGROUP2_SUPER_MAGIC
)) {
2463 unified_cache
= CGROUP_UNIFIED_SYSTEMD
;
2464 unified_systemd_v232
= true;
2466 if (statfs("/sys/fs/cgroup/systemd/", &fs
) < 0)
2468 if (!F_TYPE_EQUAL(fs
.f_type
, CGROUP_SUPER_MAGIC
))
2470 unified_cache
= CGROUP_UNIFIED_NONE
;
2473 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2474 (unsigned long long) fs
.f_type
);
2481 int cg_unified_controller(const char *controller
) {
2484 r
= cg_unified_update();
2488 if (unified_cache
== CGROUP_UNIFIED_NONE
)
2491 if (unified_cache
>= CGROUP_UNIFIED_ALL
)
2494 return streq_ptr(controller
, SYSTEMD_CGROUP_CONTROLLER
);
2497 int cg_all_unified(void) {
2500 r
= cg_unified_update();
2504 return unified_cache
>= CGROUP_UNIFIED_ALL
;
2507 int cg_hybrid_unified(void) {
2510 r
= cg_unified_update();
2514 return unified_cache
== CGROUP_UNIFIED_SYSTEMD
&& !unified_systemd_v232
;
2517 int cg_unified_flush(void) {
2518 unified_cache
= CGROUP_UNIFIED_UNKNOWN
;
2520 return cg_unified_update();
2523 int cg_enable_everywhere(CGroupMask supported
, CGroupMask mask
, const char *p
) {
2524 _cleanup_free_
char *fs
= NULL
;
2533 r
= cg_all_unified();
2536 if (r
== 0) /* on the legacy hiearchy there's no joining of controllers defined */
2539 r
= cg_get_path(SYSTEMD_CGROUP_CONTROLLER
, p
, "cgroup.subtree_control", &fs
);
2543 for (c
= 0; c
< _CGROUP_CONTROLLER_MAX
; c
++) {
2544 CGroupMask bit
= CGROUP_CONTROLLER_TO_MASK(c
);
2547 if (!(supported
& bit
))
2550 n
= cgroup_controller_to_string(c
);
2552 char s
[1 + strlen(n
) + 1];
2554 s
[0] = mask
& bit
? '+' : '-';
2557 r
= write_string_file(fs
, s
, 0);
2559 log_debug_errno(r
, "Failed to enable controller %s for %s (%s): %m", n
, p
, fs
);
2566 bool cg_is_unified_wanted(void) {
2567 static thread_local
int wanted
= -1;
2570 const bool is_default
= DEFAULT_HIERARCHY
== CGROUP_UNIFIED_ALL
;
2572 /* If we have a cached value, return that. */
2576 /* If the hierarchy is already mounted, then follow whatever
2577 * was chosen for it. */
2578 if (cg_unified_flush() >= 0)
2579 return (wanted
= unified_cache
>= CGROUP_UNIFIED_ALL
);
2581 /* Otherwise, let's see what the kernel command line has to say.
2582 * Since checking is expensive, cache a non-error result. */
2583 r
= proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b
);
2585 return (wanted
= r
> 0 ? b
: is_default
);
2588 bool cg_is_legacy_wanted(void) {
2589 static thread_local
int wanted
= -1;
2591 /* If we have a cached value, return that. */
2595 /* Check if we have cgroups2 already mounted. */
2596 if (cg_unified_flush() >= 0 &&
2597 unified_cache
== CGROUP_UNIFIED_ALL
)
2598 return (wanted
= false);
2600 /* Otherwise, assume that at least partial legacy is wanted,
2601 * since cgroups2 should already be mounted at this point. */
2602 return (wanted
= true);
2605 bool cg_is_hybrid_wanted(void) {
2606 static thread_local
int wanted
= -1;
2609 const bool is_default
= DEFAULT_HIERARCHY
>= CGROUP_UNIFIED_SYSTEMD
;
2610 /* We default to true if the default is "hybrid", obviously,
2611 * but also when the default is "unified", because if we get
2612 * called, it means that unified hierarchy was not mounted. */
2614 /* If we have a cached value, return that. */
2618 /* If the hierarchy is already mounted, then follow whatever
2619 * was chosen for it. */
2620 if (cg_unified_flush() >= 0 &&
2621 unified_cache
== CGROUP_UNIFIED_ALL
)
2622 return (wanted
= false);
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.legacy_systemd_cgroup_controller", &b
);
2628 /* The meaning of the kernel option is reversed wrt. to the return value
2629 * of this function, hence the negation. */
2630 return (wanted
= r
> 0 ? !b
: is_default
);
2633 int cg_weight_parse(const char *s
, uint64_t *ret
) {
2638 *ret
= CGROUP_WEIGHT_INVALID
;
2642 r
= safe_atou64(s
, &u
);
2646 if (u
< CGROUP_WEIGHT_MIN
|| u
> CGROUP_WEIGHT_MAX
)
2653 const uint64_t cgroup_io_limit_defaults
[_CGROUP_IO_LIMIT_TYPE_MAX
] = {
2654 [CGROUP_IO_RBPS_MAX
] = CGROUP_LIMIT_MAX
,
2655 [CGROUP_IO_WBPS_MAX
] = CGROUP_LIMIT_MAX
,
2656 [CGROUP_IO_RIOPS_MAX
] = CGROUP_LIMIT_MAX
,
2657 [CGROUP_IO_WIOPS_MAX
] = CGROUP_LIMIT_MAX
,
2660 static const char* const cgroup_io_limit_type_table
[_CGROUP_IO_LIMIT_TYPE_MAX
] = {
2661 [CGROUP_IO_RBPS_MAX
] = "IOReadBandwidthMax",
2662 [CGROUP_IO_WBPS_MAX
] = "IOWriteBandwidthMax",
2663 [CGROUP_IO_RIOPS_MAX
] = "IOReadIOPSMax",
2664 [CGROUP_IO_WIOPS_MAX
] = "IOWriteIOPSMax",
2667 DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type
, CGroupIOLimitType
);
2669 int cg_cpu_shares_parse(const char *s
, uint64_t *ret
) {
2674 *ret
= CGROUP_CPU_SHARES_INVALID
;
2678 r
= safe_atou64(s
, &u
);
2682 if (u
< CGROUP_CPU_SHARES_MIN
|| u
> CGROUP_CPU_SHARES_MAX
)
2689 int cg_blkio_weight_parse(const char *s
, uint64_t *ret
) {
2694 *ret
= CGROUP_BLKIO_WEIGHT_INVALID
;
2698 r
= safe_atou64(s
, &u
);
2702 if (u
< CGROUP_BLKIO_WEIGHT_MIN
|| u
> CGROUP_BLKIO_WEIGHT_MAX
)
2709 bool is_cgroup_fs(const struct statfs
*s
) {
2710 return is_fs_type(s
, CGROUP_SUPER_MAGIC
) ||
2711 is_fs_type(s
, CGROUP2_SUPER_MAGIC
);
2714 bool fd_is_cgroup_fs(int fd
) {
2717 if (fstatfs(fd
, &s
) < 0)
2720 return is_cgroup_fs(&s
);
2723 static const char *cgroup_controller_table
[_CGROUP_CONTROLLER_MAX
] = {
2724 [CGROUP_CONTROLLER_CPU
] = "cpu",
2725 [CGROUP_CONTROLLER_CPUACCT
] = "cpuacct",
2726 [CGROUP_CONTROLLER_IO
] = "io",
2727 [CGROUP_CONTROLLER_BLKIO
] = "blkio",
2728 [CGROUP_CONTROLLER_MEMORY
] = "memory",
2729 [CGROUP_CONTROLLER_DEVICES
] = "devices",
2730 [CGROUP_CONTROLLER_PIDS
] = "pids",
2733 DEFINE_STRING_TABLE_LOOKUP(cgroup_controller
, CGroupController
);