hare

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

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