From c6c18be35bb1d300d0b62a568783cc1c477f7151 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 11 Jul 2010 00:50:49 +0200 Subject: [PATCH 1/1] cgroup: kill processes, not tasks and other cgroup changes --- Makefile.am | 22 ++- src/cgroup-show.c | 48 +++--- src/cgroup-show.h | 4 +- src/cgroup-util.c | 374 ++++++++++++++++++++++++++++----------------- src/cgroup-util.h | 12 ++ src/cgroup.c | 95 +++++++----- src/cgroup.h | 4 +- src/dbus-manager.c | 2 + src/macro.h | 6 + src/manager.c | 27 +--- src/manager.h | 1 - src/pam-module.c | 28 ++-- src/systemctl.c | 7 +- src/systemd-cgls.c | 8 +- src/test-cgroup.c | 54 ++++--- src/unit.c | 5 +- src/util.c | 3 +- 17 files changed, 411 insertions(+), 289 deletions(-) diff --git a/Makefile.am b/Makefile.am index 57b8aa525..422d37ee6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -483,15 +483,18 @@ systemctl_SOURCES = \ src/systemctl.c \ src/utmp-wtmp.c \ src/dbus-common.c \ - src/cgroup-show.c + src/cgroup-show.c \ + src/cgroup-util.c systemctl_CFLAGS = \ $(AM_CFLAGS) \ - $(DBUS_CFLAGS) + $(DBUS_CFLAGS) \ + $(CGROUP_CLAGS) systemctl_LDADD = \ libsystemd-basic.la \ - $(DBUS_LIBS) + $(DBUS_LIBS) \ + $(CGROUP_LIBS) systemd_notify_SOURCES = \ src/notify.c \ @@ -516,13 +519,16 @@ systemd_install_CFLAGS = \ systemd_cgls_SOURCES = \ src/systemd-cgls.c \ - src/cgroup-show.c - -systemd_cgls_LDADD = \ - libsystemd-basic.la + src/cgroup-show.c \ + src/cgroup-util.c systemd_cgls_CFLAGS = \ - $(AM_CFLAGS) + $(AM_CFLAGS) \ + $(CGROUP_CLAGS) + +systemd_cgls_LDADD = \ + libsystemd-basic.la \ + $(CGROUP_LIBS) systemadm_SOURCES = \ src/systemadm.vala \ diff --git a/src/cgroup-show.c b/src/cgroup-show.c index 44efa1aae..17183ce55 100644 --- a/src/cgroup-show.c +++ b/src/cgroup-show.c @@ -26,6 +26,7 @@ #include "util.h" #include "macro.h" +#include "cgroup-util.h" #include "cgroup-show.h" static int compare(const void *a, const void *b) { @@ -40,8 +41,11 @@ static int compare(const void *a, const void *b) { static char *get_cgroup_path(const char *name) { - if (startswith(name, "name=systemd:")) - name += 13; + if (!name) + return strdup("/cgroup/systemd"); + + if (startswith(name, SYSTEMD_CGROUP_CONTROLLER ":")) + name += sizeof(SYSTEMD_CGROUP_CONTROLLER); if (path_startswith(name, "/cgroup")) return strdup(name); @@ -60,14 +64,14 @@ static unsigned ilog10(unsigned long ul) { return n; } -static int show_cgroup_full(const char *name, const char *prefix, unsigned n_columns, bool more) { +static int show_cgroup_full(const char *path, const char *prefix, unsigned n_columns, bool more) { char *fn; FILE *f; size_t n = 0, n_allocated = 0; pid_t *pids = NULL; char *p; + pid_t pid, biggest = 0; int r; - unsigned long biggest = 0; if (n_columns <= 0) n_columns = columns(); @@ -75,7 +79,7 @@ static int show_cgroup_full(const char *name, const char *prefix, unsigned n_col if (!prefix) prefix = ""; - if (!(p = get_cgroup_path(name))) + if (!(p = get_cgroup_path(path))) return -ENOMEM; r = asprintf(&fn, "%s/cgroup.procs", p); @@ -90,14 +94,7 @@ static int show_cgroup_full(const char *name, const char *prefix, unsigned n_col if (!f) return -errno; - while (!feof(f)) { - unsigned long ul; - - if (fscanf(f, "%lu", &ul) != 1) - break; - - if (ul <= 0) - continue; + while ((r = cg_read_pid(f, &pid)) > 0) { if (n >= n_allocated) { pid_t *npids; @@ -113,12 +110,15 @@ static int show_cgroup_full(const char *name, const char *prefix, unsigned n_col } assert(n < n_allocated); - pids[n++] = (pid_t) ul; + pids[n++] = pid; - if (ul > biggest) - biggest = ul; + if (pid > biggest) + biggest = pid; } + if (r < 0) + goto finish; + if (n > 0) { unsigned i, m; @@ -171,11 +171,11 @@ finish: return r; } -int show_cgroup(const char *name, const char *prefix, unsigned n_columns) { - return show_cgroup_full(name, prefix, n_columns, false); +int show_cgroup(const char *path, const char *prefix, unsigned n_columns) { + return show_cgroup_full(path, prefix, n_columns, false); } -int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_columns) { +int show_cgroup_recursive(const char *path, const char *prefix, unsigned n_columns) { DIR *d; char *last = NULL; char *p1 = NULL, *p2 = NULL, *fn = NULL; @@ -183,15 +183,13 @@ int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_colum bool shown_pids = false; int r; - assert(name); - if (n_columns <= 0) n_columns = columns(); if (!prefix) prefix = ""; - if (!(fn = get_cgroup_path(name))) + if (!(fn = get_cgroup_path(path))) return -ENOMEM; if (!(d = opendir(fn))) { @@ -208,7 +206,7 @@ int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_colum continue; if (!shown_pids) { - show_cgroup_full(name, prefix, n_columns, true); + show_cgroup_full(path, prefix, n_columns, true); shown_pids = true; } @@ -227,14 +225,14 @@ int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_colum last = NULL; } - if (asprintf(&last, "%s/%s", name, de->d_name) < 0) { + if (asprintf(&last, "%s/%s", strempty(path), de->d_name) < 0) { r = -ENOMEM; goto finish; } } if (!shown_pids) - show_cgroup_full(name, prefix, n_columns, !!last); + show_cgroup_full(path, prefix, n_columns, !!last); if (last) { printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last)); diff --git a/src/cgroup-show.h b/src/cgroup-show.h index 1f0c31905..09a436a3c 100644 --- a/src/cgroup-show.h +++ b/src/cgroup-show.h @@ -22,7 +22,7 @@ along with systemd; If not, see . ***/ -int show_cgroup(const char *name, const char *prefix, unsigned columns); -int show_cgroup_recursive(const char *name, const char *prefix, unsigned columns); +int show_cgroup(const char *path, const char *prefix, unsigned columns); +int show_cgroup_recursive(const char *path, const char *prefix, unsigned columns); #endif diff --git a/src/cgroup-util.c b/src/cgroup-util.c index 002c131f7..0b8678c01 100644 --- a/src/cgroup-util.c +++ b/src/cgroup-util.c @@ -33,6 +33,15 @@ #include "macro.h" #include "util.h" +/* + Currently, the only remaining functionality from libcgroup we call + here is: + + - cgroup_get_subsys_mount_point() + - cgroup_walk_tree_begin()/cgroup_walk_tree_next() + - cgroup_delete_cgroup_ext() + */ + int cg_translate_error(int error, int _errno) { switch (error) { @@ -73,11 +82,78 @@ static struct cgroup* cg_new(const char *controller, const char *path) { return cgroup; } +int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { + char *fs; + int r; + FILE *f; + + assert(controller); + assert(path); + assert(_f); + + if ((r = cg_get_path(controller, path, "cgroup.procs", &fs)) < 0) + return r; + + f = fopen(fs, "re"); + free(fs); + + if (!f) + return -errno; + + *_f = f; + return 0; +} + +int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f) { + char *fs; + int r; + FILE *f; + + assert(controller); + assert(path); + assert(_f); + + if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0) + return r; + + f = fopen(fs, "re"); + free(fs); + + if (!f) + return -errno; + + *_f = f; + return 0; +} + +int cg_read_pid(FILE *f, pid_t *_pid) { + unsigned long ul; + + /* Note that the cgroup.procs might contain duplicates! See + * cgroups.txt for details. */ + + errno = 0; + if (fscanf(f, "%lu", &ul) != 1) { + + if (feof(f)) + return 0; + + return errno ? -errno : -EIO; + } + + if (ul <= 0) + return -EIO; + + *_pid = (pid_t) ul; + return 1; +} + int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) { bool killed = false, done = false; Set *s; pid_t my_pid; int r, ret = 0; + FILE *f = NULL; assert(controller); assert(path); @@ -93,23 +169,22 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) my_pid = getpid(); do { - void *iterator = NULL; - pid_t pid = 0; - + pid_t pid; done = true; - r = cgroup_get_task_begin(path, controller, &iterator, &pid); - while (r == 0) { + if ((r = cg_enumerate_processes(controller, path, &f)) < 0) + goto finish; + + while ((r = cg_read_pid(f, &pid)) > 0) { if (pid == my_pid && ignore_self) - goto next; + continue; - if (set_get(s, INT_TO_PTR(pid)) == INT_TO_PTR(pid)) - goto next; + if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid)) + continue; /* If we haven't killed this process yet, kill * it */ - if (kill(pid, sig) < 0 && errno != ESRCH) { if (ret == 0) ret = -errno; @@ -118,22 +193,12 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) killed = true; done = false; - if ((r = set_put(s, INT_TO_PTR(pid))) < 0) - goto loop_exit; - - next: - r = cgroup_get_task_next(&iterator, &pid); + if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) + break; } - if (r == 0 || r == ECGEOF) - r = 0; - else if (r == ECGOTHER && errno == ENOENT) - r = -ESRCH; - else - r = cg_translate_error(r, errno); - - loop_exit: - assert_se(cgroup_get_task_end(&iterator) == 0); + fclose(f); + f = NULL; /* To avoid racing against processes which fork * quicker than we can kill them we repeat this until @@ -141,14 +206,18 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) } while (!done && r >= 0); +finish: set_free(s); - if (ret < 0) - return ret; + if (f) + fclose(f); if (r < 0) return r; + if (ret < 0) + return ret; + return !!killed; } @@ -237,63 +306,53 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path) { int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) { bool migrated = false, done = false; - struct cgroup *dest; int r, ret = 0; pid_t my_pid; + FILE *f = NULL; assert(controller); assert(from); assert(to); - if (!(dest = cg_new(controller, to))) - return -ENOMEM; - my_pid = getpid(); do { - void *iterator = NULL; - pid_t pid = 0; - + pid_t pid; done = true; - r = cgroup_get_task_begin(from, controller, &iterator, &pid); - while (r == 0) { + if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) + goto finish; + + while ((r = cg_read_pid(f, &pid)) > 0) { if (pid == my_pid && ignore_self) - goto next; + continue; - if ((r = cgroup_attach_task_pid(dest, pid)) != 0) { + if ((r = cg_attach(controller, to, pid)) < 0) { if (ret == 0) - r = cg_translate_error(r, errno); + ret = -r; } migrated = true; done = false; - - next: - - r = cgroup_get_task_next(&iterator, &pid); } - if (r == 0 || r == ECGEOF) - r = 0; - else if (r == ECGOTHER && errno == ENOENT) - r = -ESRCH; - else - r = cg_translate_error(r, errno); - - assert_se(cgroup_get_task_end(&iterator) == 0); + fclose(f); + f = NULL; } while (!done && r >= 0); - cgroup_free(&dest); +finish: - if (ret < 0) - return ret; + if (f) + fclose(f); if (r < 0) return r; + if (ret < 0) + return ret; + return !!migrated; } @@ -354,18 +413,24 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch int r; assert(controller); - assert(path); if ((r = cgroup_get_subsys_mount_point(controller, &mp)) != 0) return cg_translate_error(r, errno); - if (suffix) + if (path && suffix) r = asprintf(fs, "%s/%s/%s", mp, path, suffix); - else + else if (path) r = asprintf(fs, "%s/%s", mp, path); + else if (suffix) + r = asprintf(fs, "%s/%s", mp, suffix); + else { + path_kill_slashes(mp); + *fs = mp; + return 0; + } free(mp); - + path_kill_slashes(*fs); return r < 0 ? -ENOMEM : 0; } @@ -409,83 +474,59 @@ finish: } int cg_create(const char *controller, const char *path) { - struct cgroup *cg; + char *fs; int r; assert(controller); assert(path); - if (!(cg = cg_new(controller, path))) - return -ENOMEM; - - if ((r = cgroup_create_cgroup(cg, 1)) != 0) { - r = cg_translate_error(r, errno); - goto finish; - } - - r = 0; + if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) + return r; -finish: - cgroup_free(&cg); + r = mkdir_p(fs, 0755); + free(fs); return r; } int cg_attach(const char *controller, const char *path, pid_t pid) { - struct cgroup *cg; + char *fs; int r; + char c[32]; assert(controller); assert(path); assert(pid >= 0); - if (!(cg = cg_new(controller, path))) - return -ENOMEM; + if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0) + return r; if (pid == 0) pid = getpid(); - if ((r = cgroup_attach_task_pid(cg, pid))) { - r = cg_translate_error(r, errno); - goto finish; - } - - r = 0; + snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid); + char_array_0(c); -finish: - cgroup_free(&cg); + r = write_one_line_file(fs, c); + free(fs); return r; } int cg_create_and_attach(const char *controller, const char *path, pid_t pid) { - struct cgroup *cg; int r; assert(controller); assert(path); assert(pid >= 0); - if (!(cg = cg_new(controller, path))) - return -ENOMEM; - - if ((r = cgroup_create_cgroup(cg, 1)) != 0) { - r = cg_translate_error(r, errno); - goto finish; - } - - if (pid == 0) - pid = getpid(); - - if ((r = cgroup_attach_task_pid(cg, pid))) { - r = cg_translate_error(r, errno); - goto finish; - } + if ((r = cg_create(controller, path)) < 0) + return r; - r = 0; + if ((r = cg_attach(controller, path, pid)) < 0) + return r; -finish: - cgroup_free(&cg); + /* This does not remove the cgroup on failure */ return r; } @@ -525,40 +566,82 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui int cg_get_by_pid(const char *controller, pid_t pid, char **path) { int r; char *p = NULL; + FILE *f; + char *fs; + size_t cs; assert(controller); - assert(pid > 0); assert(path); + assert(pid >= 0); - if ((r = cgroup_get_current_controller_path(pid, controller, &p)) != 0) - return cg_translate_error(r, errno); + if (pid == 0) + pid = getpid(); - assert(p); + if (asprintf(&fs, "/proc/%lu/cgroup", (unsigned long) pid) < 0) + return -ENOMEM; - *path = p; - return 0; + f = fopen(fs, "re"); + free(fs); + + cs = strlen(controller); + + while (!feof(f)) { + char line[LINE_MAX]; + char *l; + + errno = 0; + if (!(fgets(line, sizeof(line), f))) { + if (feof(f)) + break; + + r = errno ? -errno : -EIO; + goto finish; + } + + truncate_nl(line); + + if (!(l = strchr(line, ':'))) + continue; + + l++; + if (strncmp(l, controller, cs) != 0) + continue; + + if (l[cs] != ':') + continue; + + if (!(p = strdup(l + cs + 1))) { + r = -ENOMEM; + goto finish; + } + + *path = p; + r = 0; + goto finish; + } + + r = -ENOENT; + +finish: + fclose(f); + + return r; } int cg_install_release_agent(const char *controller, const char *agent) { - char *mp = NULL, *path = NULL, *contents = NULL, *line = NULL, *sc; + char *fs = NULL, *contents = NULL, *line = NULL, *sc; int r; assert(controller); assert(agent); - if ((r = cgroup_get_subsys_mount_point(controller, &mp)) != 0) - return cg_translate_error(r, errno); - - if (asprintf(&path, "%s/release_agent", mp) < 0) { - r = -ENOMEM; - goto finish; - } + if ((r = cg_get_path(controller, NULL, "release_agent", &fs)) < 0) + return r; - if ((r = read_one_line_file(path, &contents)) < 0) + if ((r = read_one_line_file(fs, &contents)) < 0) goto finish; sc = strstrip(contents); - if (sc[0] == 0) { if (asprintf(&line, "%s\n", agent) < 0) { @@ -566,7 +649,7 @@ int cg_install_release_agent(const char *controller, const char *agent) { goto finish; } - if ((r = write_one_line_file(path, line)) < 0) + if ((r = write_one_line_file(fs, line)) < 0) goto finish; } else if (!streq(sc, agent)) { @@ -574,33 +657,33 @@ int cg_install_release_agent(const char *controller, const char *agent) { goto finish; } - free(path); - path = NULL; - if (asprintf(&path, "%s/notify_on_release", mp) < 0) { + free(fs); + fs = NULL; + if ((r = cg_get_path(controller, NULL, "notify_on_release", &fs)) < 0) { r = -ENOMEM; goto finish; } free(contents); contents = NULL; - if ((r = read_one_line_file(path, &contents)) < 0) + if ((r = read_one_line_file(fs, &contents)) < 0) goto finish; sc = strstrip(contents); if (streq(sc, "0")) { - if ((r = write_one_line_file(path, "1\n")) < 0) + if ((r = write_one_line_file(fs, "1\n")) < 0) goto finish; + + r = 1; } else if (!streq(sc, "1")) { r = -EIO; goto finish; - } - - r = 0; + } else + r = 0; finish: - free(mp); - free(path); + free(fs); free(contents); free(line); @@ -608,36 +691,32 @@ finish: } int cg_is_empty(const char *controller, const char *path, bool ignore_self) { - void *iterator = NULL; - pid_t pid = 0; + pid_t pid; int r; + FILE *f; + bool found = false; assert(controller); assert(path); - r = cgroup_get_task_begin(path, controller, &iterator, &pid); - while (r == 0) { + if ((r = cg_enumerate_tasks(controller, path, &f)) < 0) + return r; - if (ignore_self&& pid == getpid()) - goto next; + while ((r = cg_read_pid(f, &pid)) > 0) { - break; + if (ignore_self && pid == getpid()) + continue; - next: - r = cgroup_get_task_next(&iterator, &pid); + found = true; + break; } + fclose(f); - if (r == ECGEOF) - r = 1; - else if (r != 0) - r = cg_translate_error(r, errno); - else - r = 0; - - assert_se(cgroup_get_task_end(&iterator) == 0); + if (r < 0) + return r; - return r; + return !found; } int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) { @@ -692,3 +771,12 @@ int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_ return ret; } + +int cg_init(void) { + int r; + + if ((r = cgroup_init()) != 0) + return cg_translate_error(r, errno); + + return 0; +} diff --git a/src/cgroup-util.h b/src/cgroup-util.h index 4ada71bc1..716c635a9 100644 --- a/src/cgroup-util.h +++ b/src/cgroup-util.h @@ -23,9 +23,19 @@ ***/ #include +#include + +#include "set.h" + +#define SYSTEMD_CGROUP_CONTROLLER "name=systemd" int cg_translate_error(int error, int _errno); +int cg_enumerate_processes(const char *controller, const char *path, FILE **_f); +int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f); + +int cg_read_pid(FILE *f, pid_t *_pid); + int cg_kill(const char *controller, const char *path, int sig, bool ignore_self); int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self); int cg_kill_recursive_and_wait(const char *controller, const char *path); @@ -51,4 +61,6 @@ int cg_install_release_agent(const char *controller, const char *agent); int cg_is_empty(const char *controller, const char *path, bool ignore_self); int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self); +int cg_init(void); + #endif diff --git a/src/cgroup.c b/src/cgroup.c index 83fe2ef20..7397b5298 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -25,8 +25,7 @@ #include #include #include - -#include +#include #include "cgroup.h" #include "cgroup-util.h" @@ -210,71 +209,91 @@ int cgroup_bonding_is_empty_list(CGroupBonding *first) { } int manager_setup_cgroup(Manager *m) { - char *cp; + char *current = NULL, *path = NULL; int r; - pid_t pid; char suffix[32]; assert(m); - if ((r = cgroup_init()) != 0) { - log_error("Failed to initialize libcg: %s", cgroup_strerror(r)); - return cg_translate_error(r, errno); + /* 1. Initialize libcg */ + if ((r = cg_init()) < 0) { + log_error("Failed to initialize libcg: %s", strerror(-r)); + goto finish; } - free(m->cgroup_mount_point); - m->cgroup_mount_point = NULL; - if ((r = cgroup_get_subsys_mount_point(SYSTEMD_CGROUP_CONTROLLER, &m->cgroup_mount_point))) - return cg_translate_error(r, errno); - - pid = getpid(); - - if ((r = cgroup_get_current_controller_path(pid, SYSTEMD_CGROUP_CONTROLLER, &cp))) - return cg_translate_error(r, errno); + /* 2. Determine hierarchy */ + if ((r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t)) < 0) + goto finish; - snprintf(suffix, sizeof(suffix), "/systemd-%u", (unsigned) pid); + snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid()); char_array_0(suffix); free(m->cgroup_hierarchy); - - if (endswith(cp, suffix)) + if (endswith(current, suffix)) { /* We probably got reexecuted and can continue to use our root cgroup */ - m->cgroup_hierarchy = cp; - else { - /* We need a new root cgroup */ + m->cgroup_hierarchy = current; + current = NULL; + } else { + /* We need a new root cgroup */ m->cgroup_hierarchy = NULL; - r = asprintf(&m->cgroup_hierarchy, "%s%s", streq(cp, "/") ? "" : cp, suffix); - free(cp); - - if (r < 0) - return -ENOMEM; + if ((r = asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix)) < 0) { + r = -ENOMEM; + goto finish; + } } - log_debug("Using cgroup controller <" SYSTEMD_CGROUP_CONTROLLER ">, hierarchy mounted at <%s>, using root group <%s>.", - m->cgroup_mount_point, - m->cgroup_hierarchy); + /* 3. Show data */ + if ((r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path)) < 0) + goto finish; + log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); + + /* 4. Install agent */ if ((r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, CGROUP_AGENT_PATH)) < 0) log_warning("Failed to install release agent, ignoring: %s", strerror(-r)); + else if (r > 0) + log_debug("Installed release agent."); else - log_debug("Installed release agent, or already installed."); + log_debug("Release agent already installed."); - if ((r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0)) < 0) + /* 5. Realize the group */ + if ((r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0)) < 0) { log_error("Failed to create root cgroup hierarchy: %s", strerror(-r)); - else - log_debug("Created root group."); + goto finish; + } + + /* 6. And pin it, so that it cannot be unmounted */ + if (m->pin_cgroupfs_fd >= 0) + close_nointr_nofail(m->pin_cgroupfs_fd); + + if ((m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK)) < 0) { + r = -errno; + goto finish; + } + + log_debug("Created root group."); + +finish: + free(current); + free(path); return r; } -int manager_shutdown_cgroup(Manager *m) { +void manager_shutdown_cgroup(Manager *m, bool delete) { assert(m); - if (!m->cgroup_hierarchy) - return 0; + if (delete && m->cgroup_hierarchy) + cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy); - return cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy); + if (m->pin_cgroupfs_fd >= 0) { + close_nointr_nofail(m->pin_cgroupfs_fd); + m->pin_cgroupfs_fd = -1; + } + + free(m->cgroup_hierarchy); + m->cgroup_hierarchy = NULL; } int cgroup_notify_empty(Manager *m, const char *group) { diff --git a/src/cgroup.h b/src/cgroup.h index a7ca8abb0..45d043935 100644 --- a/src/cgroup.h +++ b/src/cgroup.h @@ -26,8 +26,6 @@ typedef struct CGroupBonding CGroupBonding; #include "unit.h" -#define SYSTEMD_CGROUP_CONTROLLER "name=systemd" - /* Binds a cgroup to a name */ struct CGroupBonding { char *controller; @@ -76,7 +74,7 @@ char *cgroup_bonding_to_string(CGroupBonding *b); #include "manager.h" int manager_setup_cgroup(Manager *m); -int manager_shutdown_cgroup(Manager *m); +void manager_shutdown_cgroup(Manager *m, bool delete); int cgroup_notify_empty(Manager *m, const char *group); diff --git a/src/dbus-manager.c b/src/dbus-manager.c index 5daccb14e..f52b06e5f 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -121,6 +121,7 @@ " \n" \ " \n" \ " \n" \ + " \n" \ " \n" #define INTROSPECTION_BEGIN \ @@ -229,6 +230,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, { "org.freedesktop.systemd1.Manager", "SysVInitPath", bus_property_append_strv, "as", m->lookup_paths.sysvinit_path }, { "org.freedesktop.systemd1.Manager", "SysVRcndPath", bus_property_append_strv, "as", m->lookup_paths.sysvrcnd_path }, { "org.freedesktop.systemd1.Manager", "NotifySocket", bus_property_append_string, "s", m->notify_socket }, + { "org.freedesktop.systemd1.Manager", "ControlGroupHierarchy", bus_property_append_string, "s", m->cgroup_hierarchy }, { NULL, NULL, NULL, NULL, NULL } }; diff --git a/src/macro.h b/src/macro.h index d00e70bf6..7bd57b3a8 100644 --- a/src/macro.h +++ b/src/macro.h @@ -108,12 +108,18 @@ static inline size_t ALIGN(size_t l) { #define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) #define UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u))) +#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) +#define ULONG_TO_PTR(u) ((void*) ((uintptr_t) (u))) + #define PTR_TO_INT(p) ((int) ((intptr_t) (p))) #define INT_TO_PTR(u) ((void*) ((intptr_t) (u))) #define TO_INT32(p) ((int32_t) ((intptr_t) (p))) #define INT32_TO_PTR(u) ((void*) ((intptr_t) (u))) +#define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) +#define LONG_TO_PTR(u) ((void*) ((intptr_t) (u))) + #define memzero(x,l) (memset((x), 0, (l))) #define zero(x) (memzero(&(x), sizeof(x))) diff --git a/src/manager.c b/src/manager.c index 8d016b9f5..a7e24eb81 100644 --- a/src/manager.c +++ b/src/manager.c @@ -186,7 +186,6 @@ static int manager_setup_signals(Manager *m) { int manager_new(ManagerRunningAs running_as, Manager **_m) { Manager *m; int r = -ENOMEM; - char *p; assert(_m); assert(running_as >= 0); @@ -245,14 +244,6 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { if ((r = bus_init(m)) < 0) goto fail; - if (asprintf(&p, "%s/%s", m->cgroup_mount_point, m->cgroup_hierarchy) < 0) { - r = -ENOMEM; - goto fail; - } - - m->pin_cgroupfs_fd = open(p, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); - free(p); - *_m = m; return 0; @@ -420,8 +411,7 @@ void manager_free(Manager *m) { /* If we reexecute ourselves, we keep the root cgroup * around */ - if (m->exit_code != MANAGER_REEXECUTE) - manager_shutdown_cgroup(m); + manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); bus_done(m); @@ -443,13 +433,8 @@ void manager_free(Manager *m) { lookup_paths_free(&m->lookup_paths); strv_free(m->environment); - free(m->cgroup_hierarchy); - free(m->cgroup_mount_point); - hashmap_free(m->cgroup_bondings); - - if (m->pin_cgroupfs_fd >= 0) - close_nointr_nofail(m->pin_cgroupfs_fd); + set_free_free(m->unit_path_cache); free(m); } @@ -1664,7 +1649,7 @@ static int manager_process_notify_fd(Manager *m) { ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); - if (!(u = hashmap_get(m->watch_pids, UINT32_TO_PTR(ucred->pid)))) + if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid)))) if (!(u = cgroup_unit_by_pid(m, ucred->pid))) { log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid); continue; @@ -1729,7 +1714,7 @@ static int manager_dispatch_sigchld(Manager *m) { return r; /* And now figure out the unit this belongs to */ - if (!(u = hashmap_get(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) + if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid)))) u = cgroup_unit_by_pid(m, si.si_pid); /* And now, we actually reap the zombie. */ @@ -1752,9 +1737,9 @@ static int manager_dispatch_sigchld(Manager *m) { if (!u) continue; - log_debug("Child %llu belongs to %s", (long long unsigned) si.si_pid, u->meta.id); + log_debug("Child %lu belongs to %s", (long unsigned) si.si_pid, u->meta.id); - hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)); + hashmap_remove(m->watch_pids, LONG_TO_PTR(si.si_pid)); UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status); } diff --git a/src/manager.h b/src/manager.h index e5915d37e..f3bf96ee1 100644 --- a/src/manager.h +++ b/src/manager.h @@ -171,7 +171,6 @@ struct Manager { /* Data specific to the cgroup subsystem */ Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */ - char *cgroup_mount_point; char *cgroup_hierarchy; usec_t gc_queue_timestamp; diff --git a/src/pam-module.c b/src/pam-module.c index 1834dd92e..1441987fd 100644 --- a/src/pam-module.c +++ b/src/pam-module.c @@ -217,17 +217,17 @@ static int create_user_group(pam_handle_t *handle, const char *group, struct pas assert(group); if (attach) - r = cg_create_and_attach("name=systemd", group, 0); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, group, 0); else - r = cg_create("name=systemd", group); + r = cg_create(SYSTEMD_CGROUP_CONTROLLER, group); if (r < 0) { pam_syslog(handle, LOG_ERR, "Failed to create cgroup: %s", strerror(-r)); return PAM_SESSION_ERR; } - if ((r = cg_set_task_access("name=systemd", group, 0755, pw->pw_uid, pw->pw_gid)) < 0 || - (r = cg_set_group_access("name=systemd", group, 0755, pw->pw_uid, pw->pw_gid)) < 0) { + if ((r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, group, 0755, pw->pw_uid, pw->pw_gid)) < 0 || + (r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER, group, 0755, pw->pw_uid, pw->pw_gid)) < 0) { pam_syslog(handle, LOG_ERR, "Failed to change access modes: %s", strerror(-r)); return PAM_SESSION_ERR; } @@ -258,8 +258,8 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (sd_booted() <= 0) return PAM_SUCCESS; - if ((r = cgroup_init()) != 0) { - pam_syslog(handle, LOG_ERR, "libcgroup initialization failed: %s", cgroup_strerror(r)); + if ((r = cg_init()) < 0) { + pam_syslog(handle, LOG_ERR, "libcgroup initialization failed: %s", strerror(-r)); r = PAM_SESSION_ERR; goto finish; } @@ -361,7 +361,7 @@ static int session_remains(pam_handle_t *handle, const char *user_path) { zero(info); - r = cgroup_walk_tree_begin("name=systemd", user_path, 0, &iterator, &info, &level); + r = cgroup_walk_tree_begin(SYSTEMD_CGROUP_CONTROLLER, user_path, 0, &iterator, &info, &level); while (r == 0) { if (info.type != CGROUP_FILE_TYPE_DIR) @@ -440,7 +440,7 @@ _public_ PAM_EXTERN int pam_sm_close_session( if (kill_session) { /* Kill processes in session cgroup */ - if ((r = cg_kill_recursive_and_wait("name=systemd", session_path)) < 0) + if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, session_path)) < 0) pam_syslog(handle, LOG_ERR, "Failed to kill session cgroup: %s", strerror(-r)); } else { @@ -450,7 +450,7 @@ _public_ PAM_EXTERN int pam_sm_close_session( * yet. */ create_user_group(handle, nosession_path, pw, 0); - if ((r = cg_migrate_recursive("name=systemd", session_path, nosession_path, false)) < 0) + if ((r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, session_path, nosession_path, false)) < 0) pam_syslog(handle, LOG_ERR, "Failed to migrate session cgroup: %s", strerror(-r)); } @@ -458,13 +458,13 @@ _public_ PAM_EXTERN int pam_sm_close_session( if (r < 0) pam_syslog(handle, LOG_INFO, "Couldn't empty session cgroup, not deleting."); else { - if ((r = cg_delete("name=systemd", session_path)) < 0) + if ((r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, session_path)) < 0) pam_syslog(handle, LOG_ERR, "Failed to delete session cgroup: %s", strerror(-r)); } } /* GC user tree */ - cg_trim("name=systemd", user_path, false); + cg_trim(SYSTEMD_CGROUP_CONTROLLER, user_path, false); if ((r = session_remains(handle, user_path)) < 0) pam_syslog(handle, LOG_ERR, "Failed to determine whether a session remains: %s", strerror(-r)); @@ -473,11 +473,11 @@ _public_ PAM_EXTERN int pam_sm_close_session( if (kill_user && r == 0) { /* Kill no-session cgroup */ - if ((r = cg_kill_recursive_and_wait("name=systemd", user_path)) < 0) + if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, user_path)) < 0) pam_syslog(handle, LOG_ERR, "Failed to kill user cgroup: %s", strerror(-r)); } else { - if ((r = cg_is_empty_recursive("name=systemd", user_path, true)) < 0) + if ((r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, user_path, true)) < 0) pam_syslog(handle, LOG_ERR, "Failed to check user cgroup: %s", strerror(-r)); /* If we managed to kill somebody, don't cleanup the cgroup. */ @@ -489,7 +489,7 @@ _public_ PAM_EXTERN int pam_sm_close_session( const char *runtime_dir; /* Remove user cgroup */ - if ((r = cg_delete("name=systemd", user_path)) < 0) + if ((r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, user_path)) < 0) pam_syslog(handle, LOG_ERR, "Failed to delete user cgroup: %s", strerror(-r)); /* This will migrate us to the /user cgroup. */ diff --git a/src/systemctl.c b/src/systemctl.c index 53b6b47b6..799f57d00 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -43,6 +43,7 @@ #include "strv.h" #include "dbus-common.h" #include "cgroup-show.h" +#include "cgroup-util.h" #include "list.h" static const char *arg_type = NULL; @@ -1053,6 +1054,7 @@ typedef struct UnitStatusInfo { static void print_status_info(UnitStatusInfo *i) { ExecStatusInfo *p; + int r; assert(i); @@ -1183,7 +1185,10 @@ static void print_status_info(UnitStatusInfo *i) { else c = 0; - show_cgroup_recursive(i->default_control_group, "\t\t ", c); + if ((r = cg_init()) < 0) + log_error("Failed to initialize libcg: %s", strerror(-r)); + else + show_cgroup_recursive(i->default_control_group, "\t\t ", c); } } diff --git a/src/systemd-cgls.c b/src/systemd-cgls.c index 6614f0fd2..bd8d263d0 100644 --- a/src/systemd-cgls.c +++ b/src/systemd-cgls.c @@ -27,6 +27,7 @@ #include #include "cgroup-show.h" +#include "cgroup-util.h" #include "log.h" #include "util.h" @@ -82,6 +83,11 @@ int main(int argc, char *argv[]) { goto finish; } + if (cg_init() < 0) { + log_error("Failed to initialize libcg: %s", strerror(-r)); + goto finish; + } + if (optind < argc) { unsigned i; @@ -105,7 +111,7 @@ int main(int argc, char *argv[]) { printf("Working Directory %s:\n", p); r = show_cgroup_recursive(p, NULL, 0); } else - r = show_cgroup_recursive("", NULL, 0); + r = show_cgroup_recursive(NULL, NULL, 0); free(p); } diff --git a/src/test-cgroup.c b/src/test-cgroup.c index 389df6d39..45e7f0fe0 100644 --- a/src/test-cgroup.c +++ b/src/test-cgroup.c @@ -22,8 +22,6 @@ #include #include -#include - #include "cgroup-util.h" #include "util.h" #include "log.h" @@ -31,54 +29,54 @@ int main(int argc, char*argv[]) { char *path; - assert_se(cgroup_init() == 0); + assert_se(cg_init() >= 0); - assert_se(cg_create("name=systemd", "/test-a") == 0); - assert_se(cg_create("name=systemd", "/test-a") == 0); - assert_se(cg_create("name=systemd", "/test-b") == 0); - assert_se(cg_create("name=systemd", "/test-b/test-c") == 0); - assert_se(cg_create_and_attach("name=systemd", "/test-b", 0) == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-c") == 0); + assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0) == 0); - assert_se(cg_get_by_pid("name=systemd", getpid(), &path) == 0); + assert_se(cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0); assert_se(streq(path, "/test-b")); free(path); - assert_se(cg_attach("name=systemd", "/test-a", 0) == 0); + assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) == 0); - assert_se(cg_get_by_pid("name=systemd", getpid(), &path) == 0); + assert_se(cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0); assert_se(path_equal(path, "/test-a")); free(path); - assert_se(cg_create_and_attach("name=systemd", "/test-b/test-d", 0) == 0); + assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", 0) == 0); - assert_se(cg_get_by_pid("name=systemd", getpid(), &path) == 0); + assert_se(cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0); assert_se(path_equal(path, "/test-b/test-d")); free(path); - assert_se(cg_get_path("name=systemd", "/test-b/test-d", NULL, &path) == 0); + assert_se(cg_get_path(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", NULL, &path) == 0); assert_se(path_equal(path, "/cgroup/systemd/test-b/test-d")); free(path); - assert_se(cg_is_empty("name=systemd", "/test-a", false) > 0); - assert_se(cg_is_empty("name=systemd", "/test-b", false) > 0); - assert_se(cg_is_empty_recursive("name=systemd", "/test-a", false) > 0); - assert_se(cg_is_empty_recursive("name=systemd", "/test-b", false) == 0); + assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0); + assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) == 0); - assert_se(cg_kill_recursive("name=systemd", "/test-a", 0, false) == 0); - assert_se(cg_kill_recursive("name=systemd", "/test-b", 0, false) > 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false) == 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false) > 0); - assert_se(cg_migrate_recursive("name=systemd", "/test-b", "/test-a", false) == 0); + assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", "/test-a", false) == 0); - assert_se(cg_is_empty_recursive("name=systemd", "/test-a", false) == 0); - assert_se(cg_is_empty_recursive("name=systemd", "/test-b", false) > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) == 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0); - assert_se(cg_kill_recursive("name=systemd", "/test-a", 0, false) > 0); - assert_se(cg_kill_recursive("name=systemd", "/test-b", 0, false) == 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false) > 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false) == 0); - cg_trim("name=systemd", "/", false); + cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false); - assert_se(cg_delete("name=systemd", "/test-b") < 0); - assert_se(cg_delete("name=systemd", "/test-a") == 0); + assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0); + assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); return 0; } diff --git a/src/unit.c b/src/unit.c index 5d51f9996..b362fd3b4 100644 --- a/src/unit.c +++ b/src/unit.c @@ -39,6 +39,7 @@ #include "specifier.h" #include "dbus-unit.h" #include "special.h" +#include "cgroup-util.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -1144,14 +1145,14 @@ int unit_watch_pid(Unit *u, pid_t pid) { /* Watch a specific PID. We only support one unit watching * each PID for now. */ - return hashmap_put(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u); + return hashmap_put(u->meta.manager->watch_pids, LONG_TO_PTR(pid), u); } void unit_unwatch_pid(Unit *u, pid_t pid) { assert(u); assert(pid >= 1); - hashmap_remove_value(u->meta.manager->watch_pids, UINT32_TO_PTR(pid), u); + hashmap_remove_value(u->meta.manager->watch_pids, LONG_TO_PTR(pid), u); } int unit_watch_timer(Unit *u, usec_t delay, Watch *w) { diff --git a/src/util.c b/src/util.c index fa3969b70..69eb14be6 100644 --- a/src/util.c +++ b/src/util.c @@ -956,7 +956,6 @@ int mkdir_parents(const char *path, mode_t mode) { return -ENOMEM; r = mkdir(t, mode); - free(t); if (r < 0 && errno != EEXIST) @@ -972,7 +971,7 @@ int mkdir_p(const char *path, mode_t mode) { if ((r = mkdir_parents(path, mode)) < 0) return r; - if (mkdir(path, mode) < 0) + if (mkdir(path, mode) < 0 && errno != EEXIST) return -errno; return 0; -- 2.30.2