]> git.ipfire.org Git - pakfire.git/commitdiff
cgroup: Add controllers
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 8 Aug 2022 14:47:01 +0000 (14:47 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 8 Aug 2022 14:47:01 +0000 (14:47 +0000)
This is really not working well so far. But I thought it would be better
to commit this and work on it than creating another monster commit.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/cgroup.c
src/libpakfire/include/pakfire/cgroup.h
src/libpakfire/jail.c

index 0dccef797ba73f3fa5e1a81ecfc57c0425496d60..bad6476c209acf66c368bcbf98e96f7ba3e82b78 100644 (file)
 #define ROOT                           "/sys/fs/cgroup"
 #define BUFFER_SIZE                    64 * 1024
 
+enum pakfire_cgroup_controllers {
+       PAKFIRE_CGROUP_CONTROLLER_CPU    = (1 << 0),
+       PAKFIRE_CGROUP_CONTROLLER_MEMORY = (1 << 1),
+       PAKFIRE_CGROUP_CONTROLLER_PIDS   = (1 << 2),
+       PAKFIRE_CGROUP_CONTROLLER_IO     = (1 << 3),
+};
+
+static const enum pakfire_cgroup_controllers pakfire_cgroup_accounting_controllers =
+       PAKFIRE_CGROUP_CONTROLLER_CPU |
+       PAKFIRE_CGROUP_CONTROLLER_MEMORY |
+       PAKFIRE_CGROUP_CONTROLLER_PIDS |
+       PAKFIRE_CGROUP_CONTROLLER_IO;
+
 struct pakfire_cgroup {
        struct pakfire* pakfire;
        int nrefs;
 
+       // Flags
+       int flags;
+
        // Store the path
        char path[PATH_MAX];
 
@@ -47,6 +63,10 @@ static int pakfire_cgroup_is_root(struct pakfire_cgroup* cgroup) {
        return !*cgroup->path;
 }
 
+static int pakfire_cgroup_has_flag(struct pakfire_cgroup* cgroup, int flag) {
+       return cgroup->flags & flag;
+}
+
 static const char* pakfire_cgroup_name(struct pakfire_cgroup* cgroup) {
        if (pakfire_cgroup_is_root(cgroup))
                return "(root)";
@@ -54,6 +74,76 @@ static const char* pakfire_cgroup_name(struct pakfire_cgroup* cgroup) {
        return cgroup->path;
 }
 
+static const char* pakfire_cgroup_controller_name(
+               enum pakfire_cgroup_controllers controller) {
+       switch (controller) {
+               case PAKFIRE_CGROUP_CONTROLLER_CPU:
+                       return "cpu";
+
+               case PAKFIRE_CGROUP_CONTROLLER_MEMORY:
+                       return "memory";
+
+               case PAKFIRE_CGROUP_CONTROLLER_PIDS:
+                       return "pids";
+
+               case PAKFIRE_CGROUP_CONTROLLER_IO:
+                       return "io";
+       }
+
+       return NULL;
+}
+
+static enum pakfire_cgroup_controllers pakfire_cgroup_find_controller_by_name(
+               const char* name) {
+       const char* n = NULL;
+
+       // Walk through the bitmap
+       for (unsigned int i = 1; i; i <<= 1) {
+               n = pakfire_cgroup_controller_name(i);
+               if (!n)
+                       break;
+
+               // Match
+               if (strcmp(name, n) == 0)
+                       return i;
+       }
+
+       // Nothing found
+       return 0;
+}
+
+static struct pakfire_cgroup* pakfire_cgroup_parent(struct pakfire_cgroup* cgroup) {
+       struct pakfire_cgroup* parent = NULL;
+       int r;
+
+       // Cannot return parent for root group
+       if (pakfire_cgroup_is_root(cgroup))
+               return NULL;
+
+       // Determine the path of the parent
+       char* path = pakfire_dirname(cgroup->path);
+       if (!path) {
+               ERROR(cgroup->pakfire, "Could not determine path for parent cgroup: %m\n");
+               return NULL;
+       }
+
+       // dirname() returns . if no directory component could be found
+       if (strcmp(path, ".") == 0)
+               *path = '\0';
+
+       // Open the cgroup
+       r = pakfire_cgroup_open(&parent, cgroup->pakfire, path, 0);
+       if (r) {
+               ERROR(cgroup->pakfire, "Could not open parent cgroup: %m\n");
+               parent = NULL;
+       }
+
+       // Cleanup
+       free(path);
+
+       return parent;
+}
+
 static void pakfire_cgroup_free(struct pakfire_cgroup* cgroup) {
        DEBUG(cgroup->pakfire, "Releasing cgroup %s at %p\n",
                pakfire_cgroup_name(cgroup), cgroup);
@@ -132,14 +222,14 @@ ERROR:
        return fd;
 }
 
-static int pakfire_cgroup_read(struct pakfire_cgroup* cgroup, const char* path,
+static ssize_t pakfire_cgroup_read(struct pakfire_cgroup* cgroup, const char* path,
                char* buffer, size_t length) {
-       int r = -1;
+       ssize_t bytes_read = -1;
 
        // Check if this cgroup has been destroyed already
        if (!cgroup->fd) {
                ERROR(cgroup->pakfire, "Trying to read from destroyed cgroup\n");
-               return r;
+               return -1;
        }
 
        // Open the file
@@ -151,21 +241,22 @@ static int pakfire_cgroup_read(struct pakfire_cgroup* cgroup, const char* path,
        }
 
        // Read file content into buffer
-       ssize_t bytes_read = read(fd, buffer, length);
-       if (bytes_read <= 0) {
+       bytes_read = read(fd, buffer, length);
+       if (bytes_read < 0) {
                DEBUG(cgroup->pakfire, "Could not read from %s/%s: %m\n",
                        pakfire_cgroup_name(cgroup), path);
                goto ERROR;
        }
 
-       // Return how many bytes we have read
-       r = bytes_read;
+       // Terminate the buffer
+       if (bytes_read < length)
+               buffer[bytes_read] = '\0';
 
 ERROR:
        if (fd > 0)
                close(fd);
 
-       return r;
+       return bytes_read;
 }
 
 static int pakfire_cgroup_write(struct pakfire_cgroup* cgroup,
@@ -206,36 +297,136 @@ static int pakfire_cgroup_write(struct pakfire_cgroup* cgroup,
        return r;
 }
 
-static struct pakfire_cgroup* pakfire_cgroup_parent(struct pakfire_cgroup* cgroup) {
-       struct pakfire_cgroup* parent = NULL;
+static int pakfire_cgroup_read_controllers(
+               struct pakfire_cgroup* cgroup, const char* name) {
+       char buffer[BUFFER_SIZE];
+       char* p = NULL;
        int r;
 
-       // Cannot return parent for root group
-       if (pakfire_cgroup_is_root(cgroup))
-               return NULL;
+       // Discovered controllers
+       int controllers = 0;
 
-       // Determine the path of the parent
-       char* path = pakfire_dirname(cgroup->path);
-       if (!path) {
-               ERROR(cgroup->pakfire, "Could not determine path for parent cgroup: %m\n");
-               return NULL;
+       // Read cgroup.controllers file
+       ssize_t bytes_read = pakfire_cgroup_read(cgroup, name, buffer, sizeof(buffer));
+       if (bytes_read < 0)
+               return -1;
+
+       // If the file was empty, there is nothing more to do
+       if (bytes_read == 0)
+               return 0;
+
+       char* token = strtok_r(buffer, " \n", &p);
+
+       while (token) {
+               DEBUG(cgroup->pakfire, "Found controller '%s'\n", token);
+
+               // Try finding this controller
+               int controller = pakfire_cgroup_find_controller_by_name(token);
+               if (controller)
+                       controllers |= controller;
+
+               // Move on to next token
+               token = strtok_r(NULL, " \n", &p);
        }
 
-       // dirname() returns . if no directory component could be found
-       if (strcmp(path, ".") == 0)
-               *path = '\0';
+       // Return discovered controllers
+       return controllers;
+}
 
-       // Open the cgroup
-       r = pakfire_cgroup_open(&parent, cgroup->pakfire, path);
-       if (r) {
-               ERROR(cgroup->pakfire, "Could not open parent cgroup: %m\n");
-               parent = NULL;
+/*
+       Returns a bitmap of all available controllers
+*/
+static int pakfire_cgroup_available_controllers(struct pakfire_cgroup* cgroup) {
+       return pakfire_cgroup_read_controllers(cgroup, "cgroup.controllers");
+}
+
+/*
+       Returns a bitmap of all enabled controllers
+*/
+static int pakfire_cgroup_enabled_controllers(struct pakfire_cgroup* cgroup) {
+       return pakfire_cgroup_read_controllers(cgroup, "cgroup.subtree_control");
+}
+
+/*
+       This function takes a bitmap of controllers that should be enabled.
+*/
+static int pakfire_cgroup_enable_controllers(struct pakfire_cgroup* cgroup,
+               enum pakfire_cgroup_controllers controllers) {
+       struct pakfire_cgroup* parent = NULL;
+       int r = 1;
+
+       // Find all enabled controllers
+       const int enabled_controllers = pakfire_cgroup_enabled_controllers(cgroup);
+       if (enabled_controllers < 0) {
+               ERROR(cgroup->pakfire, "Could not fetch enabled controllers: %m\n");
+               goto ERROR;
        }
 
-       // Cleanup
-       free(path);
+       // Filter out anything that is already enabled
+       controllers = (controllers & ~enabled_controllers);
 
-       return parent;
+       // Exit if everything is already enabled
+       if (!controllers) {
+               DEBUG(cgroup->pakfire, "All controllers are already enabled\n");
+               return 0;
+       }
+
+       // Find all available controllers
+       const int available_controllers = pakfire_cgroup_available_controllers(cgroup);
+       if (available_controllers < 0) {
+               ERROR(cgroup->pakfire, "Could not fetch available controllers: %m\n");
+               goto ERROR;
+       }
+
+       // Are all controllers we need available, yet?
+       if (controllers & ~available_controllers) {
+               DEBUG(cgroup->pakfire, "Not all controllers are available, yet\n");
+
+               parent = pakfire_cgroup_parent(cgroup);
+
+               // Enable everything we need on the parent group
+               if (parent) {
+                       r = pakfire_cgroup_enable_controllers(parent, controllers);
+                       if (r)
+                               goto ERROR;
+               }
+       }
+
+       // Determine how many iterations we will need
+       const int iterations = 1 << (sizeof(controllers) * 8 - __builtin_clz(controllers));
+
+       // Iterate over all known controllers
+       for (int controller = 1; controller < iterations; controller <<= 1) {
+               // Skip enabling this controller if not requested
+               if (!(controller & controllers))
+                       continue;
+
+               // Fetch name
+               const char* name = pakfire_cgroup_controller_name(controller);
+
+               DEBUG(cgroup->pakfire, "Enabling controller %s in cgroup %s\n",
+                       name, pakfire_cgroup_name(cgroup));
+
+               // Try enabling the controller (this will succeed if it already is enabled)
+               r = pakfire_cgroup_write(cgroup, "cgroup.subtree_control", "+%s\n", name);
+               if (r) {
+                       ERROR(cgroup->pakfire, "Could not enable controller %s in cgroup %s\n",
+                               name, pakfire_cgroup_name(cgroup));
+                       goto ERROR;
+               }
+       }
+
+ERROR:
+       if (parent)
+               pakfire_cgroup_unref(parent);
+
+       return r;
+}
+
+static int pakfire_cgroup_enable_accounting(struct pakfire_cgroup* cgroup) {
+       // Enable all accounting controllers
+       return pakfire_cgroup_enable_controllers(cgroup,
+               pakfire_cgroup_accounting_controllers);
 }
 
 /*
@@ -244,7 +435,7 @@ static struct pakfire_cgroup* pakfire_cgroup_parent(struct pakfire_cgroup* cgrou
        If the cgroup doesn't exist, it will be created including any parent cgroups.
 */
 int pakfire_cgroup_open(struct pakfire_cgroup** cgroup,
-               struct pakfire* pakfire, const char* path) {
+               struct pakfire* pakfire, const char* path, int flags) {
        int r = 1;
 
        // Allocate the cgroup struct
@@ -263,11 +454,21 @@ int pakfire_cgroup_open(struct pakfire_cgroup** cgroup,
        // Copy path
        pakfire_string_set(c->path, path);
 
+       // Copy flags
+       c->flags = flags;
+
        // Open a file descriptor
        c->fd = __pakfire_cgroup_open(c);
        if (c->fd < 0)
                goto ERROR;
 
+       // Enable accounting if requested
+       if (pakfire_cgroup_has_flag(c, PAKFIRE_CGROUP_ENABLE_ACCOUNTING)) {
+               r = pakfire_cgroup_enable_accounting(c);
+               if (r)
+                       goto ERROR;
+       }
+
        *cgroup = c;
        return 0;
 
index a9d3914b18c158b1832bbd85cff146b132ccf8dd..07a726d5a916befc4c109f41f3bfe24d7e90cbc5 100644 (file)
 
 struct pakfire_cgroup;
 
+enum pakfire_cgroup_flags {
+       PAKFIRE_CGROUP_ENABLE_ACCOUNTING = (1 << 0),
+};
+
 int pakfire_cgroup_open(struct pakfire_cgroup** cgroup,
-       struct pakfire* pakfire, const char* path);
+       struct pakfire* pakfire, const char* path, int flags);
 
 struct pakfire_cgroup* pakfire_cgroup_ref(struct pakfire_cgroup* cgroup);
 struct pakfire_cgroup* pakfire_cgroup_unref(struct pakfire_cgroup* cgroup);
 
+int pakfire_cgroup_enable_default_controllers(struct pakfire_cgroup* cgroup);
+
 int pakfire_cgroup_destroy(struct pakfire_cgroup* cgroup);
 
 int pakfire_cgroup_fd(struct pakfire_cgroup* cgroup);
index ce56c5769c4377d8fbd0faa0a05e6d3da7a91352..f03a4f2d0ce5da348c17cfadac9586c30fd14877 100644 (file)
@@ -1279,7 +1279,8 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[]) {
 #endif /* ENABLE_DEBUG */
 
        // Setup a cgroup
-       r = pakfire_cgroup_open(&ctx.cgroup, jail->pakfire, "jail");
+       r = pakfire_cgroup_open(&ctx.cgroup, jail->pakfire, "jail/test1",
+               PAKFIRE_CGROUP_ENABLE_ACCOUNTING);
        if (r)
                goto ERROR;