diff --git a/com.mitchellh.ghostty.yml b/com.mitchellh.ghostty.yml index fc43cbf61..ba86b30e4 100644 --- a/com.mitchellh.ghostty.yml +++ b/com.mitchellh.ghostty.yml @@ -41,6 +41,26 @@ modules: only-arches: - aarch64 + # We use this to get a proper PTY on our host spawn. We should eventually + # replace this with using dbus directly. + - name: host-spawn + buildsystem: simple + build-commands: + - mkdir -p /app/bin + - mv ./host-spawn-* /app/bin/host-spawn + - chmod +x /app/bin/host-spawn + sources: + - type: file + url: https://github.com/1player/host-spawn/releases/download/1.4.1/host-spawn-x86_64 + sha256: d81bb6125ec73a9a2e26266c48787367fbcb665d67c2e7fb42217f0981106cf7 + only-arches: + - x86_64 + - type: file + url: https://github.com/1player/host-spawn/releases/download/1.4.1/host-spawn-aarch64 + sha256: 29bff846d72e37093b3fdf7859bae16addd64acc98087f8c059548df3c2273c4 + only-arches: + - aarch64 + - name: ghostty buildsystem: simple build-commands: diff --git a/src/config.zig b/src/config.zig index 324bc713c..b0e13e1e8 100644 --- a/src/config.zig +++ b/src/config.zig @@ -344,14 +344,18 @@ pub const Config = struct { if (self.command == null or wd_home) command: { const alloc = self._arena.?.allocator(); - // First look up the command using the SHELL env var. - if (std.process.getEnvVarOwned(alloc, "SHELL")) |value| { - log.debug("default shell source=env value={s}", .{value}); - self.command = value; + // We don't do this in flatpak because SHELL in Flatpak is + // always set to /bin/sh + if (!internal_os.isFlatpak()) { + // First look up the command using the SHELL env var. + if (std.process.getEnvVarOwned(alloc, "SHELL")) |value| { + log.debug("default shell source=env value={s}", .{value}); + self.command = value; - // If we don't need the working directory, then we can exit now. - if (!wd_home) break :command; - } else |_| {} + // If we don't need the working directory, then we can exit now. + if (!wd_home) break :command; + } else |_| {} + } // We need the passwd entry for the remainder const pw = try passwd.get(alloc); diff --git a/src/passwd.zig b/src/passwd.zig index 0a97676cb..d44ca8900 100644 --- a/src/passwd.zig +++ b/src/passwd.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; +const internal_os = @import("os/main.zig"); const log = std.log.scoped(.passwd); @@ -45,6 +46,39 @@ pub fn get(alloc: Allocator) !Entry { var result: Entry = .{}; + // If we're in flatpak then our entry is always empty so we grab it + // by shelling out to the host. note that we do HAVE an entry in the + // sandbox but only the username is correct. + // + // Note: we wrap our getent call in a /bin/sh login shell because + // some operating systems (NixOS tested) don't set the PATH for various + // utilities properly until we get a login shell. + if (internal_os.isFlatpak()) { + log.info("flatpak detected, will use host-spawn to get our entry", .{}); + const exec = try std.ChildProcess.exec(.{ + .allocator = alloc, + .argv = &[_][]const u8{ + "/app/bin/host-spawn", + "-pty", + "/bin/sh", + "-l", + "-c", + try std.fmt.allocPrint( + alloc, + "getent passwd {s}", + .{std.mem.sliceTo(pw.pw_name, 0)}, + ), + }, + }); + if (exec.term == .Exited) { + // Shell and home are the last two entries + var it = std.mem.splitBackwards(u8, exec.stdout, ":"); + result.shell = it.next() orelse null; + result.home = it.next() orelse null; + return result; + } + } + if (pw.pw_shell) |ptr| { const source = std.mem.sliceTo(ptr, 0); const sh = try alloc.alloc(u8, source.len);