commit 91e8be385cdcc53bb8703f317e6f1f7fd4239e4f (tree)
parent a471a57560401c5562c4dc3e588227b689bf9487
Author: Jakub Konka <kubkon@jakubkonka.com>
Date: Mon, 14 Dec 2020 17:16:55 +0100
Merge pull request #7411 from LemonBoy/sigaction-smoke
std: Improve sigaction interface
Diffstat:
11 files changed, 219 insertions(+), 72 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
+}