a73x

f5298ecc

refactor: one owner for "make a hangup EPIPE, not a signal"

a73x   2026-08-29 10:01

Commit message
refactor: one owner for "make a hangup EPIPE, not a signal"

`server.installSignalHandlers` spelled the SIGPIPE SIG_IGN sigaction byte
for byte as `proxy.ignoreSigpipe`, which `wallview` and `main` already
call. The server now calls it too and gains a `proxy` import — a layer 0
root module, so the "server names no client and no terminal" folder rule
is untouched.

The comment saying WHY the daemon wants it (a client killed mid-run must
fail the next snapshot write with EPIPE) stays at the call site;
`ignoreSigpipe`'s own note about SIG_IGN surviving exec stays with the
code.

make check rc 0.

build.zig
Old New
@@ -255,7 +255,7 @@ const mod_table = [_]ModSpec{
255 // it names directly (the key it loads, the idle default it falls back 255 // it names directly (the key it loads, the idle default it falls back
256 // to). xdg is for endpoint_req's lazy bind — the default key path, 256 // to). xdg is for endpoint_req's lazy bind — the default key path,
257 // resolved by the daemon itself when nobody handed it a --key. 257 // resolved by the daemon itself when nobody handed it a --key.
258 .{ .name = "server", .path = "src/server/server.zig", .layer = 2, .link_libc = true, .imports = &.{ "engine", "pty", "protocol", "delta", "cmd", "shellint", "sockpath", "quic", "quic_server", "xdg", "upgrade" }, .test_imports = &.{ "replica", "testtmp" }, .quic_tests = true }, 258 .{ .name = "server", .path = "src/server/server.zig", .layer = 2, .link_libc = true, .imports = &.{ "engine", "pty", "protocol", "delta", "cmd", "shellint", "sockpath", "quic", "quic_server", "xdg", "upgrade", "proxy" }, .test_imports = &.{ "replica", "testtmp" }, .quic_tests = true },
259 // The agent-facing client. It speaks frames and owns no terminal, which 259 // The agent-facing client. It speaks frames and owns no terminal, which
260 // is the whole point — it attaches at 0x0 and never claims the grid. 260 // is the whole point — it attaches at 0x0 and never claims the grid.
261 // The transport modules are the CLI client's, minus everything that 261 // The transport modules are the CLI client's, minus everything that
src/server/server.zig
Old New
@@ -23,6 +23,7 @@ const quic = @import("quic");
23 const quic_server = @import("quic_server"); 23 const quic_server = @import("quic_server");
24 const xdg = @import("xdg"); 24 const xdg = @import("xdg");
25 const upgrade = @import("upgrade"); 25 const upgrade = @import("upgrade");
26 const proxy = @import("proxy");
26 const TmpDir = @import("testtmp").TmpDir; 27 const TmpDir = @import("testtmp").TmpDir;
27 // The agent relay is a sub-file of this module, not a row of its own: it is 28 // The agent relay is a sub-file of this module, not a row of its own: it is
28 // one cluster of Server's state, and a module row would claim it is a layer. 29 // one cluster of Server's state, and a module row would claim it is a layer.
@@ -177,12 +178,7 @@ pub fn installSignalHandlers() void {
177 178
178 // A client killed mid-run leaves a dead socket; the next snapshot 179 // A client killed mid-run leaves a dead socket; the next snapshot
179 // write must fail with EPIPE, not deliver a fatal SIGPIPE. 180 // write must fail with EPIPE, not deliver a fatal SIGPIPE.
180 var ign: std.posix.Sigaction = .{ 181 proxy.ignoreSigpipe();
181 .handler = .{ .handler = std.posix.SIG.IGN },
182 .mask = std.posix.sigemptyset(),
183 .flags = 0,
184 };
185 std.posix.sigaction(std.posix.SIG.PIPE, &ign, null);
186 } 182 }
187 183
188 /// Where one client's bytes go, and where they come from. 184 /// Where one client's bytes go, and where they come from.