hare

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

+freebsd.ha (15222B)


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