summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/host_io.zig43
-rw-r--r--src/linux/v9fs.zig10
-rw-r--r--src/look.zig1
-rw-r--r--src/lsp/lsp_client.zig1
-rw-r--r--src/selection_pipe.zig8
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 },