nx_signalfd.nx source
↩ module page · 109 lines · 4103 B
1// nx_signalfd.nx -- Linux signalfd(2) wrapper.
2//
3// signalfd lets the kernel deliver signals via file descriptor
4// reads instead of signal handlers. This is the modern way to
5// integrate signal handling with poll/epoll-driven event loops:
6// you add the signalfd to your poll set and react to signals
7// inline, no async-signal-safe constraints.
8//
9// Linux RV64 syscall: 74 (signalfd4).
10//
11// To receive a signal via signalfd, you ALSO need to mask the
12// signal in the process's normal signal mask via rt_sigprocmask
13// (otherwise the kernel still delivers it normally). We expose
14// nx_signalfd_install which does both steps.
15//
16// Read returns one or more struct signalfd_siginfo records (128
17// bytes each); the first 4 bytes are the signal number (i32).
18//
19// Pairs with nx_signal (constants + sigaction) + nx_poll
20// (consumer in event loops).
21
22// nx_safety_envelope:
23// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
24// sil_target: SIL1
25// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
26// verdict: NOT_YET_EVALUATED
27
28import "syscalls.nx"
29import "nx_signal.nx"
30
31const NX_SYS_SIGNALFD4: i64 = 74
32const NX_SYS_RT_SIGPROCMASK: i64 = 135
33
34const NX_SFD_NONBLOCK: i64 = 0x800
35const NX_SFD_CLOEXEC: i64 = 0x80000
36
37// SIG_BLOCK / SIG_UNBLOCK / SIG_SETMASK constants.
38const NX_SIG_BLOCK: i64 = 0
39const NX_SIG_UNBLOCK: i64 = 1
40const NX_SIG_SETMASK: i64 = 2
41
42// signalfd_siginfo.ssi_signo offset = 0 (i32).
43const NX_SSI_BYTES: i64 = 128
44
45// Build a sigset_t mask containing exactly `sig`. RV64 sigset_t
46// is a single 64-bit word for signals 1..64; bit (sig-1) selects
47// the signal.
48func nx_signalfd_mask_one(sig: i64) -> i64 {
49 return 1 << (sig - 1)
50}
51
52// Block `sig` in the current thread's signal mask (so the kernel
53// queues it for signalfd consumption instead of running a
54// handler). Returns 0 on success or -errno.
55func nx_signalfd_block(sig: i64) -> i64 {
56 let mask_raw: *u8 = sys_mmap(16)
57 let mask: *i64 = mask_raw as *i64
58 *mask = nx_signalfd_mask_one(sig)
59 return __syscall(NX_SYS_RT_SIGPROCMASK, NX_SIG_BLOCK, mask_raw as i64, 0, 8, 0, 0)
60}
61
62// Create a signalfd that delivers `sig` (mask containing only that
63// signal). Returns fd >= 0 or -errno. Caller must ALSO call
64// nx_signalfd_block(sig) for delivery to actually go through this fd.
65func nx_signalfd_new(sig: i64, flags: i64) -> i64 {
66 let mask_raw: *u8 = sys_mmap(16)
67 let mask: *i64 = mask_raw as *i64
68 *mask = nx_signalfd_mask_one(sig)
69 return __syscall(NX_SYS_SIGNALFD4, 0 - 1, mask_raw as i64, 8, flags, 0, 0)
70}
71
72// Convenience: block + create signalfd in one call. Returns the
73// fd or -errno.
74func nx_signalfd_install(sig: i64, flags: i64) -> i64 {
75 let r: i64 = nx_signalfd_block(sig)
76 if r < 0 { return r }
77 return nx_signalfd_new(sig, flags)
78}
79
80// Read one signalfd_siginfo record into `out` (caller supplies
81// >= 128 bytes). Returns the signal number (>0) or negative errno.
82// Note: this differs from raw read; it returns the SSI signo
83// extracted from the struct as a convenience.
84func nx_signalfd_read_one(fd: i64, out: *u8) -> i64 {
85 let n: i64 = sys_read(fd, out, NX_SSI_BYTES)
86 if n != NX_SSI_BYTES { return n }
87 let signo_p: *i32 = out as *i32
88 return signo_p[0]
89}
90
91// ---- self-test ---------------------------------------------------
92
93func main() -> i64 {
94 if NX_SFD_CLOEXEC != 0x80000 { return __syscall(93, 1, 0, 0, 0, 0, 0) }
95 if NX_SIG_BLOCK != 0 { return __syscall(93, 2, 0, 0, 0, 0, 0) }
96
97 // Mask helper: SIGUSR1 = 10, bit 9.
98 if nx_signalfd_mask_one(NX_SIGUSR1) != (1 << 9) { return __syscall(93, 3, 0, 0, 0, 0, 0) }
99 // SIGTERM = 15, bit 14.
100 if nx_signalfd_mask_one(NX_SIGTERM) != (1 << 14) { return __syscall(93, 4, 0, 0, 0, 0, 0) }
101
102 // Create signalfd for SIGUSR1. Don't block delivery (would
103 // disturb the test harness). Just verify fd creation succeeds.
104 let fd: i64 = nx_signalfd_new(NX_SIGUSR1, NX_SFD_NONBLOCK | NX_SFD_CLOEXEC)
105 if fd < 0 { return __syscall(93, 5, 0, 0, 0, 0, 0) }
106
107 sys_close(fd)
108 return 0
109}