diff options
Diffstat (limited to 'src/host_io.zig')
| -rw-r--r-- | src/host_io.zig | 45 |
1 files changed, 43 insertions, 2 deletions
diff --git a/src/host_io.zig b/src/host_io.zig index 10bcf0d7..fbdfb703 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -1156,17 +1156,58 @@ pub fn forkShell( var env_slots: ChildEnv.Slots = undefined; const envp = ChildEnv.build(&env_slots); const ws = posix.winsize{ .row = rows, .col = cols, .xpixel = 0, .ypixel = 0 }; + // The child says here why it could not become the shell (its chdir or + // its exec failed, as for a script whose interpreter is not there): the + // pipe closes on exec, so the parent reads nothing when the shell runs, + // and a reason when it did not, before anyone is told it started. + var told: [2]c_int = .{ -1, -1 }; + // pipe and then CLOEXEC, not pipe2, which macOS has not. + if (libc.pipe(&told) != 0) return error.PipeFailed; + ninep_io.setCloexec(told[0]); + ninep_io.setCloexec(told[1]); const pid = forkpty(&master, null, null, &ws); - if (pid < 0) return error.ForkFailed; + if (pid < 0) { + _ = libc.close(told[0]); + _ = libc.close(told[1]); + return error.ForkFailed; + } if (pid == 0) { var set = posix.sigemptyset(); posix.sigaddset(&set, posix.SIG.WINCH); posix.sigprocmask(posix.SIG.UNBLOCK, &set, null); - if (cwd_z) |path| if (chdir(path.ptr) != 0) _exit(126); + if (cwd_z) |path| if (chdir(path.ptr) != 0) { + const why = [_]u8{ 'c', @truncate(@intFromEnum(libc.errno(@as(c_int, -1)))) }; + _ = libc.write(told[1], &why, why.len); + _exit(126); + }; if (envp) |env| _ = execve(spawn.path, &spawn.argv, env); _ = execv(spawn.path, &spawn.argv); + const why = [_]u8{ 'x', @truncate(@intFromEnum(libc.errno(@as(c_int, -1)))) }; + _ = libc.write(told[1], &why, why.len); _exit(127); } + _ = libc.close(told[1]); + const failed = failed: { + var why: [2]u8 = undefined; + pardes.turn.yield(); + defer pardes.turn.back(); + const n = libc.read(told[0], &why, why.len); + _ = libc.close(told[0]); + if (n != 2) break :failed null; + break :failed why; + }; + if (failed) |why| { + _ = libc.close(master); + _ = libc.waitpid(pid, null, 0); + const e: libc.E = @enumFromInt(why[1]); + if (why[0] == 'c') return if (e == .NOENT or e == .NOTDIR) error.FileNotFound else error.AccessDenied; + return switch (e) { + .NOENT => error.ShellNotFound, // itself, or its script's interpreter + .ACCES, .PERM => error.ShellNotExecutable, + .NOEXEC => error.ShellNotExecutable, + else => error.ShellDidNotStart, + }; + } ninep_io.setCloexec(master); if (core) |c| c.acknowledgeShell(pane, std.mem.span(spawn.path), one_line == null and spawn.argv[1] != null); return .{ .file = .{ .handle = master, .flags = .{ .nonblocking = false } }, .pid = pid }; |
