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