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 static const char *normalize_controller(const char *controller) {
442 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
444 else if (startswith(controller, "name="))
445 return controller + 5;
450 static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
453 if (!isempty(controller)) {
454 if (!isempty(path) && !isempty(suffix))
455 t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
456 else if (!isempty(path))
457 t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
458 else if (!isempty(suffix))
459 t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
461 t = strappend("/sys/fs/cgroup/", controller);
463 if (!isempty(path) && !isempty(suffix))
464 t = strjoin(path, "/", suffix, NULL);
465 else if (!isempty(path))
474 path_kill_slashes(t);
480 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
482 static __thread bool good = false;
486 if (controller && !cg_controller_is_valid(controller, true))
489 if (_unlikely_(!good)) {
492 r = path_is_mount_point("/sys/fs/cgroup", false);
494 return r < 0 ? r : -ENOENT;
496 /* Cache this to save a few stat()s */
500 p = controller ? normalize_controller(controller) : NULL;
502 return join_path(p, path, suffix, fs);
505 static int check_hierarchy(const char *p) {
510 /* Check if this controller actually really exists */
511 cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(p));
512 strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
513 if (access(cc, F_OK) < 0)
519 int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
525 if (!cg_controller_is_valid(controller, true))
528 /* Normalize the controller syntax */
529 p = normalize_controller(controller);
531 /* Check if this controller actually really exists */
532 r = check_hierarchy(p);
536 return join_path(p, path, suffix, fs);
539 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
544 if (typeflag != FTW_DP)
547 if (ftwbuf->level < 1)
554 int cg_trim(const char *controller, const char *path, bool delete_root) {
555 _cleanup_free_ char *fs = NULL;
560 r = cg_get_path(controller, path, NULL, &fs);
565 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
566 r = errno ? -errno : -EIO;
569 if (rmdir(fs) < 0 && errno != ENOENT)
576 int cg_delete(const char *controller, const char *path) {
577 _cleanup_free_ char *parent = NULL;
582 r = path_get_parent(path, &parent);
586 r = cg_migrate_recursive(controller, path, controller, parent, false, true);
587 return r == -ENOENT ? 0 : r;
590 int cg_attach(const char *controller, const char *path, pid_t pid) {
591 _cleanup_free_ char *fs = NULL;
592 char c[DECIMAL_STR_MAX(pid_t) + 2];
598 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
605 snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
607 return write_string_file(fs, c);
610 int cg_set_group_access(
611 const char *controller,
617 _cleanup_free_ char *fs = NULL;
622 if (mode != (mode_t) -1)
625 r = cg_get_path(controller, path, NULL, &fs);
629 return chmod_and_chown(fs, mode, uid, gid);
632 int cg_set_task_access(
633 const char *controller,
639 _cleanup_free_ char *fs = NULL, *procs = NULL;
644 if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1)
647 if (mode != (mode_t) -1)
650 r = cg_get_path(controller, path, "cgroup.procs", &fs);
654 r = chmod_and_chown(fs, mode, uid, gid);
658 /* Compatibility, Always keep values for "tasks" in sync with
660 r = cg_get_path(controller, path, "tasks", &procs);
664 return chmod_and_chown(procs, mode, uid, gid);
667 int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
668 _cleanup_fclose_ FILE *f = NULL;
677 if (!cg_controller_is_valid(controller, true))
680 controller = normalize_controller(controller);
682 controller = SYSTEMD_CGROUP_CONTROLLER;
685 fs = "/proc/self/cgroup";
687 fs = procfs_file_alloca(pid, "cgroup");
691 return errno == ENOENT ? -ESRCH : -errno;
693 cs = strlen(controller);
695 FOREACH_LINE(line, f, return -errno) {
703 l = strchr(line, ':');
714 FOREACH_WORD_SEPARATOR(w, k, l, ",", state) {
716 if (k == cs && memcmp(w, controller, cs) == 0) {
722 memcmp(w, "name=", 5) == 0 &&
723 memcmp(w+5, controller, cs) == 0) {
743 int cg_install_release_agent(const char *controller, const char *agent) {
744 _cleanup_free_ char *fs = NULL, *contents = NULL;
750 r = cg_get_path(controller, NULL, "release_agent", &fs);
754 r = read_one_line_file(fs, &contents);
758 sc = strstrip(contents);
760 r = write_string_file(fs, agent);
763 } else if (!streq(sc, agent))
768 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
774 r = read_one_line_file(fs, &contents);
778 sc = strstrip(contents);
779 if (streq(sc, "0")) {
780 r = write_string_file(fs, "1");
793 int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
794 _cleanup_fclose_ FILE *f = NULL;
795 pid_t pid = 0, self_pid;
801 r = cg_enumerate_processes(controller, path, &f);
803 return r == -ENOENT ? 1 : r;
807 while ((r = cg_read_pid(f, &pid)) > 0) {
809 if (ignore_self && pid == self_pid)
822 int cg_is_empty_by_spec(const char *spec, bool ignore_self) {
823 _cleanup_free_ char *controller = NULL, *path = NULL;
828 r = cg_split_spec(spec, &controller, &path);
832 return cg_is_empty(controller, path, ignore_self);
835 int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
836 _cleanup_closedir_ DIR *d = NULL;
842 r = cg_is_empty(controller, path, ignore_self);
846 r = cg_enumerate_subgroups(controller, path, &d);
848 return r == -ENOENT ? 1 : r;
850 while ((r = cg_read_subgroup(d, &fn)) > 0) {
851 _cleanup_free_ char *p = NULL;
853 p = strjoin(path, "/", fn, NULL);
858 r = cg_is_empty_recursive(controller, p, ignore_self);
869 int cg_split_spec(const char *spec, char **controller, char **path) {
871 char *t = NULL, *u = NULL;
872 _cleanup_free_ char *v = NULL;
877 if (!path_is_safe(spec))
885 path_kill_slashes(t);
895 e = strchr(spec, ':');
897 if (!cg_controller_is_valid(spec, true))
901 t = strdup(normalize_controller(spec));
914 v = strndup(spec, e-spec);
917 t = strdup(normalize_controller(v));
920 if (!cg_controller_is_valid(t, true)) {
930 if (!path_is_safe(u) ||
931 !path_is_absolute(u)) {
937 path_kill_slashes(u);
952 int cg_join_spec(const char *controller, const char *path, char **spec) {
958 controller = "systemd";
960 if (!cg_controller_is_valid(controller, true))
963 controller = normalize_controller(controller);
966 if (!path_is_absolute(path))
969 s = strjoin(controller, ":", path, NULL);
973 path_kill_slashes(s + strlen(controller) + 1);
979 int cg_mangle_path(const char *path, char **result) {
980 _cleanup_free_ char *c = NULL, *p = NULL;
987 /* First check if it already is a filesystem path */
988 if (path_startswith(path, "/sys/fs/cgroup")) {
994 path_kill_slashes(t);
999 /* Otherwise treat it as cg spec */
1000 r = cg_split_spec(path, &c, &p);
1004 return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
1007 int cg_get_root_path(char **path) {
1013 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
1017 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
1025 char **cg_shorten_controllers(char **controllers) {
1031 for (f = controllers, t = controllers; *f; f++) {
1035 p = normalize_controller(*f);
1037 if (streq(p, "systemd")) {
1042 if (!cg_controller_is_valid(p, true)) {
1043 log_warning("Controller %s is not valid, removing from controllers list.", p);
1048 r = check_hierarchy(p);
1050 log_debug("Controller %s is not available, removing from controllers list.", p);
1059 return strv_uniq(controllers);
1062 int cg_pid_get_path_shifted(pid_t pid, char **root, char **cgroup) {
1063 _cleanup_free_ char *cg_root = NULL;
1064 char *cg_process, *p;
1067 r = cg_get_root_path(&cg_root);
1071 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
1075 p = path_startswith(cg_process, cg_root);
1094 cg_process[p-cg_process] = 0;
1102 int cg_path_decode_unit(const char *cgroup, char **unit){
1103 char *p, *e, *c, *s, *k;
1108 e = strchrnul(cgroup, '/');
1109 c = strndupa(cgroup, e - cgroup);
1112 /* Could this be a valid unit name? */
1113 if (!unit_name_is_valid(c, true))
1116 if (!unit_name_is_template(c))
1122 e += strspn(e, "/");
1124 p = strchrnul(e, '/');
1125 k = strndupa(e, p - e);
1128 if (!unit_name_is_valid(k, false))
1141 static const char *skip_slices(const char *p) {
1142 /* Skips over all slice assignments */
1147 p += strspn(p, "/");
1149 n = strcspn(p, "/");
1150 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
1157 int cg_path_get_unit(const char *path, char **unit) {
1163 e = skip_slices(path);
1165 return cg_path_decode_unit(e, unit);
1168 int cg_pid_get_unit(pid_t pid, char **unit) {
1169 _cleanup_free_ char *cgroup = NULL;
1174 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1178 return cg_path_get_unit(cgroup, unit);
1181 static const char *skip_user(const char *p) {
1186 p += strspn(p, "/");
1188 n = strcspn(p, "/");
1189 if (n <= 5 || memcmp(p + n - 5, ".user", 5) != 0)
1193 p += strspn(p, "/");
1198 static const char *skip_session(const char *p) {
1203 p += strspn(p, "/");
1205 n = strcspn(p, "/");
1206 if (n <= 8 || memcmp(p + n - 8, ".session", 8) != 0)
1210 p += strspn(p, "/");
1215 static const char *skip_systemd_label(const char *p) {
1220 p += strspn(p, "/");
1222 n = strcspn(p, "/");
1223 if (n < 8 || memcmp(p, "systemd-", 8) != 0)
1227 p += strspn(p, "/");
1232 int cg_path_get_user_unit(const char *path, char **unit) {
1238 /* We always have to parse the path from the beginning as unit
1239 * cgroups might have arbitrary child cgroups and we shouldn't get
1240 * confused by those */
1242 /* Skip slices, if there are any */
1243 e = skip_slices(path);
1245 /* Skip the user name, if there is one */
1248 /* Skip the session ID, require that there is one */
1249 e = skip_session(e);
1253 /* Skip the systemd cgroup, if there is one */
1254 e = skip_systemd_label(e);
1256 return cg_path_decode_unit(e, unit);
1259 int cg_pid_get_user_unit(pid_t pid, char **unit) {
1260 _cleanup_free_ char *cgroup = NULL;
1265 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1269 return cg_path_get_user_unit(cgroup, unit);
1272 int cg_path_get_machine_name(const char *path, char **machine) {
1273 const char *e, *n, *x;
1279 /* Skip slices, if there are any */
1280 e = skip_slices(path);
1282 n = strchrnul(e, '/');
1286 s = strndupa(e, n - e);
1289 x = endswith(s, ".machine");
1293 r = strndup(s, x - s);
1301 int cg_pid_get_machine_name(pid_t pid, char **machine) {
1302 _cleanup_free_ char *cgroup = NULL;
1307 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1311 return cg_path_get_machine_name(cgroup, machine);
1314 int cg_path_get_session(const char *path, char **session) {
1321 /* Skip slices, if there are any */
1322 e = skip_slices(path);
1324 /* Skip the user name, if there is one */
1327 n = strchrnul(e, '/');
1330 if (memcmp(n - 8, ".session", 8) != 0)
1333 s = strndup(e, n - e - 8);
1341 int cg_pid_get_session(pid_t pid, char **session) {
1342 _cleanup_free_ char *cgroup = NULL;
1347 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1351 return cg_path_get_session(cgroup, session);
1354 int cg_path_get_owner_uid(const char *path, uid_t *uid) {
1361 /* Skip slices, if there are any */
1362 e = skip_slices(path);
1364 n = strchrnul(e, '/');
1367 if (memcmp(n - 5, ".user", 5) != 0)
1370 s = strndupa(e, n - e - 5);
1374 return parse_uid(s, uid);
1377 int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1378 _cleanup_free_ char *cgroup = NULL;
1383 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1387 return cg_path_get_owner_uid(cgroup, uid);
1390 int cg_path_get_slice(const char *p, char **slice) {
1391 const char *e = NULL;
1400 p += strspn(p, "/");
1402 n = strcspn(p, "/");
1403 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
1424 int cg_pid_get_slice(pid_t pid, char **slice) {
1425 _cleanup_free_ char *cgroup = NULL;
1430 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1434 return cg_path_get_slice(cgroup, slice);
1437 int cg_controller_from_attr(const char *attr, char **controller) {
1444 if (!filename_is_safe(attr))
1447 dot = strchr(attr, '.');
1453 c = strndup(attr, dot - attr);
1457 if (!cg_controller_is_valid(c, false)) {
1466 char *cg_escape(const char *p) {
1467 bool need_prefix = false;
1469 /* This implements very minimal escaping for names to be used
1470 * as file names in the cgroup tree: any name which might
1471 * conflict with a kernel name or is prefixed with '_' is
1472 * prefixed with a '_'. That way, when reading cgroup names it
1473 * is sufficient to remove a single prefixing underscore if
1476 /* The return value of this function (unlike cg_unescape())
1482 streq(p, "notify_on_release") ||
1483 streq(p, "release_agent") ||
1489 dot = strrchr(p, '.');
1492 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1497 n = strndupa(p, dot - p);
1499 if (check_hierarchy(n) >= 0)
1506 return strappend("_", p);
1511 char *cg_unescape(const char *p) {
1514 /* The return value of this function (unlike cg_escape())
1515 * doesn't need free()! */
1523 #define CONTROLLER_VALID \
1525 "abcdefghijklmnopqrstuvwxyz" \
1526 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
1529 bool cg_controller_is_valid(const char *p, bool allow_named) {
1536 s = startswith(p, "name=");
1541 if (*p == 0 || *p == '_')
1544 for (t = p; *t; t++)
1545 if (!strchr(CONTROLLER_VALID, *t))
1548 if (t - p > FILENAME_MAX)
1554 int cg_slice_to_path(const char *unit, char **ret) {
1555 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1561 if (!unit_name_is_valid(unit, false))
1564 if (!endswith(unit, ".slice"))
1567 p = unit_name_to_prefix(unit);
1571 dash = strchr(p, '-');
1573 _cleanup_free_ char *escaped = NULL;
1574 char n[dash - p + sizeof(".slice")];
1576 strcpy(stpncpy(n, p, dash - p), ".slice");
1578 if (!unit_name_is_valid(n, false))
1581 escaped = cg_escape(n);
1585 if (!strextend(&s, escaped, "/", NULL))
1588 dash = strchr(dash+1, '-');
1591 e = cg_escape(unit);
1595 if (!strextend(&s, e, NULL))
1604 int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1605 _cleanup_free_ char *p = NULL;
1608 r = cg_get_path(controller, path, attribute, &p);
1612 return write_string_file(p, value);
1615 static const char mask_names[] =
1622 int cg_create_with_mask(CGroupControllerMask mask, const char *path) {
1623 CGroupControllerMask bit = 1;
1627 /* This one will create a cgroup in our private tree, but also
1628 * duplicate it in the trees specified in mask, and remove it
1631 /* First create the cgroup in our own hierarchy. */
1632 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1636 /* Then, do the same in the other hierarchies */
1637 NULSTR_FOREACH(n, mask_names) {
1641 cg_trim(n, path, true);
1649 int cg_attach_with_mask(CGroupControllerMask mask, const char *path, pid_t pid) {
1650 CGroupControllerMask bit = 1;
1654 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
1656 NULSTR_FOREACH(n, mask_names) {
1658 cg_attach(n, path, pid);
1660 char prefix[strlen(path) + 1], *slash;
1662 /* OK, this one is a bit harder... Now we need
1663 * to add to the closest parent cgroup we
1665 strcpy(prefix, path);
1666 while ((slash = strrchr(prefix, '/'))) {
1670 q = cg_attach(n, prefix, pid);
1682 int cg_attach_many_with_mask(CGroupControllerMask mask, const char *path, Set* pids) {
1687 SET_FOREACH(pidp, pids, i) {
1688 pid_t pid = PTR_TO_LONG(pidp);
1691 k = cg_attach_with_mask(mask, path, pid);
1699 int cg_migrate_with_mask(CGroupControllerMask mask, const char *from, const char *to) {
1700 CGroupControllerMask bit = 1;
1704 if (path_equal(from, to))
1707 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1709 NULSTR_FOREACH(n, mask_names) {
1711 cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, to, false, false);
1713 char prefix[strlen(to) + 1], *slash;
1716 while ((slash = strrchr(prefix, '/'))) {
1721 q = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, prefix, false, false);
1733 int cg_trim_with_mask(CGroupControllerMask mask, const char *path, bool delete_root) {
1734 CGroupControllerMask bit = 1;
1738 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1742 NULSTR_FOREACH(n, mask_names) {
1744 cg_trim(n, path, delete_root);
1752 CGroupControllerMask cg_mask_supported(void) {
1753 CGroupControllerMask bit = 1, mask = 0;
1756 NULSTR_FOREACH(n, mask_names) {
1757 if (check_hierarchy(n) >= 0)