hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

+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 };