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

commit39156c2594f0b1702508407e7d70c0fa587208dd
parentcaf87a9248
authorIsaac Freund <[email protected]>
date2022-08-11 12:02
river: fix SIGPIPE handling, cleanup fork/execve

Installing an empty handler does not have the same effect as using
SIG_IGN as the failing write syscall will not return EPIPE.

 river/command/spawn.zig |  5 +----
 river/main.zig          | 24 +++++++++++-------------
 river/util.zig          | 17 ++++++++++++++++-
 3 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/river/command/spawn.zig b/river/command/spawn.zig
index 1bc5c42..ced3922 100644
--- a/river/command/spawn.zig
+++ b/river/command/spawn.zig
@@ -40,10 +40,7 @@ pub fn spawn(
     };
 
     if (pid == 0) {
-        // Clean things up for the child in an intermediate fork
-        if (c.setsid() < 0) unreachable;
-        if (os.system.sigprocmask(os.SIG.SETMASK, &os.empty_sigset, null) < 0) unreachable;
-
+        util.post_fork_pre_execve();
         const pid2 = os.fork() catch c._exit(1);
         if (pid2 == 0) os.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
 
diff --git a/river/main.zig b/river/main.zig
index 53a17e5..d9fc443 100644
--- a/river/main.zig
+++ b/river/main.zig
@@ -41,18 +41,7 @@ const usage: []const u8 =
 
 pub var server: Server = undefined;
 
-fn sa_handler(_: c_int) callconv(.C) void {}
-
 pub fn main() anyerror!void {
-    // ignore SIGPIPE so we don't get killed when socket unexpectedly closes (thanks xwayland)
-    // use our own handler instead of SIG_IGN so we don't leak this when execve()
-    const act = os.Sigaction{
-        .handler = .{ .handler = sa_handler },
-        .mask = os.empty_sigset,
-        .flags = 0,
-    };
-    os.sigaction(os.SIG.PIPE, &act, null);
-
     // This line is here because of https://github.com/ziglang/zig/issues/7807
     const argv: [][*:0]const u8 = os.argv;
     const result = flags.parse(argv[1..], &[_]flags.Flag{
@@ -107,6 +96,16 @@ pub fn main() anyerror!void {
         .warn, .err => .err,
     });
 
+    // Ignore SIGPIPE so we don't get killed when writing to a socket that
+    // has had its read end closed by another process.
+    const sig_ign = os.Sigaction{
+        // TODO(zig): Remove this casting after https://github.com/ziglang/zig/pull/12410
+        .handler = .{ .handler = @intToPtr(os.Sigaction.handler_fn, @ptrToInt(os.SIG.IGN)) },
+        .mask = os.empty_sigset,
+        .flags = 0,
+    };
+    os.sigaction(os.SIG.PIPE, &sig_ign, null);
+
     std.log.info("initializing server", .{});
     try server.init();
     defer server.deinit();
@@ -120,8 +119,7 @@ pub fn main() anyerror!void {
         const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null };
         const pid = try os.fork();
         if (pid == 0) {
-            if (c.setsid() < 0) unreachable;
-            if (os.system.sigprocmask(os.SIG.SETMASK, &os.empty_sigset, null) < 0) unreachable;
+            util.post_fork_pre_execve();
             os.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
         }
         util.gpa.free(cmd);
diff --git a/river/util.zig b/river/util.zig
index 2cd8874..13976df 100644
--- a/river/util.zig
+++ b/river/util.zig
@@ -1,6 +1,6 @@
 // This file is part of river, a dynamic tiling wayland compositor.
 //
-// Copyright 2020 The River Developers
+// Copyright 2022 The River Developers
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -15,6 +15,21 @@
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
 const std = @import("std");
+const os = std.os;
+
+const c = @import("c.zig");
 
 /// The global general-purpose allocator used throughout river's code
 pub const gpa = std.heap.c_allocator;
+
+pub fn post_fork_pre_execve() void {
+    if (c.setsid() < 0) unreachable;
+    if (os.system.sigprocmask(os.SIG.SETMASK, &os.empty_sigset, null) < 0) unreachable;
+    const sig_dfl = os.Sigaction{
+        // TODO(zig): Remove this casting after https://github.com/ziglang/zig/pull/12410
+        .handler = .{ .handler = @intToPtr(?os.Sigaction.handler_fn, @ptrToInt(os.SIG.DFL)) },
+        .mask = os.empty_sigset,
+        .flags = 0,
+    };
+    os.sigaction(os.SIG.PIPE, &sig_dfl, null);
+}