chiark / gitweb /
Fix extraction of _SYSTEMD_USER_UNIT
[elogind.git] / src / shared / cgroup-util.c
index 2e630d4708d6f14814762794d2fe21345322016e..74889b636324dea71c0426e05740198a5f21286b 100644 (file)
@@ -39,6 +39,7 @@
 #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;
@@ -278,37 +279,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;
@@ -456,23 +426,14 @@ int cg_migrate_recursive_fallback(
 
                 /* This didn't work? Then let's try all prefixes of the destination */
 
-                strcpy(prefix, pto);
-                for (;;) {
-                        char *slash;
-
-                        slash = strrchr(prefix, '/');
-                        if (!slash)
-                                break;
-
-                        *slash = 0;
-
+                PATH_FOREACH_PREFIX(prefix, pto) {
                         r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
                         if (r >= 0)
                                 break;
                 }
         }
 
-        return r;
+        return 0;
 }
 
 static const char *normalize_controller(const char *controller) {
@@ -519,7 +480,7 @@ static int join_path(const char *controller, const char *path, const char *suffi
 
 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);
 
@@ -627,6 +588,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];
@@ -661,23 +662,14 @@ int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
                 /* This didn't work? Then let's try all prefixes of
                  * the destination */
 
-                strcpy(prefix, path);
-                for (;;) {
-                        char *slash;
-
-                        slash = strrchr(prefix, '/');
-                        if (!slash)
-                                break;
-
-                        *slash = 0;
-
+                PATH_FOREACH_PREFIX(prefix, path) {
                         r = cg_attach(controller, prefix, pid);
                         if (r >= 0)
                                 break;
                 }
         }
 
-        return r;
+        return 0;
 }
 
 int cg_set_group_access(
@@ -918,19 +910,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;
@@ -1021,19 +1000,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;
@@ -1048,33 +1036,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;
@@ -1121,79 +1082,42 @@ int cg_get_root_path(char **path) {
         return 0;
 }
 
-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);
+int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
+        _cleanup_free_ char *cg_root = NULL;
+        char *cg_process, *p;
+        int r;
 
-                if (streq(p, "systemd")) {
-                        free(*f);
-                        continue;
-                }
+        assert(pid >= 0);
+        assert(cgroup);
 
-                if (!cg_controller_is_valid(p, true)) {
-                        log_warning("Controller %s is not valid, removing from controllers list.", p);
-                        free(*f);
-                        continue;
-                }
+        if (!root) {
+                /* If the root was specified let's use that, otherwise
+                 * let's determine it from PID 1 */
 
-                r = check_hierarchy(p);
-                if (r < 0) {
-                        log_debug("Controller %s is not available, removing from controllers list.", p);
-                        free(*f);
-                        continue;
-                }
+                r = cg_get_root_path(&cg_root);
+                if (r < 0)
+                        return r;
 
-                *(t++) = *f;
+                root = cg_root;
         }
 
-        *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 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;
+        p = path_startswith(cg_process, root);
+        if (p) {
+                char *c;
 
-        if (cgroup) {
-                char* c;
+                c = strdup(p - 1);
+                free(cg_process);
 
-                c = strdup(p);
-                if (!c) {
-                        free(cg_process);
+                if (!c)
                         return -ENOMEM;
-                }
 
                 *cgroup = c;
-        }
-
-        if (root) {
-                cg_process[p-cg_process] = 0;
-                *root = cg_process;
         } else
-                free(cg_process);
+                *cgroup = cg_process;
 
         return 0;
 }
@@ -1259,6 +1183,9 @@ int cg_pid_get_unit(pid_t pid, char **unit) {
         return cg_path_get_unit(cgroup, unit);
 }
 
+/**
+ * Skip session-*.scope, but require it to be there.
+ */
 static const char *skip_session(const char *p) {
         size_t n;
 
@@ -1267,7 +1194,27 @@ static const char *skip_session(const char *p) {
         p += strspn(p, "/");
 
         n = strcspn(p, "/");
-        if (n <= 12 || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
+        if (n < strlen("session-x.scope") || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
+                return NULL;
+
+        p += n;
+        p += strspn(p, "/");
+
+        return p;
+}
+
+/**
+ * Skip user@*.service, but require it to be there.
+ */
+static const char *skip_user_manager(const char *p) {
+        size_t n;
+
+        assert(p);
+
+        p += strspn(p, "/");
+
+        n = strcspn(p, "/");
+        if (n < strlen("user@x.service") || memcmp(p, "user@", 5) != 0 || memcmp(p + n - 8, ".service", 8) != 0)
                 return NULL;
 
         p += n;
@@ -1277,7 +1224,7 @@ static const char *skip_session(const char *p) {
 }
 
 int cg_path_get_user_unit(const char *path, char **unit) {
-        const char *e;
+        const char *e, *t;
 
         assert(path);
         assert(unit);
@@ -1289,13 +1236,17 @@ int cg_path_get_user_unit(const char *path, char **unit) {
         /* 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);
+        /* Skip the session scope... */
+        t = skip_session(e);
+        if (t)
+                /* ... and skip more slices if there's one */
+                e = skip_slices(t);
+        else {
+                /* ... or require a user manager unit to be there */
+                e = skip_user_manager(e);
+                if (!e)
+                        return -ENOENT;
+        }
 
         return cg_path_decode_unit(e, unit);
 }
@@ -1413,36 +1364,41 @@ int cg_pid_get_session(pid_t pid, char **session) {
 
 int cg_path_get_owner_uid(const char *path, uid_t *uid) {
         _cleanup_free_ char *slice = NULL;
-        const char *e;
+        const char *start, *end;
         char *s;
+        uid_t u;
         int r;
 
         assert(path);
-        assert(uid);
 
         r = cg_path_get_slice(path, &slice);
         if (r < 0)
                 return r;
 
-        e = startswith(slice, "user-");
-        if (!e)
+        start = startswith(slice, "user-");
+        if (!start)
                 return -ENOENT;
-        if (!endswith(slice, ".slice"))
+        end = endswith(slice, ".slice");
+        if (!end)
                 return -ENOENT;
 
-        s = strndupa(e, strlen(e) - 6);
+        s = strndupa(start, end - start);
         if (!s)
-                return -ENOMEM;
+                return -ENOENT;
+
+        if (parse_uid(s, &u) < 0)
+                return -EIO;
 
-        return parse_uid(s, uid);
+        if (uid)
+                *uid = u;
+
+        return 0;
 }
 
 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;
@@ -1497,35 +1453,6 @@ int cg_pid_get_slice(pid_t pid, char **slice) {
         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;