mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-31 04:39:01 +00:00
fix process and global error handling
Restore the error handling that the removed std.posix fork and waitpid wrappers previously provided. Raw fork failures now propagate, waitpid retries interruptions before reading status, and edit-config constructs the sentinel-terminated argv required by execve. Let global initialization own cleanup through its existing errdefer so temporary paths are freed once. Report initialization failures with the static synchronous I/O provider because global I/O has already been torn down by that point.
This commit is contained in:
@@ -189,7 +189,7 @@ fn startPosix(self: *Command, arena: Allocator) !void {
|
||||
@compileError("missing env vars");
|
||||
|
||||
// Fork.
|
||||
const pid = posix.system.fork();
|
||||
const pid = try fork();
|
||||
|
||||
if (pid != 0) {
|
||||
// Parent, return immediately.
|
||||
@@ -291,6 +291,17 @@ fn startPosix(self: *Command, arena: Allocator) !void {
|
||||
return error.ExecFailedInChild;
|
||||
}
|
||||
|
||||
/// Wrapper for the raw fork syscall. This preserves the error handling from
|
||||
/// the std.posix wrapper that was removed in Zig 0.16.
|
||||
fn fork() !posix.pid_t {
|
||||
const rc = posix.system.fork();
|
||||
switch (posix.errno(rc)) {
|
||||
.SUCCESS => return @intCast(rc),
|
||||
.AGAIN, .NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn startWindows(self: *Command, arena: Allocator) !void {
|
||||
const cwd_w = if (self.cwd) |cwd| try std.unicode.utf8ToUtf16LeAllocZ(arena, cwd) else null;
|
||||
|
||||
@@ -494,10 +505,10 @@ pub fn wait(self: Command, block: bool) !Exit {
|
||||
return .{ .Exited = exit_code };
|
||||
}
|
||||
|
||||
const status = if (block) wait_block: {
|
||||
var status: c_int = undefined;
|
||||
_ = posix.system.waitpid(self.pid.?, &status, 0);
|
||||
break :wait_block status;
|
||||
const status: u32 = if (block) wait_block: {
|
||||
var status: if (builtin.link_libc) c_int else u32 = undefined;
|
||||
_ = try waitPid(self.pid.?, &status, 0);
|
||||
break :wait_block @bitCast(status);
|
||||
} else wait_nohang: {
|
||||
// We specify NOHANG because its not our fault if the process we launch
|
||||
// for the tty doesn't properly waitpid its children. We don't want
|
||||
@@ -507,13 +518,32 @@ pub fn wait(self: Command, block: bool) !Exit {
|
||||
// wait call has not been performed, so we need to keep trying until we get
|
||||
// a non-zero pid back, otherwise we end up with zombie processes.
|
||||
while (true) {
|
||||
var status: c_int = undefined;
|
||||
const pid = posix.system.waitpid(self.pid.?, &status, posix.system.W.NOHANG);
|
||||
if (pid != 0) break :wait_nohang status;
|
||||
var status: if (builtin.link_libc) c_int else u32 = undefined;
|
||||
const pid = try waitPid(self.pid.?, &status, posix.system.W.NOHANG);
|
||||
if (pid != 0) break :wait_nohang @bitCast(status);
|
||||
}
|
||||
};
|
||||
|
||||
return .init(@bitCast(status));
|
||||
return .init(status);
|
||||
}
|
||||
|
||||
/// Wrapper for the raw waitpid syscall. Status is only initialized on success;
|
||||
/// interrupted waits are retried and all other errors are propagated.
|
||||
fn waitPid(
|
||||
pid: posix.pid_t,
|
||||
status: *if (builtin.link_libc) c_int else u32,
|
||||
flags: u32,
|
||||
) !posix.pid_t {
|
||||
while (true) {
|
||||
const rc = posix.system.waitpid(pid, status, @intCast(flags));
|
||||
switch (posix.errno(rc)) {
|
||||
.SUCCESS => return @intCast(rc),
|
||||
.INTR => continue,
|
||||
.CHILD => return error.NoChildProcess,
|
||||
.INVAL => return error.InvalidWaitOptions,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets command->data to data.
|
||||
|
||||
@@ -158,9 +158,13 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||
defer alloc.free(command);
|
||||
|
||||
// Run/replace process (using execve)
|
||||
const argv = &.{ "/bin/sh", "-c", command };
|
||||
const argv = [_:null]?[*:0]const u8{
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
command.ptr,
|
||||
};
|
||||
const envp = std.c.environ;
|
||||
const err = std.posix.errno(std.posix.system.execve(argv[0], @ptrCast(argv), envp));
|
||||
const err = std.posix.errno(std.posix.system.execve(argv[0].?, &argv, envp));
|
||||
|
||||
// If we reached this point then exec failed.
|
||||
try stderr.print(
|
||||
|
||||
@@ -121,7 +121,6 @@ pub fn init(opts: InitOpts) !void {
|
||||
|
||||
// Discover and save the temporary directory path
|
||||
self.tmp_dir_path = try allocTmpDir(self.alloc, self.environ);
|
||||
errdefer freeTmpDir(self.alloc, self.tmp_dir_path.?);
|
||||
|
||||
// We first try to parse any action that we may be executing.
|
||||
// Tool binaries (ghostty-bench, ghostty-gen) have their own action
|
||||
|
||||
@@ -30,7 +30,10 @@ pub fn main(minimal: std.process.Init.Minimal) !MainReturn {
|
||||
// no other Zig code should EVER access the global state.
|
||||
global.init(.{ .main = minimal }) catch |err| {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.Io.File.stderr().writer(global.io(), &buffer);
|
||||
var stderr_writer = std.Io.File.stderr().writer(
|
||||
std.Io.Threaded.global_single_threaded.io(),
|
||||
&buffer,
|
||||
);
|
||||
const stderr = &stderr_writer.interface;
|
||||
defer std.process.exit(1);
|
||||
const ErrSet = @TypeOf(err) || error{Unknown};
|
||||
|
||||
Reference in New Issue
Block a user