#include <ftw.h>
#include "cgroup-util.h"
-#include "log.h"
#include "set.h"
#include "macro.h"
#include "util.h"
+#include "formats-util.h"
#include "path-util.h"
-#include "strv.h"
-#include "unit-name.h"
#include "fileio.h"
#include "special.h"
+#include "mkdir.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
_cleanup_free_ char *fs = NULL;
* tasks list, to properly handle forking processes */
if (!s) {
- s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
+ s = allocated_set = set_new(NULL);
if (!s)
return -ENOMEM;
}
if (kill(pid, sig) < 0) {
if (ret >= 0 && errno != ESRCH)
ret = -errno;
- } else if (ret == 0) {
-
- if (sigcont)
+ } else {
+ if (sigcont && sig != SIGKILL)
kill(pid, SIGCONT);
- ret = 1;
+ if (ret == 0)
+ ret = 1;
}
done = false;
assert(sig >= 0);
if (!s) {
- s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
+ s = allocated_set = set_new(NULL);
if (!s)
return -ENOMEM;
}
return ret;
}
-int cg_kill_recursive_and_wait(const char *controller, const char *path, bool rem) {
- unsigned i;
-
- assert(path);
-
- /* This safely kills all processes; first it sends a SIGTERM,
- * then checks 8 times after 200ms whether the group is now
- * empty, then kills everything that is left with SIGKILL and
- * finally checks 5 times after 200ms each whether the group
- * is finally empty. */
-
- for (i = 0; i < 15; i++) {
- int sig, r;
-
- if (i <= 0)
- sig = SIGTERM;
- else if (i == 9)
- sig = SIGKILL;
- else
- sig = 0;
-
- r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL);
- if (r <= 0)
- return r;
-
- usleep(200 * USEC_PER_MSEC);
- }
-
- return 0;
-}
-
int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
bool done = false;
_cleanup_set_free_ Set *s = NULL;
assert(cto);
assert(pto);
- s = set_new(trivial_hash_func, trivial_compare_func);
+ s = set_new(NULL);
if (!s)
return -ENOMEM;
/* This didn't work? Then let's try all prefixes of the destination */
- strcpy(prefix, pto);
- for (;;) {
- char *slash;
-
- slash = strrchr(prefix, '/');
- if (!slash)
- break;
-
- *slash = 0;
-
+ PATH_FOREACH_PREFIX(prefix, pto) {
r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
if (r >= 0)
break;
}
}
- return r;
+ return 0;
}
static const char *normalize_controller(const char *controller) {
assert(controller);
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
- return "systemd";
+ return "elogind";
else if (startswith(controller, "name="))
return controller + 5;
else
if (!t)
return -ENOMEM;
- path_kill_slashes(t);
-
- *fs = t;
+ *fs = path_kill_slashes(t);
return 0;
}
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
const char *p;
- static __thread bool good = false;
+ static thread_local bool good = false;
assert(fs);
int r;
r = path_is_mount_point("/sys/fs/cgroup", false);
- if (r <= 0)
- return r < 0 ? r : -ENOENT;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
/* Cache this to save a few stat()s */
good = true;
}
static int check_hierarchy(const char *p) {
- char *cc;
+ const char *cc;
assert(p);
+ if (!filename_is_valid(p))
+ return 0;
+
/* Check if this controller actually really exists */
- cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(p));
- strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
- if (access(cc, F_OK) < 0)
+ cc = strjoina("/sys/fs/cgroup/", p);
+ if (laccess(cc, F_OK) < 0)
return -errno;
return 0;
return r == -ENOENT ? 0 : r;
}
+int cg_create(const char *controller, const char *path) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ r = cg_get_path_and_check(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ r = mkdir_parents(fs, 0755);
+ if (r < 0)
+ return r;
+
+ if (mkdir(fs, 0755) < 0) {
+
+ if (errno == EEXIST)
+ return 0;
+
+ return -errno;
+ }
+
+ return 1;
+}
+
+int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
+ int r, q;
+
+ assert(pid >= 0);
+
+ r = cg_create(controller, path);
+ if (r < 0)
+ return r;
+
+ q = cg_attach(controller, path, pid);
+ if (q < 0)
+ return q;
+
+ /* This does not remove the cgroup on failure */
+ return r;
+}
+
int cg_attach(const char *controller, const char *path, pid_t pid) {
_cleanup_free_ char *fs = NULL;
char c[DECIMAL_STR_MAX(pid_t) + 2];
if (pid == 0)
pid = getpid();
- snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
+ snprintf(c, sizeof(c), PID_FMT"\n", pid);
- return write_string_file(fs, c);
+ return write_string_file_no_create(fs, c);
}
int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
/* This didn't work? Then let's try all prefixes of
* the destination */
- strcpy(prefix, path);
- for (;;) {
- char *slash;
-
- slash = strrchr(prefix, '/');
- if (!slash)
- break;
-
- *slash = 0;
-
+ PATH_FOREACH_PREFIX(prefix, path) {
r = cg_attach(controller, prefix, pid);
if (r >= 0)
break;
}
}
- return r;
+ return 0;
}
int cg_set_group_access(
assert(path);
- if (mode != (mode_t) -1)
+ if (mode != MODE_INVALID)
mode &= 0777;
r = cg_get_path(controller, path, NULL, &fs);
assert(path);
- if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1)
+ if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
return 0;
- if (mode != (mode_t) -1)
+ if (mode != MODE_INVALID)
mode &= 0666;
r = cg_get_path(controller, path, "cgroup.procs", &fs);
} else
controller = SYSTEMD_CGROUP_CONTROLLER;
- if (pid == 0)
- fs = "/proc/self/cgroup";
- else
- fs = procfs_file_alloca(pid, "cgroup");
+ fs = procfs_file_alloca(pid, "cgroup");
f = fopen(fs, "re");
if (!f)
cs = strlen(controller);
FOREACH_LINE(line, f, return -errno) {
- char *l, *p, *w, *e;
+ char *l, *p, *e;
size_t k;
- char *state;
+ const char *word, *state;
bool found = false;
truncate_nl(line);
*e = 0;
- FOREACH_WORD_SEPARATOR(w, k, l, ",", state) {
+ FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
- if (k == cs && memcmp(w, controller, cs) == 0) {
+ if (k == cs && memcmp(word, controller, cs) == 0) {
found = true;
break;
}
if (k == 5 + cs &&
- memcmp(w, "name=", 5) == 0 &&
- memcmp(w+5, controller, cs) == 0) {
+ memcmp(word, "name=", 5) == 0 &&
+ memcmp(word+5, controller, cs) == 0) {
found = true;
break;
}
sc = strstrip(contents);
if (sc[0] == 0) {
- r = write_string_file(fs, agent);
+ r = write_string_file_no_create(fs, agent);
if (r < 0)
return r;
} else if (!streq(sc, agent))
sc = strstrip(contents);
if (streq(sc, "0")) {
- r = write_string_file(fs, "1");
+ r = write_string_file_no_create(fs, "1");
if (r < 0)
return r;
if (r < 0)
return r;
- r = write_string_file(fs, "0");
+ r = write_string_file_no_create(fs, "0");
if (r < 0)
return r;
if (r < 0)
return r;
- r = write_string_file(fs, "");
+ r = write_string_file_no_create(fs, "");
if (r < 0)
return r;
return !found;
}
-int cg_is_empty_by_spec(const char *spec, bool ignore_self) {
- _cleanup_free_ char *controller = NULL, *path = NULL;
- int r;
-
- assert(spec);
-
- r = cg_split_spec(spec, &controller, &path);
- if (r < 0)
- return r;
-
- return cg_is_empty(controller, path, ignore_self);
-}
-
int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
_cleanup_closedir_ DIR *d = NULL;
char *fn;
if (!t)
return -ENOMEM;
- path_kill_slashes(t);
- *path = t;
+ *path = path_kill_slashes(t);
}
if (controller)
return -EINVAL;
}
- u = strdup(e+1);
- if (!u) {
- free(t);
- return -ENOMEM;
- }
- if (!path_is_safe(u) ||
- !path_is_absolute(u)) {
- free(t);
- free(u);
- return -EINVAL;
- }
+ if (streq(e+1, "")) {
+ u = strdup("/");
+ if (!u) {
+ free(t);
+ return -ENOMEM;
+ }
+ } else {
+ u = strdup(e+1);
+ if (!u) {
+ free(t);
+ return -ENOMEM;
+ }
+
+ if (!path_is_safe(u) ||
+ !path_is_absolute(u)) {
+ free(t);
+ free(u);
+ return -EINVAL;
+ }
- path_kill_slashes(u);
+ path_kill_slashes(u);
+ }
if (controller)
*controller = t;
return 0;
}
-int cg_join_spec(const char *controller, const char *path, char **spec) {
- char *s;
-
- assert(path);
-
- if (!controller)
- controller = "systemd";
- else {
- if (!cg_controller_is_valid(controller, true))
- return -EINVAL;
-
- controller = normalize_controller(controller);
- }
-
- if (!path_is_absolute(path))
- return -EINVAL;
-
- s = strjoin(controller, ":", path, NULL);
- if (!s)
- return -ENOMEM;
-
- path_kill_slashes(s + strlen(controller) + 1);
-
- *spec = s;
- return 0;
-}
-
int cg_mangle_path(const char *path, char **result) {
_cleanup_free_ char *c = NULL, *p = NULL;
char *t;
assert(path);
assert(result);
- /* First check if it already is a filesystem path */
+ /* First, check if it already is a filesystem path */
if (path_startswith(path, "/sys/fs/cgroup")) {
t = strdup(path);
if (!t)
return -ENOMEM;
- path_kill_slashes(t);
- *result = t;
+ *result = path_kill_slashes(t);
return 0;
}
- /* Otherwise treat it as cg spec */
+ /* Otherwise, treat it as cg spec */
r = cg_split_spec(path, &c, &p);
if (r < 0)
return r;
}
int cg_get_root_path(char **path) {
- char *p, *e;
- int r;
-
assert(path);
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
- if (r < 0)
- return r;
-
- e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
- if (e)
- *e = 0;
-
- *path = p;
- return 0;
+ return cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, path);
}
-char **cg_shorten_controllers(char **controllers) {
- char **f, **t;
-
- if (!controllers)
- return controllers;
-
- for (f = controllers, t = controllers; *f; f++) {
- const char *p;
- int r;
-
- p = normalize_controller(*f);
+int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
+ _cleanup_free_ char *rt = NULL;
+ char *p;
+ int r;
- if (streq(p, "systemd")) {
- free(*f);
- continue;
- }
+ assert(cgroup);
+ assert(shifted);
- if (!cg_controller_is_valid(p, true)) {
- log_warning("Controller %s is not valid, removing from controllers list.", p);
- free(*f);
- continue;
- }
+ if (!root) {
+ /* If the root was specified let's use that, otherwise
+ * let's determine it from PID 1 */
- r = check_hierarchy(p);
- if (r < 0) {
- log_debug("Controller %s is not available, removing from controllers list.", p);
- free(*f);
- continue;
- }
+ r = cg_get_root_path(&rt);
+ if (r < 0)
+ return r;
- *(t++) = *f;
+ root = rt;
}
- *t = NULL;
- return strv_uniq(controllers);
-}
-
-int cg_pid_get_path_shifted(pid_t pid, char **root, char **cgroup) {
- _cleanup_free_ char *cg_root = NULL;
- char *cg_process, *p;
- int r;
-
- r = cg_get_root_path(&cg_root);
- if (r < 0)
- return r;
-
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
- if (r < 0)
- return r;
-
- p = path_startswith(cg_process, cg_root);
+ p = path_startswith(cgroup, root);
if (p)
- p--;
+ *shifted = p - 1;
else
- p = cg_process;
-
- if (cgroup) {
- char* c;
-
- c = strdup(p);
- if (!c) {
- free(cg_process);
- return -ENOMEM;
- }
-
- *cgroup = c;
- }
-
- if (root) {
- cg_process[p-cg_process] = 0;
- *root = cg_process;
- } else
- free(cg_process);
-
- return 0;
-}
-
-int cg_path_decode_unit(const char *cgroup, char **unit){
- char *e, *c, *s;
-
- assert(cgroup);
- assert(unit);
-
- e = strchrnul(cgroup, '/');
- c = strndupa(cgroup, e - cgroup);
- c = cg_unescape(c);
+ *shifted = cgroup;
- if (!unit_name_is_valid(c, false))
- return -EINVAL;
-
- s = strdup(c);
- if (!s)
- return -ENOMEM;
-
- *unit = s;
return 0;
}
-static const char *skip_slices(const char *p) {
- /* Skips over all slice assignments */
-
- for (;;) {
- size_t n;
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
- return p;
-
- p += n;
- }
-}
-
-int cg_path_get_unit(const char *path, char **unit) {
- const char *e;
-
- assert(path);
- assert(unit);
-
- e = skip_slices(path);
-
- return cg_path_decode_unit(e, unit);
-}
-
-int cg_pid_get_unit(pid_t pid, char **unit) {
- _cleanup_free_ char *cgroup = NULL;
+int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
+ _cleanup_free_ char *raw = NULL;
+ const char *c;
int r;
- assert(unit);
+ assert(pid >= 0);
+ assert(cgroup);
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
if (r < 0)
return r;
- return cg_path_get_unit(cgroup, unit);
-}
-
-static const char *skip_session(const char *p) {
- size_t n;
-
- assert(p);
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (n <= 12 || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
- return NULL;
-
- p += n;
- p += strspn(p, "/");
-
- return p;
-}
-
-int cg_path_get_user_unit(const char *path, char **unit) {
- const char *e;
-
- assert(path);
- assert(unit);
-
- /* We always have to parse the path from the beginning as unit
- * cgroups might have arbitrary child cgroups and we shouldn't get
- * confused by those */
-
- /* Skip slices, if there are any */
- e = skip_slices(path);
-
- /* Skip the session scope, require that there is one */
- e = skip_session(e);
- if (!e)
- return -ENOENT;
-
- /* And skip more slices */
- e = skip_slices(e);
-
- return cg_path_decode_unit(e, unit);
-}
-
-int cg_pid_get_user_unit(pid_t pid, char **unit) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(unit);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+ r = cg_shift_path(raw, root, &c);
if (r < 0)
return r;
- return cg_path_get_user_unit(cgroup, unit);
-}
-
-int cg_path_get_machine_name(const char *path, char **machine) {
- const char *e, *n, *x;
- char *s, *r;
- size_t l;
-
- assert(path);
- assert(machine);
-
- /* Skip slices, if there are any */
- e = skip_slices(path);
-
- n = strchrnul(e, '/');
- if (e == n)
- return -ENOENT;
-
- s = strndupa(e, n - e);
- s = cg_unescape(s);
-
- x = startswith(s, "machine-");
- if (!x)
- return -ENOENT;
- if (!endswith(x, ".scope"))
- return -ENOENT;
+ if (c == raw) {
+ *cgroup = raw;
+ raw = NULL;
+ } else {
+ char *n;
- l = strlen(x);
- if (l <= 6)
- return -ENOENT;
+ n = strdup(c);
+ if (!n)
+ return -ENOMEM;
- r = strndup(x, l - 6);
- if (!r)
- return -ENOMEM;
+ *cgroup = n;
+ }
- *machine = r;
return 0;
}
-int cg_pid_get_machine_name(pid_t pid, char **machine) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(machine);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_machine_name(cgroup, machine);
-}
-
int cg_path_get_session(const char *path, char **session) {
- const char *e, *n, *x;
- char *s, *r;
- size_t l;
+ const char *e, *n, *s;
- assert(path);
- assert(session);
+ /* Elogind uses a flat hierarchy, just "/SESSION". The only
+ wrinkle is that SESSION might be escaped. */
- /* Skip slices, if there are any */
- e = skip_slices(path);
+ assert(path);
+ assert(path[0] == '/');
+ e = path + 1;
n = strchrnul(e, '/');
if (e == n)
return -ENOENT;
s = strndupa(e, n - e);
s = cg_unescape(s);
- x = startswith(s, "session-");
- if (!x)
- return -ENOENT;
- if (!endswith(x, ".scope"))
+ if (!s[0])
return -ENOENT;
- l = strlen(x);
- if (l <= 6)
- return -ENOENT;
+ if (session) {
+ char *r;
- r = strndup(x, l - 6);
- if (!r)
- return -ENOMEM;
+ r = strdup(s);
+ if (!r)
+ return -ENOMEM;
+
+ *session = r;
+ }
- *session = r;
return 0;
}
_cleanup_free_ char *cgroup = NULL;
int r;
- assert(session);
-
r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
if (r < 0)
return r;
return cg_path_get_session(cgroup, session);
}
-int cg_path_get_owner_uid(const char *path, uid_t *uid) {
- _cleanup_free_ char *slice = NULL;
- const char *e;
- char *s;
- int r;
-
- assert(path);
- assert(uid);
-
- r = cg_path_get_slice(path, &slice);
- if (r < 0)
- return r;
-
- e = startswith(slice, "user-");
- if (!e)
- return -ENOENT;
- if (!endswith(slice, ".slice"))
- return -ENOENT;
-
- s = strndupa(e, strlen(e) - 6);
- if (!s)
- return -ENOMEM;
-
- return parse_uid(s, uid);
-}
-
-int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(uid);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_owner_uid(cgroup, uid);
-}
-
-int cg_path_get_slice(const char *p, char **slice) {
- const char *e = NULL;
- size_t m = 0;
-
- assert(p);
- assert(slice);
-
- for (;;) {
- size_t n;
-
- p += strspn(p, "/");
-
- n = strcspn(p, "/");
- if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
- char *s;
-
- if (!e)
- return -ENOENT;
-
- s = strndup(e, m);
- if (!s)
- return -ENOMEM;
-
- *slice = s;
- return 0;
- }
-
- e = p;
- m = n;
-
- p += n;
- }
-}
-
-int cg_pid_get_slice(pid_t pid, char **slice) {
- _cleanup_free_ char *cgroup = NULL;
- int r;
-
- assert(slice);
-
- r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
- if (r < 0)
- return r;
-
- return cg_path_get_slice(cgroup, slice);
-}
-
-int cg_controller_from_attr(const char *attr, char **controller) {
- const char *dot;
- char *c;
-
- assert(attr);
- assert(controller);
-
- if (!filename_is_safe(attr))
- return -EINVAL;
-
- dot = strchr(attr, '.');
- if (!dot) {
- *controller = NULL;
- return 0;
- }
-
- c = strndup(attr, dot - attr);
- if (!c)
- return -ENOMEM;
-
- if (!cg_controller_is_valid(c, false)) {
- free(c);
- return -EINVAL;
- }
-
- *controller = c;
- return 1;
-}
-
char *cg_escape(const char *p) {
bool need_prefix = false;
return true;
}
-int cg_slice_to_path(const char *unit, char **ret) {
- _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
- const char *dash;
-
- assert(unit);
- assert(ret);
-
- if (!unit_name_is_valid(unit, false))
- return -EINVAL;
-
- if (!endswith(unit, ".slice"))
- return -EINVAL;
-
- p = unit_name_to_prefix(unit);
- if (!p)
- return -ENOMEM;
-
- dash = strchr(p, '-');
- while (dash) {
- _cleanup_free_ char *escaped = NULL;
- char n[dash - p + sizeof(".slice")];
-
- strcpy(stpncpy(n, p, dash - p), ".slice");
-
- if (!unit_name_is_valid(n, false))
- return -EINVAL;
-
- escaped = cg_escape(n);
- if (!escaped)
- return -ENOMEM;
-
- if (!strextend(&s, escaped, "/", NULL))
- return -ENOMEM;
-
- dash = strchr(dash+1, '-');
- }
-
- e = cg_escape(unit);
- if (!e)
- return -ENOMEM;
-
- if (!strextend(&s, e, NULL))
- return -ENOMEM;
+int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
+ _cleanup_free_ char *p = NULL;
+ int r;
- *ret = s;
- s = NULL;
+ r = cg_get_path(controller, path, attribute, &p);
+ if (r < 0)
+ return r;
- return 0;
+ return write_string_file_no_create(p, value);
}
-int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
+int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
if (r < 0)
return r;
- return write_string_file(p, value);
+ return read_one_line_file(p, ret);
}
static const char mask_names[] =
return 0;
}
-int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid) {
+int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
CGroupControllerMask bit = 1;
const char *n;
int r;
return r;
NULSTR_FOREACH(n, mask_names) {
- if (supported & bit)
+
+ if (supported & bit) {
+ const char *p = NULL;
+
+ if (path_callback)
+ p = path_callback(bit, userdata);
+
+ if (!p)
+ p = path;
+
cg_attach_fallback(n, path, pid);
+ }
bit <<= 1;
}
return 0;
}
-int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids) {
+int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
Iterator i;
void *pidp;
int r = 0;
pid_t pid = PTR_TO_LONG(pidp);
int q;
- q = cg_attach_everywhere(supported, path, pid);
+ q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
if (q < 0)
r = q;
}
return r;
}
-int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to) {
+int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
CGroupControllerMask bit = 1;
const char *n;
int r;
}
NULSTR_FOREACH(n, mask_names) {
- if (supported & bit)
- cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, to, false, false);
+ if (supported & bit) {
+ const char *p = NULL;
+
+ if (to_callback)
+ p = to_callback(bit, userdata);
+
+ if (!p)
+ p = to;
+
+ cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
+ }
bit <<= 1;
}
return mask;
}
+
+int cg_kernel_controllers(Set *controllers) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char buf[LINE_MAX];
+ int r;
+
+ assert(controllers);
+
+ f = fopen("/proc/cgroups", "re");
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ /* Ignore the header line */
+ (void) fgets(buf, sizeof(buf), f);
+
+ for (;;) {
+ char *controller;
+ int enabled = 0;
+
+ errno = 0;
+ if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
+
+ if (feof(f))
+ break;
+
+ if (ferror(f) && errno)
+ return -errno;
+
+ return -EBADMSG;
+ }
+
+ if (!enabled) {
+ free(controller);
+ continue;
+ }
+
+ if (!filename_is_valid(controller)) {
+ free(controller);
+ return -EBADMSG;
+ }
+
+ r = set_consume(controllers, controller);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}