diff options
Diffstat (limited to 'src/shared/cgroup-util.c')
-rw-r--r-- | src/shared/cgroup-util.c | 309 |
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); |