termio: on deinit, send SIGHUP to child process to exit it

This commit is contained in:
Mitchell Hashimoto
2022-11-06 14:08:42 -08:00
parent be76bc6c1a
commit ac6f960b92

View File

@ -15,6 +15,11 @@ const renderer = @import("../renderer.zig");
const log = std.log.scoped(.io_exec);
const c = @cImport({
@cInclude("signal.h");
@cInclude("unistd.h");
});
/// Allocator
alloc: Allocator,
@ -78,8 +83,8 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
.env = &env,
.cwd = opts.config.@"working-directory",
.pre_exec = (struct {
fn callback(c: *Command) void {
const p = c.getData(Pty) orelse unreachable;
fn callback(cmd: *Command) void {
const p = cmd.getData(Pty) orelse unreachable;
p.childPreExec() catch |err|
log.err("error initializing child: {}", .{err});
}
@ -113,9 +118,8 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
}
pub fn deinit(self: *Exec) void {
// Deinitialize the pty. This closes the pty handles. This should
// cause a close in the our subprocess so just wait for that.
self.pty.deinit();
// Kill our command
self.killCommand();
_ = self.command.wait() catch |err|
log.err("error waiting for command to exit: {}", .{err});
@ -123,6 +127,22 @@ pub fn deinit(self: *Exec) void {
self.terminal.deinit(self.alloc);
}
/// Kill the underlying subprocess. This closes the pty file handle and
/// sends a SIGHUP to the child process. This doesn't wait for the child
/// process to be exited.
fn killCommand(self: *Exec) void {
// Close our PTY
self.pty.deinit();
// We need to get our process group ID and send a SIGHUP to it.
if (self.command.pid) |pid| {
const pgid = c.getpgid(pid);
if (pgid > 0) {
_ = c.killpg(pgid, c.SIGHUP);
}
}
}
pub fn threadEnter(self: *Exec, loop: libuv.Loop) !ThreadData {
assert(self.data == null);
@ -367,15 +387,15 @@ fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void {
var i: usize = 0;
const end = @intCast(usize, n);
if (ev.terminal_stream.parser.state == .ground) {
for (buf[i..end]) |c| {
switch (terminal.parse_table.table[c][@enumToInt(terminal.Parser.State.ground)].action) {
for (buf[i..end]) |ch| {
switch (terminal.parse_table.table[ch][@enumToInt(terminal.Parser.State.ground)].action) {
// Print, call directly.
.print => ev.terminal_stream.handler.print(@intCast(u21, c)) catch |err|
.print => ev.terminal_stream.handler.print(@intCast(u21, ch)) catch |err|
log.err("error processing terminal data: {}", .{err}),
// C0 execute, let our stream handle this one but otherwise
// continue since we're guaranteed to be back in ground.
.execute => ev.terminal_stream.execute(c) catch |err|
.execute => ev.terminal_stream.execute(ch) catch |err|
log.err("error processing terminal data: {}", .{err}),
// Otherwise, break out and go the slow path until we're
@ -413,8 +433,8 @@ const StreamHandler = struct {
try self.ev.queueWrite(data);
}
pub fn print(self: *StreamHandler, c: u21) !void {
try self.terminal.print(c);
pub fn print(self: *StreamHandler, ch: u21) !void {
try self.terminal.print(ch);
}
pub fn bell(self: StreamHandler) !void {