chiark / gitweb /
Classify processes from sessions into cgroups
[elogind.git] / src / shared / cgroup-util.c
index 390259e3e43c5e28b18de5b18494a43291fa0b75..99d06540a1579df953bd4a88c5d84a242e718beb 100644 (file)
 #include <ftw.h>
 
 #include "cgroup-util.h"
-#include "log.h"
 #include "set.h"
 #include "macro.h"
 #include "util.h"
 #include "path-util.h"
-#include "strv.h"
-#include "unit-name.h"
 #include "fileio.h"
 #include "special.h"
+#include "mkdir.h"
 
 int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
         _cleanup_free_ char *fs = NULL;
@@ -160,7 +158,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
          * tasks list, to properly handle forking processes */
 
         if (!s) {
-                s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
+                s = allocated_set = set_new(NULL);
                 if (!s)
                         return -ENOMEM;
         }
@@ -193,12 +191,12 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
                         if (kill(pid, sig) < 0) {
                                 if (ret >= 0 && errno != ESRCH)
                                         ret = -errno;
-                        } else if (ret == 0) {
-
-                                if (sigcont)
+                        } else {
+                                if (sigcont && sig != SIGKILL)
                                         kill(pid, SIGCONT);
 
-                                ret = 1;
+                                if (ret == 0)
+                                        ret = 1;
                         }
 
                         done = false;
@@ -238,7 +236,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si
         assert(sig >= 0);
 
         if (!s) {
-                s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
+                s = allocated_set = set_new(NULL);
                 if (!s)
                         return -ENOMEM;
         }
@@ -278,37 +276,6 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si
         return ret;
 }
 
-int cg_kill_recursive_and_wait(const char *controller, const char *path, bool rem) {
-        unsigned i;
-
-        assert(path);
-
-        /* This safely kills all processes; first it sends a SIGTERM,
-         * then checks 8 times after 200ms whether the group is now
-         * empty, then kills everything that is left with SIGKILL and
-         * finally checks 5 times after 200ms each whether the group
-         * is finally empty. */
-
-        for (i = 0; i < 15; i++) {
-                int sig, r;
-
-                if (i <= 0)
-                        sig = SIGTERM;
-                else if (i == 9)
-                        sig = SIGKILL;
-                else
-                        sig = 0;
-
-                r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL);
-                if (r <= 0)
-                        return r;
-
-                usleep(200 * USEC_PER_MSEC);
-        }
-
-        return 0;
-}
-
 int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
         bool done = false;
         _cleanup_set_free_ Set *s = NULL;
@@ -320,7 +287,7 @@ int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char
         assert(cto);
         assert(pto);
 
-        s = set_new(trivial_hash_func, trivial_compare_func);
+        s = set_new(NULL);
         if (!s)
                 return -ENOMEM;
 
@@ -435,12 +402,43 @@ int cg_migrate_recursive(
         return ret;
 }
 
