hare

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

commit 8349e34943dce950ce2eb8f9cf84c1a4f2425733
parent fa8d5b5f83d66760c833d887e9ada0f300bcbfc7
Author: Drew DeVault <sir@cmpwn.com>
Date:   Mon, 11 Apr 2022 16:25:20 +0200

unix::signal: new module

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mrt/+linux/types.ha | 2+-
Mscripts/gen-stdlib | 14+++++++++++---
Mstdlib.mk | 30++++++++++++++++++++++++++++++
Aunix/signal/+linux.ha | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aunix/signal/README | 14++++++++++++++
Aunix/signal/types.ha | 5+++++
6 files changed, 261 insertions(+), 4 deletions(-)

diff --git a/rt/+linux/types.ha b/rt/+linux/types.ha @@ -400,7 +400,7 @@ export def SIG_BLOCK: int = 0; export def SIG_UNBLOCK: int = 1; export def SIG_SETMASK: int = 2; -def SI_MAX_SIZE: size = 128 / size(u8); +def SI_MAX_SIZE: size = 128; export type sigval = union { sival_t: int, diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -1225,6 +1225,13 @@ unix_resolvconf() { gen_ssa unix::resolvconf os io bufio net::ip strings } +unix_signal() { + gen_srcs -plinux unix::signal \ + types.ha \ + +linux.ha + gen_ssa -plinux unix::signal +} + unix_tty() { gen_srcs -plinux unix::tty \ types.ha \ @@ -1301,7 +1308,7 @@ hash::crc16 hash::crc32 hash::crc64 hash::fnv -io linux freebsd +io linux freebsd iobus::io_uring linux linux linux linux::keyctl linux @@ -1310,7 +1317,7 @@ linux::io_uring linux linux::vdso linux math math::random -net linux freebsd +net linux freebsd net::dial net::dns net::ip linux freebsd @@ -1318,7 +1325,7 @@ net::tcp linux freebsd net::udp linux freebsd net::unix linux freebsd net::uri -os linux freebsd +os linux freebsd os::exec linux freebsd path regex @@ -1336,6 +1343,7 @@ unix::hosts unix::passwd unix::poll linux freebsd unix::resolvconf +unix::signal linux unix::tty linux freebsd uuid" stdlib() { diff --git a/stdlib.mk b/stdlib.mk @@ -654,6 +654,10 @@ stdlib_deps_any+=$(stdlib_unix_resolvconf_any) stdlib_unix_resolvconf_linux=$(stdlib_unix_resolvconf_any) stdlib_unix_resolvconf_freebsd=$(stdlib_unix_resolvconf_any) +# gen_lib unix::signal (linux) +stdlib_unix_signal_linux=$(HARECACHE)/unix/signal/unix_signal-linux.o +stdlib_deps_linux+=$(stdlib_unix_signal_linux) + # gen_lib unix::tty (linux) stdlib_unix_tty_linux=$(HARECACHE)/unix/tty/unix_tty-linux.o stdlib_deps_linux+=$(stdlib_unix_tty_linux) @@ -1863,6 +1867,17 @@ $(HARECACHE)/unix/resolvconf/unix_resolvconf-any.ssa: $(stdlib_unix_resolvconf_a @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nunix::resolvconf \ -t$(HARECACHE)/unix/resolvconf/unix_resolvconf.td $(stdlib_unix_resolvconf_any_srcs) +# unix::signal (+linux) +stdlib_unix_signal_linux_srcs= \ + $(STDLIB)/unix/signal/types.ha \ + $(STDLIB)/unix/signal/+linux.ha + +$(HARECACHE)/unix/signal/unix_signal-linux.ssa: $(stdlib_unix_signal_linux_srcs) $(stdlib_rt) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/unix/signal + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nunix::signal \ + -t$(HARECACHE)/unix/signal/unix_signal.td $(stdlib_unix_signal_linux_srcs) + # unix::tty (+linux) stdlib_unix_tty_linux_srcs= \ $(STDLIB)/unix/tty/types.ha \ @@ -2561,6 +2576,10 @@ testlib_deps_any+=$(testlib_unix_resolvconf_any) testlib_unix_resolvconf_linux=$(testlib_unix_resolvconf_any) testlib_unix_resolvconf_freebsd=$(testlib_unix_resolvconf_any) +# gen_lib unix::signal (linux) +testlib_unix_signal_linux=$(TESTCACHE)/unix/signal/unix_signal-linux.o +testlib_deps_linux+=$(testlib_unix_signal_linux) + # gen_lib unix::tty (linux) testlib_unix_tty_linux=$(TESTCACHE)/unix/tty/unix_tty-linux.o testlib_deps_linux+=$(testlib_unix_tty_linux) @@ -3815,6 +3834,17 @@ $(TESTCACHE)/unix/resolvconf/unix_resolvconf-any.ssa: $(testlib_unix_resolvconf_ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nunix::resolvconf \ -t$(TESTCACHE)/unix/resolvconf/unix_resolvconf.td $(testlib_unix_resolvconf_any_srcs) +# unix::signal (+linux) +testlib_unix_signal_linux_srcs= \ + $(STDLIB)/unix/signal/types.ha \ + $(STDLIB)/unix/signal/+linux.ha + +$(TESTCACHE)/unix/signal/unix_signal-linux.ssa: $(testlib_unix_signal_linux_srcs) $(testlib_rt) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/unix/signal + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nunix::signal \ + -t$(TESTCACHE)/unix/signal/unix_signal.td $(testlib_unix_signal_linux_srcs) + # unix::tty (+linux) testlib_unix_tty_linux_srcs= \ $(STDLIB)/unix/tty/types.ha \ diff --git a/unix/signal/+linux.ha b/unix/signal/+linux.ha @@ -0,0 +1,200 @@ +use rt; + +// Configures a new signal handler, returning the old details (which can be +// passed to [[restore]] to restore its behavior). +// +// The variadic parameters specify either [[flag]] to enable or a signal mask +// to use via [[sigset]]; if the latter is provided no more than one may be +// used. +export fn handle( + signum: signal, + handler: *handler, + opt: (flag | sigset)... +) sigaction = { + let sa_mask = newset(); + + let sa_flags = 0u64, nmask = 0; + for (let i = 0z; i < len(opt); i += 1) { + match (opt[i]) { + case let flag: flag => + sa_flags |= flag: u64; + case let mask: sigset => + assert(nmask == 0, "Multiple signal masks provided to signal::handle"); + nmask += 1; + sa_mask = mask; + }; + }; + + let new = rt::sigact { + sa_sigaction = handler: *fn(_: int, _: *rt::siginfo, _: *void) void, + sa_mask = sa_mask, + sa_flags = sa_flags, + // Filled in by rt: + sa_restorer = null: *fn () void, + }; + let old = rt::sigact { ... }; + match (rt::sigaction(signum, &new, &old)) { + case int => + yield; + case rt::errno => + abort("sigaction failed (invalid signal?)"); + }; + return old; +}; + +// Restores previous signal behavior following [[handle]]. +export fn restore(signum: signal, action: *sigaction) void = { + match (rt::sigaction(signum, action, null)) { + case int => + yield; + case rt::errno => + abort("sigaction failed (invalid signal?)"); + }; +}; + +export type sigaction = rt::sigact; + +export type sigset = rt::sigset; + +// Creates a new signal set filled in with the provided signals (or empty if +// none are provided). +export fn newset(items: signal...) sigset = { + let set = sigset { ... }; + rt::sigemptyset(&set); + set_add(&set, items...); + return set; +}; + +// Sets a [[sigset]] to empty. +export fn set_empty(set: *sigset) void = { + rt::sigemptyset(set); +}; + +// Adds signals to a [[sigset]]. +export fn set_add(set: *sigset, items: signal...) void = { + for (let i = 0z; i < len(items); i += 1) { + rt::sigaddset(set, items[i])!; + }; +}; + +// Removes signals from a [[sigset]]. +export fn set_del(set: *sigset, items: signal...) void = { + for (let i = 0z; i < len(items); i += 1) { + rt::sigdelset(set, items[i])!; + }; +}; + +// Returns true if the given signal is a member of this [[sigset]]. +export fn set_member(set: *sigset, item: signal) bool = { + return rt::sigismember(set, item)! == 1; +}; + +// Provides additional information about signal deliveries. Only the members +// defined by POSIX are available here; cast to [[rt::siginfo]] to access +// non-portable members. +// +// TODO: Expand this with more portable options +export type siginfo = union { + struct { + // The signal number being delivered. + signo: signal, + // The errno, if any, associated with this signal. See + // [[errors::errno]] to convert to a Hare-native error. + errno: rt::errno, + // The signal code, if any. + // TODO: Add enum type for this + code: int, + }, + // Pads the structure out to the length used by the kernel; do not use. + _si_pad: [128 - 3 * size(int)]u8, +}; + +// Flags used to configure the behavior of a signal handler. +export type flag = enum int { + // For use with [[SIGCHLD]]. Prevents notifications when child processes + // stop (e.g. via [[SIGSTOP]]) or resume (i.e. [[SIGCONT]]). + NOCLDSTOP = rt::SA_NOCLDSTOP: int, + // For use with [[SIGCHLD]]. Do not transform children into zombies when + // they terminate. Note that POSIX leaves the delivery of [[SIGCHLD]] + // unspecified when this flag is present; some systems will still + // deliver a signal and others may not. + NOCLDWAIT = rt::SA_NOCLDWAIT: int, + // Uses an alternate stack when handling this signal. See + // [[setaltstack]] and [[getaltstack]] for details. + ONSTACK = rt::SA_ONSTACK: int, + // Makes certain system calls restartable across signals. See signal(7) + // or similar documentation for your local system for details. + RESTART = rt::SA_RESTART: int, + // Do not add the signal to the signal mask while executing the signal + // handler. This can cause the same signal to be delivered again during + // the execution of the signal handler. + NODEFER = rt::SA_NODEFER: int, + // Restore the signal handler to the default behavior upon entering the + // signal handler. + RESETHAND = rt::SA_RESETHAND: int, +}; + +export type signal = int; + +// Hangup. +export def SIGHUP: signal = rt::SIGHUP; +// Terminal signalerrupt signal. +export def SIGsignal: signal = rt::SIGINT; +// Terminal quit signal. +export def SIGQUIT: signal = rt::SIGQUIT; +// Illegal instruction. +export def SIGILL: signal = rt::SIGILL; +// Trace/breakposignal trap. +export def SIGTRAP: signal = rt::SIGTRAP; +// Process abort signal. +export def SIGABRT: signal = rt::SIGABRT; +// Access to an undefined portion of a memory object. +export def SIGBUS: signal = rt::SIGBUS; +// Erroneous arithmetic operation. +export def SIGFPE: signal = rt::SIGFPE; +// Kill (cannot be caught or ignored). +export def SIGKILL: signal = rt::SIGKILL; +// User-defined signal 1. +export def SIGUSR1: signal = rt::SIGUSR1; +// Invalid memory reference. +export def SIGSEGV: signal = rt::SIGSEGV; +// User-defined signal 2. +export def SIGUSR2: signal = rt::SIGUSR2; +// Write on a pipe with no one to read it. +export def SIGPIPE: signal = rt::SIGPIPE; +// Alarm clock. +export def SIGALRM: signal = rt::SIGALRM; +// Termination signal. +export def SIGTERM: signal = rt::SIGTERM; +// Child process terminated, stopped, or continued. +export def SIGCHLD: signal = rt::SIGCHLD; +// Continue executing, if stopped. +export def SIGCONT: signal = rt::SIGCONT; +// Stop executing (cannot be caught or ignored). +export def SIGSTOP: signal = rt::SIGSTOP; +// Terminal stop signal. +export def SIGTSTP: signal = rt::SIGTSTP; +// Background process attempting read. +export def SIGTTIN: signal = rt::SIGTTIN; +// Background process attempting write. +export def SIGTTOU: signal = rt::SIGTTOU; +// High bandwidth data is available at a socket. +export def SIGURG: signal = rt::SIGURG; +// CPU time limit exceeded. +export def SIGXCPU: signal = rt::SIGXCPU; +// File size limit exceeded. +export def SIGXFSZ: signal = rt::SIGXFSZ; +// Virtual timer expired. +export def SIGVTALRM: signal = rt::SIGVTALRM; +// Profiling timer expired. +export def SIGPROF: signal = rt::SIGPROF; +// Window resize signal. +export def SIGWINCH: signal = rt::SIGWINCH; +// I/O now possible. +export def SIGIO: signal = rt::SIGIO; +// Pollable event. +export def SIGPOLL: signal = rt::SIGPOLL; +// Power failure. +export def SIGPWR: signal = rt::SIGPWR; +// Bad system call. +export def SIGSYS: signal = rt::SIGSYS; diff --git a/unix/signal/README b/unix/signal/README @@ -0,0 +1,14 @@ +The signal module provides support for Unix signal handlers. Typical +applications will provide a signal handler to [[handle]] to configure it for the +desired signal, possibly along with flags and a signal mask. This function +returns the previous signal handler, which can be passed to [[restore]] to +restore the previous behavior. + +Signal handling is stupidly complicated and easy to get wrong. The standard +library makes little effort to help you deal with this. Consult your local man +pages, particularly signal-safety(7) on Linux, and perhaps a local priest as +well. We advise you to get out of the signal handler as soon as possible, for +example via the "self-pipe trick". + +Note that the necessary sa_restorer functionality is implemented (and imposed) +by the standard library. diff --git a/unix/signal/types.ha b/unix/signal/types.ha @@ -0,0 +1,5 @@ +// A function which handles a signal. The first argument is the signal number +// which was caught, the second provides information about the signal, and the +// third argument is the ucontext, which is usually ignored by most signal +// handlers. +export type handler = fn(sig: int, info: *siginfo, ucontext: *void) void;