chiark / gitweb /
cgroup: optionally mount a specific cgroup controllers together, and add cpu+cpuacct...
[elogind.git] / src / cgroup-util.c
index ccc94f86a1fc3eb4c70da24675fca9f742f23763..f74280f4952f353623325f832a03fb30e36ebb25 100644 (file)
@@ -27,6 +27,7 @@
 #include <dirent.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <ftw.h>
 
 #include "cgroup-util.h"
 #include "log.h"
@@ -153,20 +154,41 @@ int cg_read_subgroup(DIR *d, char **fn) {
         return 0;
 }
 
-int cg_rmdir(const char *controller, const char *path) {
+int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
         char *p;
         int r;
 
-        if ((r = cg_get_path(controller, path, NULL, &p)) < 0)
+        r = cg_get_path(controller, path, NULL, &p);
+        if (r < 0)
                 return r;
 
+        if (honour_sticky) {
+                char *tasks;
+
+                /* If the sticky bit is set don't remove the directory */
+
+                tasks = strappend(p, "/tasks");
+                if (!tasks) {
+                        free(p);
+                        return -ENOMEM;
+                }
+
+                r = file_is_sticky(tasks);
+                free(tasks);
+
+                if (r > 0) {
+                        free(p);
+                        return 0;
+                }
+        }
+
         r = rmdir(p);
         free(p);
 
-        return r < 0 ? -errno : 0;
+        return (r < 0 && errno != ENOENT) ? -errno : 0;
 }
 
