mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00

Related to #7433 This extracts our "launched from desktop" logic into a config option. The default value is detection using the same logic as before, but now this can be overridden by the user. This also adds the systemd and dbus activation sources from #7433. There are a number of reasons why we decided to do this: 1. It automatically gets us caching since the configuration is only loaded once (per reload, a rare occurrence). 2. It allows us to override the logic when testing. Previously, we had to do more complex environment faking to get the same behavior. 3. It forces exhaustive switches in any desktop handling code, which will make it easier to ensure valid behaviors if we introduce new launch sources (as we are in #7433). 4. It lowers code complexity since callsites don't need to have N `launchedFromX()` checks and can use a single value.
66 lines
2.9 KiB
Zig
66 lines
2.9 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
|
|
const log = std.log.scoped(.systemd);
|
|
|
|
/// Returns true if the program was launched as a systemd service.
|
|
///
|
|
/// On Linux, this returns true if the program was launched as a systemd
|
|
/// service. It will return false if Ghostty was launched any other way.
|
|
///
|
|
/// For other platforms and app runtimes, this returns false.
|
|
pub fn launchedBySystemd() bool {
|
|
return switch (builtin.os.tag) {
|
|
.linux => linux: {
|
|
// On Linux, systemd sets the `INVOCATION_ID` (v232+) and the
|
|
// `JOURNAL_STREAM` (v231+) environment variables. If these
|
|
// environment variables are not present we were not launched by
|
|
// systemd.
|
|
if (std.posix.getenv("INVOCATION_ID") == null) break :linux false;
|
|
if (std.posix.getenv("JOURNAL_STREAM") == null) break :linux false;
|
|
|
|
// If `INVOCATION_ID` and `JOURNAL_STREAM` are present, check to make sure
|
|
// that our parent process is actually `systemd`, not some other terminal
|
|
// emulator that doesn't clean up those environment variables.
|
|
const ppid = std.os.linux.getppid();
|
|
if (ppid == 1) break :linux true;
|
|
|
|
// If the parent PID is not 1 we need to check to see if we were launched by
|
|
// a user systemd daemon. Do that by checking the `/proc/<ppid>/comm`
|
|
// to see if it ends with `systemd`.
|
|
var comm_path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
|
const comm_path = std.fmt.bufPrint(&comm_path_buf, "/proc/{d}/comm", .{ppid}) catch {
|
|
log.err("unable to format comm path for pid {d}", .{ppid});
|
|
break :linux false;
|
|
};
|
|
const comm_file = std.fs.openFileAbsolute(comm_path, .{ .mode = .read_only }) catch {
|
|
log.err("unable to open '{s}' for reading", .{comm_path});
|
|
break :linux false;
|
|
};
|
|
defer comm_file.close();
|
|
|
|
// The maximum length of the command name is defined by
|
|
// `TASK_COMM_LEN` in the Linux kernel. This is usually 16
|
|
// bytes at the time of writing (Jun 2025) so its set to that.
|
|
// Also, since we only care to compare to "systemd", anything
|
|
// longer can be assumed to not be systemd.
|
|
const TASK_COMM_LEN = 16;
|
|
var comm_data_buf: [TASK_COMM_LEN]u8 = undefined;
|
|
const comm_size = comm_file.readAll(&comm_data_buf) catch {
|
|
log.err("problems reading from '{s}'", .{comm_path});
|
|
break :linux false;
|
|
};
|
|
const comm_data = comm_data_buf[0..comm_size];
|
|
|
|
break :linux std.mem.eql(
|
|
u8,
|
|
std.mem.trimRight(u8, comm_data, "\n"),
|
|
"systemd",
|
|
);
|
|
},
|
|
|
|
// No other system supports systemd so always return false.
|
|
else => false,
|
|
};
|
|
}
|