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