X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Fcgroup-util.c;h=c17e1d4d1bde18080cd82a7ebb2b1bd6778a384f;hp=18cbf0412ab823ec5d9045c1b0ef2f8238857e71;hb=3df82d5a8cdc510f518fd5e234ccb3233b748719;hpb=b08121d00467874ac9aa0dc6f59787877f1bb427 diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c index 18cbf0412..c17e1d4d1 100644 --- a/src/shared/cgroup-util.c +++ b/src/shared/cgroup-util.c @@ -36,6 +36,8 @@ #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; @@ -374,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(); @@ -394,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) { @@ -412,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) @@ -420,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; } } @@ -432,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; } @@ -489,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; } @@ -677,16 +670,16 @@ int cg_delete(const char *controller, const char *path) { 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; } int cg_attach(const char *controller, const char *path, pid_t pid) { - char *fs; + _cleanup_free_ char *fs = NULL; + char c[DECIMAL_STR_MAX(pid_t) + 2]; int r; - char c[32]; assert(controller); assert(path); @@ -700,16 +693,18 @@ int cg_attach(const char *controller, const char *path, pid_t pid) { pid = getpid(); snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid); - char_array_0(c); - - r = write_one_line_file(fs, c); - free(fs); - return r; + return write_string_file(fs, c); } -int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) { - char *fs; +int cg_set_group_access( + const char *controller, + const char *path, + mode_t mode, + uid_t uid, + gid_t gid) { + + _cleanup_free_ char *fs = NULL; int r; assert(controller); @@ -722,14 +717,18 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u if (r < 0) return r; - r = chmod_and_chown(fs, mode, uid, gid); - free(fs); - - return r; + return chmod_and_chown(fs, mode, uid, gid); } -int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) { - char *fs; +int cg_set_task_access( + const char *controller, + const char *path, + mode_t mode, + uid_t uid, + gid_t gid, + int sticky) { + + _cleanup_free_ char *fs = NULL, *procs = NULL; int r; assert(controller); @@ -756,10 +755,8 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui * mode from the file itself */ r = lstat(fs, &st); - if (r < 0) { - free(fs); + if (r < 0) return -errno; - } if (mode == (mode_t) -1) /* No mode set, we just shall set the sticky bit */ @@ -770,9 +767,15 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui } r = chmod_and_chown(fs, mode, uid, gid); - free(fs); + if (r < 0) + return r; - return r; + /* Always keep values for "cgroup.procs" in sync with "tasks" */ + r = cg_get_path(controller, path, "cgroup.procs", &procs); + if (r < 0) + return r; + + return chmod_and_chown(procs, mode, uid, gid); } int cg_get_by_pid(const char *controller, pid_t pid, char **path) { @@ -819,7 +822,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] != ':') @@ -864,7 +867,8 @@ int cg_install_release_agent(const char *controller, const char *agent) { goto finish; } - if ((r = write_one_line_file(fs, line)) < 0) + r = write_string_file(fs, line); + if (r < 0) goto finish; } else if (!streq(sc, agent)) { @@ -885,7 +889,7 @@ int cg_install_release_agent(const char *controller, const char *agent) { sc = strstrip(contents); if (streq(sc, "0")) { - if ((r = write_one_line_file(fs, "1\n")) < 0) + if ((r = write_string_file(fs, "1\n")) < 0) goto finish; r = 1; @@ -947,7 +951,6 @@ int cg_is_empty_by_spec(const char *spec, bool ignore_self) { 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; @@ -997,12 +1000,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; @@ -1014,13 +1019,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; @@ -1032,26 +1038,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; } @@ -1216,10 +1230,63 @@ int cg_pid_get_cgroup(pid_t pid, char **root, char **cgroup) { return 0; } -int cg_pid_get_unit(pid_t pid, char **unit) { +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 *cgroup, *p, *at, *b; - size_t k; + 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); @@ -1228,43 +1295,46 @@ int cg_pid_get_unit(pid_t pid, char **unit) { if (r < 0) return r; - if (!startswith(cgroup, "/system/")) { - free(cgroup); + if (!startswith(cgroup, prefix)) return -ENOENT; - } - p = cgroup + 8; - k = strcspn(p, "/"); + r = cgroup_to_unit(cgroup, unit); + return r; +} - at = memchr(p, '@', k); - if (at && at[1] == '.') { - size_t j; +int cg_pid_get_unit(pid_t pid, char **unit) { + return cg_pid_get("/system/", pid, unit); +} - /* This is a templated service */ - if (p[k] != '/') { - free(cgroup); - return -EIO; - } +int cg_pid_get_user_unit(pid_t pid, char **unit) { + return cg_pid_get("/user/", pid, unit); +} - j = strcspn(p+k+1, "/"); +int cg_controller_from_attr(const char *attr, char **controller) { + const char *dot; + char *c; - b = malloc(k + j + 1); + assert(attr); + assert(controller); - if (b) { - memcpy(b, p, at - p + 1); - memcpy(b + (at - p) + 1, p + k + 1, j); - memcpy(b + (at - p) + 1 + j, at + 1, k - (at - p) - 1); - b[k+j] = 0; - } - } else - b = strndup(p, k); + if (!filename_is_safe(attr)) + return -EINVAL; - free(cgroup); + dot = strchr(attr, '.'); + if (!dot) { + *controller = NULL; + return 0; + } - if (!b) + c = strndup(attr, dot - attr); + if (!c) return -ENOMEM; - *unit = b; - return 0; + if (!filename_is_safe(c)) { + free(c); + return -EINVAL; + } + *controller = c; + return 1; }