mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
apprt/gtk: cgroup initialization
This commit is contained in:
@ -23,6 +23,7 @@ const CoreSurface = @import("../../Surface.zig");
|
|||||||
|
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
|
const cgroup = @import("cgroup.zig");
|
||||||
const Surface = @import("Surface.zig");
|
const Surface = @import("Surface.zig");
|
||||||
const Window = @import("Window.zig");
|
const Window = @import("Window.zig");
|
||||||
const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig");
|
const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig");
|
||||||
@ -373,9 +374,27 @@ pub fn wakeup(self: App) void {
|
|||||||
|
|
||||||
/// Run the event loop. This doesn't return until the app exits.
|
/// Run the event loop. This doesn't return until the app exits.
|
||||||
pub fn run(self: *App) !void {
|
pub fn run(self: *App) !void {
|
||||||
|
// Running will be false when we're not the primary instance and should
|
||||||
|
// exit (GTK single instance mode). If we're not running, we're done
|
||||||
|
// right away.
|
||||||
if (!self.running) return;
|
if (!self.running) return;
|
||||||
|
|
||||||
// If we're not remote, then we also setup our actions and menus.
|
// If we are running, then we proceed to setup our app.
|
||||||
|
|
||||||
|
// Setup our cgroup configurations for our tabs.
|
||||||
|
if (cgroup.init(self)) |cgroup_path| {
|
||||||
|
self.core_app.alloc.free(cgroup_path);
|
||||||
|
} else |err| {
|
||||||
|
// If we can't initialize cgroups then that's okay. We
|
||||||
|
// want to continue to run so we just won't isolate surfaces.
|
||||||
|
// NOTE(mitchellh): do we want a config to force it?
|
||||||
|
log.warn(
|
||||||
|
"failed to initialize cgroups, terminals will not be isolated err={}",
|
||||||
|
.{err},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup our menu items
|
||||||
self.initActions();
|
self.initActions();
|
||||||
self.initMenu();
|
self.initMenu();
|
||||||
|
|
||||||
|
129
src/apprt/gtk/cgroup.zig
Normal file
129
src/apprt/gtk/cgroup.zig
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/// Contains all the logic for putting the Ghostty process and
|
||||||
|
/// each individual surface into its own cgroup.
|
||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const c = @import("c.zig");
|
||||||
|
const App = @import("App.zig");
|
||||||
|
const internal_os = @import("../../os/main.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.gtk_systemd_cgroup);
|
||||||
|
|
||||||
|
/// Initialize the cgroup for the app. This will create our
|
||||||
|
/// transient scope, initialize the cgroups we use for the app,
|
||||||
|
/// configure them, and return the cgroup path for the app.
|
||||||
|
pub fn init(app: *App) ![]const u8 {
|
||||||
|
const pid = std.os.linux.getpid();
|
||||||
|
const alloc = app.core_app.alloc;
|
||||||
|
const connection = c.g_application_get_dbus_connection(@ptrCast(app.app)) orelse
|
||||||
|
return error.DbusConnectionRequired;
|
||||||
|
|
||||||
|
// Get our initial cgroup. We need this so we can compare
|
||||||
|
// and detect when we've switched to our transient group.
|
||||||
|
const original = try internal_os.linux.cgroupPath(
|
||||||
|
alloc,
|
||||||
|
pid,
|
||||||
|
) orelse "";
|
||||||
|
defer alloc.free(original);
|
||||||
|
|
||||||
|
// Create our transient scope. If this succeeds then the unit
|
||||||
|
// was created, but we may not have moved into it yet, so we need
|
||||||
|
// to do a dumb busy loop to wait for the move to complete.
|
||||||
|
try createScope(connection);
|
||||||
|
const transient = transient: while (true) {
|
||||||
|
const current = try internal_os.linux.cgroupPath(
|
||||||
|
alloc,
|
||||||
|
pid,
|
||||||
|
) orelse "";
|
||||||
|
if (!std.mem.eql(u8, original, current)) break :transient current;
|
||||||
|
std.time.sleep(25 * std.time.ns_per_ms);
|
||||||
|
};
|
||||||
|
errdefer alloc.free(transient);
|
||||||
|
log.info("transient scope created cgroup={s}", .{transient});
|
||||||
|
|
||||||
|
return transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a transient systemd scope unit for the current process.
|
||||||
|
///
|
||||||
|
/// On success this will return the name of the transient scope
|
||||||
|
/// cgroup prefix, allocated with the given allocator.
|
||||||
|
fn createScope(connection: *c.GDBusConnection) !void {
|
||||||
|
// Our pid that we will move into the cgroup
|
||||||
|
const pid: c.guint32 = @intCast(std.os.linux.getpid());
|
||||||
|
|
||||||
|
// The unit name needs to be unique. We use the pid for this.
|
||||||
|
var name_buf: [256]u8 = undefined;
|
||||||
|
const name = std.fmt.bufPrintZ(
|
||||||
|
&name_buf,
|
||||||
|
"app-ghostty-transient-{}.scope",
|
||||||
|
.{pid},
|
||||||
|
) catch unreachable;
|
||||||
|
|
||||||
|
// Initialize our builder to build up our parameters
|
||||||
|
var builder: c.GVariantBuilder = undefined;
|
||||||
|
c.g_variant_builder_init(&builder, c.G_VARIANT_TYPE("(ssa(sv)a(sa(sv)))"));
|
||||||
|
c.g_variant_builder_add(&builder, "s", name.ptr);
|
||||||
|
c.g_variant_builder_add(&builder, "s", "fail");
|
||||||
|
{
|
||||||
|
// Properties
|
||||||
|
c.g_variant_builder_open(&builder, c.G_VARIANT_TYPE("a(sv)"));
|
||||||
|
defer c.g_variant_builder_close(&builder);
|
||||||
|
|
||||||
|
// https://www.freedesktop.org/software/systemd/man/latest/systemd-oomd.service.html
|
||||||
|
c.g_variant_builder_add(
|
||||||
|
&builder,
|
||||||
|
"(sv)",
|
||||||
|
"ManagedOOMMemoryPressure",
|
||||||
|
c.g_variant_new_string("kill"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delegate
|
||||||
|
c.g_variant_builder_add(
|
||||||
|
&builder,
|
||||||
|
"(sv)",
|
||||||
|
"Delegate",
|
||||||
|
c.g_variant_new_boolean(1),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pid to move into the unit
|
||||||
|
c.g_variant_builder_add(
|
||||||
|
&builder,
|
||||||
|
"(sv)",
|
||||||
|
"PIDs",
|
||||||
|
c.g_variant_new_fixed_array(
|
||||||
|
c.G_VARIANT_TYPE("u"),
|
||||||
|
&pid,
|
||||||
|
1,
|
||||||
|
@sizeOf(c.guint32),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Aux
|
||||||
|
c.g_variant_builder_open(&builder, c.G_VARIANT_TYPE("a(sa(sv))"));
|
||||||
|
defer c.g_variant_builder_close(&builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
var err: ?*c.GError = null;
|
||||||
|
defer if (err) |e| c.g_error_free(e);
|
||||||
|
_ = c.g_dbus_connection_call_sync(
|
||||||
|
connection,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1",
|
||||||
|
"org.freedesktop.systemd1.Manager",
|
||||||
|
"StartTransientUnit",
|
||||||
|
c.g_variant_builder_end(&builder),
|
||||||
|
c.G_VARIANT_TYPE("(o)"),
|
||||||
|
c.G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
null,
|
||||||
|
&err,
|
||||||
|
) orelse {
|
||||||
|
if (err) |e| log.err(
|
||||||
|
"creating transient cgroup scope failed err={s}",
|
||||||
|
.{e.message},
|
||||||
|
);
|
||||||
|
return error.DbusCallFailed;
|
||||||
|
};
|
||||||
|
}
|
Reference in New Issue
Block a user