diff --git a/lib/std/c.zig b/lib/std/c.zig index 5ebbb9dd22..a8ac19053d 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -200,7 +200,7 @@ pub usingnamespace switch (builtin.os.tag) { pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn sched_yield() c_int; - pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; + pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int; pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *libc_stat) c_int; @@ -215,7 +215,7 @@ pub usingnamespace switch (builtin.os.tag) { pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn sched_yield() c_int; - pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; + pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int; pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *libc_stat) c_int; }, @@ -227,7 +227,7 @@ pub usingnamespace switch (builtin.os.tag) { pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn sched_yield() c_int; - pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; + pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int; pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *libc_stat) c_int; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 4f0f44d1b7..be5635ee8f 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1721,7 +1721,7 @@ pub fn attachSegfaultHandler() void { return; } var act = os.Sigaction{ - .sigaction = handleSegfaultLinux, + .handler = .{ .sigaction = handleSegfaultLinux }, .mask = os.empty_sigset, .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND), }; @@ -1740,7 +1740,7 @@ fn resetSegfaultHandler() void { return; } var act = os.Sigaction{ - .sigaction = os.SIG_DFL, + .handler = .{ .sigaction = os.SIG_DFL }, .mask = os.empty_sigset, .flags = 0, }; diff --git a/lib/std/os.zig b/lib/std/os.zig index e3afe90e5d..b385ffa19f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4592,7 +4592,7 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void { } /// Examine and change a signal action. -pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void { +pub fn sigaction(sig: u6, act: ?*const Sigaction, oact: ?*Sigaction) void { switch (errno(system.sigaction(sig, act, oact))) { 0 => return, EFAULT => unreachable, diff --git a/lib/std/os/bits/darwin.zig b/lib/std/os/bits/darwin.zig index 83ca09a9e1..8bd40ed9a3 100644 --- a/lib/std/os/bits/darwin.zig +++ b/lib/std/os/bits/darwin.zig @@ -124,13 +124,40 @@ pub const timespec = extern struct { }; pub const sigset_t = u32; -pub const empty_sigset = sigset_t(0); +pub const empty_sigset: sigset_t = 0; + +pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); +pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0); +pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1); +pub const SIG_HOLD = @intToPtr(?Sigaction.sigaction_fn, 5); + +pub const siginfo_t = extern struct { + signo: c_int, + errno: c_int, + code: c_int, + pid: pid_t, + uid: uid_t, + status: c_int, + addr: *c_void, + value: extern union { + int: c_int, + ptr: *c_void, + }, + si_band: c_long, + _pad: [7]c_ulong, +}; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name. pub const Sigaction = extern struct { - handler: fn (c_int) callconv(.C) void, - sa_mask: sigset_t, - sa_flags: c_int, + pub const handler_fn = fn (c_int) callconv(.C) void; + pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void; + + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, + mask: sigset_t, + flags: c_uint, }; pub const dirent = extern struct { diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig index 2fd9e39c7b..61b6b9f363 100644 --- a/lib/std/os/bits/dragonfly.zig +++ b/lib/std/os/bits/dragonfly.zig @@ -530,12 +530,16 @@ pub const sigset_t = extern struct { }; pub const sig_atomic_t = c_int; pub const Sigaction = extern struct { - __sigaction_u: extern union { - __sa_handler: ?fn (c_int) callconv(.C) void, - __sa_sigaction: ?fn (c_int, [*c]siginfo_t, ?*c_void) callconv(.C) void, + pub const handler_fn = fn (c_int) callconv(.C) void; + pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void; + + /// signal handler + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, }, - sa_flags: c_int, - sa_mask: sigset_t, + flags: c_uint, + mask: sigset_t, }; pub const sig_t = [*c]fn (c_int) callconv(.C) void; diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index 5dce94bd90..6378dcaf8d 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -736,23 +736,61 @@ pub const winsize = extern struct { const NSIG = 32; -pub const SIG_ERR = @intToPtr(fn (i32) callconv(.C) void, maxInt(usize)); -pub const SIG_DFL = @intToPtr(fn (i32) callconv(.C) void, 0); -pub const SIG_IGN = @intToPtr(fn (i32) callconv(.C) void, 1); +pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); +pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0); +pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1); /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { + pub const handler_fn = fn (c_int) callconv(.C) void; + pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void; + /// signal handler - __sigaction_u: extern union { - __sa_handler: fn (i32) callconv(.C) void, - __sa_sigaction: fn (i32, *__siginfo, usize) callconv(.C) void, + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, }, /// see signal options - sa_flags: u32, + flags: c_uint, /// signal mask to apply - sa_mask: sigset_t, + mask: sigset_t, +}; + +pub const siginfo_t = extern struct { + signo: c_int, + errno: c_int, + code: c_int, + pid: pid_t, + uid: uid_t, + status: c_int, + addr: ?*c_void, + value: sigval, + reason: extern union { + fault: extern struct { + trapno: c_int, + }, + timer: extern struct { + timerid: c_int, + overrun: c_int, + }, + mesgq: extern struct { + mqd: c_int, + }, + poll: extern struct { + band: c_long, + }, + spare: extern struct { + spare1: c_long, + spare2: [7]c_int, + }, + }, +}; + +pub const sigval = extern union { + int: c_int, + ptr: ?*c_void, }; pub const _SIG_WORDS = 4; @@ -775,6 +813,8 @@ pub const sigset_t = extern struct { __bits: [_SIG_WORDS]u32, }; +pub const empty_sigset = sigset_t{ .__bits = [_]u32{0} ** _SIG_WORDS }; + pub const EPERM = 1; // Operation not permitted pub const ENOENT = 2; // No such file or directory pub const ESRCH = 3; // No such process diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 6952ab7e0e..e86a08e861 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -864,27 +864,38 @@ pub const sigset_t = [1024 / 32]u32; pub const all_mask: sigset_t = [_]u32{0xffffffff} ** sigset_t.len; pub const app_mask: sigset_t = [2]u32{ 0xfffffffc, 0x7fffffff } ++ [_]u32{0xffffffff} ** 30; -pub const k_sigaction = if (is_mips) - extern struct { - flags: usize, - sigaction: ?fn (i32, *siginfo_t, ?*c_void) callconv(.C) void, - mask: [4]u32, +pub const k_sigaction = switch (builtin.arch) { + .mips, .mipsel => extern struct { + flags: c_uint, + handler: ?fn (c_int) callconv(.C) void, + mask: [4]c_ulong, restorer: fn () callconv(.C) void, - } -else - extern struct { - sigaction: ?fn (i32, *siginfo_t, ?*c_void) callconv(.C) void, - flags: usize, + }, + .mips64, .mips64el => extern struct { + flags: c_uint, + handler: ?fn (c_int) callconv(.C) void, + mask: [2]c_ulong, restorer: fn () callconv(.C) void, - mask: [2]u32, - }; + }, + else => extern struct { + handler: ?fn (c_int) callconv(.C) void, + flags: c_ulong, + restorer: fn () callconv(.C) void, + mask: [2]c_uint, + }, +}; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const sigaction_fn = fn (i32, *siginfo_t, ?*c_void) callconv(.C) void; - sigaction: ?sigaction_fn, + pub const handler_fn = fn (c_int) callconv(.C) void; + pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void; + + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, mask: sigset_t, - flags: u32, + flags: c_uint, restorer: ?fn () callconv(.C) void = null, }; diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index 0f4b6b60fa..be25284b73 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -716,13 +716,18 @@ pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1); /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const sigaction_fn = fn (i32, *siginfo_t, ?*c_void) callconv(.C) void; + pub const handler_fn = fn (c_int) callconv(.C) void; + pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void; + /// signal handler - sigaction: ?sigaction_fn, + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, /// signal mask to apply mask: sigset_t, /// signal options - flags: u32, + flags: c_uint, }; pub const sigval_t = extern union { @@ -800,6 +805,10 @@ pub const sigset_t = extern struct { __bits: [_SIG_WORDS]u32, }; +pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); +pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0); +pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1); + pub const empty_sigset = sigset_t{ .__bits = [_]u32{0} ** _SIG_WORDS }; // XXX x86_64 specific diff --git a/lib/std/os/bits/openbsd.zig b/lib/std/os/bits/openbsd.zig index c85a476e02..5a2d929d03 100644 --- a/lib/std/os/bits/openbsd.zig +++ b/lib/std/os/bits/openbsd.zig @@ -750,16 +750,23 @@ const NSIG = 33; pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0); pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1); +pub const SIG_CATCH = @intToPtr(?Sigaction.sigaction_fn, 2); +pub const SIG_HOLD = @intToPtr(?Sigaction.sigaction_fn, 3); /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const sigaction_fn = fn (c_int, *siginfo_t, ?*c_void) callconv(.C) void; + pub const handler_fn = fn (c_int) callconv(.C) void; + pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void; + /// signal handler - sigaction: ?sigaction_fn, + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, /// signal mask to apply mask: sigset_t, /// signal options - flags: c_int, + flags: c_uint, }; pub const sigval = extern union { @@ -767,12 +774,7 @@ pub const sigval = extern union { ptr: ?*c_void, }; -pub const siginfo_t = extern union { - pad: [128]u8, - info: _ksiginfo, -}; - -pub const _ksiginfo = extern struct { +pub const siginfo_t = extern struct { signo: c_int, code: c_int, errno: c_int, @@ -789,11 +791,20 @@ pub const _ksiginfo = extern struct { addr: ?*c_void, trapno: c_int, }, - } align(@sizeOf(usize)), + __pad: [128 - 3 * @sizeOf(c_int)]u8, + }, }; +comptime { + if (@sizeOf(usize) == 4) + std.debug.assert(@sizeOf(siginfo_t) == 128) + else + // Take into account the padding between errno and data fields. + std.debug.assert(@sizeOf(siginfo_t) == 136); +} + pub const sigset_t = c_uint; -pub const empty_sigset = sigset_t(0); +pub const empty_sigset: sigset_t = 0; pub const EPERM = 1; // Operation not permitted pub const ENOENT = 2; // No such file or directory diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index b669ed2436..f840f6a255 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -857,35 +857,42 @@ pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?* return syscall4(.rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); } -pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { +pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize { assert(sig >= 1); assert(sig != SIGKILL); assert(sig != SIGSTOP); - const restorer_fn = if ((act.flags & SA_SIGINFO) != 0) restore_rt else restore; - var ksa = k_sigaction{ - .sigaction = act.sigaction, - .flags = act.flags | SA_RESTORER, - .mask = undefined, - .restorer = @ptrCast(fn () callconv(.C) void, restorer_fn), - }; - var ksa_old: k_sigaction = undefined; - const ksa_mask_size = @sizeOf(@TypeOf(ksa_old.mask)); - @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), ksa_mask_size); + var ksa: k_sigaction = undefined; + var oldksa: k_sigaction = undefined; + const mask_size = @sizeOf(@TypeOf(ksa.mask)); + + if (act) |new| { + const restorer_fn = if ((new.flags & SA_SIGINFO) != 0) restore_rt else restore; + ksa = k_sigaction{ + .handler = new.handler.handler, + .flags = new.flags | SA_RESTORER, + .mask = undefined, + .restorer = @ptrCast(fn () callconv(.C) void, restorer_fn), + }; + @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &new.mask), mask_size); + } + + const ksa_arg = if (act != null) @ptrToInt(&ksa) else 0; + const oldksa_arg = if (oact != null) @ptrToInt(&oldksa) else 0; + const result = switch (builtin.arch) { // The sparc version of rt_sigaction needs the restorer function to be passed as an argument too. - .sparc, .sparcv9 => syscall5(.rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @ptrToInt(ksa.restorer), ksa_mask_size), - else => syscall4(.rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), ksa_mask_size), + .sparc, .sparcv9 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @ptrToInt(ksa.restorer), mask_size), + else => syscall4(.rt_sigaction, sig, ksa_arg, oldksa_arg, mask_size), }; - const err = getErrno(result); - if (err != 0) { - return result; - } + if (getErrno(result) != 0) return result; + if (oact) |old| { - old.sigaction = ksa_old.sigaction; - old.flags = @truncate(u32, ksa_old.flags); - @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), ksa_mask_size); + old.handler.handler = oldksa.handler; + old.flags = @truncate(c_uint, oldksa.flags); + @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &oldksa.mask), mask_size); } + return 0; } diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 81f9922d13..8ad172679b 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -646,3 +646,41 @@ test "shutdown socket" { }; os.closeSocket(sock); } + +var signal_test_failed = true; + +test "sigaction" { + if (builtin.os.tag == .wasi or builtin.os.tag == .windows) + return error.SkipZigTest; + + // https://github.com/ziglang/zig/issues/7427 + if (builtin.os.tag == .linux and builtin.arch == .i386) + return error.SkipZigTest; + + const S = struct { + fn handler(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) void { + // Check that we received the correct signal. + if (sig == os.SIGUSR1 and sig == info.signo) + signal_test_failed = false; + } + }; + + var sa = os.Sigaction{ + .handler = .{ .sigaction = S.handler }, + .mask = os.empty_sigset, + .flags = os.SA_SIGINFO | os.SA_RESETHAND, + }; + var old_sa: os.Sigaction = undefined; + // Install the new signal handler. + os.sigaction(os.SIGUSR1, &sa, null); + // Check that we can read it back correctly. + os.sigaction(os.SIGUSR1, null, &old_sa); + testing.expectEqual(S.handler, old_sa.handler.sigaction.?); + testing.expect((old_sa.flags & os.SA_SIGINFO) != 0); + // Invoke the handler. + try os.raise(os.SIGUSR1); + testing.expect(signal_test_failed == false); + // Check if the handler has been correctly reset to SIG_DFL + os.sigaction(os.SIGUSR1, null, &old_sa); + testing.expectEqual(os.SIG_DFL, old_sa.handler.sigaction); +}