feat(signal): signal handling and sigset_t
This commit is contained in:
@@ -0,0 +1,436 @@
|
||||
#ifndef VLIBC_SIGNAL_H
|
||||
#define VLIBC_SIGNAL_H
|
||||
|
||||
/*
|
||||
* vlibc — <signal.h>.
|
||||
*
|
||||
* Signal handling (POSIX.1-2008 base) plus the XSI/obsolete conveniences.
|
||||
* The core calls ride the kernel's rt_* signal ABI (SYS_rt_sigaction,
|
||||
* SYS_rt_sigprocmask, SYS_rt_sigpending, SYS_rt_sigsuspend,
|
||||
* SYS_rt_sigtimedwait, SYS_rt_sigqueueinfo) with the x86_64 kernel
|
||||
* sigset size of one 64-bit word.
|
||||
*
|
||||
* Level 1 (onlyposix): signal numbers, SIG_DFL/SIG_IGN/SIG_ERR, the
|
||||
* sigset_t manipulation and query calls, sigaction,
|
||||
* sigprocmask, sigpending, sigsuspend, sigwait,
|
||||
* sigwaitinfo, sigtimedwait, kill, killpg, raise,
|
||||
* pause, alarm, abort, sigqueue, and the signal()
|
||||
* convenience.
|
||||
* Level 2 (muslmimic): sigaltstack (and stack_t / SS_* / SIGSTKSZ),
|
||||
* ualarm, siginterrupt, psignal, psiginfo (XSI /
|
||||
* obsolete — sigaction/sigprocmask/alarm are POSIX
|
||||
* base and stay at level 1).
|
||||
*
|
||||
* The layout facts below are x86_64 kernel-ABI:
|
||||
*
|
||||
* - The kernel sigset_t is 8 bytes (one word); signal N occupies bit
|
||||
* N-1, signals 1..64 are usable (32..64 are the realtime signals).
|
||||
* The same single-word sigset_t is declared by <sys/select.h> and
|
||||
* <poll.h> under the shared VLIBC_SIGSET_T_DEFINED guard so pselect/
|
||||
* ppoll and the sig* API agree on the type no matter the include order.
|
||||
* - SIG_DFL/SIG_IGN/SIG_ERR are the magic handler values 0/1/-1 cast to
|
||||
* the handler type; the kernel dispatches on the raw value.
|
||||
* - The PUBLIC struct sigaction is the POSIX layout (handler, mask,
|
||||
* int flags, restorer). The kernel's rt_sigaction ABI layout is
|
||||
* DIFFERENT (handler, flags, restorer, mask — flags is a full word);
|
||||
* src/signal/sigaction.c converts between the two.
|
||||
* - SA_RESTORER (0x04000000) is the kernel-private bit that makes the
|
||||
* kernel jump to the user-supplied sa_restorer trampoline when the
|
||||
* handler returns; every real (non-DFL/IGN) handler install sets it
|
||||
* (see sigaction.c). It is masked out of the flags reported back.
|
||||
*
|
||||
* siginfo_t is the minimal 128-byte layout shared with <sys/wait.h>
|
||||
* under the VLIBC_INTERNAL_SIGINFO_DEFINED guard (see the block below);
|
||||
* either header may be included first. No SIGEV_* / SI_* constants are
|
||||
* defined: the signal-handling details behind them are owned by a later
|
||||
* todo and nothing here needs the names.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* struct timespec is defined by <time.h>; a forward declaration is enough
|
||||
* for sigtimedwait()'s pointer parameter, and the two headers may be
|
||||
* included in either order.
|
||||
*/
|
||||
struct timespec;
|
||||
|
||||
/* Signal numbers (x86_64 asm-generic values; kernel-ABI facts). */
|
||||
#define SIGHUP 1 /* hangup detected on controlling terminal */
|
||||
#define SIGINT 2 /* interactive attention (^C) */
|
||||
#define SIGQUIT 3 /* interactive quit (^\) */
|
||||
#define SIGILL 4 /* illegal instruction */
|
||||
#define SIGTRAP 5 /* trace/breakpoint trap */
|
||||
#define SIGABRT 6 /* abnormal termination (abort()) */
|
||||
#define SIGBUS 7 /* bus error */
|
||||
#define SIGFPE 8 /* floating-point exception */
|
||||
#define SIGKILL 9 /* kill (cannot be caught or ignored) */
|
||||
#define SIGUSR1 10 /* user-defined signal 1 */
|
||||
#define SIGSEGV 11 /* invalid memory reference */
|
||||
#define SIGUSR2 12 /* user-defined signal 2 */
|
||||
#define SIGPIPE 13 /* write on a pipe with no reader */
|
||||
#define SIGALRM 14 /* real-time timer expired (alarm/ualarm/setitimer) */
|
||||
#define SIGTERM 15 /* termination request */
|
||||
#define SIGSTKFLT 16 /* stack fault (unused) */
|
||||
#define SIGCHLD 17 /* child stopped or terminated */
|
||||
#define SIGCONT 18 /* continue if stopped */
|
||||
#define SIGSTOP 19 /* stop (cannot be caught or ignored) */
|
||||
#define SIGTSTP 20 /* interactive stop (^Z) */
|
||||
#define SIGTTIN 21 /* background process read from terminal */
|
||||
#define SIGTTOU 22 /* background process write to terminal */
|
||||
#define SIGURG 23 /* urgent condition on a socket */
|
||||
#define SIGXCPU 24 /* CPU time limit exceeded */
|
||||
#define SIGXFSZ 25 /* file size limit exceeded */
|
||||
#define SIGVTALRM 26 /* virtual timer expired */
|
||||
#define SIGPROF 27 /* profiling timer expired */
|
||||
#define SIGWINCH 28 /* window size change */
|
||||
#define SIGIO 29 /* I/O now possible */
|
||||
#define SIGPWR 30 /* power failure */
|
||||
#define SIGSYS 31 /* bad system call */
|
||||
|
||||
/* Realtime signals: the 32..64 range, usable for user-defined purposes. */
|
||||
#define SIGRTMIN 32
|
||||
#define SIGRTMAX 64
|
||||
|
||||
/* One more than the highest signal number; signals 1..64 are usable. */
|
||||
#define NSIG 65
|
||||
|
||||
/* The magic handler values. The kernel dispatches on the raw value. */
|
||||
#define SIG_DFL ((void (*)(int))0) /* default action */
|
||||
#define SIG_IGN ((void (*)(int))1) /* ignore the signal */
|
||||
#define SIG_ERR ((void (*)(int)) - 1) /* signal() error return */
|
||||
|
||||
/*
|
||||
* Integer type that can be accessed as an atomic entity even when an
|
||||
* asynchronous signal interrupts the access (volatile sig_atomic_t is the
|
||||
* conventional spelling). int is atomic on x86_64.
|
||||
*/
|
||||
typedef int sig_atomic_t;
|
||||
|
||||
/* sigprocmask()/pthread_sigmask() how values (kernel-identical). */
|
||||
#define SIG_BLOCK 0 /* add the given set to the blocked mask */
|
||||
#define SIG_UNBLOCK 1 /* remove the given set from the blocked mask */
|
||||
#define SIG_SETMASK 2 /* replace the blocked mask with the given set */
|
||||
|
||||
#ifndef VLIBC_SIGSET_T_DEFINED
|
||||
#define VLIBC_SIGSET_T_DEFINED
|
||||
/*
|
||||
* Signal mask type: a single 64-bit word — the x86_64 Linux sigset_t,
|
||||
* where signal N is bit N-1 (see setjmp.h, whose sigsetjmp stores this
|
||||
* same word). The typedef is shared with <sys/select.h> and <poll.h>
|
||||
* under this guard, so pselect()/ppoll() and the sig* API agree on the
|
||||
* type regardless of include order.
|
||||
*/
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Value carried with a queued (realtime) signal. sival_int and sival_ptr
|
||||
* are alternative views of the same 64-bit payload.
|
||||
*/
|
||||
union sigval
|
||||
{
|
||||
int sival_int; /* integer payload */
|
||||
void *sival_ptr; /* pointer payload */
|
||||
};
|
||||
|
||||
/* POSIX spelling of the payload type. */
|
||||
typedef union sigval sigval_t;
|
||||
|
||||
/*
|
||||
* Description of a delivered signal: number, errno, code and one of the
|
||||
* per-code unions. This is the minimal x86_64 kernel layout (128 bytes)
|
||||
* shared with <sys/wait.h>, which fills si_pid/si_uid/si_status/si_utime/
|
||||
* si_stime for waitid(); the two headers define this exact block under the
|
||||
* common VLIBC_INTERNAL_SIGINFO_DEFINED guard, so whichever is included
|
||||
* first wins and the other skips — the type is identical either way. The
|
||||
* kernel copies the full 128 bytes on signal delivery (sigaction with
|
||||
* SA_SIGINFO, sigwaitinfo, sigtimedwait), so the size must stay 128 and
|
||||
* the pad member guarantees it; the fields behind each si_code value
|
||||
* (si_addr, si_value, timers, ...) are owned by a later todo.
|
||||
*/
|
||||
#ifndef VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
#define VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
typedef struct
|
||||
{
|
||||
int si_signo;
|
||||
int si_errno;
|
||||
int si_code;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
pid_t si_pid; /* 16 */
|
||||
uid_t si_uid; /* 20 */
|
||||
int si_status; /* 24 */
|
||||
long si_utime; /* 32 — 8-byte clock_t, matches the kernel/glibc ABI */
|
||||
long si_stime; /* 40 */
|
||||
};
|
||||
int vlibc_siginfo_pad[28]; /* union sized 112 so the struct stays 128 */
|
||||
};
|
||||
} siginfo_t;
|
||||
|
||||
_Static_assert(sizeof(siginfo_t) == 128, "siginfo_t must match the kernel size");
|
||||
_Static_assert(offsetof(siginfo_t, si_pid) == 16, "si_pid must sit at offset 16");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Action taken when signal sig is delivered. sa_handler and sa_sigaction
|
||||
* are two views of the same slot (POSIX: sa_sigaction is used when
|
||||
* SA_SIGINFO is set and receives (sig, siginfo_t *, void *) instead of
|
||||
* just sig). The layout — handler, mask, flags, restorer — is the PUBLIC
|
||||
* POSIX order; the kernel ABI uses a different order (see the file-top
|
||||
* note) that sigaction() translates.
|
||||
*/
|
||||
struct sigaction
|
||||
{
|
||||
union
|
||||
{
|
||||
void (*sa_handler)(int); /* SIG_DFL/SIG_IGN/a handler */
|
||||
void (*sa_sigaction)(int, siginfo_t *, void *); /* SA_SIGINFO form */
|
||||
};
|
||||
sigset_t sa_mask; /* signals additionally blocked in the handler */
|
||||
int sa_flags; /* SA_* bits below */
|
||||
void (*sa_restorer)(void); /* kernel-private; do not set (see top) */
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct sigaction) == 32, "struct sigaction must be 32 bytes");
|
||||
_Static_assert(offsetof(struct sigaction, sa_mask) == 8, "sa_mask must sit at offset 8");
|
||||
_Static_assert(offsetof(struct sigaction, sa_flags) == 16, "sa_flags must sit at offset 16");
|
||||
_Static_assert(offsetof(struct sigaction, sa_restorer) == 24, "sa_restorer must sit at offset 24");
|
||||
|
||||
/* sa_flags bits (x86_64 kernel values; SA_RESTORER is kernel-private). */
|
||||
#define SA_NOCLDSTOP 1 /* do not generate SIGCHLD when children stop */
|
||||
#define SA_NOCLDWAIT 2 /* do not leave zombies on child exit */
|
||||
#define SA_SIGINFO 4 /* call the handler with the siginfo form */
|
||||
#define SA_RESTORER 0x04000000 /* handler returns via sa_restorer (kernel) */
|
||||
#define SA_ONSTACK 0x08000000 /* run the handler on the alternate stack */
|
||||
#define SA_RESTART 0x10000000 /* restart interrupted syscalls after the handler */
|
||||
#define SA_NODEFER 0x40000000 /* do not block the delivered signal in its handler */
|
||||
#define SA_RESETHAND 0x80000000 /* reset the disposition to SIG_DFL on entry */
|
||||
|
||||
/*
|
||||
* Install the action act for signal sig (NULL leaves it unchanged) and
|
||||
* store the previous action through oact (NULL skips the store). Return
|
||||
* 0, or -1 with errno set. sig must be in 1..64 and (for a catching
|
||||
* action) not SIGKILL/SIGSTOP; the kernel rejects invalid values with
|
||||
* EINVAL. Real handler pointers are installed with SA_RESTORER and the
|
||||
* internal return trampoline so the handler may return normally.
|
||||
*/
|
||||
int
|
||||
sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
|
||||
|
||||
/*
|
||||
* Inspect or change the calling thread's blocked-signal mask. how is
|
||||
* SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK; set is the mask operand (NULL
|
||||
* queries without changing: how is then ignored) and the previous mask is
|
||||
* stored through oldset (NULL skips the store). Return 0, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset);
|
||||
|
||||
/* Store the set of blocked-and-pending signals through set. 0 or -1. */
|
||||
int
|
||||
sigpending(sigset_t *set);
|
||||
|
||||
/*
|
||||
* Atomically install mask as the blocked mask and wait until a signal
|
||||
* whose delivery is not blocked by mask is caught; when its handler
|
||||
* returns, the previous mask is restored and sigsuspend returns -1 with
|
||||
* errno EINTR.
|
||||
*/
|
||||
int
|
||||
sigsuspend(const sigset_t *mask);
|
||||
|
||||
/* Empty set: no signal blocked, and no member set. Always 0. */
|
||||
int
|
||||
sigemptyset(sigset_t *set);
|
||||
|
||||
/* Set every signal 1..64 blocked/member. Always 0. */
|
||||
int
|
||||
sigfillset(sigset_t *set);
|
||||
|
||||
/*
|
||||
* Add/remove signal sig to/from set. Return 0, or -1 with errno EINVAL
|
||||
* when sig is outside 1..64.
|
||||
*/
|
||||
int
|
||||
sigaddset(sigset_t *set, int sig);
|
||||
int
|
||||
sigdelset(sigset_t *set, int sig);
|
||||
|
||||
/*
|
||||
* Nonzero when sig is a member of set, 0 otherwise, or -1 with errno
|
||||
* EINVAL when sig is outside 1..64.
|
||||
*/
|
||||
int
|
||||
sigismember(const sigset_t *set, int sig);
|
||||
|
||||
/*
|
||||
* Synchronously wait for one of the signals in set (which should be
|
||||
* blocked in the calling thread) and store the delivered signal number
|
||||
* through sig. Return 0 on success, or the error number (EINTR is
|
||||
* retried internally). The signal is consumed and never delivered to a
|
||||
* handler.
|
||||
*/
|
||||
int
|
||||
sigwait(const sigset_t *restrict set, int *restrict sig);
|
||||
|
||||
/*
|
||||
* Like sigwait, but return the delivered signal number directly (or -1
|
||||
* with errno set) and, when info is not NULL, store the full 128-byte
|
||||
* kernel siginfo through it.
|
||||
*/
|
||||
int
|
||||
sigwaitinfo(const sigset_t *restrict set, siginfo_t *restrict info);
|
||||
|
||||
/*
|
||||
* sigwaitinfo bounded by timeout (relative; NULL waits indefinitely).
|
||||
* Returns the signal number, 0 is never returned for a successful wait,
|
||||
* or -1 with errno set (EAGAIN on timeout, EINTR if a handler ran).
|
||||
*/
|
||||
int
|
||||
sigtimedwait(const sigset_t *restrict set, siginfo_t *restrict info,
|
||||
const struct timespec *restrict timeout);
|
||||
|
||||
/*
|
||||
* Send signal sig to the process pid (negative pid targets a process
|
||||
* group; 0 targets the caller's process group; see killpg). Return 0, or
|
||||
* -1 with errno set. Permission, existence and signal validity are
|
||||
* checked by the kernel.
|
||||
*/
|
||||
int
|
||||
kill(pid_t pid, int sig);
|
||||
|
||||
/*
|
||||
* Send signal sig to every process in the process group pgrp (0 selects
|
||||
* the caller's process group). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
killpg(pid_t pgrp, int sig);
|
||||
|
||||
/*
|
||||
* Send signal sig to the calling thread (thread-directed, so it is
|
||||
* delivered even when another thread of the process has it blocked).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
raise(int sig);
|
||||
|
||||
/*
|
||||
* Wait until a signal is caught, then return -1 with errno EINTR. If the
|
||||
* process is terminated by the signal instead, pause never returns.
|
||||
*/
|
||||
int
|
||||
pause(void);
|
||||
|
||||
/*
|
||||
* Schedule delivery of SIGALRM to the calling process after seconds
|
||||
* seconds (0 cancels any pending alarm). Return the number of seconds
|
||||
* remaining on any previously scheduled alarm, or 0.
|
||||
*/
|
||||
unsigned
|
||||
alarm(unsigned seconds);
|
||||
|
||||
/*
|
||||
* Abnormally terminate the calling process: raise SIGABRT with default
|
||||
* disposition — if SIGABRT is blocked it is unblocked first, and if it
|
||||
* is caught or ignored the disposition is reset to SIG_DFL and the raise
|
||||
* repeated. If that still returns, terminate with exit status 134 as a
|
||||
* last resort. abort never returns.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
abort(void);
|
||||
|
||||
/*
|
||||
* Send signal sig with payload value to process pid, as if by a kernel
|
||||
* queue operation (the kernel sees a negative si_code, SI_QUEUE). Return
|
||||
* 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
sigqueue(pid_t pid, int sig, const union sigval value);
|
||||
|
||||
/*
|
||||
* Install handler as the action for sig with SA_RESTART (BSD semantics:
|
||||
* syscalls interrupted by the signal are restarted). Return the previous
|
||||
* handler, or SIG_ERR with errno set.
|
||||
*/
|
||||
void (*signal(int sig, void (*handler)(int)))(int);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI and obsolete conveniences. */
|
||||
|
||||
/*
|
||||
* Alternate signal stack descriptor. ss_sp/ss_size name a region the
|
||||
* kernel switches to when delivering a handler installed with SA_ONSTACK
|
||||
* (see sigaltstack below). stack_t is the POSIX spelling.
|
||||
*/
|
||||
typedef struct sigaltstack
|
||||
{
|
||||
void *ss_sp; /* stack base or current stack base */
|
||||
int ss_flags; /* SS_ONSTACK/SS_DISABLE */
|
||||
size_t ss_size; /* stack bytes */
|
||||
} stack_t;
|
||||
|
||||
/* ss_flags values. */
|
||||
#define SS_ONSTACK 1 /* the process is currently executing on this stack */
|
||||
#define SS_DISABLE 2 /* the alternate stack is currently disabled */
|
||||
|
||||
/* Minimum / default alternate stack sizes (x86_64 values). */
|
||||
#define MINSIGSTKSZ 2048
|
||||
#define SIGSTKSZ 8192
|
||||
|
||||
/*
|
||||
* Install ss as the alternate signal stack (NULL leaves it unchanged)
|
||||
* and store the previous descriptor through oss (NULL skips the store).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
sigaltstack(const stack_t *restrict ss, stack_t *restrict oss);
|
||||
|
||||
/*
|
||||
* Schedule SIGALRM after usecs microseconds, repeating every interval
|
||||
* microseconds (0 fires once). Return the number of microseconds
|
||||
* remaining on any previously scheduled alarm, or -1 on error.
|
||||
*/
|
||||
useconds_t
|
||||
ualarm(useconds_t usecs, useconds_t interval);
|
||||
|
||||
/*
|
||||
* Toggle the SA_RESTART bit of sig's disposition: flag nonzero removes
|
||||
* it (interrupted syscalls return EINTR, System V semantics), flag zero
|
||||
* restores it. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
siginterrupt(int sig, int flag);
|
||||
|
||||
/*
|
||||
* Write s (when non-NULL and nonempty), ": " and strsignal(sig) to
|
||||
* stderr. The output is a diagnostic only.
|
||||
*/
|
||||
void
|
||||
psignal(int sig, const char *s);
|
||||
|
||||
/*
|
||||
* Like psignal, but the signal number and details are taken from the
|
||||
* siginfo si (si_signo names the signal). si may be NULL, in which case
|
||||
* only the message prefix is written.
|
||||
*/
|
||||
void
|
||||
psiginfo(const siginfo_t *si, const char *s);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SIGNAL_H */
|
||||
Reference in New Issue
Block a user