diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/host_io.zig | 43 | ||||
| -rw-r--r-- | src/linux/v9fs.zig | 10 | ||||
| -rw-r--r-- | src/look.zig | 1 | ||||
| -rw-r--r-- | src/lsp/lsp_client.zig | 1 | ||||
| -rw-r--r-- | src/selection_pipe.zig | 8 |
5 files changed, 60 insertions, 3 deletions
diff --git a/src/host_io.zig b/src/host_io.zig index 9d4b27c2..bbd664a9 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -1075,6 +1075,45 @@ pub const Shell = struct { } }; +/// In a child between fork and exec: every signal back to its default and +/// none blocked, whatever this process ignores or blocks (the tty's SIGWINCH, +/// a SIGHUP nohup ignored). A handler resets at exec by itself; an ignored +/// signal and the mask do not. Async-signal-safe: sigaction and sigprocmask. +pub fn resetChildSignals() void { + const default: posix.Sigaction = .{ .handler = .{ .handler = posix.SIG.DFL }, .mask = posix.sigemptyset(), .flags = 0 }; + var sig: u8 = 1; + while (sig < 65) : (sig += 1) { + if (sig == @intFromEnum(posix.SIG.KILL) or sig == @intFromEnum(posix.SIG.STOP)) continue; + _ = std.c.sigaction(@enumFromInt(sig), &default, null); + } + const none = posix.sigemptyset(); + posix.sigprocmask(posix.SIG.SETMASK, &none, null); +} + +test "a child starts with every signal at its default and none blocked" { + var blocked = posix.sigemptyset(); + posix.sigaddset(&blocked, posix.SIG.WINCH); + posix.sigprocmask(posix.SIG.BLOCK, &blocked, null); + defer posix.sigprocmask(posix.SIG.UNBLOCK, &blocked, null); + const ignore: posix.Sigaction = .{ .handler = .{ .handler = posix.SIG.IGN }, .mask = posix.sigemptyset(), .flags = 0 }; + var was: posix.Sigaction = undefined; + posix.sigaction(posix.SIG.HUP, &ignore, &was); + defer posix.sigaction(posix.SIG.HUP, &was, null); + const pid = libc.fork(); + if (pid == 0) { + resetChildSignals(); + var now: posix.Sigaction = undefined; + posix.sigaction(posix.SIG.HUP, null, &now); + var mask = posix.sigemptyset(); + posix.sigprocmask(posix.SIG.BLOCK, null, &mask); + const ok = now.handler.handler == posix.SIG.DFL and !posix.sigismember(&mask, posix.SIG.WINCH); + libc._exit(if (ok) 0 else 1); + } + var status: c_int = 0; + _ = libc.waitpid(pid, &status, 0); + try std.testing.expectEqual(@as(c_int, 0), status); +} + extern "c" fn forkpty(amaster: *c_int, name: ?[*:0]u8, termp: ?*const anyopaque, winp: ?*const posix.winsize) c_int; extern "c" fn execv(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int; extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; @@ -1278,9 +1317,7 @@ pub fn forkShell( return error.ForkFailed; } if (pid == 0) { - var set = posix.sigemptyset(); - posix.sigaddset(&set, posix.SIG.WINCH); - posix.sigprocmask(posix.SIG.UNBLOCK, &set, null); + resetChildSignals(); 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); diff --git a/src/linux/v9fs.zig b/src/linux/v9fs.zig index 4e18660d..5e031bfe 100644 --- a/src/linux/v9fs.zig +++ b/src/linux/v9fs.zig @@ -118,6 +118,16 @@ fn launch(arena: std.mem.Allocator, args: []const [:0]const u8) !u8 { const pid = fork(); if (pid < 0) return error.ForkFailed; if (pid == 0) { + // host_io's resetChildSignals, here in its own module: every signal + // back to its default and none blocked before the exec. + const default: std.posix.Sigaction = .{ .handler = .{ .handler = std.posix.SIG.DFL }, .mask = std.posix.sigemptyset(), .flags = 0 }; + var sig: u8 = 1; + while (sig < 65) : (sig += 1) { + if (sig == @intFromEnum(std.posix.SIG.KILL) or sig == @intFromEnum(std.posix.SIG.STOP)) continue; + _ = std.c.sigaction(@enumFromInt(sig), &default, null); + } + const none = std.posix.sigemptyset(); + std.posix.sigprocmask(std.posix.SIG.SETMASK, &none, null); _ = execvp("sudo", argv.ptr); _exit(127); } diff --git a/src/look.zig b/src/look.zig index 5cf26240..4e948fad 100644 --- a/src/look.zig +++ b/src/look.zig @@ -49,6 +49,7 @@ pub fn openLink(url: []const u8) void { if (pid < 0) return; if (pid == 0) { if (fork() == 0) { + @import("host_io.zig").resetChildSignals(); const argv: [3:null]?[*:0]const u8 = .{ opener, url_z.ptr, null }; _ = execv(opener, &argv); } diff --git a/src/lsp/lsp_client.zig b/src/lsp/lsp_client.zig index 55df1f91..91ea67d6 100644 --- a/src/lsp/lsp_client.zig +++ b/src/lsp/lsp_client.zig @@ -1148,6 +1148,7 @@ fn ensure(c: *Conn, si: usize, arena: std.mem.Allocator, req: lsp.Req, tr: *Trac // signal-safe calls, no allocation, no locks. (Same rule as tty.zig's // forkShell.) _ = setsid(); // Ctrl-C in pardes's terminal is not the server's business + @import("../host_io.zig").resetChildSignals(); _ = libc.dup2(sv[1], 0); _ = libc.dup2(sv[1], 1); const devnull = libc.open("/dev/null", .{ .ACCMODE = .WRONLY }); diff --git a/src/selection_pipe.zig b/src/selection_pipe.zig index e1a42962..44cc1709 100644 --- a/src/selection_pipe.zig +++ b/src/selection_pipe.zig @@ -202,6 +202,14 @@ pub fn runOne( if (std.mem.indexOfScalar(u8, command, 0) != null or std.mem.indexOfScalar(u8, cwd, 0) != null) return fail.k(.spawn); + // std's spawn runs no code in the child: this thread's mask, which the + // child inherits, is cleared across the fork (only the tty's SIGWINCH is + // ever blocked, and its default is to be ignored). A handler resets at + // exec by itself, and nothing here is ignored. + const none = std.posix.sigemptyset(); + var kept: std.posix.sigset_t = undefined; + std.posix.sigprocmask(std.posix.SIG.SETMASK, &none, &kept); + defer std.posix.sigprocmask(std.posix.SIG.SETMASK, &kept, null); var child = std.process.spawn(io, .{ .argv = &.{ "/bin/sh", "-c", command }, .cwd = if (cwd.len == 0) .inherit else .{ .path = cwd }, |
