mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
150 lines
5.4 KiB
Zig
150 lines
5.4 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
|
|
const native_os = builtin.target.os.tag;
|
|
|
|
pub fn main() !void {
|
|
if (native_os == .wasi or native_os == .windows) {
|
|
return; // no sigaction
|
|
}
|
|
|
|
try test_sigaction();
|
|
try test_sigset_bits();
|
|
}
|
|
|
|
fn test_sigaction() !void {
|
|
if (native_os == .macos and builtin.target.cpu.arch == .x86_64) {
|
|
return; // https://github.com/ziglang/zig/issues/15381
|
|
}
|
|
|
|
const test_signo: std.posix.SIG = .URG; // URG only because it is ignored by default in debuggers
|
|
|
|
const S = struct {
|
|
var handler_called_count: u32 = 0;
|
|
|
|
fn handler(sig: std.posix.SIG, info: *const std.posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void {
|
|
_ = ctx_ptr;
|
|
// Check that we received the correct signal.
|
|
const info_sig = switch (native_os) {
|
|
.netbsd => info.info.signo,
|
|
else => info.signo,
|
|
};
|
|
if (sig == test_signo and sig == info_sig) {
|
|
handler_called_count += 1;
|
|
}
|
|
}
|
|
};
|
|
|
|
var sa: std.posix.Sigaction = .{
|
|
.handler = .{ .sigaction = &S.handler },
|
|
.mask = std.posix.sigemptyset(),
|
|
.flags = std.posix.SA.SIGINFO | std.posix.SA.RESETHAND,
|
|
};
|
|
|
|
var old_sa: std.posix.Sigaction = undefined;
|
|
|
|
// Install the new signal handler.
|
|
std.posix.sigaction(test_signo, &sa, null);
|
|
|
|
// Check that we can read it back correctly.
|
|
std.posix.sigaction(test_signo, null, &old_sa);
|
|
try std.testing.expectEqual(&S.handler, old_sa.handler.sigaction.?);
|
|
try std.testing.expect((old_sa.flags & std.posix.SA.SIGINFO) != 0);
|
|
|
|
// Invoke the handler.
|
|
try std.posix.raise(test_signo);
|
|
try std.testing.expectEqual(1, S.handler_called_count);
|
|
|
|
// Check if passing RESETHAND correctly reset the handler to SIG_DFL
|
|
std.posix.sigaction(test_signo, null, &old_sa);
|
|
try std.testing.expectEqual(std.posix.SIG.DFL, old_sa.handler.handler);
|
|
|
|
// Reinstall the signal w/o RESETHAND and re-raise
|
|
sa.flags = std.posix.SA.SIGINFO;
|
|
std.posix.sigaction(test_signo, &sa, null);
|
|
try std.posix.raise(test_signo);
|
|
try std.testing.expectEqual(2, S.handler_called_count);
|
|
|
|
// Now set the signal to ignored
|
|
sa.handler = .{ .handler = std.posix.SIG.IGN };
|
|
sa.flags = 0;
|
|
std.posix.sigaction(test_signo, &sa, null);
|
|
|
|
// Re-raise to ensure handler is actually ignored
|
|
try std.posix.raise(test_signo);
|
|
try std.testing.expectEqual(2, S.handler_called_count);
|
|
|
|
// Ensure that ignored state is returned when querying
|
|
std.posix.sigaction(test_signo, null, &old_sa);
|
|
try std.testing.expectEqual(std.posix.SIG.IGN, old_sa.handler.handler);
|
|
}
|
|
|
|
fn test_sigset_bits() !void {
|
|
const S = struct {
|
|
var expected_sig: std.posix.SIG = undefined;
|
|
var seen_sig: ?std.posix.SIG = null;
|
|
|
|
fn handler(sig: std.posix.SIG, info: *const std.posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void {
|
|
_ = ctx_ptr;
|
|
|
|
const info_sig = switch (native_os) {
|
|
.netbsd => info.info.signo,
|
|
else => info.signo,
|
|
};
|
|
if (seen_sig == null and sig == expected_sig and sig == info_sig) {
|
|
seen_sig = sig;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Assume this is a single-threaded process where the current thread has the
|
|
// 'pid' thread id. (The sigprocmask calls are thread-private state.)
|
|
const self_tid = std.posix.system.getpid();
|
|
|
|
// To check that sigset_t mapping matches kernel (think u32/u64 mismatches on
|
|
// big-endian), try sending a blocked signal to make sure the mask matches the
|
|
// signal. (Send URG and CHLD because they're ignored by default in the
|
|
// debugger, vs. USR1 or other named signals)
|
|
inline for ([_]std.posix.SIG{ .URG, .CHLD }) |test_signo| {
|
|
S.expected_sig = test_signo;
|
|
S.seen_sig = null;
|
|
|
|
const sa: std.posix.Sigaction = .{
|
|
.handler = .{ .sigaction = &S.handler },
|
|
.mask = std.posix.sigemptyset(),
|
|
.flags = std.posix.SA.SIGINFO | std.posix.SA.RESETHAND,
|
|
};
|
|
|
|
var old_sa: std.posix.Sigaction = undefined;
|
|
|
|
// Install the new signal handler.
|
|
std.posix.sigaction(test_signo, &sa, &old_sa);
|
|
|
|
// block the signal and see that its delayed until unblocked
|
|
var block_one: std.posix.sigset_t = std.posix.sigemptyset();
|
|
std.posix.sigaddset(&block_one, test_signo);
|
|
std.posix.sigprocmask(std.posix.SIG.BLOCK, &block_one, null);
|
|
|
|
// qemu maps target signals to host signals 1-to-1, so targets
|
|
// with more signals than the host will fail to send the signal.
|
|
const rc = std.posix.system.kill(self_tid, test_signo);
|
|
switch (std.posix.errno(rc)) {
|
|
.SUCCESS => {
|
|
// See that the signal is blocked, then unblocked
|
|
try std.testing.expectEqual(null, S.seen_sig);
|
|
std.posix.sigprocmask(std.posix.SIG.UNBLOCK, &block_one, null);
|
|
try std.testing.expectEqual(test_signo, S.seen_sig);
|
|
},
|
|
.INVAL => {
|
|
// Signal won't get delviered. Just clean up.
|
|
std.posix.sigprocmask(std.posix.SIG.UNBLOCK, &block_one, null);
|
|
try std.testing.expectEqual(null, S.seen_sig);
|
|
},
|
|
else => |errno| return std.posix.unexpectedErrno(errno),
|
|
}
|
|
|
|
// Restore original handler
|
|
std.posix.sigaction(test_signo, &old_sa, null);
|
|
}
|
|
}
|