From dafefe9c9d3ffd484915ead0474c7e772f1dfcfb Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 18 Feb 2023 11:46:24 -0700 Subject: [PATCH] use std_options for keep_sigpipe and existence of SIG.PIPE to check for support --- lib/std/os.zig | 34 ++------------------------- lib/std/std.zig | 16 +++++++++++++ test/standalone/sigpipe/breakpipe.zig | 2 +- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 9752df7f16..bd6719ec8f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -7057,42 +7057,12 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { }; } -/// Whether or not the current target support SIGPIPE -pub const have_sigpipe_support = switch (builtin.os.tag) { - .linux, - .macos, - .netbsd, - .solaris, - .freebsd, - .openbsd, - => true, - else => false, -}; - -pub const keep_sigpipe: bool = if (@hasDecl(root, "keep_sigpipe")) - root.keep_sigpipe -else - false; +pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE"); fn noopSigHandler(_: c_int) callconv(.C) void {} -/// This function will tell the kernel to ignore SIGPIPE rather than terminate -/// the process. This function is automatically called in `start.zig` before -/// `main`. This behavior can be disabled by adding this to your root module: -/// -/// pub const keep_sigpipe = true; -/// -/// SIGPIPE is triggered when a process attempts to write to a broken pipe. -/// By default, SIGPIPE will terminate the process without giving the program -/// an opportunity to handle the situation. Unlike a segfault, it doesn't -/// trigger the panic handler so all the developer sees is that the program -/// terminated with no indication as to why. -/// -/// By telling the kernel to instead ignore SIGPIPE, writes to broken pipes -/// will return the EPIPE error (error.BrokenPipe) and the program can handle -/// it like any other error. pub fn maybeIgnoreSigpipe() void { - if (have_sigpipe_support and !keep_sigpipe) { + if (have_sigpipe_support and !std.options.keep_sigpipe) { const act = Sigaction{ // We set handler to a noop function instead of SIG.IGN so we don't leak our // signal disposition to a child process diff --git a/lib/std/std.zig b/lib/std/std.zig index e02be2ebaf..5b0963ba20 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -167,6 +167,22 @@ pub const options = struct { options_override.crypto_always_getrandom else false; + + /// By default Zig disables SIGPIPE by setting a "no-op" handler for it. Set this option + /// to `true` to prevent that. + /// + /// Note that we use a "no-op" handler instead of SIG_IGN because it will not be inherited by + /// any child process. + /// + /// SIGPIPE is triggered when a process attempts to write to a broken pipe. By default, SIGPIPE + /// will terminate the process instead of exiting. It doesn't trigger the panic handler so in many + /// cases it's unclear why the process was terminated. By capturing SIGPIPE instead, functions that + /// write to broken pipes will return the EPIPE error (error.BrokenPipe) and the program can handle + /// it like any other error. + pub const keep_sigpipe: bool = if (@hasDecl(options_override, "keep_sigpipe")) + options_override.keep_sigpipe + else + false; }; // This forces the start.zig file to be imported, and the comptime logic inside that diff --git a/test/standalone/sigpipe/breakpipe.zig b/test/standalone/sigpipe/breakpipe.zig index 6498f5b2eb..3623451db5 100644 --- a/test/standalone/sigpipe/breakpipe.zig +++ b/test/standalone/sigpipe/breakpipe.zig @@ -1,7 +1,7 @@ const std = @import("std"); const build_options = @import("build_options"); -pub usingnamespace if (build_options.keep_sigpipe) struct { +pub const std_options = if (build_options.keep_sigpipe) struct { pub const keep_sigpipe = true; } else struct { // intentionally not setting keep_sigpipe to ensure the default behavior is equivalent to false