git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit322e9a89b1f4a5b91f51dc25feed5a74beb1403e
parenta6524cc608
authorIsaac Freund <[email protected]>
date2020-07-08 14:00
command/spawn: clean up and disown children

- Call setsid
- Clean up the signal mask.
- Use a double fork so that processes get inherited by init.

 river/c.zig             |  5 ++++-
 river/command/spawn.zig | 34 +++++++++++++++++++++++++---------
 2 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/river/c.zig b/river/c.zig
index 829bdb7..dacb06e 100644
--- a/river/c.zig
+++ b/river/c.zig
@@ -19,9 +19,12 @@ pub usingnamespace @cImport({
     @cDefine("_POSIX_C_SOURCE", "200809L");
     @cDefine("WLR_USE_UNSTABLE", {});
 
-    @cInclude("time.h");
     @cInclude("stdlib.h");
+    @cInclude("time.h");
+    @cInclude("unistd.h");
+
     @cInclude("linux/input-event-codes.h");
+
     @cInclude("wayland-server-core.h");
     //@cInclude("wlr/backend.h");
     //@cInclude("wlr/render/wlr_renderer.h");
diff --git a/river/command/spawn.zig b/river/command/spawn.zig
index 96e25cd..fdc3ccb 100644
--- a/river/command/spawn.zig
+++ b/river/command/spawn.zig
@@ -17,6 +17,8 @@
 
 const std = @import("std");
 
+const c = @import("../c.zig");
+
 const Error = @import("../command.zig").Error;
 const Seat = @import("../Seat.zig");
 
@@ -31,17 +33,31 @@ pub fn spawn(
 
     const cmd = try std.mem.join(allocator, " ", args[1..]);
     defer allocator.free(cmd);
+    const cmdZ = try std.cstr.addNullByte(allocator, cmd);
+    defer allocator.free(cmdZ);
 
-    const child_args = [_][]const u8{ "/bin/sh", "-c", cmd };
-    const child = try std.ChildProcess.init(&child_args, allocator);
-    defer child.deinit();
+    const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmdZ, null };
 
-    std.ChildProcess.spawn(child) catch |err| {
-        out.* = try std.fmt.allocPrint(
-            allocator,
-            "failed to spawn {}: {}.",
-            .{ cmd, err },
-        );
+    const pid = std.os.fork() catch {
+        out.* = try std.fmt.allocPrint(allocator, "fork/execve failed", .{});
         return Error.Other;
     };
+
+    if (pid == 0) {
+        // Clean things up for the child in an intermediate fork
+        if (c.setsid() < 0) unreachable;
+        if (std.os.system.sigprocmask(std.os.SIG_SETMASK, &std.os.empty_sigset, null) < 0) unreachable;
+
+        const pid2 = std.os.fork() catch std.os.exit(1);
+        if (pid2 == 0) std.os.execveZ("/bin/sh", &child_args, std.c.environ) catch std.os.exit(1);
+
+        std.os.exit(0);
+    }
+
+    // Wait the intermediate child.
+    const status = std.os.waitpid(pid, 0);
+    if (std.os.WIFEXITED(status) and std.os.WEXITSTATUS(status) != 0) {
+        out.* = try std.fmt.allocPrint(allocator, "fork/execve failed", .{});
+        return Error.Other;
+    }
 }