From dc51b8269c23db663b1150b11e63646ac719b074 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 4 Jun 2024 21:37:34 -0700 Subject: [PATCH] plumb the linux cgroup through to termio --- src/Surface.zig | 10 +++++++++ src/apprt/gtk/Surface.zig | 44 ++++++++++++++++++++++++++++++++++++++- src/termio/Exec.zig | 9 +++++++- src/termio/Options.zig | 8 +++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 39c9b460e..2673b9722 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -404,6 +404,16 @@ pub fn init( .renderer_wakeup = render_thread.wakeup, .renderer_mailbox = render_thread.mailbox, .surface_mailbox = .{ .surface = self, .app = app_mailbox }, + + // Get the cgroup if we're on linux and have the decl. I'd love + // to change this from a decl to a surface options struct because + // then we can do memory management better (don't need to retain + // the string around). + .linux_cgroup = if (comptime builtin.os.tag == .linux and + @hasDecl(apprt.runtime.Surface, "cgroup")) + rt_surface.cgroup() + else + termio.Options.linux_cgroup_default, }); errdefer io.deinit(); diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 794e2eaf4..f3543ed0a 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -11,6 +11,7 @@ const font = @import("../../font/main.zig"); const input = @import("../../input.zig"); const terminal = @import("../../terminal/main.zig"); const CoreSurface = @import("../../Surface.zig"); +const internal_os = @import("../../os/main.zig"); const App = @import("App.zig"); const Split = @import("Split.zig"); @@ -255,6 +256,10 @@ im_commit_buffered: bool = false, im_buf: [128]u8 = undefined, im_len: u7 = 0, +/// The surface-specific cgroup path. See App.transient_cgroup_path for +/// details on what this is. +cgroup_path: ?[]const u8 = null, + pub fn create(alloc: Allocator, app: *App, opts: Options) !*Surface { var surface = try alloc.create(Surface); errdefer alloc.destroy(surface); @@ -342,6 +347,36 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { break :font_size parent.font_size; }; + // If the parent has a transient cgroup, then we're creating cgroups + // for each surface if we can. We need to create a child cgroup. + const cgroup_path: ?[]const u8 = cgroup: { + const base = app.transient_cgroup_base orelse break :cgroup null; + + // For the unique group name we use the self pointer. This may + // not be a good idea for security reasons but not sure yet. We + // may want to change this to something else eventually to be safe. + var buf: [256]u8 = undefined; + const name = std.fmt.bufPrint( + &buf, + "surface({X}).scope", + .{@intFromPtr(self)}, + ) catch unreachable; + + // Create the cgroup. If it fails, no big deal... just ignore. + internal_os.cgroup.create(base, name, null) catch |err| { + log.err("failed to create surface cgroup err={}", .{err}); + break :cgroup null; + }; + + // Success, save the cgroup path. + break :cgroup std.fmt.allocPrint( + app.core_app.alloc, + "{s}/{s}", + .{ base, name }, + ) catch null; + }; + errdefer if (cgroup_path) |path| app.core_app.alloc.free(path); + // Build our result self.* = .{ .app = app, @@ -354,6 +389,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { .size = .{ .width = 800, .height = 600 }, .cursor_pos = .{ .x = 0, .y = 0 }, .im_context = im_context, + .cgroup_path = cgroup_path, }; errdefer self.* = undefined; @@ -442,9 +478,10 @@ pub fn deinit(self: *Surface) void { self.core_surface.deinit(); self.core_surface = undefined; + if (self.cgroup_path) |path| self.app.core_app.alloc.free(path); + // Free all our GTK stuff c.g_object_unref(self.im_context); - if (self.cursor) |cursor| c.g_object_unref(cursor); } @@ -463,6 +500,11 @@ fn render(self: *Surface) !void { try self.core_surface.renderer.drawFrame(self); } +/// Called by core surface to get the cgroup. +pub fn cgroup(self: *const Surface) ?[]const u8 { + return self.cgroup_path; +} + /// Queue the inspector to render if we have one. pub fn queueInspectorRender(self: *Surface) void { if (self.inspector) |v| v.queueRender(); diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 1287600eb..72041cb22 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -179,7 +179,14 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec { term.width_px = subprocess.screen_size.width; term.height_px = subprocess.screen_size.height; - return Exec{ + // TODO: make work + if (comptime builtin.os.tag == .linux) { + if (opts.linux_cgroup) |cgroup| { + log.warn("DESIRED cgroup={s}", .{cgroup}); + } + } + + return .{ .alloc = alloc, .terminal = term, .subprocess = subprocess, diff --git a/src/termio/Options.zig b/src/termio/Options.zig index 1fd9d034a..079828f38 100644 --- a/src/termio/Options.zig +++ b/src/termio/Options.zig @@ -1,5 +1,6 @@ //! The options that are used to configure a terminal IO implementation. +const builtin = @import("builtin"); const xev = @import("xev"); const apprt = @import("../apprt.zig"); const renderer = @import("../renderer.zig"); @@ -41,3 +42,10 @@ renderer_mailbox: *renderer.Thread.Mailbox, /// The mailbox for sending the surface messages. surface_mailbox: apprt.surface.Mailbox, + +/// The cgroup to apply to the started termio process, if able by +/// the termio implementation. This only applies to Linux. +linux_cgroup: LinuxCgroup = linux_cgroup_default, + +pub const LinuxCgroup = if (builtin.os.tag == .linux) ?[]const u8 else void; +pub const linux_cgroup_default = if (LinuxCgroup == void) {} else null;