cgroup-util: add helpers for opening cgroup by id
authorLennart Poettering <lennart@poettering.net>
Tue, 12 Dec 2023 09:44:57 +0000 (10:44 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 6 Apr 2024 14:08:23 +0000 (16:08 +0200)
src/basic/cgroup-util.c
src/basic/cgroup-util.h
src/basic/missing_fs.h
src/shared/cgroup-show.c
src/test/test-cgroup.c

index 6991460f86e65a0b7b9a30293f6c71bba8025da6..8303b376843e755fa0994a59d4fd61b6e0c2eba4 100644 (file)
@@ -22,6 +22,7 @@
 #include "log.h"
 #include "login-util.h"
 #include "macro.h"
+#include "missing_fs.h"
 #include "missing_magic.h"
 #include "missing_threads.h"
 #include "mkdir.h"
 #include "user-util.h"
 #include "xattr-util.h"
 
+int cg_path_open(const char *controller, const char *path) {
+        _cleanup_free_ char *fs = NULL;
+        int r;
+
+        r = cg_get_path(controller, path, /* item=*/ NULL, &fs);
+        if (r < 0)
+                return r;
+
+        return RET_NERRNO(open(fs, O_DIRECTORY|O_CLOEXEC));
+}
+
+int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) {
+        _cleanup_close_ int fsfd = -EBADF;
+
+        if (cgroupfs_fd < 0) {
+                fsfd = open("/sys/fs/cgroup", O_CLOEXEC|O_DIRECTORY);
+                if (fsfd < 0)
+                        return -errno;
+
+                cgroupfs_fd = fsfd;
+        }
+
+        cg_file_handle fh = CG_FILE_HANDLE_INIT;
+        CG_FILE_HANDLE_CGROUPID(fh) = id;
+
+        int fd = open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
 static int cg_enumerate_items(const char *controller, const char *path, FILE **ret, const char *item) {
         _cleanup_free_ char *fs = NULL;
         FILE *f;
@@ -1404,7 +1437,7 @@ int cg_pid_get_machine_name(pid_t pid, char **ret_machine) {
 
 int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
         cg_file_handle fh = CG_FILE_HANDLE_INIT;
-        int mnt_id = -1;
+        int mnt_id;
 
         assert(path);
         assert(ret);
@@ -1418,6 +1451,20 @@ int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
         return 0;
 }
 
+int cg_fd_get_cgroupid(int fd, uint64_t *ret) {
+        cg_file_handle fh = CG_FILE_HANDLE_INIT;
+        int mnt_id = -1;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH) < 0)
+                return -errno;
+
+        *ret = CG_FILE_HANDLE_CGROUPID(fh);
+        return 0;
+}
+
 int cg_path_get_session(const char *path, char **ret_session) {
         _cleanup_free_ char *unit = NULL;
         char *start, *end;
index 244f3b657bf2e55fe20949978c6351796ddd5178..9ad6dd8eb6ce52e232ef670fbffea4760ddacb16 100644 (file)
@@ -180,6 +180,9 @@ typedef enum CGroupUnified {
  * generate paths with multiple adjacent / removed.
  */
 
+int cg_path_open(const char *controller, const char *path);
+int cg_cgroupid_open(int fsfd, uint64_t id);
+
 int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
 int cg_read_pid(FILE *f, pid_t *ret);
 int cg_read_pidref(FILE *f, PidRef *ret);
@@ -267,6 +270,7 @@ int cg_is_empty_recursive(const char *controller, const char *path);
 int cg_get_root_path(char **path);
 
 int cg_path_get_cgroupid(const char *path, uint64_t *ret);
+int cg_fd_get_cgroupid(int fd, uint64_t *ret);
 int cg_path_get_session(const char *path, char **ret_session);
 int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
 int cg_path_get_unit(const char *path, char **ret_unit);
@@ -352,5 +356,10 @@ typedef union {
         uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
 } cg_file_handle;
 
-#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) }
+#define CG_FILE_HANDLE_INIT                                     \
+        (cg_file_handle) {                                      \
+                .file_handle.handle_bytes = sizeof(uint64_t),   \
+                .file_handle.handle_type = FILEID_KERNFS,       \
+        }
+
 #define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)
index ee70ae08dbcc4c02143976842267fd8dba57217c..d97b1901316b6d2e58d1240cd16d762b518d0ea7 100644 (file)
@@ -111,3 +111,8 @@ assert_cc(FS_PROJINHERIT_FL == 0x20000000);
 #else
 assert_cc(FS_KEY_DESCRIPTOR_SIZE == 8);
 #endif
+
+/* linux/exportfs.h */
+#ifndef FILEID_KERNFS
+#define FILEID_KERNFS 0xfe
+#endif
index c2ee1c5aef6c7974af89d65c6d8a0ec6c196db47..c7af10687df13b43c71240a06999c73d30e05dd6 100644 (file)
@@ -150,18 +150,9 @@ static int show_cgroup_name(
         delegate = r > 0;
 
         if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) {
-                cg_file_handle fh = CG_FILE_HANDLE_INIT;
-                int mnt_id = -1;
-
-                if (name_to_handle_at(
-                                    fd,
-                                    "",
-                                    &fh.file_handle,
-                                    &mnt_id,
-                                    AT_EMPTY_PATH) < 0)
+                r = cg_fd_get_cgroupid(fd, &cgroupid);
+                if (r < 0)
                         log_debug_errno(errno, "Failed to determine cgroup ID of %s, ignoring: %m", path);
-                else
-                        cgroupid = CG_FILE_HANDLE_CGROUPID(fh);
         }
 
         r = path_extract_filename(path, &b);
index 9b174d708b562b572793202cf54f7a39743045f4..06cf886729654934ac5021a50b47faccdd749fca 100644 (file)
@@ -5,6 +5,7 @@
 #include "cgroup-setup.h"
 #include "cgroup-util.h"
 #include "errno-util.h"
+#include "fd-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "string-util.h"
@@ -129,4 +130,47 @@ TEST(cg_create) {
         assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0);
 }
 
+TEST(id) {
+        _cleanup_free_ char *p = NULL, *p2 = NULL;
+        _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
+        uint64_t id, id2;
+        int r;
+
+        r = cg_all_unified();
+        if (r == 0) {
+                log_tests_skipped("skipping cgroupid test, not running in unified mode");
+                return;
+        }
+        if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+                log_tests_skipped("cgroupfs is not mounted");
+                return;
+        }
+        assert_se(r > 0);
+
+        fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
+        assert_se(fd >= 0);
+
+        assert_se(fd_get_path(fd, &p) >= 0);
+        assert_se(path_equal(p, "/sys/fs/cgroup"));
+
+        assert_se(cg_fd_get_cgroupid(fd, &id) >= 0);
+
+        fd2 = cg_cgroupid_open(fd, id);
+
+        if (ERRNO_IS_NEG_PRIVILEGE(fd2))
+                log_notice("Skipping open-by-cgroup-id test because lacking privs.");
+        else {
+                assert_se(fd2 >= 0);
+
+                assert_se(fd_get_path(fd2, &p2) >= 0);
+                assert_se(path_equal(p2, "/sys/fs/cgroup"));
+
+                assert_se(cg_fd_get_cgroupid(fd2, &id2) >= 0);
+
+                assert_se(id == id2);
+
+                assert_se(inode_same_at(fd, NULL, fd2, NULL, AT_EMPTY_PATH) > 0);
+        }
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);