From c0b061edd915a009fa898cba5618c7603a4a81d7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 4 Jun 2024 19:23:18 -0700 Subject: [PATCH] os: API for listing cgroup controllers --- src/apprt/gtk/cgroup.zig | 27 +++++++++++++++++++++++++++ src/os/linux.zig | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/apprt/gtk/cgroup.zig b/src/apprt/gtk/cgroup.zig index 861b1e9cb..15673159f 100644 --- a/src/apprt/gtk/cgroup.zig +++ b/src/apprt/gtk/cgroup.zig @@ -41,9 +41,36 @@ pub fn init(app: *App) ![]const u8 { errdefer alloc.free(transient); log.info("transient scope created cgroup={s}", .{transient}); + // Enable all of our cgroup controllers. If these fail then + // we just log. We can't reasonably undo what we've done above + // so we log the warning and still return the transient group. + // I don't know a scenario where this fails yet. + try enableControllers(alloc, transient); + return transient; } +/// Enable all the cgroup controllers for the given cgroup. +fn enableControllers(alloc: Allocator, cgroup: []const u8) !void { + const raw = try internal_os.linux.cgroupControllers(alloc, cgroup); + defer alloc.free(raw); + + // Build our string builder for enabling all controllers + var builder = std.ArrayList(u8).init(alloc); + defer builder.deinit(); + + // Controllers are space-separated + var it = std.mem.splitScalar(u8, raw, ' '); + while (it.next()) |controller| { + try builder.append('+'); + try builder.appendSlice(controller); + if (it.rest().len > 0) try builder.append(' '); + } + + // TODO + log.warn("enabling controllers={s}", .{builder.items}); +} + /// Create a transient systemd scope unit for the current process. /// /// On success this will return the name of the transient scope diff --git a/src/os/linux.zig b/src/os/linux.zig index e399883dc..c9d4d0ba8 100644 --- a/src/os/linux.zig +++ b/src/os/linux.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const assert = std.debug.assert; const Allocator = std.mem.Allocator; /// Returns the path to the cgroup for the given pid. @@ -26,3 +27,37 @@ pub fn cgroupPath(alloc: Allocator, pid: std.os.linux.pid_t) !?[]const u8 { const result = std.mem.trimRight(u8, contents[idx + 1 ..], " \r\n"); return try alloc.dupe(u8, result); } + +/// Returns all available cgroup controllers for the given cgroup. +/// The cgroup should have a '/'-prefix. +/// +/// The returned list of is the raw space-separated list of +/// controllers from the /sys/fs directory. This avoids some extra +/// work since creating an iterator over this is easy and much cheaper +/// than allocating a bunch of copies for an array. +pub fn cgroupControllers(alloc: Allocator, cgroup: []const u8) ![]const u8 { + assert(cgroup[0] == '/'); + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + + // Read the available controllers. These will be space separated. + const path = try std.fmt.bufPrint( + &buf, + "/sys/fs/cgroup{s}/cgroup.controllers", + .{cgroup}, + ); + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + + // Read it all into memory -- we don't expect this file to ever + // be that large. + var buf_reader = std.io.bufferedReader(file.reader()); + const contents = try buf_reader.reader().readAllAlloc( + alloc, + 1 * 1024 * 1024, // 1MB + ); + defer alloc.free(contents); + + // Return our raw list of controllers + const result = std.mem.trimRight(u8, contents, " \r\n"); + return try alloc.dupe(u8, result); +}