summaryrefslogtreecommitdiff
path: root/src/shared/cgroup-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/cgroup-util.c')
-rw-r--r--src/shared/cgroup-util.c309
1 files changed, 217 insertions, 92 deletions
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index dfd8689b72..9988e5c574 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -30,16 +30,17 @@
#include <ftw.h>
#include "cgroup-util.h"
-#include "log.h"
#include "set.h"
#include "macro.h"
#include "util.h"
+#include "formats-util.h"
+#include "process-util.h"
#include "path-util.h"
-#include "strv.h"
#include "unit-name.h"
#include "fileio.h"
#include "special.h"
#include "mkdir.h"
+#include "login-shared.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
_cleanup_free_ char *fs = NULL;
@@ -488,9 +489,11 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
if (_unlikely_(!good)) {
int r;
- r = path_is_mount_point("/sys/fs/cgroup", false);
- if (r <= 0)
- return r < 0 ? r : -ENOENT;
+ r = path_is_mount_point("/sys/fs/cgroup", 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
/* Cache this to save a few stat()s */
good = true;
@@ -1138,17 +1141,21 @@ int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
}
int cg_path_decode_unit(const char *cgroup, char **unit){
- char *e, *c, *s;
+ char *c, *s;
+ size_t n;
assert(cgroup);
assert(unit);
- e = strchrnul(cgroup, '/');
- c = strndupa(cgroup, e - cgroup);
+ n = strcspn(cgroup, "/");
+ if (n < 3)
+ return -ENXIO;
+
+ c = strndupa(cgroup, n);
c = cg_unescape(c);
- if (!unit_name_is_valid(c, TEMPLATE_INVALID))
- return -EINVAL;
+ if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
+ return -ENXIO;
s = strdup(c);
if (!s)
@@ -1158,7 +1165,31 @@ int cg_path_decode_unit(const char *cgroup, char **unit){
return 0;
}
+static bool valid_slice_name(const char *p, size_t n) {
+
+ if (!p)
+ return false;
+
+ if (n < strlen("x.slice"))
+ return false;
+
+ if (memcmp(p + n - 6, ".slice", 6) == 0) {
+ char buf[n+1], *c;
+
+ memcpy(buf, p, n);
+ buf[n] = 0;
+
+ c = cg_unescape(buf);
+
+ return unit_name_is_valid(c, UNIT_NAME_PLAIN);
+ }
+
+ return false;
+}
+
static const char *skip_slices(const char *p) {
+ assert(p);
+
/* Skips over all slice assignments */
for (;;) {
@@ -1167,22 +1198,35 @@ static const char *skip_slices(const char *p) {
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
+ if (!valid_slice_name(p, n))
return p;
p += n;
}
}
-int cg_path_get_unit(const char *path, char **unit) {
+int cg_path_get_unit(const char *path, char **ret) {
const char *e;
+ char *unit;
+ int r;
assert(path);
- assert(unit);
+ assert(ret);
e = skip_slices(path);
- return cg_path_decode_unit(e, unit);
+ r = cg_path_decode_unit(e, &unit);
+ if (r < 0)
+ return r;
+
+ /* We skipped over the slices, don't accept any now */
+ if (endswith(unit, ".slice")) {
+ free(unit);
+ return -ENXIO;
+ }
+
+ *ret = unit;
+ return 0;
}
int cg_pid_get_unit(pid_t pid, char **unit) {
@@ -1204,18 +1248,35 @@ int cg_pid_get_unit(pid_t pid, char **unit) {
static const char *skip_session(const char *p) {
size_t n;
- assert(p);
+ if (isempty(p))
+ return NULL;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n < strlen("session-x.scope") || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
+ if (n < strlen("session-x.scope"))
return NULL;
- p += n;
- p += strspn(p, "/");
+ if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
+ char buf[n - 8 - 6 + 1];
+
+ memcpy(buf, p + 8, n - 8 - 6);
+ buf[n - 8 - 6] = 0;
+
+ /* Note that session scopes never need unescaping,
+ * since they cannot conflict with the kernel's own
+ * names, hence we don't need to call cg_unescape()
+ * here. */
+
+ if (!session_id_valid(buf))
+ return false;
- return p;
+ p += n;
+ p += strspn(p, "/");
+ return p;
+ }
+
+ return NULL;
}
/**
@@ -1224,44 +1285,69 @@ static const char *skip_session(const char *p) {
static const char *skip_user_manager(const char *p) {
size_t n;
- assert(p);
+ if (isempty(p))
+ return NULL;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n < strlen("user@x.service") || memcmp(p, "user@", 5) != 0 || memcmp(p + n - 8, ".service", 8) != 0)
+ if (n < strlen("user@x.service"))
return NULL;
- p += n;
- p += strspn(p, "/");
+ if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
+ char buf[n - 5 - 8 + 1];
- return p;
+ memcpy(buf, p + 5, n - 5 - 8);
+ buf[n - 5 - 8] = 0;
+
+ /* Note that user manager services never need unescaping,
+ * since they cannot conflict with the kernel's own
+ * names, hence we don't need to call cg_unescape()
+ * here. */
+
+ if (parse_uid(buf, NULL) < 0)
+ return NULL;
+
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+ }
+
+ return NULL;
}
-int cg_path_get_user_unit(const char *path, char **unit) {
+static const char *skip_user_prefix(const char *path) {
const char *e, *t;
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 or user manager... */
- t = skip_session(e);
- if (!t)
- t = skip_user_manager(e);
- if (!t)
- return -ENOENT;
+ /* Skip the user manager, if it's in the path now... */
+ t = skip_user_manager(e);
+ if (t)
+ return t;
+
+ /* Alternatively skip the user session if it is in the path... */
+ return skip_session(e);
+}
- /* ... and skip more slices if there are any */
- e = skip_slices(t);
+int cg_path_get_user_unit(const char *path, char **ret) {
+ const char *t;
- return cg_path_decode_unit(e, unit);
+ assert(path);
+ assert(ret);
+
+ t = skip_user_prefix(path);
+ if (!t)
+ return -ENXIO;
+
+ /* And from here on it looks pretty much the same as for a
+ * system unit, hence let's use the same parser from here
+ * on. */
+ return cg_path_get_unit(t, ret);
}
int cg_pid_get_user_unit(pid_t pid, char **unit) {
@@ -1306,36 +1392,35 @@ int cg_pid_get_machine_name(pid_t pid, char **machine) {
}
int cg_path_get_session(const char *path, char **session) {
- const char *e, *n, *x, *y;
- char *s;
+ _cleanup_free_ char *unit = NULL;
+ char *start, *end;
+ int r;
assert(path);
- /* Skip slices, if there are any */
- e = skip_slices(path);
-
- n = strchrnul(e, '/');
- if (e == n)
- return -ENOENT;
+ r = cg_path_get_unit(path, &unit);
+ if (r < 0)
+ return r;
- s = strndupa(e, n - e);
- s = cg_unescape(s);
+ start = startswith(unit, "session-");
+ if (!start)
+ return -ENXIO;
+ end = endswith(start, ".scope");
+ if (!end)
+ return -ENXIO;
- x = startswith(s, "session-");
- if (!x)
- return -ENOENT;
- y = endswith(x, ".scope");
- if (!y || x == y)
- return -ENOENT;
+ *end = 0;
+ if (!session_id_valid(start))
+ return -ENXIO;
if (session) {
- char *r;
+ char *rr;
- r = strndup(x, y - x);
- if (!r)
+ rr = strdup(start);
+ if (!rr)
return -ENOMEM;
- *session = r;
+ *session = rr;
}
return 0;
@@ -1354,9 +1439,7 @@ int cg_pid_get_session(pid_t pid, char **session) {
int cg_path_get_owner_uid(const char *path, uid_t *uid) {
_cleanup_free_ char *slice = NULL;
- const char *start, *end;
- char *s;
- uid_t u;
+ char *start, *end;
int r;
assert(path);
@@ -1367,20 +1450,14 @@ int cg_path_get_owner_uid(const char *path, uid_t *uid) {
start = startswith(slice, "user-");
if (!start)
- return -ENOENT;
- end = endswith(slice, ".slice");
+ return -ENXIO;
+ end = endswith(start, ".slice");
if (!end)
- return -ENOENT;
+ return -ENXIO;
- s = strndupa(start, end - start);
- if (!s)
- return -ENOENT;
-
- if (parse_uid(s, &u) < 0)
- return -EIO;
-
- if (uid)
- *uid = u;
+ *end = 0;
+ if (parse_uid(start, uid) < 0)
+ return -ENXIO;
return 0;
}
@@ -1398,34 +1475,36 @@ int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
int cg_path_get_slice(const char *p, char **slice) {
const char *e = NULL;
- size_t m = 0;
assert(p);
assert(slice);
+ /* Finds the right-most slice unit from the beginning, but
+ * stops before we come to the first non-slice unit. */
+
for (;;) {
size_t n;
p += strspn(p, "/");
n = strcspn(p, "/");
- if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
- char *s;
+ if (!valid_slice_name(p, n)) {
- if (!e)
- return -ENOENT;
+ if (!e) {
+ char *s;
- s = strndup(e, m);
- if (!s)
- return -ENOMEM;
+ s = strdup("-.slice");
+ if (!s)
+ return -ENOMEM;
- *slice = s;
- return 0;
+ *slice = s;
+ return 0;
+ }
+
+ return cg_path_decode_unit(e, slice);
}
e = p;
- m = n;
-
p += n;
}
}
@@ -1443,6 +1522,33 @@ int cg_pid_get_slice(pid_t pid, char **slice) {
return cg_path_get_slice(cgroup, slice);
}
+int cg_path_get_user_slice(const char *p, char **slice) {
+ const char *t;
+ assert(p);
+ assert(slice);
+
+ t = skip_user_prefix(p);
+ if (!t)
+ return -ENXIO;
+
+ /* And now it looks pretty much the same as for a system
+ * slice, so let's just use the same parser from here on. */
+ return cg_path_get_slice(t, slice);
+}
+
+int cg_pid_get_user_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_user_slice(cgroup, slice);
+}
+
char *cg_escape(const char *p) {
bool need_prefix = false;
@@ -1532,28 +1638,47 @@ bool cg_controller_is_valid(const char *p, bool allow_named) {
int cg_slice_to_path(const char *unit, char **ret) {
_cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
const char *dash;
+ int r;
assert(unit);
assert(ret);
- if (!unit_name_is_valid(unit, TEMPLATE_INVALID))
+ if (streq(unit, "-.slice")) {
+ char *x;
+
+ x = strdup("");
+ if (!x)
+ return -ENOMEM;
+ *ret = x;
+ return 0;
+ }
+
+ if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
return -EINVAL;
if (!endswith(unit, ".slice"))
return -EINVAL;
- p = unit_name_to_prefix(unit);
- if (!p)
- return -ENOMEM;
+ r = unit_name_to_prefix(unit, &p);
+ if (r < 0)
+ return r;
dash = strchr(p, '-');
+
+ /* Don't allow initial dashes */
+ if (dash == p)
+ return -EINVAL;
+
while (dash) {
_cleanup_free_ char *escaped = NULL;
char n[dash - p + sizeof(".slice")];
- strcpy(stpncpy(n, p, dash - p), ".slice");
+ /* Don't allow trailing or double dashes */
+ if (dash[1] == 0 || dash[1] == '-')
+ return -EINVAL;
- if (!unit_name_is_valid(n, TEMPLATE_INVALID))
+ strcpy(stpncpy(n, p, dash - p), ".slice");
+ if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
return -EINVAL;
escaped = cg_escape(n);