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:
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;