// zwanzig-disable: file-as-struct // Pure-function module keeps the PascalCase name per project convention. // SPDX-FileCopyrightText: © 2026 mtmn // // SPDX-License-Identifier: BSD-3-Clause const std = @import("std"); /// Run command with `/bin/sh -c` in a detached grandchild process. pub fn run(command: [:0]const u8) void { const shell: [*:0]const u8 = "/bin/sh"; const child_args = [_:null]?[*:0]const u8{ shell, "-c", command, null }; const process_id: std.c.pid_t = forkProcessId() orelse return; if (process_id == 0) { _ = std.c.setsid(); // main blocks SIGHUP for its signalfd, and a blocked signal stays // blocked across fork and exec. dash clears the mask for plain // commands, but not for exec, background jobs or pipelines, and // bash keeps it even for a single command. Unblock it so spawned // programs and their children still get hangups, for example when // their terminal closes. const linux = std.os.linux; var hup_mask = linux.sigemptyset(); linux.sigaddset(&hup_mask, .HUP); _ = linux.sigprocmask(linux.SIG.UNBLOCK, &hup_mask, null); const grandchild: std.c.pid_t = forkProcessId() orelse std.c._exit(1); if (grandchild == 0) { _ = std.c.execve(shell, &child_args, std.c.environ); std.c._exit(1); } std.c._exit(0); } var status: c_int = 0; while (true) { switch (std.c.errno(std.c.waitpid(process_id, &status, 0))) { .SUCCESS => return, .INTR => continue, else => return, } } } fn forkProcessId() ?std.c.pid_t { const fork_result = std.c.fork(); switch (std.c.errno(fork_result)) { .SUCCESS => {}, else => return null, } return @intCast(fork_result); }