+linux.ha (13578B)
1 // License: MPL-2.0 2 // (c) 2022 Drew DeVault <sir@cmpwn.com> 3 use errors; 4 use io; 5 use rt; 6 7 // Configures a new signal handler, returning the old details (which can be 8 // passed to [[restore]] to restore its behavior). 9 // 10 // The variadic parameters specify either [[flag]]s to enable or a signal mask 11 // to use via [[sigset]]; if the latter is provided no more than one may be 12 // used. 13 export fn handle( 14 signum: sig, 15 handler: *handler, 16 opt: (flag | sigset)... 17 ) sigaction = { 18 let sa_mask = newsigset(); 19 20 let sa_flags = 0u64, nmask = 0; 21 for (let i = 0z; i < len(opt); i += 1) { 22 match (opt[i]) { 23 case let flag: flag => 24 sa_flags |= flag: u64; 25 case let mask: sigset => 26 assert(nmask == 0, "Multiple signal masks provided to signal::handle"); 27 nmask += 1; 28 sa_mask = mask; 29 }; 30 }; 31 32 let new = rt::sigact { 33 sa_sigaction = handler: *fn(int, *rt::siginfo, *void) void, 34 sa_mask = sa_mask, 35 sa_flags = sa_flags, 36 // Filled in by rt: 37 sa_restorer = null: *fn () void, 38 }; 39 let old = rt::sigact { 40 // Filled in by rt: 41 sa_sigaction = null: *fn(int, *rt::siginfo, *void) void, 42 sa_restorer = null: *fn() void, 43 ... 44 }; 45 match (rt::sigaction(signum, &new, &old)) { 46 case int => 47 yield; 48 case rt::errno => 49 abort("sigaction failed (invalid signal?)"); 50 }; 51 return old; 52 }; 53 54 // Restores previous signal behavior following [[handle]]. 55 export fn restore(signum: sig, action: *sigaction) void = { 56 match (rt::sigaction(signum, action: *rt::sigact, null)) { 57 case int => 58 yield; 59 case rt::errno => 60 abort("sigaction failed (invalid signal?)"); 61 }; 62 }; 63 64 // Unregisters signal handlers for the specified signal. 65 export fn reset(signum: sig) void = { 66 handle(signum, rt::SIG_DFL: *handler); 67 }; 68 69 // Unregisters all signal handlers. 70 export fn resetall() void = { 71 // sig::KILL and sig::STOP deliberately omitted; see sigaction(2) 72 reset(sig::HUP); 73 reset(sig::INT); 74 reset(sig::QUIT); 75 reset(sig::ILL); 76 reset(sig::TRAP); 77 reset(sig::ABRT); 78 reset(sig::BUS); 79 reset(sig::FPE); 80 reset(sig::USR1); 81 reset(sig::SEGV); 82 reset(sig::USR2); 83 reset(sig::PIPE); 84 reset(sig::ALRM); 85 reset(sig::TERM); 86 reset(sig::CHLD); 87 reset(sig::CONT); 88 reset(sig::TSTP); 89 reset(sig::TTIN); 90 reset(sig::TTOU); 91 reset(sig::URG); 92 reset(sig::XCPU); 93 reset(sig::XFSZ); 94 reset(sig::VTALRM); 95 reset(sig::PROF); 96 reset(sig::WINCH); 97 reset(sig::IO); 98 reset(sig::POLL); 99 reset(sig::PWR); 100 reset(sig::SYS); 101 }; 102 103 // Prevents given signal from arriving to the current process. 104 // One common use case is to ignore SIGCHLD to avoid zombie child processes. 105 export fn ignore(signum: sig) void = { 106 handle(signum, rt::SIG_IGN: *handler); 107 }; 108 109 // Adds the given list of signals to the process's current signal mask, 110 // returning the old signal mask. This is a convenience function around 111 // [[setprocmask]]. 112 export fn block(signals: sig...) sigset = { 113 let new = newsigset(signals...); 114 return setprocmask(how::BLOCK, &new); 115 }; 116 117 // Removes the given list of signals from the process's current signal mask, 118 // returning the old signal mask. This is a convenience function around 119 // [[setprocmask]]. 120 export fn unblock(signals: sig...) sigset = { 121 let new = newsigset(signals...); 122 return setprocmask(how::UNBLOCK, &new); 123 }; 124 125 // Sets the process's signal mask, returning the previous mask. 126 export fn setprocmask(how: how, mask: *sigset) sigset = { 127 let old = sigset { ... }; 128 rt::sigprocmask(how, mask: *rt::sigset, &old)!; 129 return old; 130 }; 131 132 // Gets the current process's signal mask. 133 export fn getprocmask() sigset = { 134 let old = sigset { ... }; 135 rt::sigprocmask(how::SETMASK, null, &old)!; 136 return old; 137 }; 138 139 // Defines the modes of operation for [[setprocmask]]. 140 export type how = enum int { 141 // Adds the given set of signals to the current mask. 142 BLOCK = rt::SIG_BLOCK, 143 // Removes the given set of signals from the current mask. 144 UNBLOCK = rt::SIG_UNBLOCK, 145 // Sets the process mask to the given set. 146 SETMASK = rt::SIG_SETMASK, 147 }; 148 149 export type sigaction = rt::sigact; 150 151 export type sigset = rt::sigset; 152 153 // Creates a new signal set filled in with the provided signals (or empty if 154 // none are provided). 155 export fn newsigset(items: sig...) sigset = { 156 let set = sigset { ... }; 157 rt::sigemptyset(&set); 158 sigset_add(&set, items...); 159 return set; 160 }; 161 162 // Sets a [[sigset]] to empty. 163 export fn sigset_empty(set: *sigset) void = { 164 rt::sigemptyset(set: *rt::sigset); 165 }; 166 167 // Adds signals to a [[sigset]]. 168 export fn sigset_add(set: *sigset, items: sig...) void = { 169 for (let i = 0z; i < len(items); i += 1) { 170 rt::sigaddset(set: *rt::sigset, items[i])!; 171 }; 172 }; 173 174 // Removes signals from a [[sigset]]. 175 export fn sigset_del(set: *sigset, items: sig...) void = { 176 for (let i = 0z; i < len(items); i += 1) { 177 rt::sigdelset(set: *rt::sigset, items[i])!; 178 }; 179 }; 180 181 // Returns true if the given signal is a member of this [[sigset]]. 182 export fn sigset_member(set: *sigset, item: sig) bool = { 183 return rt::sigismember(set: *rt::sigset, item)!; 184 }; 185 186 // Provides additional information about signal deliveries. Only the members 187 // defined by POSIX are available here; cast to [[rt::siginfo]] to access 188 // non-portable members. 189 // 190 // TODO: Expand this with more portable options 191 export type siginfo = union { 192 struct { 193 // The signal number being delivered. 194 signo: sig, 195 // The errno, if any, associated with this signal. See 196 // [[errors::errno]] to convert to a Hare-native error. 197 errno: rt::errno, 198 // The signal code, if any. 199 code: code, 200 }, 201 // Pads the structure out to the length used by the kernel; do not use. 202 _si_pad: [128 - 3 * size(int)]u8, 203 }; 204 205 // A code indicating why a signal was sent. 206 export type code = enum int { 207 USER = 0, // sent by userspace program (kill) 208 KERNEL = 128, // sent by kernel 209 QUEUE = -1, // sent by sigqueue 210 TIMER = -2, // generated by expiration of a timer 211 MESQ = -3, // generated by arrival of a message on an empty queue 212 ASYNCIO = -4, // generated by completion of an asynchronous I/O request 213 SIGIO = -5, 214 TKILL = -6, // sent by userspace program (tkill, tgkill) 215 ASYNCNL = -60, 216 217 ILLOPC = 1, // sig::ILL: illegal opcode 218 ILLOPN = 2, // sig::ILL: illegal operand 219 ILLADR = 3, // sig::ILL: illegal addressing mode 220 ILLTRP = 4, // sig::ILL: illegal trap 221 PRVOPC = 5, // sig::ILL: privileged opcode 222 PRVREG = 6, // sig::ILL: privileged register 223 COPROC = 7, // sig::ILL: coprocessor error 224 BADSTK = 8, // sig::ILL: internal stack error 225 226 INTDIV = 1, // sig::FPE: integer divide by zero 227 INTOVF = 2, // sig::FPE: integer overflow 228 FLTDIV = 3, // sig::FPE: floating-point divide by zero 229 FLTOVF = 4, // sig::FPE: floating-point overflow 230 FLTUND = 5, // sig::FPE: floating-point underflow 231 FLTRES = 6, // sig::FPE: floating-point inexact result 232 FLTINV = 7, // sig::FPE: invalid floating-point operation 233 FLTSUB = 8, // sig::FPE: subscript out of range 234 235 MAPERR = 1, // sig::SEGV: address not mapped to object 236 ACCERR = 2, // sig::SEGV: invalid permissions for mapped object 237 BNDERR = 3, // sig::SEGV: failed address bound checks 238 PKUERR = 4, // sig::SEGV: access was denied by memory protection keys 239 MTEAERR = 8, // sig::SEGV 240 MTESERR = 9, // sig::SEGV 241 242 ADRALN = 1, // sig::BUS: invalid address alignment 243 ADRERR = 2, // sig::BUS: nonexistent physical address 244 OBJERR = 3, // sig::BUS: object-specific hardware error 245 MCEERR_AR = 4, // sig::BUS: hardware memory error consumed on a machine check; action required 246 MCEERR_AO = 5, // sig::BUS: hardware memory error detected in process but not consumed; action optional 247 248 BRKPT = 1, // sig::TRAP: process breakpoint 249 TRACE = 2, // sig::TRAP: process trace trap 250 BRANCH = 3, // sig::TRAP: process taken branch trap 251 HWBKPT = 4, // sig::TRAP: hardware breakpoint/watchpoint 252 UNK = 5, // sig::TRAP 253 254 EXITED = 1, // sig::CHLD: child exited 255 KILLED = 2, // sig::CHLD: child terminated abnormally without a core file 256 DUMPED = 3, // sig::CHLD: child terminated abnormally with a core file 257 TRAPPED = 4, // sig::CHLD: traced child has trapped 258 STOPPED = 5, // sig::CHLD: child has stopped 259 CONTINUED = 6, // sig::CHLD: stopped child has continued 260 261 IN = 1, // sig::POLL: data input available 262 OUT = 2, // sig::POLL: output buffers available 263 MSG = 3, // sig::POLL: input message available 264 ERR = 4, // sig::POLL: I/O error 265 PRI = 5, // sig::POLL: high priority input available 266 HUP = 6, // sig::POLL: device disconnected 267 }; 268 269 // Flags used to configure the behavior of a signal handler. 270 export type flag = enum int { 271 // For use with sig::CHLD. Prevents notifications when child processes 272 // stop (e.g. via sig::STOp) or resume (i.e. sig::CONT). 273 NOCLDSTOP = rt::SA_NOCLDSTOP: int, 274 // For use with sig::CHLD. Do not transform children into zombies when 275 // they terminate. Note that POSIX leaves the delivery of sig::CHLD 276 // unspecified when this flag is present; some systems will still 277 // deliver a signal and others may not. 278 NOCLDWAIT = rt::SA_NOCLDWAIT: int, 279 // Uses an alternate stack when handling this signal. See 280 // [[setaltstack]] and [[getaltstack]] for details. 281 ONSTACK = rt::SA_ONSTACK: int, 282 // Makes certain system calls restartable across signals. See signal(7) 283 // or similar documentation for your local system for details. 284 RESTART = rt::SA_RESTART: int, 285 // Do not add the signal to the signal mask while executing the signal 286 // handler. This can cause the same signal to be delivered again during 287 // the execution of the signal handler. 288 NODEFER = rt::SA_NODEFER: int, 289 // Restore the signal handler to the default behavior upon entering the 290 // signal handler. 291 RESETHAND = rt::SA_RESETHAND: int, 292 }; 293 294 // All possible signals. 295 export type sig = enum int { 296 HUP = rt::SIGHUP, // Hangup. 297 INT = rt::SIGINT, // Terminal interrupt. 298 QUIT = rt::SIGQUIT, // Terminal quit. 299 ILL = rt::SIGILL, // Illegal instruction. 300 TRAP = rt::SIGTRAP, // Trace/breakpoint trap. 301 ABRT = rt::SIGABRT, // Process abort. 302 BUS = rt::SIGBUS, // Access to an undefined portion of a memory object. 303 FPE = rt::SIGFPE, // Erroneous arithmetic operation. 304 KILL = rt::SIGKILL, // Kill (cannot be caught or ignored). 305 USR1 = rt::SIGUSR1, // User-defined signal 1. 306 SEGV = rt::SIGSEGV, // Invalid memory reference. 307 USR2 = rt::SIGUSR2, // User-defined signal 2. 308 PIPE = rt::SIGPIPE, // Write on a pipe with no one to read it. 309 ALRM = rt::SIGALRM, // Alarm clock. 310 TERM = rt::SIGTERM, // Termination. 311 CHLD = rt::SIGCHLD, // Child process terminated, stopped, or continued. 312 CONT = rt::SIGCONT, // Continue executing if stopped. 313 STOP = rt::SIGSTOP, // Stop executing (cannot be caught or ignored). 314 TSTP = rt::SIGTSTP, // Terminal stop. 315 TTIN = rt::SIGTTIN, // Background process attempting read. 316 TTOU = rt::SIGTTOU, // Background process attempting write. 317 URG = rt::SIGURG, // High bandwidth data is available at a socket. 318 XCPU = rt::SIGXCPU, // CPU time limit exceeded. 319 XFSZ = rt::SIGXFSZ, // File size limit exceeded. 320 VTALRM = rt::SIGVTALRM, // Virtual timer expired. 321 PROF = rt::SIGPROF, // Profiling timer expired. 322 WINCH = rt::SIGWINCH, // Window resize signal. 323 IO = rt::SIGIO, // I/O now possible. 324 POLL = rt::SIGPOLL, // Pollable event. 325 PWR = rt::SIGPWR, // Power failure. 326 SYS = rt::SIGSYS, // Bad system call. 327 }; 328 329 // Creates a signal file that handles the given set of signals. 330 export fn signalfd(signals: sig...) (io::file | errors::error) = { 331 let sa_mask = newsigset(signals...); 332 match (rt::signalfd(-1, &sa_mask, rt::SFD_CLOEXEC)) { 333 case let fd: int => 334 return fd; 335 case let err: rt::errno => 336 return errors::errno(err); 337 }; 338 }; 339 340 // Updates a signalfd with a new set of signals. The signal set is overwritten, 341 // rather than appended to, with the provided set of signals. 342 export fn update(fd: io::file, signals: sig...) (void | errors::error) = { 343 let sa_mask = newsigset(signals...); 344 345 match (rt::signalfd(fd, &sa_mask, rt::SFD_CLOEXEC)) { 346 case int => 347 return; 348 case let err: rt::errno => 349 return errors::errno(err); 350 }; 351 }; 352 353 // Reads pending signal info from a signalfd. 354 export fn read(fd: io::file) (siginfo | errors::error) = { 355 let si = rt::signalfd_siginfo { ... }; 356 match (rt::read(fd, &si, size(rt::signalfd_siginfo))) { 357 case let err: rt::errno => 358 return errors::errno(err); 359 case let z: size => 360 assert(z == size(rt::signalfd_siginfo)); 361 }; 362 return siginfo { 363 signo = si.ssi_signo: sig, 364 errno = si.ssi_errno: rt::errno, 365 code = si.ssi_code: code, 366 ... 367 }; 368 }; 369 370 // Returns the human friendly name of a given signal. 371 export fn signame(sig: sig) const str = { 372 switch (sig) { 373 case sig::HUP => 374 return "SIGHUP"; 375 case sig::INT => 376 return "SIGINT"; 377 case sig::QUIT => 378 return "SIGQUIT"; 379 case sig::ILL => 380 return "SIGILL"; 381 case sig::TRAP => 382 return "SIGTRAP"; 383 case sig::ABRT => 384 return "SIGABRT"; 385 case sig::BUS => 386 return "SIGBUS"; 387 case sig::FPE => 388 return "SIGFPE"; 389 case sig::KILL => 390 return "SIGKILL"; 391 case sig::USR1 => 392 return "SIGUSR1"; 393 case sig::SEGV => 394 return "SIGSEGV"; 395 case sig::USR2 => 396 return "SIGUSR2"; 397 case sig::PIPE => 398 return "SIGPIPE"; 399 case sig::ALRM => 400 return "SIGALRM"; 401 case sig::TERM => 402 return "SIGTERM"; 403 case sig::CHLD => 404 return "SIGCHLD"; 405 case sig::CONT => 406 return "SIGCONT"; 407 case sig::STOP => 408 return "SIGSTOP"; 409 case sig::TSTP => 410 return "SIGTSTP"; 411 case sig::TTIN => 412 return "SIGTTIN"; 413 case sig::TTOU => 414 return "SIGTTOU"; 415 case sig::URG => 416 return "SIGURG"; 417 case sig::XCPU => 418 return "SIGXCPU"; 419 case sig::XFSZ => 420 return "SIGXFSZ"; 421 case sig::VTALRM => 422 return "SIGVTALRM"; 423 case sig::PROF => 424 return "SIGPROF"; 425 case sig::WINCH => 426 return "SIGWINCH"; 427 case sig::IO => 428 return "SIGIO"; 429 case sig::POLL => 430 return "SIGPOLL"; 431 case sig::PWR => 432 return "SIGPWR"; 433 case sig::SYS => 434 return "SIGSYS"; 435 case => abort(); // unreachable 436 }; 437 };