-int cg_kill(const char *controller, const char *path, int sig, bool ignore_self, Set *s) {
+int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
         bool done = false;
         int r, ret = 0;
         pid_t my_pid;
@@ -188,7 +210,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self,
         my_pid = getpid();
 
         do {
-                pid_t pid;
+                pid_t pid = 0;
                 done = true;
 
                 if ((r = cg_enumerate_processes(controller, path, &f)) < 0) {
@@ -211,8 +233,13 @@ int cg_kill(const char *controller, const char *path, int sig, bool ignore_self,
                         if (kill(pid, sig) < 0) {
                                 if (ret >= 0 && errno != ESRCH)
                                         ret = -errno;
-                        } else if (ret == 0)
+                        } else if (ret == 0) {
+
+                                if (sigcont)
+                                        kill(pid, SIGCONT);
+
                                 ret = 1;
+                        }
 
                         done = false;
 
@@ -250,7 +277,7 @@ finish:
         return ret;
 }
 
-int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self, bool rem, Set *s) {
+int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
         int r, ret = 0;
         DIR *d = NULL;
         char *fn;
@@ -264,7 +291,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool ig
                 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
                         return -ENOMEM;
 
-        ret = cg_kill(controller, path, sig, ignore_self, s);
+        ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
 
         if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) {
                 if (ret >= 0 && r != -ENOENT)
@@ -286,7 +313,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool ig
                         goto finish;
                 }
 
-                r = cg_kill_recursive(controller, p, sig, ignore_self, rem, s);
+                r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
                 free(p);
 
                 if (r != 0 && ret >= 0)
@@ -297,7 +324,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool ig
                 ret = r;
 
         if (rem)
-                if ((r = cg_rmdir(controller, path)) < 0) {
+                if ((r = cg_rmdir(controller, path, true)) < 0) {
                         if (ret >= 0 &&
                             r != -ENOENT &&
                             r != -EBUSY)
@@ -321,24 +348,25 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path, bool re
         assert(controller);
 
         /* This safely kills all processes; first it sends a SIGTERM,
-         * then checks 8 times after 50ms whether the group is
-         * now empty, and finally kills everything that is left with
-         * SIGKILL */
+         * 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 < 10; i++) {
+        for (i = 0; i < 15; i++) {
                 int sig, r;
 
                 if (i <= 0)
                         sig = SIGTERM;
-                else if (i >= 9)
+                else if (i == 9)
                         sig = SIGKILL;
                 else
                         sig = 0;
 
-                if ((r = cg_kill_recursive(controller, path, sig, true, rem, NULL)) <= 0)
+                if ((r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL)) <= 0)
                         return r;
 
-                usleep(50 * USEC_PER_MSEC);
+                usleep(200 * USEC_PER_MSEC);
         }
 
         return 0;
@@ -361,7 +389,7 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig
         my_pid = getpid();
 
         do {
-                pid_t pid;
+                pid_t pid = 0;
                 done = true;
 
                 if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) {
@@ -460,7 +488,7 @@ int cg_migrate_recursive(const char *controller, const char *from, const char *t
                 ret = r;
 
         if (rem)
-                if ((r = cg_rmdir(controller, from)) < 0) {
+                if ((r = cg_rmdir(controller, from, true)) < 0) {
                         if (ret >= 0 &&
                             r != -ENOENT &&
                             r != -EBUSY)
@@ -476,12 +504,26 @@ finish:
 
 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
         const char *p;
-        char *mp;
-        int r;
+        char *t;
+        static __thread bool good = false;
 
         assert(controller);
         assert(fs);
 
+        if (_unlikely_(!good)) {
+                int r;
+
+                r = path_is_mount_point("/sys/fs/cgroup", false);
+                if (r <= 0)
+                        return r < 0 ? r : -ENOENT;
+
+                /* Cache this to save a few stat()s */
+                good = true;
+        }
+
+        if (isempty(controller))
+                return -EINVAL;
+
         /* This is a very minimal lookup from controller names to
          * paths. Since we have mounted most hierarchies ourselves
          * should be kinda safe, but eventually we might want to
@@ -495,45 +537,88 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
         else
                 p = controller;
 
-        if (asprintf(&mp, "/sys/fs/cgroup/%s", p) < 0)
-                return -ENOMEM;
-
-        if ((r = path_is_mount_point(mp)) <= 0) {
-                free(mp);
-                return r < 0 ? r : -ENOENT;
-        }
-
         if (path && suffix)
-                r = asprintf(fs, "%s/%s/%s", mp, path, suffix);
+                t = join("/sys/fs/cgroup/", p, "/", path, "/", suffix, NULL);
         else if (path)
-                r = asprintf(fs, "%s/%s", mp, path);
+                t = join("/sys/fs/cgroup/", p, "/", path, NULL);
         else if (suffix)
-                r = asprintf(fs, "%s/%s", mp, suffix);
-        else {
-                path_kill_slashes(mp);
-                *fs = mp;
+                t = join("/sys/fs/cgroup/", p, "/", suffix, NULL);
+        else
+                t = join("/sys/fs/cgroup/", p, NULL);
+
+        if (!t)
+                return -ENOMEM;
+
+        path_kill_slashes(t);
+
+        *fs = t;
+        return 0;
+}
+
+static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
+        char *p;
+        bool is_sticky;
+
+        if (typeflag != FTW_DP)
+                return 0;
+
+        if (ftwbuf->level < 1)
                 return 0;
+
+        p = strappend(path, "/tasks");
+        if (!p) {
+                errno = ENOMEM;
+                return 1;
         }
 
-        free(mp);
-        path_kill_slashes(*fs);
-        return r < 0 ? -ENOMEM : 0;
+        is_sticky = file_is_sticky(p) > 0;
+        free(p);
+
+        if (is_sticky)
+                return 0;
+
+        rmdir(path);
+        return 0;
 }
 
 int cg_trim(const char *controller, const char *path, bool delete_root) {
         char *fs;
-        int r;
+        int r = 0;
 
         assert(controller);
         assert(path);
 
-        if ((r = cg_get_path(controller, path, NULL, &fs)) < 0)
+        r = cg_get_path(controller, path, NULL, &fs);
+        if (r < 0)
                 return r;
 
-        r = rm_rf(fs, true, delete_root);
+        errno = 0;
+        if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
+                r = errno ? -errno : -EIO;
+
+        if (delete_root) {
+                bool is_sticky;
+                char *p;
+
+                p = strappend(fs, "/tasks");
+                if (!p) {
+                        free(fs);
+                        return -ENOMEM;
+                }
+
+                is_sticky = file_is_sticky(p) > 0;
+                free(p);
+
+                if (!is_sticky)
+                        if (rmdir(fs) < 0 && errno != ENOENT) {
+                                if (r == 0)
+                                        r = -errno;
+                        }
+        }
+
         free(fs);
 
-        return r == -ENOENT ? 0 : r;
+        return r;
 }
 
 int cg_delete(const char *controller, const char *path) {
@@ -781,9 +866,9 @@ finish:
 }
 
 int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
-        pid_t pid;
+        pid_t pid = 0;
         int r;
-        FILE *f;
+        FILE *f = NULL;
         bool found = false;
 
         assert(controller);
@@ -961,3 +1046,31 @@ int cg_fix_path(const char *path, char **result) {
 
         return r;
 }
+
+int cg_get_user_path(char **path) {
+        char *root, *p;
+
+        assert(path);
+
+        /* Figure out the place to put user cgroups below. We use the
+         * same as PID 1 has but with the "/system" suffix replaced by
+         * "/user" */
+
+        if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root) < 0)
+                p = strdup("/user");
+        else {
+                if (endswith(root, "/system"))
+                        root[strlen(root) - 7] = 0;
+                else if (streq(root, "/"))
+                        root[0] = 0;
+
+                p = strappend(root, "/user");
+                free(root);
+        }
+
+        if (!p)
+                return -ENOMEM;
+
+        *path = p;
+        return 0;
+}