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