chiark / gitweb /
libsystemd-logind: fix detection of session/user/machine of a PID
[elogind.git] / src / shared / cgroup-util.c
index 9cbc64a5412fea7e78a72f0e33d77f9f7a7c5716..390259e3e43c5e28b18de5b18494a43291fa0b75 100644 (file)
@@ -132,7 +132,7 @@ int cg_read_subgroup(DIR *d, char **fn) {
         return 0;
 }
 
-int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
+int cg_rmdir(const char *controller, const char *path) {
         _cleanup_free_ char *p = NULL;
         int r;
 
@@ -140,34 +140,6 @@ int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
         if (r < 0)
                 return r;
 
-        if (honour_sticky) {
-                char *fn;
-
-                /* If the sticky bit is set on cgroup.procs, don't
-                 * remove the directory */
-
-                fn = strappend(p, "/cgroup.procs");
-                if (!fn)
-                        return -ENOMEM;
-
-                r = file_is_priv_sticky(fn);
-                free(fn);
-
-                if (r > 0)
-                        return 0;
-
-                /* Compatibility ... */
-                fn = strappend(p, "/tasks");
-                if (!fn)
-                        return -ENOMEM;
-
-                r = file_is_priv_sticky(fn);
-                free(fn);
-
-                if (r > 0)
-                        return 0;
-        }
-
         r = rmdir(p);
         if (r < 0 && errno != ENOENT)
                 return -errno;
@@ -298,7 +270,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si
                 ret = r;
 
         if (rem) {
-                r = cg_rmdir(controller, path, true);
+                r = cg_rmdir(controller, path);
                 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
                         return r;
         }
@@ -407,7 +379,14 @@ int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char
         return ret;
 }
 
-int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool rem) {
+int cg_migrate_recursive(
+                const char *cfrom,
+                const char *pfrom,
+                const char *cto,
+                const char *pto,
+                bool ignore_self,
+                bool rem) {
+
         _cleanup_closedir_ DIR *d = NULL;
         int r, ret = 0;
         char *fn;
@@ -448,7 +427,7 @@ int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto,
                 ret = r;
 
         if (rem) {
-                r = cg_rmdir(cfrom, pfrom, true);
+                r = cg_rmdir(cfrom, pfrom);
                 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
                         return r;
         }
@@ -558,8 +537,9 @@ int cg_get_path_and_check(const char *controller, const char *path, const char *
 }
 
 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
-        char *p;
-        bool is_sticky;
+        assert(path);
+        assert(sb);
+        assert(ftwbuf);
 
         if (typeflag != FTW_DP)
                 return 0;
@@ -567,31 +547,6 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct
         if (ftwbuf->level < 1)
                 return 0;
 
-        p = strappend(path, "/cgroup.procs");
-        if (!p) {
-                errno = ENOMEM;
-                return 1;
-        }
-
-        is_sticky = file_is_priv_sticky(p) > 0;
-        free(p);
-
-        if (is_sticky)
-                return 0;
-
-        /* Compatibility */
-        p = strappend(path, "/tasks");
-        if (!p) {
-                errno = ENOMEM;
-                return 1;
-        }
-
-        is_sticky = file_is_priv_sticky(p) > 0;
-        free(p);
-
-        if (is_sticky)
-                return 0;
-
         rmdir(path);
         return 0;
 }
@@ -611,28 +566,8 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
                 r = errno ? -errno : -EIO;
 
         if (delete_root) {
-                bool is_sticky;
-                char *p;
-
-                p = strappend(fs, "/cgroup.procs");
-                if (!p)
-                        return -ENOMEM;
-
-                is_sticky = file_is_priv_sticky(p) > 0;
-                free(p);
-
-                if (!is_sticky) {
-                        p = strappend(fs, "/tasks");
-                        if (!p)
-                                return -ENOMEM;
-
-                        is_sticky = file_is_priv_sticky(p) > 0;
-                        free(p);
-                }
-
-                if (!is_sticky)
-                        if (rmdir(fs) < 0 && errno != ENOENT && r == 0)
-                                return -errno;
+                if (rmdir(fs) < 0 && errno != ENOENT)
+                        return -errno;
         }
 
         return r;
@@ -699,15 +634,14 @@ int cg_set_task_access(
                 const char *path,
                 mode_t mode,
                 uid_t uid,
-                gid_t gid,
-                int sticky) {
+                gid_t gid) {
 
         _cleanup_free_ char *fs = NULL, *procs = NULL;
         int r;
 
         assert(path);
 
-        if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0)
+        if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1)
                 return 0;
 
         if (mode != (mode_t) -1)
