#include "strv.h"
#include "unit-name.h"
#include "fileio.h"
+#include "special.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
_cleanup_free_ char *fs = NULL;
return 0;
}
-int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f) {
- _cleanup_free_ char *fs = NULL;
- FILE *f;
- int r;
-
- assert(_f);
-
- r = cg_get_path(controller, path, "tasks", &fs);
- if (r < 0)
- return r;
-
- f = fopen(fs, "re");
- if (!f)
- return -errno;
-
- *_f = f;
- return 0;
-}
-
int cg_read_pid(FILE *f, pid_t *_pid) {
unsigned long ul;
return r;
if (honour_sticky) {
- char *tasks;
+ char *fn;
- /* If the sticky bit is set don't remove the directory */
+ /* If the sticky bit is set on cgroup.procs, don't
+ * remove the directory */
- tasks = strappend(p, "/tasks");
- if (!tasks)
+ fn = strappend(p, "/cgroup.procs");
+ if (!fn)
return -ENOMEM;
- r = file_is_priv_sticky(tasks);
- free(tasks);
+ r = file_is_priv_sticky(fn);
+ free(fn);
+
+ if (r > 0)
+ return 0;
+
+ /* Compatibility ... */
+ fn = strappend(p, "/tasks");
+ if (!fn)
+ return -ENOMEM;
+
+ r = file_is_priv_sticky(fn);
+ free(fn);
if (r > 0)
return 0;
pid_t pid = 0;
done = true;
- r = cg_enumerate_tasks(cfrom, pfrom, &f);
+ r = cg_enumerate_processes(cfrom, pfrom, &f);
if (r < 0) {
if (ret >= 0 && r != -ENOENT)
return r;
static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
char *t = NULL;
- if (controller) {
- if (path && suffix)
+ if (!isempty(controller)) {
+ if (!isempty(path) && !isempty(suffix))
t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
- else if (path)
+ else if (!isempty(path))
t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
- else if (suffix)
+ else if (!isempty(suffix))
t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
else
t = strappend("/sys/fs/cgroup/", controller);
} else {
- if (path && suffix)
+ if (!isempty(path) && !isempty(suffix))
t = strjoin(path, "/", suffix, NULL);
- else if (path)
+ else if (!isempty(path))
t = strdup(path);
else
return -EINVAL;
if (ftwbuf->level < 1)
return 0;
+ p = strappend(path, "/cgroup.procs");
+ if (!p) {
+ errno = ENOMEM;
+ return 1;
+ }
+
+ is_sticky = file_is_priv_sticky(p) > 0;
+ free(p);
+
+ if (is_sticky)
+ return 0;
+
+ /* Compatibility */
p = strappend(path, "/tasks");
if (!p) {
errno = ENOMEM;
bool is_sticky;
char *p;
- p = strappend(fs, "/tasks");
+ p = strappend(fs, "/cgroup.procs");
if (!p)
return -ENOMEM;
is_sticky = file_is_priv_sticky(p) > 0;
free(p);
+ if (!is_sticky) {
+ p = strappend(fs, "/tasks");
+ if (!p)
+ return -ENOMEM;
+
+ is_sticky = file_is_priv_sticky(p) > 0;
+ free(p);
+ }
+
if (!is_sticky)
if (rmdir(fs) < 0 && errno != ENOENT && r == 0)
return -errno;
assert(path);
assert(pid >= 0);
- r = cg_get_path_and_check(controller, path, "tasks", &fs);
+ r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
if (r < 0)
return r;
if (mode != (mode_t) -1)
mode &= 0666;
- r = cg_get_path(controller, path, "tasks", &fs);
+ r = cg_get_path(controller, path, "cgroup.procs", &fs);
if (r < 0)
return r;
if (r < 0)
return r;
- /* Always keep values for "cgroup.procs" in sync with "tasks" */
- r = cg_get_path(controller, path, "cgroup.procs", &procs);
+ /* Compatibility, Always keep values for "tasks" in sync with
+ * "cgroup.procs" */
+ r = cg_get_path(controller, path, "tasks", &procs);
if (r < 0)
return r;
assert(path);
- r = cg_enumerate_tasks(controller, path, &f);
+ r = cg_enumerate_processes(controller, path, &f);
if (r < 0)
return r == -ENOENT ? 1 : r;
return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
}
-int cg_get_system_path(char **path) {
- char *p;
- int r;
-
- assert(path);
-
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
- if (r < 0) {
- p = strdup("/system");
- if (!p)
- return -ENOMEM;
- }
-
- if (endswith(p, "/system"))
- *path = p;
- else {
- char *q;
-
- q = strappend(p, "/system");
- free(p);
- if (!q)
- return -ENOMEM;
-
- *path = q;
- }
-
- return 0;
-}
-
int cg_get_root_path(char **path) {
- char *root, *e;
+ char *p, *e;
int r;
assert(path);
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &root);
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
if (r < 0)
return r;
- e = endswith(root, "/system");
- if (e == root)
- e[1] = 0;
- else if (e)
+ e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
+ if (e)
*e = 0;
- *path = root;
- return 0;
-}
-
-int cg_get_user_path(char **path) {
- _cleanup_free_ char *root = NULL;
- char *p;
-
- assert(path);
-
- /* Figure out the place to put user cgroups below. We use the
- * same as PID 1 has but with the "/system" suffix replaced by
- * "/user" */
-
- if (cg_get_root_path(&root) < 0 || streq(root, "/"))
- p = strdup("/user");
- else
- p = strappend(root, "/user");
-
- if (!p)
- return -ENOMEM;
-
- *path = p;
- return 0;
-}
-
-int cg_get_machine_path(const char *machine, char **path) {
- _cleanup_free_ char *root = NULL, *escaped = NULL;
- char *p;
-
- assert(path);
-
- if (machine) {
- const char *name = strappenda(machine, ".nspawn");
-
- escaped = cg_escape(name);
- if (!escaped)
- return -ENOMEM;
- }
-
- p = strjoin(cg_get_root_path(&root) >= 0 && !streq(root, "/") ? root : "",
- "/machine", machine ? "/" : "", machine ? escaped : "", NULL);
- if (!p)
- return -ENOMEM;
-
*path = p;
return 0;
}
return 0;
}
+static const char *skip_slices(const char *p) {
+ size_t n;
+
+ /* Skips over all slice assignments */
+
+ for (;;) {
+ 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 = path_startswith(path, "/system/");
- if (!e)
- return -ENOENT;
+ e = skip_slices(path);
return cg_path_decode_unit(e, unit);
}
return cg_path_get_unit(cgroup, unit);
}
-static const char *skip_label(const char *e) {
- assert(e);
+static const char *skip_user(const char *p) {
+ size_t n;
- e = strchr(e, '/');
- if (!e)
+ assert(p);
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n <= 5 || memcmp(p + n - 5, ".user", 5) != 0)
+ return p;
+
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+}
+
+static const char *skip_session(const char *p) {
+ size_t n;
+
+ assert(p);
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n <= 8 || memcmp(p + n - 8, ".session", 8) != 0)
return NULL;
- e += strspn(e, "/");
- return e;
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+}
+
+static const char *skip_systemd_label(const char *p) {
+ size_t n;
+
+ assert(p);
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n < 8 || memcmp(p, "systemd-", 8) != 0)
+ return p;
+
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
}
int cg_path_get_user_unit(const char *path, char **unit) {
* cgroups might have arbitrary child cgroups and we shouldn't get
* confused by those */
- e = path_startswith(path, "/user/");
- if (!e)
- return -ENOENT;
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
- /* Skip the user name */
- e = skip_label(e);
- if (!e)
- return -ENOENT;
+ /* Skip the user name, if there is one */
+ e = skip_user(e);
- /* Skip the session ID */
- e = skip_label(e);
+ /* Skip the session ID, require that there is one */
+ e = skip_session(e);
if (!e)
return -ENOENT;
- /* Skip the systemd cgroup */
- e = skip_label(e);
- if (!e)
- return -ENOENT;
+ /* Skip the systemd cgroup, if there is one */
+ e = skip_systemd_label(e);
return cg_path_decode_unit(e, unit);
}
}
int cg_path_get_machine_name(const char *path, char **machine) {
- const char *e, *n;
+ const char *e, *n, *x;
char *s, *r;
assert(path);
assert(machine);
- e = path_startswith(path, "/machine/");
- if (!e)
- return -ENOENT;
+ /* 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 = endswith(s, ".machine");
+ if (!x)
+ return -ENOENT;
- r = strdup(cg_unescape(s));
+ r = strndup(s, x - s);
if (!r)
return -ENOMEM;
assert(path);
assert(session);
- e = path_startswith(path, "/user/");
- if (!e)
- return -ENOENT;
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
- /* Skip the user name */
- e = skip_label(e);
- if (!e)
- return -ENOENT;
+ /* Skip the user name, if there is one */
+ e = skip_user(e);
n = strchrnul(e, '/');
if (n - e < 8)
assert(path);
assert(uid);
- e = path_startswith(path, "/user/");
- if (!e)
- return -ENOENT;
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
n = strchrnul(e, '/');
if (n - e < 5)
/* The return value of this function (unlike cg_unescape())
* needs free()! */
- if (p[0] == '_' || streq(p, "notify_on_release") || streq(p, "release_agent") || streq(p, "tasks"))
+ if (p[0] == 0 ||
+ p[0] == '_' ||
+ p[0] == '.' ||
+ streq(p, "notify_on_release") ||
+ streq(p, "release_agent") ||
+ streq(p, "tasks"))
need_prefix = true;
else {
const char *dot;
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;
+
+ *ret = s;
+ s = NULL;
+
+ return 0;
+}