1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include <sys/types.h>
32 #include "cgroup-util.h"
37 #include "path-util.h"
39 #include "unit-name.h"
43 int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
44 _cleanup_free_ char *fs = NULL;
50 r = cg_get_path(controller, path, "cgroup.procs", &fs);
62 int cg_read_pid(FILE *f, pid_t *_pid) {
65 /* Note that the cgroup.procs might contain duplicates! See
66 * cgroups.txt for details. */
72 if (fscanf(f, "%lu", &ul) != 1) {
77 return errno ? -errno : -EIO;
87 int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
88 _cleanup_free_ char *fs = NULL;
94 /* This is not recursive! */
96 r = cg_get_path(controller, path, NULL, &fs);
108 int cg_read_subgroup(DIR *d, char **fn) {
114 FOREACH_DIRENT(de, d, return -errno) {
117 if (de->d_type != DT_DIR)
120 if (streq(de->d_name, ".") ||
121 streq(de->d_name, ".."))
124 b = strdup(de->d_name);
135 int cg_rmdir(const char *controller, const char *path) {
136 _cleanup_free_ char *p = NULL;
139 r = cg_get_path(controller, path, NULL, &p);
144 if (r < 0 && errno != ENOENT)
150 int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
151 _cleanup_set_free_ Set *allocated_set = NULL;
158 /* This goes through the tasks list and kills them all. This
159 * is repeated until no further processes are added to the
160 * tasks list, to properly handle forking processes */
163 s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
171 _cleanup_fclose_ FILE *f = NULL;
175 r = cg_enumerate_processes(controller, path, &f);
177 if (ret >= 0 && r != -ENOENT)
183 while ((r = cg_read_pid(f, &pid)) > 0) {
185 if (ignore_self && pid == my_pid)
188 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
191 /* If we haven't killed this process yet, kill
193 if (kill(pid, sig) < 0) {
194 if (ret >= 0 && errno != ESRCH)
196 } else if (ret == 0) {
206 r = set_put(s, LONG_TO_PTR(pid));
222 /* To avoid racing against processes which fork
223 * quicker than we can kill them we repeat this until
224 * no new pids need to be killed. */
231 int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
232 _cleanup_set_free_ Set *allocated_set = NULL;
233 _cleanup_closedir_ DIR *d = NULL;
241 s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
246 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
248 r = cg_enumerate_subgroups(controller, path, &d);
250 if (ret >= 0 && r != -ENOENT)
256 while ((r = cg_read_subgroup(d, &fn)) > 0) {
257 _cleanup_free_ char *p = NULL;
259 p = strjoin(path, "/", fn, NULL);
264 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
265 if (ret >= 0 && r != 0)
269 if (ret >= 0 && r < 0)
273 r = cg_rmdir(controller, path);
274 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
281 int cg_kill_recursive_and_wait(const char *controller, const char *path, bool rem) {
286 /* This safely kills all processes; first it sends a SIGTERM,
287 * then checks 8 times after 200ms whether the group is now
288 * empty, then kills everything that is left with SIGKILL and
289 * finally checks 5 times after 200ms each whether the group
290 * is finally empty. */
292 for (i = 0; i < 15; i++) {
302 r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL);
306 usleep(200 * USEC_PER_MSEC);
312 int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
314 _cleanup_set_free_ Set *s = NULL;
323 s = set_new(trivial_hash_func, trivial_compare_func);
330 _cleanup_fclose_ FILE *f = NULL;
334 r = cg_enumerate_processes(cfrom, pfrom, &f);
336 if (ret >= 0 && r != -ENOENT)
342 while ((r = cg_read_pid(f, &pid)) > 0) {
344 /* This might do weird stuff if we aren't a
345 * single-threaded program. However, we
346 * luckily know we are not */
347 if (ignore_self && pid == my_pid)
350 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
353 r = cg_attach(cto, pto, pid);
355 if (ret >= 0 && r != -ESRCH)
362 r = set_put(s, LONG_TO_PTR(pid));
382 int cg_migrate_recursive(
390 _cleanup_closedir_ DIR *d = NULL;
399 ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
401 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
403 if (ret >= 0 && r != -ENOENT)
409 while ((r = cg_read_subgroup(d, &fn)) > 0) {
410 _cleanup_free_ char *p = NULL;
412 p = strjoin(pfrom, "/", fn, NULL);
421 r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
422 if (r != 0 && ret >= 0)
426 if (r < 0 && ret >= 0)
430 r = cg_rmdir(cfrom, pfrom);
431 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
438 int cg_migrate_recursive_fallback(
453 r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
455 char prefix[strlen(pto) + 1];
457 /* This didn't work? Then let's try all prefixes of the destination */
459 PATH_FOREACH_PREFIX(prefix, pto) {
460 r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
469 static const char *normalize_controller(const char *controller) {
473 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
475 else if (startswith(controller, "name="))
476 return controller + 5;
481 static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
484 if (!isempty(controller)) {
485 if (!isempty(path) && !isempty(suffix))
486 t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
487 else if (!isempty(path))
488 t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
489 else if (!isempty(suffix))
490 t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
492 t = strappend("/sys/fs/cgroup/", controller);
494 if (!isempty(path) && !isempty(suffix))
495 t = strjoin(path, "/", suffix, NULL);
496 else if (!isempty(path))
505 path_kill_slashes(t);
511 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
513 static __thread bool good = false;
517 if (controller && !cg_controller_is_valid(controller, true))
520 if (_unlikely_(!good)) {
523 r = path_is_mount_point("/sys/fs/cgroup", false);
525 return r < 0 ? r : -ENOENT;
527 /* Cache this to save a few stat()s */
531 p = controller ? normalize_controller(controller) : NULL;
533 return join_path(p, path, suffix, fs);
536 static int check_hierarchy(const char *p) {
541 /* Check if this controller actually really exists */
542 cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(p));
543 strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
544 if (access(cc, F_OK) < 0)
550 int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
556 if (!cg_controller_is_valid(controller, true))
559 /* Normalize the controller syntax */
560 p = normalize_controller(controller);
562 /* Check if this controller actually really exists */
563 r = check_hierarchy(p);
567 return join_path(p, path, suffix, fs);
570 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
575 if (typeflag != FTW_DP)
578 if (ftwbuf->level < 1)
585 int cg_trim(const char *controller, const char *path, bool delete_root) {
586 _cleanup_free_ char *fs = NULL;
591 r = cg_get_path(controller, path, NULL, &fs);
596 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
597 r = errno ? -errno : -EIO;
600 if (rmdir(fs) < 0 && errno != ENOENT)
607 int cg_delete(const char *controller, const char *path) {
608 _cleanup_free_ char *parent = NULL;
613 r = path_get_parent(path, &parent);
617 r = cg_migrate_recursive(controller, path, controller, parent, false, true);
618 return r == -ENOENT ? 0 : r;
621 int cg_attach(const char *controller, const char *path, pid_t pid) {
622 _cleanup_free_ char *fs = NULL;
623 char c[DECIMAL_STR_MAX(pid_t) + 2];
629 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
636 snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
638 return write_string_file(fs, c);
641 int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
648 r = cg_attach(controller, path, pid);
650 char prefix[strlen(path) + 1];
652 /* This didn't work? Then let's try all prefixes of
655 PATH_FOREACH_PREFIX(prefix, path) {
656 r = cg_attach(controller, prefix, pid);
665 int cg_set_group_access(
666 const char *controller,
672 _cleanup_free_ char *fs = NULL;
677 if (mode != (mode_t) -1)
680 r = cg_get_path(controller, path, NULL, &fs);
684 return chmod_and_chown(fs, mode, uid, gid);
687 int cg_set_task_access(
688 const char *controller,
694 _cleanup_free_ char *fs = NULL, *procs = NULL;
699 if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1)
702 if (mode != (mode_t) -1)
705 r = cg_get_path(controller, path, "cgroup.procs", &fs);
709 r = chmod_and_chown(fs, mode, uid, gid);
713 /* Compatibility, Always keep values for "tasks" in sync with
715 r = cg_get_path(controller, path, "tasks", &procs);
719 return chmod_and_chown(procs, mode, uid, gid);
722 int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
723 _cleanup_fclose_ FILE *f = NULL;
732 if (!cg_controller_is_valid(controller, true))
735 controller = normalize_controller(controller);
737 controller = SYSTEMD_CGROUP_CONTROLLER;
740 fs = "/proc/self/cgroup";
742 fs = procfs_file_alloca(pid, "cgroup");
746 return errno == ENOENT ? -ESRCH : -errno;
748 cs = strlen(controller);
750 FOREACH_LINE(line, f, return -errno) {
758 l = strchr(line, ':');
769 FOREACH_WORD_SEPARATOR(w, k, l, ",", state) {
771 if (k == cs && memcmp(w, controller, cs) == 0) {
777 memcmp(w, "name=", 5) == 0 &&
778 memcmp(w+5, controller, cs) == 0) {
798 int cg_install_release_agent(const char *controller, const char *agent) {
799 _cleanup_free_ char *fs = NULL, *contents = NULL;
805 r = cg_get_path(controller, NULL, "release_agent", &fs);
809 r = read_one_line_file(fs, &contents);
813 sc = strstrip(contents);
815 r = write_string_file(fs, agent);
818 } else if (!streq(sc, agent))
823 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
829 r = read_one_line_file(fs, &contents);
833 sc = strstrip(contents);
834 if (streq(sc, "0")) {
835 r = write_string_file(fs, "1");
848 int cg_uninstall_release_agent(const char *controller) {
849 _cleanup_free_ char *fs = NULL;
852 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
856 r = write_string_file(fs, "0");
863 r = cg_get_path(controller, NULL, "release_agent", &fs);
867 r = write_string_file(fs, "");
874 int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
875 _cleanup_fclose_ FILE *f = NULL;
876 pid_t pid = 0, self_pid;
882 r = cg_enumerate_processes(controller, path, &f);
884 return r == -ENOENT ? 1 : r;
888 while ((r = cg_read_pid(f, &pid)) > 0) {
890 if (ignore_self && pid == self_pid)
903 int cg_is_empty_by_spec(const char *spec, bool ignore_self) {
904 _cleanup_free_ char *controller = NULL, *path = NULL;
909 r = cg_split_spec(spec, &controller, &path);
913 return cg_is_empty(controller, path, ignore_self);
916 int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
917 _cleanup_closedir_ DIR *d = NULL;
923 r = cg_is_empty(controller, path, ignore_self);
927 r = cg_enumerate_subgroups(controller, path, &d);
929 return r == -ENOENT ? 1 : r;
931 while ((r = cg_read_subgroup(d, &fn)) > 0) {
932 _cleanup_free_ char *p = NULL;
934 p = strjoin(path, "/", fn, NULL);
939 r = cg_is_empty_recursive(controller, p, ignore_self);
950 int cg_split_spec(const char *spec, char **controller, char **path) {
952 char *t = NULL, *u = NULL;
953 _cleanup_free_ char *v = NULL;
958 if (!path_is_safe(spec))
966 path_kill_slashes(t);
976 e = strchr(spec, ':');
978 if (!cg_controller_is_valid(spec, true))
982 t = strdup(normalize_controller(spec));
995 v = strndup(spec, e-spec);
998 t = strdup(normalize_controller(v));
1001 if (!cg_controller_is_valid(t, true)) {
1006 if (streq(e+1, "")) {
1019 if (!path_is_safe(u) ||
1020 !path_is_absolute(u)) {
1026 path_kill_slashes(u);
1042 int cg_join_spec(const char *controller, const char *path, char **spec) {
1048 controller = "systemd";
1050 if (!cg_controller_is_valid(controller, true))
1053 controller = normalize_controller(controller);
1056 if (!path_is_absolute(path))
1059 s = strjoin(controller, ":", path, NULL);
1063 path_kill_slashes(s + strlen(controller) + 1);
1069 int cg_mangle_path(const char *path, char **result) {
1070 _cleanup_free_ char *c = NULL, *p = NULL;
1077 /* First check if it already is a filesystem path */
1078 if (path_startswith(path, "/sys/fs/cgroup")) {
1084 path_kill_slashes(t);
1089 /* Otherwise treat it as cg spec */
1090 r = cg_split_spec(path, &c, &p);
1094 return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
1097 int cg_get_root_path(char **path) {
1103 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
1107 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
1115 char **cg_shorten_controllers(char **controllers) {
1121 for (f = controllers, t = controllers; *f; f++) {
1125 p = normalize_controller(*f);
1127 if (streq(p, "systemd")) {
1132 if (!cg_controller_is_valid(p, true)) {
1133 log_warning("Controller %s is not valid, removing from controllers list.", p);
1138 r = check_hierarchy(p);
1140 log_debug("Controller %s is not available, removing from controllers list.", p);
1149 return strv_uniq(controllers);
1152 int cg_pid_get_path_shifted(pid_t pid, char **root, char **cgroup) {
1153 _cleanup_free_ char *cg_root = NULL;
1154 char *cg_process, *p;
1157 r = cg_get_root_path(&cg_root);
1161 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
1165 p = path_startswith(cg_process, cg_root);
1184 cg_process[p-cg_process] = 0;
1192 int cg_path_decode_unit(const char *cgroup, char **unit){
1198 e = strchrnul(cgroup, '/');
1199 c = strndupa(cgroup, e - cgroup);
1202 if (!unit_name_is_valid(c, false))
1213 static const char *skip_slices(const char *p) {
1214 /* Skips over all slice assignments */
1219 p += strspn(p, "/");
1221 n = strcspn(p, "/");
1222 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
1229 int cg_path_get_unit(const char *path, char **unit) {
1235 e = skip_slices(path);
1237 return cg_path_decode_unit(e, unit);
1240 int cg_pid_get_unit(pid_t pid, char **unit) {
1241 _cleanup_free_ char *cgroup = NULL;
1246 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1250 return cg_path_get_unit(cgroup, unit);
1253 static const char *skip_session(const char *p) {
1258 p += strspn(p, "/");
1260 n = strcspn(p, "/");
1261 if (n <= 12 || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
1265 p += strspn(p, "/");
1270 int cg_path_get_user_unit(const char *path, char **unit) {
1276 /* We always have to parse the path from the beginning as unit
1277 * cgroups might have arbitrary child cgroups and we shouldn't get
1278 * confused by those */
1280 /* Skip slices, if there are any */
1281 e = skip_slices(path);
1283 /* Skip the session scope, require that there is one */
1284 e = skip_session(e);
1288 /* And skip more slices */
1291 return cg_path_decode_unit(e, unit);
1294 int cg_pid_get_user_unit(pid_t pid, char **unit) {
1295 _cleanup_free_ char *cgroup = NULL;
1300 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1304 return cg_path_get_user_unit(cgroup, unit);
1307 int cg_path_get_machine_name(const char *path, char **machine) {
1308 const char *e, *n, *x;
1315 /* Skip slices, if there are any */
1316 e = skip_slices(path);
1318 n = strchrnul(e, '/');
1322 s = strndupa(e, n - e);
1325 x = startswith(s, "machine-");
1328 if (!endswith(x, ".scope"))
1335 r = strndup(x, l - 6);
1343 int cg_pid_get_machine_name(pid_t pid, char **machine) {
1344 _cleanup_free_ char *cgroup = NULL;
1349 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1353 return cg_path_get_machine_name(cgroup, machine);
1356 int cg_path_get_session(const char *path, char **session) {
1357 const char *e, *n, *x;
1364 /* Skip slices, if there are any */
1365 e = skip_slices(path);
1367 n = strchrnul(e, '/');
1371 s = strndupa(e, n - e);
1374 x = startswith(s, "session-");
1377 if (!endswith(x, ".scope"))
1384 r = strndup(x, l - 6);
1392 int cg_pid_get_session(pid_t pid, char **session) {
1393 _cleanup_free_ char *cgroup = NULL;
1398 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1402 return cg_path_get_session(cgroup, session);
1405 int cg_path_get_owner_uid(const char *path, uid_t *uid) {
1406 _cleanup_free_ char *slice = NULL;
1414 r = cg_path_get_slice(path, &slice);
1418 e = startswith(slice, "user-");
1421 if (!endswith(slice, ".slice"))
1424 s = strndupa(e, strlen(e) - 6);
1428 return parse_uid(s, uid);
1431 int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1432 _cleanup_free_ char *cgroup = NULL;
1437 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1441 return cg_path_get_owner_uid(cgroup, uid);
1444 int cg_path_get_slice(const char *p, char **slice) {
1445 const char *e = NULL;
1454 p += strspn(p, "/");
1456 n = strcspn(p, "/");
1457 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
1478 int cg_pid_get_slice(pid_t pid, char **slice) {
1479 _cleanup_free_ char *cgroup = NULL;
1484 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1488 return cg_path_get_slice(cgroup, slice);
1491 int cg_controller_from_attr(const char *attr, char **controller) {
1498 if (!filename_is_safe(attr))
1501 dot = strchr(attr, '.');
1507 c = strndup(attr, dot - attr);
1511 if (!cg_controller_is_valid(c, false)) {
1520 char *cg_escape(const char *p) {
1521 bool need_prefix = false;
1523 /* This implements very minimal escaping for names to be used
1524 * as file names in the cgroup tree: any name which might
1525 * conflict with a kernel name or is prefixed with '_' is
1526 * prefixed with a '_'. That way, when reading cgroup names it
1527 * is sufficient to remove a single prefixing underscore if
1530 /* The return value of this function (unlike cg_unescape())
1536 streq(p, "notify_on_release") ||
1537 streq(p, "release_agent") ||
1543 dot = strrchr(p, '.');
1546 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1551 n = strndupa(p, dot - p);
1553 if (check_hierarchy(n) >= 0)
1560 return strappend("_", p);
1565 char *cg_unescape(const char *p) {
1568 /* The return value of this function (unlike cg_escape())
1569 * doesn't need free()! */
1577 #define CONTROLLER_VALID \
1581 bool cg_controller_is_valid(const char *p, bool allow_named) {
1588 s = startswith(p, "name=");
1593 if (*p == 0 || *p == '_')
1596 for (t = p; *t; t++)
1597 if (!strchr(CONTROLLER_VALID, *t))
1600 if (t - p > FILENAME_MAX)
1606 int cg_slice_to_path(const char *unit, char **ret) {
1607 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1613 if (!unit_name_is_valid(unit, false))
1616 if (!endswith(unit, ".slice"))
1619 p = unit_name_to_prefix(unit);
1623 dash = strchr(p, '-');
1625 _cleanup_free_ char *escaped = NULL;
1626 char n[dash - p + sizeof(".slice")];
1628 strcpy(stpncpy(n, p, dash - p), ".slice");
1630 if (!unit_name_is_valid(n, false))
1633 escaped = cg_escape(n);
1637 if (!strextend(&s, escaped, "/", NULL))
1640 dash = strchr(dash+1, '-');
1643 e = cg_escape(unit);
1647 if (!strextend(&s, e, NULL))
1656 int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1657 _cleanup_free_ char *p = NULL;
1660 r = cg_get_path(controller, path, attribute, &p);
1664 return write_string_file(p, value);
1667 static const char mask_names[] =
1674 int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
1675 CGroupControllerMask bit = 1;
1679 /* This one will create a cgroup in our private tree, but also
1680 * duplicate it in the trees specified in mask, and remove it
1683 /* First create the cgroup in our own hierarchy. */
1684 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1688 /* Then, do the same in the other hierarchies */
1689 NULSTR_FOREACH(n, mask_names) {
1692 else if (supported & bit)
1693 cg_trim(n, path, true);
1701 int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid) {
1702 CGroupControllerMask bit = 1;
1706 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
1710 NULSTR_FOREACH(n, mask_names) {
1711 if (supported & bit)
1712 cg_attach_fallback(n, path, pid);
1720 int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids) {
1725 SET_FOREACH(pidp, pids, i) {
1726 pid_t pid = PTR_TO_LONG(pidp);
1729 q = cg_attach_everywhere(supported, path, pid);
1737 int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to) {
1738 CGroupControllerMask bit = 1;
1742 if (!path_equal(from, to)) {
1743 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1748 NULSTR_FOREACH(n, mask_names) {
1749 if (supported & bit)
1750 cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, to, false, false);
1758 int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
1759 CGroupControllerMask bit = 1;
1763 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1767 NULSTR_FOREACH(n, mask_names) {
1768 if (supported & bit)
1769 cg_trim(n, path, delete_root);
1777 CGroupControllerMask cg_mask_supported(void) {
1778 CGroupControllerMask bit = 1, mask = 0;
1781 NULSTR_FOREACH(n, mask_names) {
1782 if (check_hierarchy(n) >= 0)