@@ -717,28 +651,6 @@ int cg_set_task_access(
         if (r < 0)
                 return r;
 
-        if (sticky >= 0 && mode != (mode_t) -1)
-                /* Both mode and sticky param are passed */
-                mode |= (sticky ? S_ISVTX : 0);
-        else if ((sticky >= 0 && mode == (mode_t) -1) ||
-                 (mode != (mode_t) -1 && sticky < 0)) {
-                struct stat st;
-
-                /* Only one param is passed, hence read the current
-                 * mode from the file itself */
-
-                r = lstat(fs, &st);
-                if (r < 0)
-                        return -errno;
-
-                if (mode == (mode_t) -1)
-                        /* No mode set, we just shall set the sticky bit */
-                        mode = (st.st_mode & ~S_ISVTX) | (sticky ? S_ISVTX : 0);
-                else
-                        /* Only mode set, leave sticky bit untouched */
-                        mode = (st.st_mode & ~0777) | mode;
-        }
-
         r = chmod_and_chown(fs, mode, uid, gid);
         if (r < 0)
                 return r;
@@ -1266,23 +1178,6 @@ int cg_pid_get_unit(pid_t pid, char **unit) {
         return cg_path_get_unit(cgroup, unit);
 }
 
-static const char *skip_user(const char *p) {
-        size_t n;
-
-        assert(p);
-
-        p += strspn(p, "/");
-
-        n = strcspn(p, "/");
-        if (n <= 5 || memcmp(p + n - 5, ".user", 5) != 0)
-                return p;
-
-        p += n;
-        p += strspn(p, "/");
-
-        return p;
-}
-
 static const char *skip_session(const char *p) {
         size_t n;
 
@@ -1291,7 +1186,7 @@ static const char *skip_session(const char *p) {
         p += strspn(p, "/");
 
         n = strcspn(p, "/");
-        if (n <= 8 || memcmp(p + n - 8, ".session", 8) != 0)
+        if (n <= 12 || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
                 return NULL;
 
         p += n;
@@ -1300,23 +1195,6 @@ static const char *skip_session(const char *p) {
         return p;
 }
 
-static const char *skip_systemd_label(const char *p) {
-        size_t n;
-
-        assert(p);
-
-        p += strspn(p, "/");
-
-        n = strcspn(p, "/");
-        if (n < 8 || memcmp(p, "systemd-", 8) != 0)
-                return p;
-
-        p += n;
-        p += strspn(p, "/");
-
-        return p;
-}
-
 int cg_path_get_user_unit(const char *path, char **unit) {
         const char *e;
 
@@ -1330,16 +1208,13 @@ int cg_path_get_user_unit(const char *path, char **unit) {
         /* Skip slices, if there are any */
         e = skip_slices(path);
 
-        /* Skip the user name, if there is one */
-        e = skip_user(e);
-
-        /* Skip the session ID, require that there is one */
+        /* Skip the session scope, require that there is one */
         e = skip_session(e);
         if (!e)
                 return -ENOENT;
 
-        /* Skip the systemd cgroup, if there is one */
-        e = skip_systemd_label(e);
+        /* And skip more slices */
+        e = skip_slices(e);
 
         return cg_path_decode_unit(e, unit);
 }
@@ -1360,6 +1235,7 @@ int cg_pid_get_user_unit(pid_t pid, char **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);
@@ -1374,11 +1250,17 @@ int cg_path_get_machine_name(const char *path, char **machine) {
         s = strndupa(e, n - e);
         s = cg_unescape(s);
 
-        x = endswith(s, ".machine");
+        x = startswith(s, "machine-");
         if (!x)
                 return -ENOENT;
+        if (!endswith(x, ".scope"))
+                return -ENOENT;
+
+        l = strlen(x);
+        if (l <= 6)
+                return -ENOENT;
 
-        r = strndup(s, x - s);
+        r = strndup(x, l - 6);
         if (!r)
                 return -ENOMEM;
 
@@ -1400,8 +1282,9 @@ int cg_pid_get_machine_name(pid_t pid, char **machine) {
 }
 
 int cg_path_get_session(const char *path, char **session) {
-        const char *e, *n;
-        char *s;
+        const char *e, *n, *x;
+        char *s, *r;
+        size_t l;
 
         assert(path);
         assert(session);
@@ -1409,20 +1292,28 @@ int cg_path_get_session(const char *path, char **session) {
         /* Skip slices, if there are any */
         e = skip_slices(path);
 
-        /* Skip the user name, if there is one */
-        e = skip_user(e);
-
         n = strchrnul(e, '/');
-        if (n - e < 8)
+        if (e == n)
+                return -ENOENT;
+
+        s = strndupa(e, n - e);
+        s = cg_unescape(s);
+
+        x = startswith(s, "session-");
+        if (!x)
                 return -ENOENT;
-        if (memcmp(n - 8, ".session", 8) != 0)
+        if (!endswith(x, ".scope"))
                 return -ENOENT;
 
-        s = strndup(e, n - e - 8);
-        if (!s)
+        l = strlen(x);
+        if (l <= 6)
+                return -ENOENT;
+
+        r = strndup(x, l - 6);
+        if (!r)
                 return -ENOMEM;
 
-        *session = s;
+        *session = r;
         return 0;
 }
 
@@ -1440,22 +1331,25 @@ int cg_pid_get_session(pid_t pid, char **session) {
 }
 
 int cg_path_get_owner_uid(const char *path, uid_t *uid) {
-        const char *e, *n;
+        _cleanup_free_ char *slice = NULL;
+        const char *e;
         char *s;
+        int r;
 
         assert(path);
         assert(uid);
 
-        /* Skip slices, if there are any */
-        e = skip_slices(path);
+        r = cg_path_get_slice(path, &slice);
+        if (r < 0)
+                return r;
 
-        n = strchrnul(e, '/');
-        if (n - e < 5)
+        e = startswith(slice, "user-");
+        if (!e)
                 return -ENOENT;
-        if (memcmp(n - 5, ".user", 5) != 0)
+        if (!endswith(slice, ".slice"))
                 return -ENOENT;
 
-        s = strndupa(e, n - e - 5);
+        s = strndupa(e, strlen(e) - 6);
         if (!s)
                 return -ENOMEM;
 
@@ -1688,3 +1582,165 @@ int cg_slice_to_path(const char *unit, char **ret) {
 
         return 0;
 }
+
+int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        r = cg_get_path(controller, path, attribute, &p);
+        if (r < 0)
+                return r;
+
+        return write_string_file(p, value);
+}
+
+static const char mask_names[] =
+        "cpu\0"
+        "cpuacct\0"
+        "blkio\0"
+        "memory\0"
+        "devices\0";
+
+int cg_create_with_mask(CGroupControllerMask mask, const char *path) {
+        CGroupControllerMask bit = 1;
+        const char *n;
+        int r;
+
+        /* This one will create a cgroup in our private tree, but also
+         * duplicate it in the trees specified in mask, and remove it
+         * in all others */
+
+        /* First create the cgroup in our own hierarchy. */
+        r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+        if (r < 0)
+                return r;
+
+        /* Then, do the same in the other hierarchies */
+        NULSTR_FOREACH(n, mask_names) {
+                if (bit & mask)
+                        cg_create(n, path);
+                else
+                        cg_trim(n, path, true);
+
+                bit <<= 1;
+        }
+
+        return r;
+}
+
+int cg_attach_with_mask(CGroupControllerMask mask, const char *path, pid_t pid) {
+        CGroupControllerMask bit = 1;
+        const char *n;
+        int r;
+
+        r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
+
+        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;
+                        }
+                }
+
+                bit <<= 1;
+        }
+
+        return r;
+}
+
+int cg_attach_many_with_mask(CGroupControllerMask mask, const char *path, Set* pids) {
+        Iterator i;
+        void *pidp;
+        int r = 0;
+
+        SET_FOREACH(pidp, pids, i) {
+                pid_t pid = PTR_TO_LONG(pidp);
+                int k;
+
+                k = cg_attach_with_mask(mask, path, pid);
+                if (k < 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+int cg_migrate_with_mask(CGroupControllerMask mask, const char *from, const char *to) {
+        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);
+
+        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;
+
+                        strcpy(prefix, to);
+                        while ((slash = strrchr(prefix, '/'))) {
+                                int q;
+
+                                *slash = 0;
+
+                                q = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, prefix, false, false);
+                                if (q >= 0)
+                                        break;
+                        }
+                }
+
+                bit <<= 1;
+        }
+
+        return r;
+}
+
+int cg_trim_with_mask(CGroupControllerMask mask, const char *path, bool delete_root) {
+        CGroupControllerMask bit = 1;
+        const char *n;
+        int r;
+
+        r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
+        if (r < 0)
+                return r;
+
+        NULSTR_FOREACH(n, mask_names) {
+                if (bit & mask)
+                        cg_trim(n, path, delete_root);
+
+                bit <<= 1;
+        }
+
+        return r;
+}
+
+CGroupControllerMask cg_mask_supported(void) {
+        CGroupControllerMask bit = 1, mask = 0;
+        const char *n;
+
+        NULSTR_FOREACH(n, mask_names) {
+                if (check_hierarchy(n) >= 0)
+                        mask |= bit;
+
+                bit <<= 1;
+        }
+
+        return mask;
+}