From: Lennart Poettering Date: Mon, 12 Jul 2010 16:16:44 +0000 (+0200) Subject: cgroup: reimplement the last bit of libcgroup functionality natively X-Git-Tag: v3~34 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=35d2e7ec19f8d3960a14dc04642060ccee3faa43;hp=dbd821acb4d91181b309860bb4d4b711b38da7b4 cgroup: reimplement the last bit of libcgroup functionality natively --- diff --git a/Makefile.am b/Makefile.am index 422d37ee6..6351537fa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -285,14 +285,12 @@ libsystemd_core_la_SOURCES = \ libsystemd_core_la_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) \ - $(CGROUP_CFLAGS) + $(UDEV_CFLAGS) libsystemd_core_la_LIBADD = \ libsystemd-basic.la \ $(DBUS_LIBS) \ $(UDEV_LIBS) \ - $(CGROUP_LIBS) \ $(LIBWRAP_LIBS) \ $(PAM_LIBS) @@ -396,8 +394,7 @@ systemd_SOURCES = \ systemd_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) \ - $(CGROUP_CFLAGS) + $(UDEV_CFLAGS) systemd_LDADD = \ libsystemd-core.la @@ -439,12 +436,10 @@ test_cgroup_SOURCES = \ src/cgroup-util.c test_cgroup_CFLAGS = \ - $(AM_CFLAGS) \ - $(CGROUP_CFLAGS) + $(AM_CFLAGS) test_cgroup_LDADD = \ - libsystemd-basic.la \ - $(CGROUP_LIBS) + libsystemd-basic.la systemd_logger_SOURCES = \ src/logger.c \ @@ -488,13 +483,11 @@ systemctl_SOURCES = \ systemctl_CFLAGS = \ $(AM_CFLAGS) \ - $(DBUS_CFLAGS) \ - $(CGROUP_CLAGS) + $(DBUS_CFLAGS) systemctl_LDADD = \ libsystemd-basic.la \ - $(DBUS_LIBS) \ - $(CGROUP_LIBS) + $(DBUS_LIBS) systemd_notify_SOURCES = \ src/notify.c \ @@ -523,12 +516,10 @@ systemd_cgls_SOURCES = \ src/cgroup-util.c systemd_cgls_CFLAGS = \ - $(AM_CFLAGS) \ - $(CGROUP_CLAGS) + $(AM_CFLAGS) systemd_cgls_LDADD = \ - libsystemd-basic.la \ - $(CGROUP_LIBS) + libsystemd-basic.la systemadm_SOURCES = \ src/systemadm.vala \ @@ -559,8 +550,7 @@ pam_systemd_la_SOURCES = \ src/sd-daemon.c pam_systemd_la_CFLAGS = \ - $(AM_CFLAGS) \ - $(CGROUP_CFLAGS) \ + $(AM_CFLAGS) -fvisibility=hidden pam_systemd_la_LDFLAGS = \ @@ -572,8 +562,7 @@ pam_systemd_la_LDFLAGS = \ pam_systemd_la_LIBADD = \ libsystemd-basic.la \ - $(PAM_LIBS) \ - $(CGROUP_LIBS) + $(PAM_LIBS) SED_PROCESS = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ diff --git a/configure.ac b/configure.ac index 6b98442d1..5e5dc0d2a 100644 --- a/configure.ac +++ b/configure.ac @@ -109,10 +109,6 @@ PKG_CHECK_MODULES(DBUSGLIB, [ dbus-glib-1 ]) AC_SUBST(DBUSGLIB_CFLAGS) AC_SUBST(DBUSGLIB_LIBS) -PKG_CHECK_MODULES(CGROUP, [ libcgroup >= 0.36 ]) -AC_SUBST(CGROUP_CFLAGS) -AC_SUBST(CGROUP_LIBS) - AC_ARG_ENABLE([tcpwrap], AS_HELP_STRING([--disable-tcpwrap],[Disable optional TCP wrappers support]), [case "${enableval}" in diff --git a/src/cgroup-show.c b/src/cgroup-show.c index 17183ce55..24d2ba398 100644 --- a/src/cgroup-show.c +++ b/src/cgroup-show.c @@ -39,20 +39,6 @@ static int compare(const void *a, const void *b) { return 0; } -static char *get_cgroup_path(const char *name) { - - 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); - - return strappend("/cgroup/systemd/", name); -} - static unsigned ilog10(unsigned long ul) { int n = 0; @@ -64,7 +50,7 @@ static unsigned ilog10(unsigned long ul) { return n; } -static int show_cgroup_full(const char *path, const char *prefix, unsigned n_columns, bool more) { +static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more) { char *fn; FILE *f; size_t n = 0, n_allocated = 0; @@ -79,8 +65,8 @@ static int show_cgroup_full(const char *path, const char *prefix, unsigned n_col if (!prefix) prefix = ""; - if (!(p = get_cgroup_path(path))) - return -ENOMEM; + if ((r = cg_fix_path(path, &p)) < 0) + return r; r = asprintf(&fn, "%s/cgroup.procs", p); free(p); @@ -171,15 +157,10 @@ finish: return r; } -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 *path, const char *prefix, unsigned n_columns) { +int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns) { DIR *d; char *last = NULL; - char *p1 = NULL, *p2 = NULL, *fn = NULL; - struct dirent *de; + char *p1 = NULL, *p2 = NULL, *fn = NULL, *gn = NULL; bool shown_pids = false; int r; @@ -189,24 +170,18 @@ int show_cgroup_recursive(const char *path, const char *prefix, unsigned n_colum if (!prefix) prefix = ""; - if (!(fn = get_cgroup_path(path))) - return -ENOMEM; + if ((r = cg_fix_path(path, &fn)) < 0) + return r; if (!(d = opendir(fn))) { free(fn); return -errno; } - while ((de = readdir(d))) { - - if (de->d_type != DT_DIR) - continue; - - if (ignore_file(de->d_name)) - continue; + while ((r = cg_read_subgroup(d, &gn)) > 0) { if (!shown_pids) { - show_cgroup_full(path, prefix, n_columns, true); + show_cgroup_one_by_path(path, prefix, n_columns, true); shown_pids = true; } @@ -219,20 +194,26 @@ int show_cgroup_recursive(const char *path, const char *prefix, unsigned n_colum goto finish; } - show_cgroup_recursive(last, p1, n_columns-2); + show_cgroup_by_path(last, p1, n_columns-2); free(last); last = NULL; } - if (asprintf(&last, "%s/%s", strempty(path), de->d_name) < 0) { + r = asprintf(&last, "%s/%s", fn, gn); + free(gn); + + if (r < 0) { r = -ENOMEM; goto finish; } } + if (r < 0) + goto finish; + if (!shown_pids) - show_cgroup_full(path, prefix, n_columns, !!last); + show_cgroup_one_by_path(path, prefix, n_columns, !!last); if (last) { printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last)); @@ -243,7 +224,7 @@ int show_cgroup_recursive(const char *path, const char *prefix, unsigned n_colum goto finish; } - show_cgroup_recursive(last, p2, n_columns-2); + show_cgroup_by_path(last, p2, n_columns-2); } r = 0; @@ -258,3 +239,19 @@ finish: return r; } + +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns) { + char *p; + int r; + + assert(controller); + assert(path); + + if ((r = cg_get_path(controller, path, NULL, &p)) < 0) + return r; + + r = show_cgroup_by_path(p, prefix, n_columns); + free(p); + + return r; +} diff --git a/src/cgroup-show.h b/src/cgroup-show.h index 09a436a3c..da794dee1 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 *path, const char *prefix, unsigned columns); -int show_cgroup_recursive(const char *path, const char *prefix, unsigned columns); +int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns); +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns); #endif diff --git a/src/cgroup-util.c b/src/cgroup-util.c index e38d6f5c1..26e8f691d 100644 --- a/src/cgroup-util.c +++ b/src/cgroup-util.c @@ -24,8 +24,7 @@ #include #include #include - -#include +#include #include "cgroup-util.h" #include "log.h" @@ -33,54 +32,6 @@ #include "macro.h" #include "util.h" -/* - Currently, the only remaining functionality from libcgroup we call - here is: - - - cgroup_walk_tree_begin()/cgroup_walk_tree_next() - - cgroup_delete_cgroup_ext() - */ - -int cg_translate_error(int error, int _errno) { - - switch (error) { - - case ECGROUPNOTCOMPILED: - case ECGROUPNOTMOUNTED: - case ECGROUPNOTEXIST: - case ECGROUPNOTCREATED: - return -ENOENT; - - case ECGINVAL: - return -EINVAL; - - case ECGROUPNOTALLOWED: - return -EPERM; - - case ECGOTHER: - return -_errno; - } - - return -EIO; -} - -static struct cgroup* cg_new(const char *controller, const char *path) { - struct cgroup *cgroup; - - assert(path); - assert(controller); - - if (!(cgroup = cgroup_new_cgroup(path))) - return NULL; - - if (!cgroup_add_controller(cgroup, controller)) { - cgroup_free(&cgroup); - return NULL; - } - - return cgroup; -} - int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { char *fs; int r; @@ -147,11 +98,77 @@ int cg_read_pid(FILE *f, pid_t *_pid) { return 1; } +int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) { + char *fs; + int r; + DIR *d; + + assert(controller); + assert(path); + assert(_d); + + /* This is not recursive! */ + + if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) + return r; + + d = opendir(fs); + free(fs); + + if (!d) + return -errno; + + *_d = d; + return 0; +} + +int cg_read_subgroup(DIR *d, char **fn) { + struct dirent *de; + + assert(d); + + errno = 0; + while ((de = readdir(d))) { + char *b; + + if (de->d_type != DT_DIR) + continue; + + if (streq(de->d_name, ".") || + streq(de->d_name, "..")) + continue; + + if (!(b = strdup(de->d_name))) + return -ENOMEM; + + *fn = b; + return 1; + } + + if (errno) + return -errno; + + return 0; +} + +int cg_rmdir(const char *controller, const char *path) { + char *p; + int r; + + if ((r = cg_get_path(controller, path, NULL, &p)) < 0) + return r; + + r = rmdir(p); + free(p); + + return r < 0 ? -errno : 0; +} + int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) { - bool killed = false, done = false; + bool done = false; Set *s; - pid_t my_pid; int r, ret = 0; + pid_t my_pid; FILE *f = NULL; assert(controller); @@ -171,8 +188,12 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) pid_t pid; done = true; - if ((r = cg_enumerate_processes(controller, path, &f)) < 0) + if ((r = cg_enumerate_processes(controller, path, &f)) < 0) { + if (ret >= 0) + ret = r; + goto finish; + } while ((r = cg_read_pid(f, &pid)) > 0) { @@ -185,15 +206,26 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) /* If we haven't killed this process yet, kill * it */ if (kill(pid, sig) < 0 && errno != ESRCH) { - if (ret == 0) + if (ret >= 0) ret = -errno; - } + } else if (ret == 0) + ret = 1; - killed = true; done = false; - if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) - break; + if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) { + if (ret >= 0) + ret = r; + + goto finish; + } + } + + if (r < 0) { + if (ret >= 0) + ret = r; + + goto finish; } fclose(f); @@ -203,7 +235,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) * quicker than we can kill them we repeat this until * no new pids need to be killed. */ - } while (!done && r >= 0); + } while (!done); finish: set_free(s); @@ -211,69 +243,64 @@ finish: if (f) fclose(f); - if (r < 0) - return r; - - if (ret < 0) - return ret; - - return !!killed; + return ret; } -int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self) { - struct cgroup_file_info info; - int level = 0, r, ret = 0; - void *iterator = NULL; - bool killed = false; +int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self, bool rem) { + int r, ret = 0; + DIR *d = NULL; + char *fn; assert(path); assert(controller); assert(sig >= 0); - zero(info); + ret = cg_kill(controller, path, sig, ignore_self); - r = cgroup_walk_tree_begin(controller, path, 0, &iterator, &info, &level); - while (r == 0) { - int k; - char *p; + if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) { + if (ret >= 0) + ret = r; - if (info.type != CGROUP_FILE_TYPE_DIR) - goto next; + goto finish; + } - if (asprintf(&p, "%s/%s", path, info.path) < 0) { - ret = -ENOMEM; - break; - } + while ((r = cg_read_subgroup(d, &fn)) > 0) { + char *p = NULL; - k = cg_kill(controller, p, sig, ignore_self); - free(p); + r = asprintf(&p, "%s/%s", path, fn); + free(fn); + + if (r < 0) { + if (ret >= 0) + ret = -ENOMEM; - if (k < 0) { - if (ret == 0) - ret = k; - } else if (k > 0) - killed = true; + goto finish; + } - next: + r = cg_kill_recursive(controller, p, sig, ignore_self, rem); + free(p); - r = cgroup_walk_tree_next(0, &iterator, &info, level); + if (r != 0 && ret >= 0) + ret = r; } - if (ret == 0) { - if (r == 0 || r == ECGEOF) - ret = !!killed; - else if (r == ECGOTHER && errno == ENOENT) - ret = -ESRCH; - else - ret = cg_translate_error(r, errno); - } + if (r < 0 && ret >= 0) + ret = r; + + if (rem) + if ((r = cg_rmdir(controller, path)) < 0) { + if (ret >= 0) + ret = r; + } - assert_se(cgroup_walk_tree_end(&iterator) == 0); +finish: + if (d) + closedir(d); return ret; } -int cg_kill_recursive_and_wait(const char *controller, const char *path) { +int cg_kill_recursive_and_wait(const char *controller, const char *path, bool rem) { unsigned i; assert(path); @@ -294,7 +321,7 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path) { else sig = 0; - if ((r = cg_kill_recursive(controller, path, sig, true)) <= 0) + if ((r = cg_kill_recursive(controller, path, sig, true, rem)) <= 0) return r; usleep(50 * USEC_PER_MSEC); @@ -304,7 +331,8 @@ 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; + bool done = false; + Set *s; int r, ret = 0; pid_t my_pid; FILE *f = NULL; @@ -313,96 +341,119 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig assert(from); assert(to); + if (!(s = set_new(trivial_hash_func, trivial_compare_func))) + return -ENOMEM; + my_pid = getpid(); do { pid_t pid; done = true; - if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) + if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) { + if (ret >= 0) + ret = r; + goto finish; + } while ((r = cg_read_pid(f, &pid)) > 0) { + /* This might do weird stuff if we aren't a + * single-threaded program. However, we + * luckily know we are not */ if (pid == my_pid && ignore_self) continue; + if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid)) + continue; + if ((r = cg_attach(controller, to, pid)) < 0) { - if (ret == 0) - ret = -r; - } + if (ret >= 0) + ret = r; + } else if (ret == 0) + ret = 1; - migrated = true; done = false; + + if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) { + if (ret >= 0) + ret = r; + + goto finish; + } + } + + if (r < 0) { + if (ret >= 0) + ret = r; + + goto finish; } fclose(f); f = NULL; - } while (!done && r >= 0); + } while (!done); finish: + set_free(s); if (f) fclose(f); - if (r < 0) - return r; - - if (ret < 0) - return ret; - - return !!migrated; + return ret; } -int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self) { - struct cgroup_file_info info; - int level = 0, r, ret = 0; - void *iterator = NULL; - bool migrated = false; +int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self, bool rem) { + int r, ret = 0; + DIR *d = NULL; + char *fn; assert(controller); assert(from); assert(to); - zero(info); + ret = cg_migrate(controller, from, to, ignore_self); - r = cgroup_walk_tree_begin(controller, from, 0, &iterator, &info, &level); - while (r == 0) { - int k; - char *p; + if ((r = cg_enumerate_subgroups(controller, from, &d)) < 0) { + if (ret >= 0) + ret = r; + goto finish; + } + + while ((r = cg_read_subgroup(d, &fn)) > 0) { + char *p = NULL; - if (info.type != CGROUP_FILE_TYPE_DIR) - goto next; + r = asprintf(&p, "%s/%s", from, fn); + free(fn); - if (asprintf(&p, "%s/%s", from, info.path) < 0) { - ret = -ENOMEM; - break; + if (r < 0) { + if (ret >= 0) + ret = -ENOMEM; + + goto finish; } - k = cg_migrate(controller, p, to, ignore_self); + r = cg_migrate_recursive(controller, p, to, ignore_self, rem); free(p); - if (k < 0) { - if (ret == 0) - ret = k; - } else if (k > 0) - migrated = true; - - next: - r = cgroup_walk_tree_next(0, &iterator, &info, level); + if (r != 0 && ret >= 0) + ret = r; } - if (ret == 0) { - if (r == 0 || r == ECGEOF) - r = !!migrated; - else if (r == ECGOTHER && errno == ENOENT) - r = -ESRCH; - else - r = cg_translate_error(r, errno); - } + if (r < 0 && ret >= 0) + ret = r; + + if (rem) + if ((r = cg_rmdir(controller, from)) < 0) { + if (ret >= 0) + ret = r; + } - assert_se(cgroup_walk_tree_end(&iterator) == 0); +finish: + if (d) + closedir(d); return ret; } @@ -470,24 +521,17 @@ int cg_trim(const char *controller, const char *path, bool delete_root) { } int cg_delete(const char *controller, const char *path) { - struct cgroup *cg; + char *parent; int r; assert(controller); assert(path); - if (!(cg = cg_new(controller, path))) - return -ENOMEM; - - if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_RECURSIVE|CGFLAG_DELETE_IGNORE_MIGRATION)) != 0) { - r = cg_translate_error(r, errno); - goto finish; - } - - r = 0; + if ((r = parent_of_path(path, &parent)) < 0) + return r; -finish: - cgroup_free(&cg); + r = cg_migrate_recursive(controller, path, parent, false, true); + free(parent); return r; } @@ -739,63 +783,154 @@ 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) { - struct cgroup_file_info info; - int level = 0, r, ret = 0; - void *iterator = NULL; - bool empty = true; + int r; + DIR *d = NULL; + char *fn; assert(controller); assert(path); - zero(info); + if ((r = cg_is_empty(controller, path, ignore_self)) <= 0) + return r; + + if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) + return r; - r = cgroup_walk_tree_begin(controller, path, 0, &iterator, &info, &level); - while (r == 0) { - int k; - char *p; + while ((r = cg_read_subgroup(d, &fn)) > 0) { + char *p = NULL; - if (info.type != CGROUP_FILE_TYPE_DIR) - goto next; + r = asprintf(&p, "%s/%s", path, fn); + free(fn); - if (asprintf(&p, "%s/%s", path, info.path) < 0) { - ret = -ENOMEM; - break; + if (r < 0) { + r = -ENOMEM; + goto finish; } - k = cg_is_empty(controller, p, ignore_self); + r = cg_is_empty_recursive(controller, p, ignore_self); free(p); - if (k < 0) { - ret = k; - break; - } else if (k == 0) { - empty = false; - break; + if (r <= 0) + goto finish; + } + + if (r >= 0) + r = 1; + +finish: + + if (d) + closedir(d); + + return r; +} + +int cg_split_spec(const char *spec, char **controller, char **path) { + const char *e; + char *t = NULL, *u = NULL; + + assert(spec); + assert(controller || path); + + if (*spec == '/') { + + if (path) { + if (!(t = strdup(spec))) + return -ENOMEM; + + *path = t; } - next: - r = cgroup_walk_tree_next(0, &iterator, &info, level); + if (controller) + *controller = NULL; + + return 0; } - if (ret == 0) { - if (r == 0 || r == ECGEOF) - ret = !!empty; - else if (r == ECGOTHER && errno == ENOENT) - ret = -ESRCH; - else - ret = cg_translate_error(r, errno); + if (!(e = strchr(spec, ':'))) { + + if (strchr(spec, '/') || spec[0] == 0) + return -EINVAL; + + if (controller) { + if (!(t = strdup(spec))) + return -ENOMEM; + + *controller = t; + } + + if (path) + *path = NULL; + + return 0; } - assert_se(cgroup_walk_tree_end(&iterator) == 0); + if (e[1] != '/' || + e == spec || + memchr(spec, '/', e-spec)) + return -EINVAL; - return ret; + if (controller) + if (!(t = strndup(spec, e-spec))) + return -ENOMEM; + + if (path) + if (!(u = strdup(e+1))) { + free(t); + return -ENOMEM; + } + + if (controller) + *controller = t; + + if (path) + *path = u; + + return 0; } -int cg_init(void) { - int r; +int cg_join_spec(const char *controller, const char *path, char **spec) { + assert(controller); + assert(path); - if ((r = cgroup_init()) != 0) - return cg_translate_error(r, errno); + if (!path_is_absolute(path) || + controller[0] == 0 || + strchr(controller, ':') || + strchr(controller, '/')) + return -EINVAL; + + if (asprintf(spec, "%s:%s", controller, path) < 0) + return -ENOMEM; return 0; } + +int cg_fix_path(const char *path, char **result) { + char *t, *c, *p; + int r; + + assert(path); + assert(result); + + /* First check if it already is a filesystem path */ + if (path_is_absolute(path) && + path_startswith(path, "/cgroup") && + access(path, F_OK) >= 0) { + + if (!(t = strdup(path))) + return -ENOMEM; + + *result = t; + return 0; + } + + /* Otherwise treat it as cg spec */ + if ((r = cg_split_spec(path, &c, &p)) < 0) + return r; + + r = cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result); + free(c); + free(p); + + return r; +} diff --git a/src/cgroup-util.h b/src/cgroup-util.h index 716c635a9..8f9657124 100644 --- a/src/cgroup-util.h +++ b/src/cgroup-util.h @@ -24,29 +24,36 @@ #include #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_enumerate_subgroups(const char *controller, const char *path, DIR **_d); +int cg_read_subgroup(DIR *d, char **fn); + 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); +int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self, bool remove); +int cg_kill_recursive_and_wait(const char *controller, const char *path, bool remove); int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self); -int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self); +int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self, bool remove); + +int cg_split_spec(const char *spec, char **controller, char **path); +int cg_join_spec(const char *controller, const char *path, char **spec); +int cg_fix_path(const char *path, char **result); int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs); int cg_get_by_pid(const char *controller, pid_t pid, char **path); int cg_trim(const char *controller, const char *path, bool delete_root); + +int cg_rmdir(const char *controller, const char *path); int cg_delete(const char *controller, const char *path); int cg_create(const char *controller, const char *path); @@ -61,6 +68,4 @@ 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 7397b5298..02c291918 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -149,7 +149,7 @@ int cgroup_bonding_kill(CGroupBonding *b, int sig) { assert(b->realized); - return cg_kill_recursive(b->controller, b->path, sig, true); + return cg_kill_recursive(b->controller, b->path, sig, true, false); } int cgroup_bonding_kill_list(CGroupBonding *first, int sig) { @@ -215,13 +215,7 @@ int manager_setup_cgroup(Manager *m) { assert(m); - /* 1. Initialize libcg */ - if ((r = cg_init()) < 0) { - log_error("Failed to initialize libcg: %s", strerror(-r)); - goto finish; - } - - /* 2. Determine hierarchy */ + /* 1. Determine hierarchy */ if ((r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t)) < 0) goto finish; @@ -243,13 +237,13 @@ int manager_setup_cgroup(Manager *m) { } } - /* 3. Show data */ + /* 2. 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 */ + /* 3. 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) @@ -257,13 +251,13 @@ int manager_setup_cgroup(Manager *m) { else log_debug("Release agent already installed."); - /* 5. Realize the group */ + /* 4. 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)); goto finish; } - /* 6. And pin it, so that it cannot be unmounted */ + /* 5. And pin it, so that it cannot be unmounted */ if (m->pin_cgroupfs_fd >= 0) close_nointr_nofail(m->pin_cgroupfs_fd); diff --git a/src/pam-module.c b/src/pam-module.c index 1441987fd..69b9b0ce7 100644 --- a/src/pam-module.c +++ b/src/pam-module.c @@ -258,12 +258,6 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (sd_booted() <= 0) return PAM_SUCCESS; - if ((r = cg_init()) < 0) { - pam_syslog(handle, LOG_ERR, "libcgroup initialization failed: %s", strerror(-r)); - r = PAM_SESSION_ERR; - goto finish; - } - if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS) goto finish; @@ -354,44 +348,29 @@ finish: } static int session_remains(pam_handle_t *handle, const char *user_path) { - struct cgroup_file_info info; - int level = 0, r; - void *iterator = NULL; + int r; bool remains = false; + DIR *d; + char *subgroup; - zero(info); - - r = cgroup_walk_tree_begin(SYSTEMD_CGROUP_CONTROLLER, user_path, 0, &iterator, &info, &level); - while (r == 0) { - - if (info.type != CGROUP_FILE_TYPE_DIR) - goto next; - - if (streq(info.path, "")) - goto next; - - if (streq(info.path, "no-session")) - goto next; + if ((r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, user_path, &d)) < 0) + return r; - remains = true; - break; + while ((r = cg_read_subgroup(d, &subgroup)) > 0) { - next: + remains = !streq(subgroup, "no-session"); + free(subgroup); - r = cgroup_walk_tree_next(0, &iterator, &info, level); + if (remains) + break; } + closedir(d); - if (remains) - r = 1; - else if (r == 0 || r == ECGEOF) - r = 0; - else - r = cg_translate_error(r, errno); - - assert_se(cgroup_walk_tree_end(&iterator) == 0); + if (r < 0) + return r; - return r; + return !!remains; } _public_ PAM_EXTERN int pam_sm_close_session( @@ -425,6 +404,10 @@ _public_ PAM_EXTERN int pam_sm_close_session( goto finish; } + /* We are probably still in some session/no-session dir. Move ourselves out of the way as first step */ + if ((r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, "/user", 0)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to move us away: %s", strerror(-r)); + if (asprintf(&user_path, "/user/%s", username) < 0) { r = PAM_BUF_ERR; goto finish; @@ -439,28 +422,19 @@ _public_ PAM_EXTERN int pam_sm_close_session( } if (kill_session) { - /* Kill processes in session cgroup */ - if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, session_path)) < 0) + /* Kill processes in session cgroup, and delete it */ + if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, session_path, true)) < 0) pam_syslog(handle, LOG_ERR, "Failed to kill session cgroup: %s", strerror(-r)); - - } else { + } else { /* Migrate processes from session to * no-session cgroup. First, try to create the * no-session group in case it doesn't exist - * yet. */ + * yet. Also, delete the session group. */ create_user_group(handle, nosession_path, pw, 0); - if ((r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, session_path, nosession_path, false)) < 0) + if ((r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, session_path, nosession_path, false, true)) < 0) pam_syslog(handle, LOG_ERR, "Failed to migrate session cgroup: %s", strerror(-r)); } - - /* Delete session cgroup */ - if (r < 0) - pam_syslog(handle, LOG_INFO, "Couldn't empty session cgroup, not deleting."); - else { - 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 */ @@ -473,25 +447,26 @@ _public_ PAM_EXTERN int pam_sm_close_session( if (kill_user && r == 0) { /* Kill no-session cgroup */ - if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, user_path)) < 0) + if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, user_path, true)) < 0) pam_syslog(handle, LOG_ERR, "Failed to kill user cgroup: %s", strerror(-r)); } else { 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. */ - if (r == 0) + /* Remove user cgroup */ + if (r > 0) { + if ((r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, user_path)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to delete user cgroup: %s", strerror(-r)); + + /* If we managed to find somebody, don't cleanup the cgroup. */ + } else if (r == 0) r = -EBUSY; } if (r >= 0) { const char *runtime_dir; - /* Remove user cgroup */ - 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. */ if ((runtime_dir = pam_getenv(handle, "XDG_RUNTIME_DIR"))) diff --git a/src/systemctl.c b/src/systemctl.c index 45e900c5f..32974aecc 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -1064,7 +1064,6 @@ typedef struct UnitStatusInfo { static void print_status_info(UnitStatusInfo *i) { ExecStatusInfo *p; - int r; assert(i); @@ -1195,10 +1194,7 @@ static void print_status_info(UnitStatusInfo *i) { else c = 0; - 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); + show_cgroup_by_path(i->default_control_group, "\t\t ", c); } } diff --git a/src/systemd-cgls.c b/src/systemd-cgls.c index bd8d263d0..cd03e91b8 100644 --- a/src/systemd-cgls.c +++ b/src/systemd-cgls.c @@ -83,11 +83,6 @@ 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; @@ -95,7 +90,7 @@ int main(int argc, char *argv[]) { int q; printf("%s:\n", argv[i]); - if ((q = show_cgroup_recursive(argv[i], NULL, 0)) < 0) + if ((q = show_cgroup_by_path(argv[i], NULL, 0)) < 0) r = q; } @@ -109,9 +104,9 @@ int main(int argc, char *argv[]) { if (path_startswith(p, "/cgroup")) { printf("Working Directory %s:\n", p); - r = show_cgroup_recursive(p, NULL, 0); + r = show_cgroup_by_path(p, NULL, 0); } else - r = show_cgroup_recursive(NULL, NULL, 0); + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, "/", NULL, 0); free(p); } diff --git a/src/test-cgroup.c b/src/test-cgroup.c index 45e7f0fe0..4a54b1bc8 100644 --- a/src/test-cgroup.c +++ b/src/test-cgroup.c @@ -28,8 +28,7 @@ int main(int argc, char*argv[]) { char *path; - - assert_se(cg_init() >= 0); + char *c, *p; assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); @@ -62,21 +61,44 @@ int main(int argc, char*argv[]) { 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(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false) == 0); - assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false) > 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false) == 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false) > 0); - assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", "/test-a", false) == 0); + assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", "/test-a", false, 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(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false) > 0); - assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false) == 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false) > 0); + assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false) == 0); cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false); assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0); - assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); + assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-a") >= 0); + + assert_se(cg_split_spec("foobar:/", &c, &p) == 0); + assert(streq(c, "foobar")); + assert(streq(p, "/")); + free(c); + free(p); + + assert_se(cg_split_spec("foobar:", &c, &p) < 0); + assert_se(cg_split_spec("foobar:asdfd", &c, &p) < 0); + assert_se(cg_split_spec(":///", &c, &p) < 0); + assert_se(cg_split_spec(":", &c, &p) < 0); + assert_se(cg_split_spec("", &c, &p) < 0); + assert_se(cg_split_spec("fo/obar:/", &c, &p) < 0); + + assert_se(cg_split_spec("/", &c, &p) >= 0); + assert(c == NULL); + assert(streq(p, "/")); + free(p); + + assert_se(cg_split_spec("foo", &c, &p) >= 0); + assert(streq(c, "foo")); + assert(p == NULL); + free(c); return 0; } diff --git a/src/unit.c b/src/unit.c index 66372f2a9..f786359fa 100644 --- a/src/unit.c +++ b/src/unit.c @@ -1511,12 +1511,9 @@ char *unit_dbus_path(Unit *u) { if (!(e = bus_path_escape(u->meta.id))) return NULL; - if (asprintf(&p, "/org/freedesktop/systemd1/unit/%s", e) < 0) { - free(e); - return NULL; - } - + p = strappend("/org/freedesktop/systemd1/unit/", e); free(e); + return p; } @@ -1526,8 +1523,13 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { assert(u); assert(b); + assert(b->path); + if (!b->controller) + if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER))) + return -ENOMEM; + /* Ensure this hasn't been added yet */ assert(!b->unit); @@ -1566,7 +1568,6 @@ static char *default_cgroup_path(Unit *u) { } int unit_add_cgroup_from_text(Unit *u, const char *name) { - size_t n; char *controller = NULL, *path = NULL; CGroupBonding *b = NULL; int r; @@ -1574,38 +1575,20 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { assert(u); assert(name); - /* Detect controller name */ - n = strcspn(name, ":"); - - if (name[n] == 0 || - (name[n] == ':' && name[n+1] == 0)) { - - /* Only controller name, no path? */ - - if (!(path = default_cgroup_path(u))) - return -ENOMEM; - - } else { - const char *p; - - /* Controller name, and path. */ - p = name+n+1; + if ((r = cg_split_spec(name, &controller, &path)) < 0) + return r; - if (!path_is_absolute(p)) - return -EINVAL; + if (!path) + path = default_cgroup_path(u); - if (!(path = strdup(p))) - return -ENOMEM; - } - - if (n > 0) - controller = strndup(name, n); - else + if (!controller) controller = strdup(SYSTEMD_CGROUP_CONTROLLER); - if (!controller) { - r = -ENOMEM; - goto fail; + if (!path || !controller) { + free(path); + free(controller); + + return -ENOMEM; } if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) { @@ -1650,9 +1633,6 @@ int unit_add_default_cgroup(Unit *u) { if (!(b = new0(CGroupBonding, 1))) return -ENOMEM; - if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER))) - goto fail; - if (!(b->path = default_cgroup_path(u))) goto fail; diff --git a/src/util.c b/src/util.c index 69eb14be6..fa34137bd 100644 --- a/src/util.c +++ b/src/util.c @@ -609,8 +609,22 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { fclose(f); - if (r[0] == 0) - return get_process_name(pid, line); + /* Kernel threads have no argv[] */ + if (r[0] == 0) { + char *t; + int h; + + free(r); + + if ((h = get_process_name(pid, &t)) < 0) + return h; + + h = asprintf(&r, "[%s]", t); + free(t); + + if (h < 0) + return -ENOMEM; + } *line = r; return 0; @@ -698,6 +712,48 @@ int readlink_and_make_absolute(const char *p, char **r) { return 0; } +int parent_of_path(const char *path, char **_r) { + const char *e, *a = NULL, *b = NULL, *p; + char *r; + bool slash = false; + + assert(path); + assert(_r); + + if (!*path) + return -EINVAL; + + for (e = path; *e; e++) { + + if (!slash && *e == '/') { + a = b; + b = e; + slash = true; + } else if (slash && *e != '/') + slash = false; + } + + if (*(e-1) == '/') + p = a; + else + p = b; + + if (!p) + return -EINVAL; + + if (p == path) + r = strdup("/"); + else + r = strndup(path, p-path); + + if (!r) + return -ENOMEM; + + *_r = r; + return 0; +} + + char *file_name_from_path(const char *p) { char *r; @@ -2201,25 +2257,24 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { int path_is_mount_point(const char *t) { struct stat a, b; - char *copy; + char *parent; + int r; if (lstat(t, &a) < 0) { - if (errno == ENOENT) return 0; return -errno; } - if (!(copy = strdup(t))) - return -ENOMEM; + if ((r = parent_of_path(t, &parent)) < 0) + return r; - if (lstat(dirname(copy), &b) < 0) { - free(copy); - return -errno; - } + r = lstat(parent, &b); + free(parent); - free(copy); + if (r < 0) + return -errno; return a.st_dev != b.st_dev; } diff --git a/src/util.h b/src/util.h index f1cd080e4..e4b1f81c0 100644 --- a/src/util.h +++ b/src/util.h @@ -212,6 +212,8 @@ int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid); int mkdir_parents(const char *path, mode_t mode); int mkdir_p(const char *path, mode_t mode); +int parent_of_path(const char *path, char **parent); + int rmdir_parents(const char *path, const char *stop); int get_process_name(pid_t pid, char **name);