Fix zombie processes from keybind spawns
Previously, beansprout used std.process.Child.spawn without ever calling wait(), leaving exited children as zombies. This commit switches to a double-fork based off river-classic's spawn handling, where the actual spawned process gets orphaned and reparented to PID 1. This way, the parent (i.e. beansprout) only has to wait for the intermediate child. Also switch tokenizeShell and expandTilde to produce [:0]const u8 tokens so the argv array for execvpeZ can be built without copying each string. Fixes: #12
This commit is contained in:
parent
4cf0899457
commit
f16f07fa26
4 changed files with 53 additions and 17 deletions
|
|
@ -5,7 +5,7 @@
|
|||
const XkbBindings = @This();
|
||||
|
||||
pub const Command = union(enum) {
|
||||
spawn: []const []const u8,
|
||||
spawn: []const [:0]const u8,
|
||||
focus_next_window,
|
||||
focus_prev_window,
|
||||
focus_next_output,
|
||||
|
|
@ -139,13 +139,7 @@ const XkbBinding = struct {
|
|||
|
||||
// TODO: Should I log.warn when commands return early?
|
||||
switch (xkb_binding.command) {
|
||||
.spawn => |cmd| {
|
||||
var child = std.process.Child.init(cmd, utils.gpa);
|
||||
child.env_map = &context.env;
|
||||
_ = child.spawn() catch |err| {
|
||||
log.err("Failed to spawn \"{s}\": {}", .{ cmd[0], err });
|
||||
};
|
||||
},
|
||||
.spawn => |cmd| spawnProcess(cmd),
|
||||
.focus_next_window => focusWindow(context, .next),
|
||||
.focus_prev_window => focusWindow(context, .prev),
|
||||
.focus_next_output => focusOutput(context, .next),
|
||||
|
|
@ -403,6 +397,44 @@ const XkbBinding = struct {
|
|||
}
|
||||
}
|
||||
|
||||
// Borrowed and modified from river-classic
|
||||
// https://codeberg.org/river/river-classic/src/commit/d72408df18310d5945147d485fe4bb66eef043d3/river/process.zig
|
||||
fn spawnProcess(cmd: []const [:0]const u8) void {
|
||||
const pid = posix.fork() catch |err| {
|
||||
return log.err("Failed to fork \"{s}\": {}", .{ cmd[0], err });
|
||||
};
|
||||
|
||||
if (pid == 0) {
|
||||
cleanupChild();
|
||||
|
||||
const c_argv = utils.gpa.allocSentinel(?[*:0]const u8, cmd.len, null) catch posix.exit(1);
|
||||
for (cmd, 0..) |arg, i| c_argv[i] = arg.ptr;
|
||||
|
||||
const pid2 = posix.fork() catch posix.exit(1);
|
||||
if (pid2 == 0) {
|
||||
posix.execvpeZ(c_argv[0].?, c_argv, std.c.environ) catch posix.exit(1);
|
||||
}
|
||||
|
||||
posix.exit(0);
|
||||
}
|
||||
|
||||
_ = posix.waitpid(pid, 0);
|
||||
}
|
||||
|
||||
// Borrowed and modified from river-classic
|
||||
// https://codeberg.org/river/river-classic/src/commit/d72408df18310d5945147d485fe4bb66eef043d3/river/process.zig
|
||||
fn cleanupChild() void {
|
||||
_ = posix.setsid() catch unreachable;
|
||||
if (posix.system.sigprocmask(posix.SIG.SETMASK, &posix.sigemptyset(), null) < 0) unreachable;
|
||||
|
||||
const sig_dfl = posix.Sigaction{
|
||||
.handler = .{ .handler = posix.SIG.DFL },
|
||||
.mask = posix.sigemptyset(),
|
||||
.flags = 0,
|
||||
};
|
||||
posix.sigaction(posix.SIG.PIPE, &sig_dfl, null);
|
||||
}
|
||||
|
||||
fn moveFloatingWindow(context: *Context, dx: i32, dy: i32) void {
|
||||
const seat = context.wm.seats.first() orelse return;
|
||||
const window = seat.focused_window orelse return;
|
||||
|
|
@ -532,6 +564,8 @@ pub fn manage(xkb_bindings: *XkbBindings) void {
|
|||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const posix = std.posix;
|
||||
const process = std.process;
|
||||
|
||||
const wayland = @import("wayland");
|
||||
const wl = wayland.client.wl;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue