diff options
Diffstat (limited to 'src/host_io.zig')
| -rw-r--r-- | src/host_io.zig | 35 |
1 files changed, 26 insertions, 9 deletions
diff --git a/src/host_io.zig b/src/host_io.zig index 81d784c5..6009c00f 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -22,6 +22,9 @@ pub const Host = struct { pty_signal: ?*const fn (ctx: ?*anyopaque, pane: u8, sig: pardes.PtySignal) void = null, close_pty: ?*const fn (ctx: ?*anyopaque, pane: u8) void = null, tty_taken: ?*const fn (ctx: ?*anyopaque, pane: u8) bool = null, + /// Answered at once, not queued as an effect: Kill asks while the + /// job it saw running is still the one in the tty (host_io.killJob). + kill_job: ?*const fn (ctx: ?*anyopaque, pane: u8) bool = null, gpio_toggle: ?*const fn (ctx: ?*anyopaque, pin: u16, was: *u8, now: *u8) bool = null, write_file: ?*const fn (ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void = null, write_dump: ?*const fn (ctx: ?*anyopaque, bytes: []const u8) void = null, @@ -1366,14 +1369,8 @@ pub fn signalTty(shell_pid: libc.pid_t, master_fd: c_int, which: pardes.PtySigna .hup => libc.SIG.HUP, .quit => libc.SIG.QUIT, .kill => libc.SIG.KILL, - .command => libc.SIG.TERM, }; const fg = tcgetpgrp(master_fd); - // The shell leads its own group: a job it runs has another. - if (which == .command) { - if (fg > 0 and fg != shell_pid) _ = libc.kill(-fg, sig); - return; - } if (fg > 0) { _ = libc.kill(-fg, sig); return; @@ -1381,6 +1378,16 @@ pub fn signalTty(shell_pid: libc.pid_t, master_fd: c_int, which: pardes.PtySigna if (shell_pid > 0) _ = libc.kill(shell_pid, sig); } +/// Kill: SIGTERM to the job in the tty's foreground, never to the shell. +/// The shell leads its own group, and a job it runs has another; with job +/// control off (`set +m`, a shell run as `sh -c`) the job shares the shell's +/// group, and there is no job to signal apart from it: false. +pub fn killJob(shell_pid: libc.pid_t, master_fd: c_int) bool { + const fg = tcgetpgrp(master_fd); + if (fg <= 0 or fg == shell_pid) return false; + return libc.kill(-fg, libc.SIG.TERM) == 0; +} + fn readProc(path: [*:0]const u8, buf: []u8) ?[]const u8 { const fd = libc.open(path, .{ .ACCMODE = .RDONLY }); if (fd < 0) return null; @@ -1675,23 +1682,33 @@ test "an idle prompt is free, a foreground job takes the tty, and Ctrl-C hands i try std.testing.expect(sh.waitText(test_prompt, 10_000)); } -test "Kill's signal stops the foreground job and never the shell" { +test "Kill stops the foreground job, never the shell, and finds none with job control off" { if (comptime !tty_probe_platform) return error.SkipZigTest; var sh = TestShell.start() orelse return error.SkipZigTest; defer sh.stop(); // At the prompt the shell is the foreground: there is nothing to stop. - signalTty(sh.pid, sh.master, .command); + try std.testing.expect(!killJob(sh.pid, sh.master)); sleepMs(100); try std.testing.expectEqual(@as(libc.pid_t, 0), libc.waitpid(sh.pid, null, libc.W.NOHANG)); sh.send("sleep 30\n"); try std.testing.expect(sh.waitTaken(true, 10_000)); sh.forget(); - signalTty(sh.pid, sh.master, .command); + try std.testing.expect(killJob(sh.pid, sh.master)); try std.testing.expect(sh.waitTaken(false, 10_000)); try std.testing.expect(sh.waitText(test_prompt, 10_000)); try std.testing.expectEqual(@as(libc.pid_t, 0), libc.waitpid(sh.pid, null, libc.W.NOHANG)); + + // Job control off: the job runs in the shell's own group. + sh.forget(); + sh.send("set +m; sleep 30\n"); + sleepMs(300); + try std.testing.expect(!killJob(sh.pid, sh.master)); + sh.forget(); + sh.send("\x03"); + try std.testing.expect(sh.waitText(test_prompt, 10_000)); + try std.testing.expectEqual(@as(libc.pid_t, 0), libc.waitpid(sh.pid, null, libc.W.NOHANG)); } test "a retired shell that ignores the hangup is killed and reaped, not left a zombie" { |
