aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/libghostty-vt/src/apprt/gtk/pre_exec.zig
blob: a4eda462004acff4e237e9bbda1be673033fb7d2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
const std = @import("std");

const log = std.log.scoped(.gtk_pre_exec);

const configpkg = @import("../../config.zig");
const global = @import("../../global.zig");

const internal_os = @import("../../os/main.zig");
const Command = @import("../../Command.zig");
const cgroup = @import("./cgroup.zig");

pub const PreExecInfo = struct {
    gtk_single_instance: configpkg.Config.GtkSingleInstance,
    linux_cgroup: configpkg.Config.LinuxCgroup,
    linux_cgroup_hard_fail: bool,

    pub fn init(cfg: *const configpkg.Config) PreExecInfo {
        return .{
            .gtk_single_instance = cfg.@"gtk-single-instance",
            .linux_cgroup = cfg.@"linux-cgroup",
            .linux_cgroup_hard_fail = cfg.@"linux-cgroup-hard-fail",
        };
    }
};

/// If we are expecting to be moved to a transient systemd scope, wait to see if
/// that happens by checking for the correct name of the current cgroup. Wait at
/// most 250ms so that we don't overly delay the soft-fail scenario.
///
/// If we are configured to hard fail, log an error message and return an error
/// code if we don't detect the move in time.
pub fn preExec(cmd: *Command) ?u8 {
    switch (cmd.rt_pre_exec_info.linux_cgroup) {
        .always => {},
        .never => return null,
        .@"single-instance" => switch (cmd.rt_pre_exec_info.gtk_single_instance) {
            .true => {},
            .false => return null,
            .detect => {
                log.err("gtk-single-instance is set to detect", .{});
                return 127;
            },
        },
    }

    const pid: u32 = @intCast(std.os.linux.getpid());

    var expected_cgroup_buf: [256]u8 = undefined;
    const expected_cgroup = cgroup.fmtScope(&expected_cgroup_buf, pid);

    const start: std.Io.Timestamp = .now(global.io(), .awake);

    while (true) {
        if (start.untilNow(global.io(), .awake).toMilliseconds() > 250) {
            if (cmd.rt_pre_exec_info.linux_cgroup_hard_fail) {
                log.err("transition to new transient systemd scope took too long", .{});
                return 127;
            }
            break;
        }

        not_found: {
            var current_cgroup_buf: [4096]u8 = undefined;

            const current_cgroup_raw = internal_os.cgroup.current(
                &current_cgroup_buf,
                @intCast(pid),
            ) orelse break :not_found;

            const index = std.mem.lastIndexOfScalar(u8, current_cgroup_raw, '/') orelse break :not_found;
            const current_cgroup = current_cgroup_raw[index + 1 ..];

            if (std.mem.eql(u8, current_cgroup, expected_cgroup)) return null;
        }

        std.Io.sleep(global.io(), .fromMilliseconds(25), .awake) catch unreachable;
    }

    return null;
}