+int cg_migrate_recursive_fallback(
+                const char *cfrom,
+                const char *pfrom,
+                const char *cto,
+                const char *pto,
+                bool ignore_self,
+                bool rem) {
+
+        int r;
+
+        assert(cfrom);
+        assert(pfrom);
+        assert(cto);
+        assert(pto);
+
+        r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
+        if (r < 0) {
+                char prefix[strlen(pto) + 1];
+
+                /* This didn't work? Then let's try all prefixes of the destination */
+
+                PATH_FOREACH_PREFIX(prefix, pto) {
+                        r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
+                        if (r >= 0)
+                                break;
+                }
+        }
+
+        return 0;
+}
+
 static const char *normalize_controller(const char *controller) {
 
         assert(controller);
 
         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
-                return "systemd";
+                return "elogind";
         else if (startswith(controller, "name="))
                 return controller + 5;
         else
@@ -471,15 +469,13 @@ static int join_path(const char *controller, const char *path, const char *suffi
         if (!t)
                 return -ENOMEM;
 
-        path_kill_slashes(t);
-
-        *fs = t;
+        *fs = path_kill_slashes(t);
         return 0;
 }
 
 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
         const char *p;
-        static __thread bool good = false;
+        static thread_local bool good = false;
 
         assert(fs);
 
@@ -503,14 +499,16 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
 }
 
 static int check_hierarchy(const char *p) {
-        char *cc;
+        const char *cc;
 
         assert(p);
 
+        if (!filename_is_valid(p))
+                return 0;
+
         /* 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)
+        cc = strjoina("/sys/fs/cgroup/", p);
+        if (laccess(cc, F_OK) < 0)
                 return -errno;
 
         return 0;
@@ -587,6 +585,46 @@ int cg_delete(const char *controller, const char *path) {
         return r == -ENOENT ? 0 : r;
 }
 
+int cg_create(const char *controller, const char *path) {
+        _cleanup_free_ char *fs = NULL;
+        int r;
+
+        r = cg_get_path_and_check(controller, path, NULL, &fs);
+        if (r < 0)
+                return r;
+
+        r = mkdir_parents(fs, 0755);
+        if (r < 0)
+                return r;
+
+        if (mkdir(fs, 0755) < 0) {
+
+                if (errno == EEXIST)
+                        return 0;
+
+                return -errno;
+        }
+
+        return 1;
+}
+
+int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
+        int r, q;
+
+        assert(pid >= 0);
+
+        r = cg_create(controller, path);
+        if (r < 0)
+                return r;
+
+        q = cg_attach(controller, path, pid);
+        if (q < 0)
+                return q;
+
+        /* This does not remove the cgroup on failure */
+        return r;
+}
+
 int cg_attach(const char *controller, const char *path, pid_t pid) {
         _cleanup_free_ char *fs = NULL;
         char c[DECIMAL_STR_MAX(pid_t) + 2];
@@ -602,9 +640,33 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
         if (pid == 0)
                 pid = getpid();
 
-        snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
+        snprintf(c, sizeof(c), PID_FMT"\n", pid);
 
-        return write_string_file(fs, c);
+        return write_string_file_no_create(fs, c);
+}
+
+int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
+        int r;
+
+        assert(controller);
+        assert(path);
+        assert(pid >= 0);
+
+        r = cg_attach(controller, path, pid);
+        if (r < 0) {
+                char prefix[strlen(path) + 1];
+
+                /* This didn't work? Then let's try all prefixes of
+                 * the destination */
+
+                PATH_FOREACH_PREFIX(prefix, path) {
+                        r = cg_attach(controller, prefix, pid);
+                        if (r >= 0)
+                                break;
+                }
+        }
+
+        return 0;
 }
 
 int cg_set_group_access(
@@ -619,7 +681,7 @@ int cg_set_group_access(
 
         assert(path);
 
-        if (mode != (mode_t) -1)
+        if (mode != MODE_INVALID)
                 mode &= 0777;
 
         r = cg_get_path(controller, path, NULL, &fs);
@@ -641,10 +703,10 @@ int cg_set_task_access(
 
         assert(path);
 
-        if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1)
+        if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
                 return 0;
 
-        if (mode != (mode_t) -1)
+        if (mode != MODE_INVALID)
                 mode &= 0666;
 
         r = cg_get_path(controller, path, "cgroup.procs", &fs);
@@ -681,10 +743,7 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
         } else
                 controller = SYSTEMD_CGROUP_CONTROLLER;
 
-        if (pid == 0)
-                fs = "/proc/self/cgroup";
-        else
-                fs = procfs_file_alloca(pid, "cgroup");
+        fs = procfs_file_alloca(pid, "cgroup");
 
         f = fopen(fs, "re");
         if (!f)
@@ -693,9 +752,9 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
         cs = strlen(controller);
 
         FOREACH_LINE(line, f, return -errno) {
-                char *l, *p, *w, *e;
+                char *l, *p, *e;
                 size_t k;
-                char *state;
+                const char *word, *state;
                 bool found = false;
 
                 truncate_nl(line);
@@ -711,16 +770,16 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
 
                 *e = 0;
 
-                FOREACH_WORD_SEPARATOR(w, k, l, ",", state) {
+                FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
 
-                        if (k == cs && memcmp(w, controller, cs) == 0) {
+                        if (k == cs && memcmp(word, controller, cs) == 0) {
                                 found = true;
                                 break;
                         }
 
                         if (k == 5 + cs &&
-                            memcmp(w, "name=", 5) == 0 &&
-                            memcmp(w+5, controller, cs) == 0) {
+                            memcmp(word, "name=", 5) == 0 &&
+                            memcmp(word+5, controller, cs) == 0) {
                                 found = true;
                                 break;
                         }
@@ -757,7 +816,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
 
         sc = strstrip(contents);
         if (sc[0] == 0) {
-                r = write_string_file(fs, agent);
+                r = write_string_file_no_create(fs, agent);
                 if (r < 0)
                         return r;
         } else if (!streq(sc, agent))
@@ -777,7 +836,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
 
         sc = strstrip(contents);
         if (streq(sc, "0")) {
-                r = write_string_file(fs, "1");
+                r = write_string_file_no_create(fs, "1");
                 if (r < 0)
                         return r;
 
@@ -790,6 +849,32 @@ int cg_install_release_agent(const char *controller, const char *agent) {
         return 0;
 }
 
+int cg_uninstall_release_agent(const char *controller) {
+        _cleanup_free_ char *fs = NULL;
+        int r;
+
+        r = cg_get_path(controller, NULL, "notify_on_release", &fs);
+        if (r < 0)
+                return r;
+
+        r = write_string_file_no_create(fs, "0");
+        if (r < 0)
+                return r;
+
+        free(fs);
+        fs = NULL;
+
+        r = cg_get_path(controller, NULL, "release_agent", &fs);
+        if (r < 0)
+                return r;
+
+        r = write_string_file_no_create(fs, "");
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
         _cleanup_fclose_ FILE *f = NULL;
         pid_t pid = 0, self_pid;
@@ -819,19 +904,6 @@ 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) {
-        _cleanup_free_ char *controller = NULL, *path = NULL;
-        int r;
-
-        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) {
         _cleanup_closedir_ DIR *d = NULL;
         char *fn;
@@ -882,8 +954,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
                         if (!t)
                                 return -ENOMEM;
 
-                        path_kill_slashes(t);
-                        *path = t;
+                        *path = path_kill_slashes(t);
                 }
 
                 if (controller)
@@ -922,19 +993,28 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
                 return -EINVAL;
         }
 
-        u = strdup(e+1);
-        if (!u) {
-                free(t);
-                return -ENOMEM;
-        }
-        if (!path_is_safe(u) ||
-            !path_is_absolute(u)) {
-                free(t);
-                free(u);
-                return -EINVAL;
-        }
+        if (streq(e+1, "")) {
+                u = strdup("/");
+                if (!u) {
+                        free(t);
+                        return -ENOMEM;
+                }
+        } else {
+                u = strdup(e+1);
+                if (!u) {
+                        free(t);
+                        return -ENOMEM;
+                }
+
+                if (!path_is_safe(u) ||
+                    !path_is_absolute(u)) {
+                        free(t);
+                        free(u);
+                        return -EINVAL;
+                }
 
-        path_kill_slashes(u);
+                path_kill_slashes(u);
+        }
 
         if (controller)
                 *controller = t;
@@ -949,33 +1029,6 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
         return 0;
 }
 
-int cg_join_spec(const char *controller, const char *path, char **spec) {
-        char *s;
-
-        assert(path);
-
-        if (!controller)
-                controller = "systemd";
-        else {
-                if (!cg_controller_is_valid(controller, true))
-                        return -EINVAL;
-
-                controller = normalize_controller(controller);
-        }
-
-        if (!path_is_absolute(path))
-                return -EINVAL;
-
-        s = strjoin(controller, ":", path, NULL);
-        if (!s)
-                return -ENOMEM;
-
-        path_kill_slashes(s + strlen(controller) + 1);
-
-        *spec = s;
-        return 0;
-}
-
 int cg_mangle_path(const char *path, char **result) {
         _cleanup_free_ char *c = NULL, *p = NULL;
         char *t;
@@ -984,19 +1037,18 @@ int cg_mangle_path(const char *path, char **result) {
         assert(path);
         assert(result);
 
-        /* First check if it already is a filesystem path */
+        /* First, check if it already is a filesystem path */
         if (path_startswith(path, "/sys/fs/cgroup")) {
 
                 t = strdup(path);
                 if (!t)
                         return -ENOMEM;
 
-                path_kill_slashes(t);
-                *result = t;
+                *result = path_kill_slashes(t);
                 return 0;
         }
 
-        /* Otherwise treat it as cg spec */
+        /* Otherwise, treat it as cg spec */
         r = cg_split_spec(path, &c, &p);
         if (r < 0)
                 return r;
@@ -1005,293 +1057,81 @@ int cg_mangle_path(const char *path, char **result) {
 }
 
 int cg_get_root_path(char **path) {
-        char *p, *e;
-        int r;
-
         assert(path);
 
-        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
-        if (r < 0)
-                return r;
-
-        e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
-        if (e)
-                *e = 0;
-
-        *path = p;
-        return 0;
+        return cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, path);
 }
 
-char **cg_shorten_controllers(char **controllers) {
-        char **f, **t;
-
-        if (!controllers)
-                return controllers;
-
-        for (f = controllers, t = controllers; *f; f++) {
-                const char *p;
-                int r;
-
-                p = normalize_controller(*f);
-
-                if (streq(p, "systemd")) {
-                        free(*f);
-                        continue;
-                }
-
-                if (!cg_controller_is_valid(p, true)) {
-                        log_warning("Controller %s is not valid, removing from controllers list.", p);
-                        free(*f);
-                        continue;
-                }
-
-                r = check_hierarchy(p);
-                if (r < 0) {
-                        log_debug("Controller %s is not available, removing from controllers list.", p);
-                        free(*f);
-                        continue;
-                }
-
-                *(t++) = *f;
-        }
-
-        *t = NULL;
-        return strv_uniq(controllers);
-}
-
-int cg_pid_get_path_shifted(pid_t pid, char **root, char **cgroup) {
-        _cleanup_free_ char *cg_root = NULL;
-        char *cg_process, *p;
+int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
+        _cleanup_free_ char *rt = NULL;
+        char *p;
         int r;
 
-        r = cg_get_root_path(&cg_root);
-        if (r < 0)
-                return r;
-
-        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
-        if (r < 0)
-                return r;
-
-        p = path_startswith(cg_process, cg_root);
-        if (p)
-                p--;
-        else
-                p = cg_process;
-
-        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;
-}
-
-int cg_path_decode_unit(const char *cgroup, char **unit){
-        char *p, *e, *c, *s, *k;
-
         assert(cgroup);
-        assert(unit);
-
-        e = strchrnul(cgroup, '/');
-        c = strndupa(cgroup, e - cgroup);
-        c = cg_unescape(c);
+        assert(shifted);
 
-        /* Could this be a valid unit name? */
-        if (!unit_name_is_valid(c, true))
-                return -EINVAL;
-
-        if (!unit_name_is_template(c))
-                s = strdup(c);
-        else {
-                if (*e != '/')
-                        return -EINVAL;
+        if (!root) {
+                /* If the root was specified let's use that, otherwise
+                 * let's determine it from PID 1 */
 
-                e += strspn(e, "/");
-
-                p = strchrnul(e, '/');
-                k = strndupa(e, p - e);
-                k = cg_unescape(k);
-
-                if (!unit_name_is_valid(k, false))
-                        return -EINVAL;
+                r = cg_get_root_path(&rt);
+                if (r < 0)
+                        return r;
 
-                s = strdup(k);
+                root = rt;
         }
 
-        if (!s)
-                return -ENOMEM;
+        p = path_startswith(cgroup, root);
+        if (p)
+                *shifted = p - 1;
+        else
+                *shifted = cgroup;
 
-        *unit = s;
         return 0;
 }
 
-static const char *skip_slices(const char *p) {
-        /* Skips over all slice assignments */
-
-        for (;;) {
-                size_t n;
-
-                p += strspn(p, "/");
-
-                n = strcspn(p, "/");
-                if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
-                        return p;
-
-                p += n;
-        }
-}
-
-int cg_path_get_unit(const char *path, char **unit) {
-        const char *e;
-
-        assert(path);
-        assert(unit);
-
-        e = skip_slices(path);
-
-        return cg_path_decode_unit(e, unit);
-}
-
-int cg_pid_get_unit(pid_t pid, char **unit) {
-        _cleanup_free_ char *cgroup = NULL;
+int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
+        _cleanup_free_ char *raw = NULL;
+        const char *c;
         int r;
 
-        assert(unit);
+        assert(pid >= 0);
+        assert(cgroup);
 
-        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
         if (r < 0)
                 return r;
 
-        return cg_path_get_unit(cgroup, unit);
-}
-
-static const char *skip_session(const char *p) {
-        size_t n;
-
-        assert(p);
-
-        p += strspn(p, "/");
-
-        n = strcspn(p, "/");
-        if (n <= 12 || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
-                return NULL;
-
-        p += n;
-        p += strspn(p, "/");
-
-        return p;
-}
-
-int cg_path_get_user_unit(const char *path, char **unit) {
-        const char *e;
-
-        assert(path);
-        assert(unit);
-
-        /* We always have to parse the path from the beginning as unit
-         * cgroups might have arbitrary child cgroups and we shouldn't get
-         * confused by those */
-
-        /* Skip slices, if there are any */
-        e = skip_slices(path);
-
-        /* Skip the session scope, require that there is one */
-        e = skip_session(e);
-        if (!e)
-                return -ENOENT;
-
-        /* And skip more slices */
-        e = skip_slices(e);
-
-        return cg_path_decode_unit(e, unit);
-}
-
-int cg_pid_get_user_unit(pid_t pid, char **unit) {
-        _cleanup_free_ char *cgroup = NULL;
-        int r;
-
-        assert(unit);
-
-        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
+        r = cg_shift_path(raw, root, &c);
         if (r < 0)
                 return r;
 
-        return cg_path_get_user_unit(cgroup, unit);
-}
-
-int cg_path_get_machine_name(const char *path, char **machine) {
-        const char *e, *n, *x;
-        char *s, *r;
-        size_t l;
-
-        assert(path);
-        assert(machine);
-
-        /* Skip slices, if there are any */
-        e = skip_slices(path);
-
-        n = strchrnul(e, '/');
-        if (e == n)
-                return -ENOENT;
-
-        s = strndupa(e, n - e);
-        s = cg_unescape(s);
-
-        x = startswith(s, "machine-");
-        if (!x)
-                return -ENOENT;
-        if (!endswith(x, ".scope"))
-                return -ENOENT;
+        if (c == raw) {
+                *cgroup = raw;
+                raw = NULL;
+        } else {
+                char *n;
 
-        l = strlen(x);
-        if (l <= 6)
-                return -ENOENT;
+                n = strdup(c);
+                if (!n)
+                        return -ENOMEM;
 
-        r = strndup(x, l - 6);
-        if (!r)
-                return -ENOMEM;
+                *cgroup = n;
+        }
 
-        *machine = r;
         return 0;
 }
 
-int cg_pid_get_machine_name(pid_t pid, char **machine) {
-        _cleanup_free_ char *cgroup = NULL;
-        int r;
-
-        assert(machine);
-
-        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
-        if (r < 0)
-                return r;
-
-        return cg_path_get_machine_name(cgroup, machine);
-}
-
 int cg_path_get_session(const char *path, char **session) {
-        const char *e, *n, *x;
-        char *s, *r;
-        size_t l;
+        const char *e, *n, *s;
 
-        assert(path);
-        assert(session);
+        /* Elogind uses a flat hierarchy, just "/SESSION".  The only
+           wrinkle is that SESSION might be escaped.  */
 
-        /* Skip slices, if there are any */
-        e = skip_slices(path);
+        assert(path);
+        assert(path[0] == '/');
 
+        e = path + 1;
         n = strchrnul(e, '/');
         if (e == n)
                 return -ENOENT;
@@ -1299,21 +1139,19 @@ int cg_path_get_session(const char *path, char **session) {
         s = strndupa(e, n - e);
         s = cg_unescape(s);
 
-        x = startswith(s, "session-");
-        if (!x)
-                return -ENOENT;
-        if (!endswith(x, ".scope"))
+        if (!s[0])
                 return -ENOENT;
 
-        l = strlen(x);
-        if (l <= 6)
-                return -ENOENT;
+        if (session) {
+                char *r;
 
-        r = strndup(x, l - 6);
-        if (!r)
-                return -ENOMEM;
+                r = strdup(s);
+                if (!r)
+                        return -ENOMEM;
+
+                *session = r;
+        }
 
-        *session = r;
         return 0;
 }
 
@@ -1321,8 +1159,6 @@ int cg_pid_get_session(pid_t pid, char **session) {
         _cleanup_free_ char *cgroup = NULL;
         int r;
 
-        assert(session);
-
         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
         if (r < 0)
                 return r;
@@ -1330,121 +1166,6 @@ int cg_pid_get_session(pid_t pid, char **session) {
         return cg_path_get_session(cgroup, session);
 }
 
-int cg_path_get_owner_uid(const char *path, uid_t *uid) {
-        _cleanup_free_ char *slice = NULL;
-        const char *e;
-        char *s;
-        int r;
-
-        assert(path);
-        assert(uid);
-
-        r = cg_path_get_slice(path, &slice);
-        if (r < 0)
-                return r;
-
-        e = startswith(slice, "user-");
-        if (!e)
-                return -ENOENT;
-        if (!endswith(slice, ".slice"))
-                return -ENOENT;
-
-        s = strndupa(e, strlen(e) - 6);
-        if (!s)
-                return -ENOMEM;
-
-        return parse_uid(s, uid);
-}
-
-int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
-        _cleanup_free_ char *cgroup = NULL;
-        int r;
-
-        assert(uid);
-
-        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
-        if (r < 0)
-                return r;
-
-        return cg_path_get_owner_uid(cgroup, uid);
-}
-
-int cg_path_get_slice(const char *p, char **slice) {
-        const char *e = NULL;
-        size_t m = 0;
-
-        assert(p);
-        assert(slice);
-
-        for (;;) {
-                size_t n;
-
-                p += strspn(p, "/");
-
-                n = strcspn(p, "/");
-                if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
-                        char *s;
-
-                        if (!e)
-                                return -ENOENT;
-
-                        s = strndup(e, m);
-                        if (!s)
-                                return -ENOMEM;
-
-                        *slice = s;
-                        return 0;
-                }
-
-                e = p;
-                m = n;
-
-                p += n;
-        }
-}
-
-int cg_pid_get_slice(pid_t pid, char **slice) {
-        _cleanup_free_ char *cgroup = NULL;
-        int r;
-
-        assert(slice);
-
-        r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
-        if (r < 0)
-                return r;
-
-        return cg_path_get_slice(cgroup, slice);
-}
-
-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 (!cg_controller_is_valid(c, false)) {
-                free(c);
-                return -EINVAL;
-        }
-
-        *controller = c;
-        return 1;
-}
-
 char *cg_escape(const char *p) {
         bool need_prefix = false;
 
@@ -1503,9 +1224,7 @@ char *cg_unescape(const char *p) {
 }
 
 #define CONTROLLER_VALID                        \
-        "0123456789"                            \
-        "abcdefghijklmnopqrstuvwxyz"            \
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"            \
+        DIGITS LETTERS                          \
         "_"
 
 bool cg_controller_is_valid(const char *p, bool allow_named) {
@@ -1533,57 +1252,18 @@ bool cg_controller_is_valid(const char *p, bool allow_named) {
         return true;
 }
 
-int cg_slice_to_path(const char *unit, char **ret) {
-        _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
-        const char *dash;
-
-        assert(unit);
-        assert(ret);
-
-        if (!unit_name_is_valid(unit, false))
-                return -EINVAL;
-
-        if (!endswith(unit, ".slice"))
-                return -EINVAL;
-
-        p = unit_name_to_prefix(unit);
-        if (!p)
-                return -ENOMEM;
-
-        dash = strchr(p, '-');
-        while (dash) {
-                _cleanup_free_ char *escaped = NULL;
-                char n[dash - p + sizeof(".slice")];
-
-                strcpy(stpncpy(n, p, dash - p), ".slice");
-
-                if (!unit_name_is_valid(n, false))
-                        return -EINVAL;
-
-                escaped = cg_escape(n);
-                if (!escaped)
-                        return -ENOMEM;
-
-                if (!strextend(&s, escaped, "/", NULL))
-                        return -ENOMEM;
-
-                dash = strchr(dash+1, '-');
-        }
-
-        e = cg_escape(unit);
-        if (!e)
-                return -ENOMEM;
-
-        if (!strextend(&s, e, NULL))
-                return -ENOMEM;
+int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
+        _cleanup_free_ char *p = NULL;
+        int r;
 
-        *ret = s;
-        s = NULL;
+        r = cg_get_path(controller, path, attribute, &p);
+        if (r < 0)
+                return r;
 
-        return 0;
+        return write_string_file_no_create(p, value);
 }
 
-int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
+int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
         _cleanup_free_ char *p = NULL;
         int r;
 
@@ -1591,7 +1271,7 @@ int cg_set_attribute(const char *controller, const char *path, const char *attri
         if (r < 0)
                 return r;
 
-        return write_string_file(p, value);
+        return read_one_line_file(p, ret);
 }
 
 static const char mask_names[] =
@@ -1601,7 +1281,7 @@ static const char mask_names[] =
         "memory\0"
         "devices\0";
 
-int cg_create_with_mask(CGroupControllerMask mask, const char *path) {
+int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
         CGroupControllerMask bit = 1;
         const char *n;
         int r;
@@ -1617,102 +1297,94 @@ int cg_create_with_mask(CGroupControllerMask mask, const char *path) {
 
         /* Then, do the same in the other hierarchies */
         NULSTR_FOREACH(n, mask_names) {
-                if (bit & mask)
+                if (mask & bit)
                         cg_create(n, path);
-                else
+                else if (supported & bit)
                         cg_trim(n, path, true);
 
                 bit <<= 1;
         }
 
-        return r;
+        return 0;
 }
 
-int cg_attach_with_mask(CGroupControllerMask mask, const char *path, pid_t pid) {
+int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
         CGroupControllerMask bit = 1;
         const char *n;
         int r;
 
         r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
+        if (r < 0)
+                return r;
 
         NULSTR_FOREACH(n, mask_names) {
-                if (bit & mask)
-                        cg_attach(n, path, pid);
-                else {
-                        char prefix[strlen(path) + 1], *slash;
-
-                        /* OK, this one is a bit harder... Now we need
-                         * to add to the closest parent cgroup we
-                         * can find */
-                        strcpy(prefix, path);
-                        while ((slash = strrchr(prefix, '/'))) {
-                                int q;
-                                *slash = 0;
-
-                                q = cg_attach(n, prefix, pid);
-                                if (q >= 0)
-                                        break;
-                        }
+
+                if (supported & bit) {
+                        const char *p = NULL;
+
+                        if (path_callback)
+                                p = path_callback(bit, userdata);
+
+                        if (!p)
+                                p = path;
+
+                        cg_attach_fallback(n, path, pid);
                 }
 
                 bit <<= 1;
         }
 
-        return r;
+        return 0;
 }
 
-int cg_attach_many_with_mask(CGroupControllerMask mask, const char *path, Set* pids) {
+int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
         Iterator i;
         void *pidp;
         int r = 0;
 
         SET_FOREACH(pidp, pids, i) {
                 pid_t pid = PTR_TO_LONG(pidp);
-                int k;
+                int q;
 
-                k = cg_attach_with_mask(mask, path, pid);
-                if (k < 0)
-                        r = k;
+                q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
+                if (q < 0)
+                        r = q;
         }
 
         return r;
 }
 
-int cg_migrate_with_mask(CGroupControllerMask mask, const char *from, const char *to) {
+int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
         CGroupControllerMask bit = 1;
         const char *n;
         int r;
 
-        if (path_equal(from, to))
-                return 0;
-
-        r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
+        if (!path_equal(from, to))  {
+                r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
+                if (r < 0)
+                        return r;
+        }
 
         NULSTR_FOREACH(n, mask_names) {
-                if (bit & mask)
-                        cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, to, false, false);
-                else {
-                        char prefix[strlen(to) + 1], *slash;
+                if (supported & bit) {
+                        const char *p = NULL;
 
-                        strcpy(prefix, to);
-                        while ((slash = strrchr(prefix, '/'))) {
-                                int q;
+                        if (to_callback)
+                                p = to_callback(bit, userdata);
 
-                                *slash = 0;
+                        if (!p)
+                                p = to;
 
-                                q = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, prefix, false, false);
-                                if (q >= 0)
-                                        break;
-                        }
+                        cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
                 }
 
                 bit <<= 1;
         }
 
-        return r;
+        return 0;
 }
 
-int cg_trim_with_mask(CGroupControllerMask mask, const char *path, bool delete_root) {
+int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
         CGroupControllerMask bit = 1;
         const char *n;
         int r;
@@ -1722,13 +1394,13 @@ int cg_trim_with_mask(CGroupControllerMask mask, const char *path, bool delete_r
                 return r;
 
         NULSTR_FOREACH(n, mask_names) {
-                if (bit & mask)
+                if (supported & bit)
                         cg_trim(n, path, delete_root);
 
                 bit <<= 1;
         }
 
-        return r;
+        return 0;
 }
 
 CGroupControllerMask cg_mask_supported(void) {
@@ -1744,3 +1416,54 @@ CGroupControllerMask cg_mask_supported(void) {
 
         return mask;
 }
+
+int cg_kernel_controllers(Set *controllers) {
+        _cleanup_fclose_ FILE *f = NULL;
+        char buf[LINE_MAX];
+        int r;
+
+        assert(controllers);
+
+        f = fopen("/proc/cgroups", "re");
+        if (!f) {
+                if (errno == ENOENT)
+                        return 0;
+                return -errno;
+        }
+
+        /* Ignore the header line */
+        (void) fgets(buf, sizeof(buf), f);
+
+        for (;;) {
+                char *controller;
+                int enabled = 0;
+
+                errno = 0;
+                if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
+
+                        if (feof(f))
+                                break;
+
+                        if (ferror(f) && errno)
+                                return -errno;
+
+                        return -EBADMSG;
+                }
+
+                if (!enabled) {
+                        free(controller);
+                        continue;
+                }
+
+                if (!filename_is_valid(controller)) {
+                        free(controller);
+                        return -EBADMSG;
+                }
+
+                r = set_consume(controllers, controller);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}