chiark / gitweb /
core: simplify cg_[all_]unified()
[elogind.git] / src / basic / cgroup-util.c
index e3b67e3d8c05b507fc3b01ecc36618eb30f8efec..bcb022896816450bfe101370a6d735e62c235433 100644 (file)
@@ -28,6 +28,7 @@
 #include <sys/stat.h>
 //#include <sys/statfs.h>
 #include <sys/types.h>
+#include <sys/xattr.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -37,7 +38,7 @@
 #include "extract-word.h"
 #include "fd-util.h"
 #include "fileio.h"
-#include "formats-util.h"
+#include "format-util.h"
 #include "fs-util.h"
 //#include "log.h"
 #include "login-util.h"
@@ -183,8 +184,7 @@ int cg_read_subgroup(DIR *d, char **fn) {
                 if (de->d_type != DT_DIR)
                         continue;
 
-                if (streq(de->d_name, ".") ||
-                    streq(de->d_name, ".."))
+                if (dot_or_dot_dot(de->d_name))
                         continue;
 
                 b = strdup(de->d_name);
@@ -346,7 +346,7 @@ int cg_kill_recursive(
         while ((r = cg_read_subgroup(d, &fn)) > 0) {
                 _cleanup_free_ char *p = NULL;
 
-                p = strjoin(path, "/", fn, NULL);
+                p = strjoin(path, "/", fn);
                 free(fn);
                 if (!p)
                         return -ENOMEM;
@@ -484,7 +484,7 @@ int cg_migrate_recursive(
         while ((r = cg_read_subgroup(d, &fn)) > 0) {
                 _cleanup_free_ char *p = NULL;
 
-                p = strjoin(pfrom, "/", fn, NULL);
+                p = strjoin(pfrom, "/", fn);
                 free(fn);
                 if (!p)
                         return -ENOMEM;
@@ -567,11 +567,11 @@ static int join_path_legacy(const char *controller, const char *path, const char
         if (isempty(path) && isempty(suffix))
                 t = strappend("/sys/fs/cgroup/", dn);
         else if (isempty(path))
-                t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL);
+                t = strjoin("/sys/fs/cgroup/", dn, "/", suffix);
         else if (isempty(suffix))
-                t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL);
+                t = strjoin("/sys/fs/cgroup/", dn, "/", path);
         else
-                t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL);
+                t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix);
         if (!t)
                 return -ENOMEM;
 
@@ -591,7 +591,7 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) {
         else if (isempty(suffix))
                 t = strappend("/sys/fs/cgroup/", path);
         else
-                t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL);
+                t = strjoin("/sys/fs/cgroup/", path, "/", suffix);
         if (!t)
                 return -ENOMEM;
 
@@ -600,7 +600,7 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) {
 }
 
 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
-        int unified, r;
+        int r;
 
         assert(fs);
 
@@ -618,7 +618,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
                 else if (!path)
                         t = strdup(suffix);
                 else
-                        t = strjoin(path, "/", suffix, NULL);
+                        t = strjoin(path, "/", suffix);
                 if (!t)
                         return -ENOMEM;
 
@@ -629,11 +629,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
         if (!cg_controller_is_valid(controller))
                 return -EINVAL;
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-
-        if (unified > 0)
+        if (cg_all_unified())
                 r = join_path_unified(path, suffix, fs);
         else
                 r = join_path_legacy(controller, path, suffix, fs);
