mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #1191 from mitchellh/exec-failed
termio/exec: detect exec failure and show an error message
This commit is contained in:
@ -155,6 +155,11 @@ fn startPosix(self: *Command, arena: Allocator) !void {
|
|||||||
|
|
||||||
// Finally, replace our process.
|
// Finally, replace our process.
|
||||||
_ = std.os.execveZ(pathZ, argsZ, envp) catch null;
|
_ = std.os.execveZ(pathZ, argsZ, envp) catch null;
|
||||||
|
|
||||||
|
// If we are executing this code, the exec failed. In that scenario,
|
||||||
|
// we return a very specific error that can be detected to determine
|
||||||
|
// we're in the child.
|
||||||
|
return error.ExecFailedInChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn startWindows(self: *Command, arena: Allocator) !void {
|
fn startWindows(self: *Command, arena: Allocator) !void {
|
||||||
|
@ -217,7 +217,20 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
|||||||
const alloc = self.alloc;
|
const alloc = self.alloc;
|
||||||
|
|
||||||
// Start our subprocess
|
// Start our subprocess
|
||||||
const pty_fds = try self.subprocess.start(alloc);
|
const pty_fds = self.subprocess.start(alloc) catch |err| {
|
||||||
|
// If we specifically got this error then we are in the forked
|
||||||
|
// process and our child failed to execute. In that case
|
||||||
|
if (err != error.ExecFailedInChild) return err;
|
||||||
|
|
||||||
|
// Output an error message about the exec faililng and exit.
|
||||||
|
// This generally should NOT happen because we always wrap
|
||||||
|
// our command execution either in login (macOS) or /bin/sh
|
||||||
|
// (Linux) which are usually guaranteed to exist. Still, we
|
||||||
|
// want to handle this scenario.
|
||||||
|
self.execFailedInChild() catch {};
|
||||||
|
std.os.exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
errdefer self.subprocess.stop();
|
errdefer self.subprocess.stop();
|
||||||
const pid = pid: {
|
const pid = pid: {
|
||||||
const command = self.subprocess.command orelse return error.ProcessNotStarted;
|
const command = self.subprocess.command orelse return error.ProcessNotStarted;
|
||||||
@ -316,6 +329,26 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This outputs an error message when exec failed and we are the
|
||||||
|
/// child process. This returns so the caller should probably exit
|
||||||
|
/// after calling this.
|
||||||
|
///
|
||||||
|
/// Note that this usually is only called under very very rare
|
||||||
|
/// circumstances because we wrap our command execution in login
|
||||||
|
/// (macOS) or /bin/sh (Linux). So this output can be pretty crude
|
||||||
|
/// because it should never happen. Notably, this is not the error
|
||||||
|
/// users see when `command` is invalid.
|
||||||
|
fn execFailedInChild(self: *Exec) !void {
|
||||||
|
_ = self;
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
try stderr.writeAll("exec failed\n");
|
||||||
|
try stderr.writeAll("press any key to exit\n");
|
||||||
|
|
||||||
|
var buf: [1]u8 = undefined;
|
||||||
|
var reader = std.io.getStdIn().reader();
|
||||||
|
_ = try reader.read(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn threadExit(self: *Exec, data: ThreadData) void {
|
pub fn threadExit(self: *Exec, data: ThreadData) void {
|
||||||
// Clear out our data since we're not active anymore.
|
// Clear out our data since we're not active anymore.
|
||||||
self.data = null;
|
self.data = null;
|
||||||
|
Reference in New Issue
Block a user