X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Fcgroup-util.c;h=be00b40fa2346afb2ebf43ab6847d4d61a025f6f;hb=9f26c90cb50c45d4549c4dd569917b4ac143b94d;hp=3cb41c7c5fc55576d4fc8ad33ba77c366a6f48f4;hpb=b59e246565337d6c6967059dd15a52a15277433e;p=elogind.git diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c index 3cb41c7c5..be00b40fa 100644 --- a/src/shared/cgroup-util.c +++ b/src/shared/cgroup-util.c @@ -34,7 +34,10 @@ #include "set.h" #include "macro.h" #include "util.h" +#include "path-util.h" #include "strv.h" +#include "unit-name.h" +#include "fileio.h" int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { char *fs; @@ -373,18 +376,20 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path, bool re return 0; } -int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) { +int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) { bool done = false; - Set *s; + _cleanup_set_free_ Set *s = NULL; int r, ret = 0; pid_t my_pid; - FILE *f = NULL; + _cleanup_fclose_ FILE *f = NULL; - assert(controller); - assert(from); - assert(to); + assert(cfrom); + assert(pfrom); + assert(cto); + assert(pto); - if (!(s = set_new(trivial_hash_func, trivial_compare_func))) + s = set_new(trivial_hash_func, trivial_compare_func); + if (!s) return -ENOMEM; my_pid = getpid(); @@ -393,11 +398,12 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig pid_t pid = 0; done = true; - if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) { + r = cg_enumerate_tasks(cfrom, pfrom, &f); + if (r < 0) { if (ret >= 0 && r != -ENOENT) ret = r; - goto finish; + return ret; } while ((r = cg_read_pid(f, &pid)) > 0) { @@ -411,7 +417,8 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid)) continue; - if ((r = cg_attach(controller, to, pid)) < 0) { + r = cg_attach(cto, pto, pid); + if (r < 0) { if (ret >= 0 && r != -ESRCH) ret = r; } else if (ret == 0) @@ -419,11 +426,12 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig done = false; - if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) { + r = set_put(s, LONG_TO_PTR(pid)); + if (r < 0) { if (ret >= 0) ret = r; - goto finish; + return ret; } } @@ -431,56 +439,48 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig if (ret >= 0) ret = r; - goto finish; + return ret; } fclose(f); f = NULL; - } while (!done); -finish: - set_free(s); - - if (f) - fclose(f); - return ret; } -int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self, bool rem) { +int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool rem) { int r, ret = 0; - DIR *d = NULL; + _cleanup_closedir_ DIR *d = NULL; char *fn; - assert(controller); - assert(from); - assert(to); + assert(cfrom); + assert(pfrom); + assert(cto); + assert(pto); - ret = cg_migrate(controller, from, to, ignore_self); + ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self); - if ((r = cg_enumerate_subgroups(controller, from, &d)) < 0) { + r = cg_enumerate_subgroups(cfrom, pfrom, &d); + if (r < 0) { if (ret >= 0 && r != -ENOENT) ret = r; - goto finish; + return ret; } while ((r = cg_read_subgroup(d, &fn)) > 0) { - char *p = NULL; + _cleanup_free_ char *p = NULL; - r = asprintf(&p, "%s/%s", from, fn); + p = strjoin(pfrom, "/", fn, NULL); free(fn); - - if (r < 0) { + if (!p) { if (ret >= 0) ret = -ENOMEM; - goto finish; + return ret; } - r = cg_migrate_recursive(controller, p, to, ignore_self, rem); - free(p); - + r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem); if (r != 0 && ret >= 0) ret = r; } @@ -488,17 +488,11 @@ int cg_migrate_recursive(const char *controller, const char *from, const char *t if (r < 0 && ret >= 0) ret = r; - if (rem) - if ((r = cg_rmdir(controller, from, true)) < 0) { - if (ret >= 0 && - r != -ENOENT && - r != -EBUSY) - ret = r; - } - -finish: - if (d) - closedir(d); + if (rem) { + r = cg_rmdir(cfrom, pfrom, true); + if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) + return r; + } return ret; } @@ -514,23 +508,23 @@ static const char *normalize_controller(const char *controller) { } static int join_path(const char *controller, const char *path, const char *suffix, char **fs) { - char *t; + char *t = NULL; if (!(controller || path)) return -EINVAL; if (controller) { if (path && suffix) - t = join("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL); + t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL); else if (path) - t = join("/sys/fs/cgroup/", controller, "/", path, NULL); + t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL); else if (suffix) - t = join("/sys/fs/cgroup/", controller, "/", suffix, NULL); + t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL); else - t = join("/sys/fs/cgroup/", controller, NULL); + t = strjoin("/sys/fs/cgroup/", controller, NULL); } else { if (path && suffix) - t = join(path, "/", suffix, NULL); + t = strjoin(path, "/", suffix, NULL); else if (path) t = strdup(path); } @@ -565,9 +559,23 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch return join_path(p, path, suffix, fs); } +static int check(const char *p) { + char *cc; + + assert(p); + + /* 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) + return -errno; + + return 0; +} + int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) { const char *p; - char *cc; + int r; assert(controller); assert(fs); @@ -575,13 +583,13 @@ int cg_get_path_and_check(const char *controller, const char *path, const char * if (isempty(controller)) return -EINVAL; + /* Normalize the controller syntax */ p = normalize_controller(controller); /* 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) - return -errno; + r = check(p); + if (r < 0) + return r; return join_path(p, path, suffix, fs); } @@ -659,10 +667,10 @@ int cg_delete(const char *controller, const char *path) { assert(controller); assert(path); - if ((r = parent_of_path(path, &parent)) < 0) + if ((r = path_get_parent(path, &parent)) < 0) return r; - r = cg_migrate_recursive(controller, path, parent, false, true); + r = cg_migrate_recursive(controller, path, controller, parent, false, true); free(parent); return r == -ENOENT ? 0 : r; @@ -804,7 +812,7 @@ int cg_get_by_pid(const char *controller, pid_t pid, char **path) { continue; l++; - if (strncmp(l, controller, cs) != 0) + if (!strneq(l, controller, cs)) continue; if (l[cs] != ':') @@ -919,6 +927,19 @@ int cg_is_empty(const char *controller, const char *path, bool ignore_self) { return !found; } +int cg_is_empty_by_spec(const char *spec, bool ignore_self) { + int r; + _cleanup_free_ char *controller = NULL, *path = NULL; + + 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) { int r; DIR *d = NULL; @@ -968,12 +989,14 @@ int cg_split_spec(const char *spec, char **controller, char **path) { char *t = NULL, *u = NULL; assert(spec); - assert(controller || path); if (*spec == '/') { + if (!path_is_safe(spec)) + return -EINVAL; if (path) { - if (!(t = strdup(spec))) + t = strdup(spec); + if (!t) return -ENOMEM; *path = t; @@ -985,13 +1008,14 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return 0; } - if (!(e = strchr(spec, ':'))) { - - if (strchr(spec, '/') || spec[0] == 0) + e = strchr(spec, ':'); + if (!e) { + if (!filename_is_safe(spec)) return -EINVAL; if (controller) { - if (!(t = strdup(spec))) + t = strdup(spec); + if (!t) return -ENOMEM; *controller = t; @@ -1003,26 +1027,34 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return 0; } - if (e[1] != '/' || - e == spec || - memchr(spec, '/', e-spec)) + t = strndup(spec, e-spec); + if (!t) + return -ENOMEM; + if (!filename_is_safe(t)) { + free(t); return -EINVAL; + } - if (controller) - if (!(t = strndup(spec, e-spec))) - return -ENOMEM; - - if (path) - if (!(u = strdup(e+1))) { - free(t); - return -ENOMEM; - } + u = strdup(e+1); + if (!u) { + free(t); + return -ENOMEM; + } + if (!path_is_safe(u)) { + free(t); + free(u); + return -EINVAL; + } if (controller) *controller = t; + else + free(t); if (path) *path = u; + else + free(u); return 0; } @@ -1111,17 +1143,18 @@ char **cg_shorten_controllers(char **controllers) { return controllers; for (f = controllers, t = controllers; *f; f++) { - char *cc; + int r; + const char *p; if (streq(*f, "systemd") || streq(*f, SYSTEMD_CGROUP_CONTROLLER)) { free(*f); continue; } - cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(*f)); - strcpy(stpcpy(cc, "/sys/fs/cgroup/"), *f); + p = normalize_controller(*f); - if (access(cc, F_OK) < 0) { + r = check(p); + if (r < 0) { log_debug("Controller %s is not available, removing from controllers list.", *f); free(*f); continue; @@ -1133,3 +1166,164 @@ char **cg_shorten_controllers(char **controllers) { *t = NULL; return controllers; } + +int cg_pid_get_cgroup(pid_t pid, char **root, char **cgroup) { + char *cg_process, *cg_init, *p; + int r; + + assert(pid >= 0); + + if (pid == 0) + pid = getpid(); + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process); + if (r < 0) + return r; + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &cg_init); + if (r < 0) { + free(cg_process); + return r; + } + + if (endswith(cg_init, "/system")) + cg_init[strlen(cg_init)-7] = 0; + else if (streq(cg_init, "/")) + cg_init[0] = 0; + + if (startswith(cg_process, cg_init)) + p = cg_process + strlen(cg_init); + else + p = cg_process; + + free(cg_init); + + 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; +} + +static int instance_unit_from_cgroup(char *cgroup){ + char *at; + + assert(cgroup); + + at = strstr(cgroup, "@."); + if (at) { + /* This is a templated service */ + + char *i; + char _cleanup_free_ *i2 = NULL, *s = NULL; + + i = strchr(at, '/'); + if (!i || !i[1]) /* disallow empty instances */ + return -EINVAL; + + s = strndup(at + 1, i - at - 1); + i2 = strdup(i + 1); + if (!s || !i2) + return -ENOMEM; + + strcpy(at + 1, i2); + strcat(at + 1, s); + } + + return 0; +} + +/* non-static only for testing purposes */ +int cgroup_to_unit(char *cgroup, char **unit){ + int r; + char *p; + + assert(cgroup); + assert(unit); + + r = instance_unit_from_cgroup(cgroup); + if (r < 0) + return r; + + p = strrchr(cgroup, '/'); + assert(p); + + r = unit_name_is_valid(p + 1, true); + if (!r) + return -EINVAL; + + *unit = strdup(p + 1); + if (!*unit) + return -ENOMEM; + + return 0; +} + +static int cg_pid_get(const char *prefix, pid_t pid, char **unit) { + int r; + char _cleanup_free_ *cgroup = NULL; + + assert(pid >= 0); + assert(unit); + + r = cg_pid_get_cgroup(pid, NULL, &cgroup); + if (r < 0) + return r; + + if (!startswith(cgroup, prefix)) + return -ENOENT; + + r = cgroup_to_unit(cgroup, unit); + return r; +} + +int cg_pid_get_unit(pid_t pid, char **unit) { + return cg_pid_get("/system/", pid, unit); +} + +int cg_pid_get_user_unit(pid_t pid, char **unit) { + return cg_pid_get("/user/", pid, unit); +} + +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 (!filename_is_safe(c)) { + free(c); + return -EINVAL; + } + + *controller = c; + return 1; +}