@@ -645,7 +641,6 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
 }
 
 static int controller_is_accessible(const char *controller) {
-        int unified;
 
         assert(controller);
 
@@ -657,10 +652,7 @@ static int controller_is_accessible(const char *controller) {
         if (!cg_controller_is_valid(controller))
                 return -EINVAL;
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified > 0) {
+        if (cg_all_unified()) {
                 /* We don't support named hierarchies if we are using
                  * the unified hierarchy. */
 
@@ -858,7 +850,7 @@ int cg_set_task_access(
                 gid_t gid) {
 
         _cleanup_free_ char *fs = NULL, *procs = NULL;
-        int r, unified;
+        int r;
 
         assert(path);
 
@@ -876,10 +868,7 @@ int cg_set_task_access(
         if (r < 0)
                 return r;
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified)
+        if (cg_unified(controller))
                 return 0;
 
         /* Compatibility, Always keep values for "tasks" in sync with
@@ -889,6 +878,43 @@ int cg_set_task_access(
 
         return 0;
 }
+
+int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
+        _cleanup_free_ char *fs = NULL;
+        int r;
+
+        assert(path);
+        assert(name);
+        assert(value || size <= 0);
+
+        r = cg_get_path(controller, path, NULL, &fs);
+        if (r < 0)
+                return r;
+
+        if (setxattr(fs, name, value, size, flags) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
+        _cleanup_free_ char *fs = NULL;
+        ssize_t n;
+        int r;
+
+        assert(path);
+        assert(name);
+
+        r = cg_get_path(controller, path, NULL, &fs);
+        if (r < 0)
+                return r;
+
+        n = getxattr(fs, name, value, size);
+        if (n < 0)
+                return -errno;
+
+        return (int) n;
+}
 #endif // 0
 
 int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
@@ -896,23 +922,20 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
         char line[LINE_MAX];
         const char *fs;
         size_t cs = 0;
-        int unified;
+        bool unified;
 
         assert(path);
         assert(pid >= 0);
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified == 0) {
-                if (controller) {
-                        if (!cg_controller_is_valid(controller))
-                                return -EINVAL;
-                } else
-                        controller = SYSTEMD_CGROUP_CONTROLLER;
+        if (controller) {
+                if (!cg_controller_is_valid(controller))
+                        return -EINVAL;
+        } else
+                controller = SYSTEMD_CGROUP_CONTROLLER;
 
+        unified = cg_unified(controller);
+        if (!unified)
                 cs = strlen(controller);
-        }
 
         fs = procfs_file_alloca(pid, "cgroup");
         log_debug_elogind("Searching for PID %u in \"%s\" (controller \"%s\")",
@@ -976,14 +999,11 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
 int cg_install_release_agent(const char *controller, const char *agent) {
         _cleanup_free_ char *fs = NULL, *contents = NULL;
         const char *sc;
-        int r, unified;
+        int r;
 
         assert(agent);
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified) /* doesn't apply to unified hierarchy */
+        if (cg_unified(controller)) /* doesn't apply to unified hierarchy */
                 return -EOPNOTSUPP;
 
         r = cg_get_path(controller, NULL, "release_agent", &fs);
@@ -1029,12 +1049,9 @@ int cg_install_release_agent(const char *controller, const char *agent) {
 
 int cg_uninstall_release_agent(const char *controller) {
         _cleanup_free_ char *fs = NULL;
-        int r, unified;
+        int r;
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified) /* Doesn't apply to unified hierarchy */
+        if (cg_unified(controller)) /* Doesn't apply to unified hierarchy */
                 return -EOPNOTSUPP;
 
         r = cg_get_path(controller, NULL, "notify_on_release", &fs);
@@ -1079,7 +1096,7 @@ int cg_is_empty(const char *controller, const char *path) {
 }
 
 int cg_is_empty_recursive(const char *controller, const char *path) {
-        int unified, r;
+        int r;
 
         assert(path);
 
@@ -1087,11 +1104,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
         if (controller && (isempty(path) || path_equal(path, "/")))
                 return false;
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-
-        if (unified > 0) {
+        if (cg_unified(controller)) {
                 _cleanup_free_ char *t = NULL;
 
                 /* On the unified hierarchy we can check empty state
@@ -1119,7 +1132,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
                 while ((r = cg_read_subgroup(d, &fn)) > 0) {
                         _cleanup_free_ char *p = NULL;
 
-                        p = strjoin(path, "/", fn, NULL);
+                        p = strjoin(path, "/", fn);
                         free(fn);
                         if (!p)
                                 return -ENOMEM;
@@ -1293,7 +1306,11 @@ int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
         }
 
         p = path_startswith(cgroup, root);
+#if 0 /// With other controllers, elogind might end up in /elogind, and *p is 0
         if (p && p > cgroup)
+#else
+        if (p && p[0] && (p > cgroup))
+#endif // 0
                 *shifted = p - 1;
         else
                 *shifted = cgroup;
@@ -1712,7 +1729,7 @@ int cg_path_get_slice(const char *p, char **slice) {
                         if (!e) {
                                 char *s;
 
-                                s = strdup("-.slice");
+                                s = strdup(SPECIAL_ROOT_SLICE);
                                 if (!s)
                                         return -ENOMEM;
 
@@ -1869,7 +1886,7 @@ int cg_slice_to_path(const char *unit, char **ret) {
         assert(unit);
         assert(ret);
 
-        if (streq(unit, "-.slice")) {
+        if (streq(unit, SPECIAL_ROOT_SLICE)) {
                 char *x;
 
                 x = strdup("");
@@ -1999,7 +2016,7 @@ int cg_get_keyed_attribute(const char *controller, const char *path, const char
 
 int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
         CGroupController c;
-        int r, unified;
+        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
@@ -2011,10 +2028,7 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
                 return r;
 
         /* If we are in the unified hierarchy, we are done now */
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified > 0)
+        if (cg_all_unified())
                 return 0;
 
         /* Otherwise, do the same in the other hierarchies */
@@ -2035,16 +2049,13 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
 
 int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
         CGroupController c;
-        int r, unified;
+        int r;
 
         r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
         if (r < 0)
                 return r;
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified > 0)
+        if (cg_all_unified())
                 return 0;
 
         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2085,7 +2096,7 @@ int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids,
 
 int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
         CGroupController c;
-        int r = 0, unified;
+        int r = 0;
 
         if (!path_equal(from, to))  {
                 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
@@ -2093,10 +2104,7 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
                         return r;
         }
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified > 0)
+        if (cg_all_unified())
                 return r;
 
         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2120,16 +2128,13 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
 
 int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
         CGroupController c;
-        int r, unified;
+        int r;
 
         r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
         if (r < 0)
                 return r;
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified > 0)
+        if (cg_all_unified())
                 return r;
 
         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2147,16 +2152,13 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root)
 
 int cg_mask_supported(CGroupMask *ret) {
         CGroupMask mask = 0;
-        int r, unified;
+        int r;
 
         /* Determines the mask of supported cgroup controllers. Only
          * includes controllers we can make sense of and that are
          * actually accessible. */
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (unified > 0) {
+        if (cg_all_unified()) {
                 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
                 const char *c;
 
@@ -2276,9 +2278,10 @@ int cg_kernel_controllers(Set *controllers) {
 }
 #endif // 0
 
-static thread_local int unified_cache = -1;
+static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
+
+static int cg_update_unified(void) {
 
-int cg_all_unified(void) {
         struct statfs fs;
 
         /* Checks if we support the unified hierarchy. Returns an
@@ -2286,50 +2289,68 @@ int cg_all_unified(void) {
          * have any other trouble determining if the unified hierarchy
          * is supported. */
 
-        if (unified_cache >= 0)
-                return unified_cache;
+        if (unified_cache >= CGROUP_UNIFIED_NONE)
+                return 0;
 
         if (statfs("/sys/fs/cgroup/", &fs) < 0)
                 return -errno;
 
 #if 0 /// UNNEEDED by elogind
         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
+                unified_cache = CGROUP_UNIFIED_ALL;
+        else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
+                if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
+                        return -errno;
+
+                unified_cache = F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC) ?
+                        CGROUP_UNIFIED_SYSTEMD : CGROUP_UNIFIED_NONE;
+        } else
+                return -ENOMEDIUM;
 #else
         /* elogind can not support the unified hierarchy as a controller,
          * so always assume a classical hierarchy.
          * If, and only *if*, someone really wants to substitute systemd-login
          * in an environment managed by systemd with elogind, we might have to
          * add such a support. */
-        if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
+        unified_cache = CGROUP_UNIFIED_NONE;
 #endif // 0
-                unified_cache = true;
-        else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
-                unified_cache = false;
+
+        return 0;
+}
+
+bool cg_unified(const char *controller) {
+
+        assert(cg_update_unified() >= 0);
+
+        if (streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER))
+                return unified_cache >= CGROUP_UNIFIED_SYSTEMD;
         else
-                return -ENOMEDIUM;
+                return unified_cache >= CGROUP_UNIFIED_ALL;
+}
 
-        return unified_cache;
+bool cg_all_unified(void) {
+
+        return cg_unified(NULL);
 }
 
 #if 0 /// UNNEEDED by elogind
-void cg_unified_flush(void) {
-        unified_cache = -1;
+int cg_unified_flush(void) {
+        unified_cache = CGROUP_UNIFIED_UNKNOWN;
+
+        return cg_update_unified();
 }
 
 int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
         _cleanup_free_ char *fs = NULL;
         CGroupController c;
-        int r, unified;
+        int r;
 
         assert(p);
 
         if (supported == 0)
                 return 0;
 
-        unified = cg_all_unified();
-        if (unified < 0)
-                return unified;
-        if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */
+        if (!cg_all_unified()) /* on the legacy hiearchy there's no joining of controllers defined */
                 return 0;
 
         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
@@ -2361,13 +2382,13 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
 
 bool cg_is_unified_wanted(void) {
         static thread_local int wanted = -1;
-        int r, unified;
+        int r;
+        bool b;
 
         /* If the hierarchy is already mounted, then follow whatever
          * was chosen for it. */
-        unified = cg_all_unified();
-        if (unified >= 0)
-                return unified;
+        if (cg_unified_flush() >= 0)
+                return cg_all_unified();
 
         /* Otherwise, let's see what the kernel command line has to
          * say. Since checking that is expensive, let's cache the
@@ -2375,32 +2396,21 @@ bool cg_is_unified_wanted(void) {
         if (wanted >= 0)
                 return wanted;
 
-        r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL);
-        if (r > 0)
-                return (wanted = true);
-        else {
-                _cleanup_free_ char *value = NULL;
-
-                r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value);
-                if (r < 0)
-                        return false;
-                if (r == 0)
-                        return (wanted = false);
+        r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
+        if (r < 0)
+                return false;
 
-                return (wanted = parse_boolean(value) > 0);
-        }
+        return (wanted = r > 0 ? b : false);
 }
 
 bool cg_is_legacy_wanted(void) {
         return !cg_is_unified_wanted();
 }
-#else
-bool cg_is_legacy_wanted(void) {
-        return true;
 
 bool cg_is_unified_systemd_controller_wanted(void) {
         static thread_local int wanted = -1;
-        int r, unified;
+        int r;
+        bool b;
 
         /* If the unified hierarchy is requested in full, no need to
          * bother with this. */
@@ -2409,9 +2419,8 @@ bool cg_is_unified_systemd_controller_wanted(void) {
 
         /* If the hierarchy is already mounted, then follow whatever
          * was chosen for it. */
-        unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
-        if (unified >= 0)
-                return unified;
+        if (cg_unified_flush() >= 0)
+                return cg_unified(SYSTEMD_CGROUP_CONTROLLER);
 
         /* Otherwise, let's see what the kernel command line has to
          * say. Since checking that is expensive, let's cache the
@@ -2419,23 +2428,17 @@ bool cg_is_unified_systemd_controller_wanted(void) {
         if (wanted >= 0)
                 return wanted;
 
-        r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL);
-        if (r > 0) {
-                wanted = false;
-        } else {
-                _cleanup_free_ char *value = NULL;
-
-                r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
-                if (r < 0)
-                        return true;
-
-                if (r == 0)
-                        wanted = true;
-                else
-                        wanted = parse_boolean(value) <= 0;
-        }
+        r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
+        if (r < 0)
+                return false;
 
-        return wanted;
+#else
+bool cg_is_legacy_wanted(void) {
+        return true;
+        /* The meaning of the kernel option is reversed wrt. to the return value
+         * of this function, hence the negation. */
+        return (wanted = r > 0 ? !b : false);
+        return (wanted = r > 0 ? b : false);
 }
 
 bool cg_is_legacy_systemd_controller_wanted(void) {
@@ -2521,6 +2524,20 @@ int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
 }
 #endif // 0
 
+bool is_cgroup_fs(const struct statfs *s) {
+        return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
+               is_fs_type(s, CGROUP2_SUPER_MAGIC);
+}
+
+bool fd_is_cgroup_fs(int fd) {
+        struct statfs s;
+
+        if (fstatfs(fd, &s) < 0)
+                return -errno;
+
+        return is_cgroup_fs(&s);
+}
+
 static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
         [CGROUP_CONTROLLER_CPU] = "cpu",
         [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",