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 */
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* abort: raise SIGABRT with default disposition, then die regardless.
|
||||
*
|
||||
* The sequence follows POSIX: first the blocked mask is cleared for
|
||||
* SIGABRT (a blocked SIGABRT would silently pend and abort would look
|
||||
* like success), then SIGABRT is raised with whatever disposition is
|
||||
* installed. If that raise returned, the disposition was caught (handler
|
||||
* that returned) or ignored — so the disposition is reset to SIG_DFL and
|
||||
* the raise repeated, which terminates the process. If even the default
|
||||
* raise returned (impossible in a healthy kernel), _exit with 128+6
|
||||
* (= 134, the shell convention for death-by-SIGABRT) is the last resort.
|
||||
* All of it is raw syscalls and the functions in this directory: abort
|
||||
* must work from a half-initialized runtime and never touch stdio.
|
||||
*/
|
||||
|
||||
__attribute__((noreturn)) void
|
||||
abort(void)
|
||||
{
|
||||
sigset_t unblock;
|
||||
struct sigaction dfl;
|
||||
|
||||
/* Make sure SIGABRT is not blocked, then try the installed action. */
|
||||
sigemptyset(&unblock);
|
||||
sigaddset(&unblock, SIGABRT);
|
||||
sigprocmask(SIG_UNBLOCK, &unblock, NULL);
|
||||
raise(SIGABRT);
|
||||
|
||||
/* Returned: caught by a returning handler, or ignored. Go default. */
|
||||
dfl.sa_handler = SIG_DFL;
|
||||
dfl.sa_flags = 0;
|
||||
sigemptyset(&dfl.sa_mask);
|
||||
sigaction(SIGABRT, &dfl, NULL);
|
||||
raise(SIGABRT);
|
||||
|
||||
/* Still here: the kernel failed to deliver. Die with the SIGABRT code. */
|
||||
__syscall1(SYS_exit_group, 134);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* alarm: schedule a one-shot SIGALRM in seconds seconds.
|
||||
*
|
||||
* Pure SYS_alarm pass-through: the kernel arms the private per-process
|
||||
* alarm timer, returns the seconds remaining on any alarm it just
|
||||
* replaced (0 when none was pending), and treats 0 as "cancel the pending
|
||||
* alarm". The raw result is always non-negative, so syscall_ret cannot
|
||||
* mistake it for an error.
|
||||
*/
|
||||
|
||||
unsigned
|
||||
alarm(unsigned seconds)
|
||||
{
|
||||
return (unsigned)syscall_ret(__syscall1(SYS_alarm, (long)seconds));
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* kill/killpg: process- and process-group-directed signals over SYS_kill.
|
||||
*
|
||||
* kill passes its pid straight to the kernel, which interprets it exactly
|
||||
* as POSIX specifies: pid > 0 is a single process, pid == 0 the caller's
|
||||
* process group, pid == -1 every process the caller may signal, and
|
||||
* pid < -1 the process group -pid. killpg(pgrp, sig) is equivalent to
|
||||
* kill(-pgrp, sig): a negative pgrp is not a valid group id and is
|
||||
* rejected with EINVAL here (mirroring musl) rather than inverted into a
|
||||
* positive-pid send by accident.
|
||||
*/
|
||||
|
||||
int
|
||||
kill(pid_t pid, int sig)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_kill, (long)pid, sig));
|
||||
}
|
||||
|
||||
int
|
||||
killpg(pid_t pgrp, int sig)
|
||||
{
|
||||
if (pgrp < 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return kill(-pgrp, sig);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
/*
|
||||
* pause: sleep until a signal is caught.
|
||||
*
|
||||
* pause() must wait with nothing blocked, so it is sigsuspend on an empty
|
||||
* mask. When a caught signal's handler returns, sigsuspend reports -1
|
||||
* with errno EINTR (never restarted by the kernel), which is exactly what
|
||||
* POSIX specifies for pause. A signal whose default action terminates the
|
||||
* process makes pause never return.
|
||||
*/
|
||||
|
||||
int
|
||||
pause(void)
|
||||
{
|
||||
sigset_t empty = 0;
|
||||
|
||||
return sigsuspend(&empty);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* psignal/psiginfo: print a signal diagnostic to stderr (XSI).
|
||||
*
|
||||
* Both write the message argument s (when present), ": ", and
|
||||
* strsignal()'s description of the signal, then a newline, directly to
|
||||
* fd 2 with raw SYS_write — no stdio, so the calls work even when the
|
||||
* runtime is only partially up. psignal takes the signal number;
|
||||
* psiginfo reads si_signo out of a filled siginfo (a NULL si prints only
|
||||
* the message prefix).
|
||||
*/
|
||||
|
||||
/* Write the NUL-terminated string s to fd 2. */
|
||||
static void
|
||||
write_stderr(const char *s)
|
||||
{
|
||||
if (s != NULL)
|
||||
{
|
||||
__syscall3(SYS_write, 2, (long)s, (long)strlen(s));
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the shared "<s>: <description>\n" shape to stderr. */
|
||||
static void
|
||||
signal_message(const char *s, const char *description)
|
||||
{
|
||||
write_stderr(s);
|
||||
if (s != NULL && s[0] != '\0')
|
||||
{
|
||||
write_stderr(": ");
|
||||
}
|
||||
write_stderr(description);
|
||||
write_stderr("\n");
|
||||
}
|
||||
|
||||
void
|
||||
psignal(int sig, const char *s)
|
||||
{
|
||||
signal_message(s, strsignal(sig));
|
||||
}
|
||||
|
||||
void
|
||||
psiginfo(const siginfo_t *si, const char *s)
|
||||
{
|
||||
if (si == NULL)
|
||||
{
|
||||
signal_message(s, "");
|
||||
return;
|
||||
}
|
||||
signal_message(s, strsignal(si->si_signo));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* raise: deliver sig to the calling thread.
|
||||
*
|
||||
* POSIX requires raise to be thread-directed (the calling thread must
|
||||
* receive the signal even when sibling threads of the same process block
|
||||
* it), so this rides SYS_tgkill with the calling thread's pid and tid,
|
||||
* not the process-directed SYS_kill. The pid/tid come from raw syscalls:
|
||||
* they are kernel reads, and gettid also has no thread-library
|
||||
* dependency. The library is single-threaded today, but a process->
|
||||
* directed raise would already be the wrong thread once threads exist.
|
||||
*/
|
||||
|
||||
int
|
||||
raise(int sig)
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall3(SYS_tgkill, __syscall0(SYS_getpid), __syscall0(SYS_gettid), (long)sig));
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigaction: install or query a signal disposition over SYS_rt_sigaction.
|
||||
*
|
||||
* The kernel's x86_64 sigaction ABI is NOT the POSIX struct layout:
|
||||
*
|
||||
* POSIX (public, struct sigaction): handler, mask, flags(int), restorer
|
||||
* kernel (rt_sigaction argument): handler, flags(64-bit), restorer,
|
||||
* mask
|
||||
*
|
||||
* and the kernel copies a full 32-byte word for the flags slot, so the
|
||||
* upper 32 bits of that slot must be zero (they hold the padding of the
|
||||
* int-typed public flags). This file therefore marshals between a
|
||||
* zeroed kernel-layout struct and the public struct, field by field.
|
||||
*
|
||||
* The kernel does not fall back to the vDSO return stub on x86_64: when a
|
||||
* real (non-SIG_DFL/SIG_IGN) handler is installed it MUST carry
|
||||
* SA_RESTORER plus a sa_restorer trampoline, or delivery crashes when the
|
||||
* handler returns. The trampoline below is the classic two-instruction
|
||||
* sequence (mov $SYS_rt_sigreturn; syscall) — the same shape glibc and
|
||||
* musl ship — entered with the stack already positioned at the kernel's
|
||||
* rt_sigframe, so it must be naked: any prologue would shift the stack
|
||||
* and make rt_sigreturn restore garbage. SA_RESTORER is masked out of the
|
||||
* flags reported through oact so callers only ever see the POSIX bits.
|
||||
*
|
||||
* No signal validity checking happens here: the kernel rejects out-of
|
||||
* range numbers (EINVAL) and attempts to catch SIGKILL/SIGSTOP, and this
|
||||
* wrapper is a pure pass-through of its results.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The kernel-layout sigaction (see above). sa_restorer and the trampoline
|
||||
* are only attached for real handlers.
|
||||
*/
|
||||
struct vlibc_kernel_sigaction
|
||||
{
|
||||
void (*vlibc_handler)(int); /* offset 0: handler or sa_sigaction */
|
||||
unsigned long vlibc_flags; /* offset 8: full 64-bit flags word */
|
||||
void (*vlibc_restorer)(void); /* offset 16: return trampoline */
|
||||
unsigned long vlibc_mask; /* offset 24: blocked-during-handler set */
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct vlibc_kernel_sigaction) == 32, "kernel sigaction must be 32 bytes");
|
||||
_Static_assert(offsetof(struct vlibc_kernel_sigaction, vlibc_flags) == 8,
|
||||
"kernel flags must sit at offset 8");
|
||||
_Static_assert(offsetof(struct vlibc_kernel_sigaction, vlibc_restorer) == 16,
|
||||
"kernel restorer must sit at offset 16");
|
||||
_Static_assert(offsetof(struct vlibc_kernel_sigaction, vlibc_mask) == 24,
|
||||
"kernel mask must sit at offset 24");
|
||||
|
||||
/*
|
||||
* The rt_sigreturn trampoline every real handler returns through. Naked:
|
||||
* it is entered by the handler's ret with the stack already pointing at
|
||||
* the kernel's rt_sigframe, so no prologue/epilogue may run.
|
||||
*/
|
||||
static void __attribute__((naked, noreturn))
|
||||
vlibc_restore_rt(void)
|
||||
{
|
||||
__asm__ volatile("movq $15, %rax\n\tsyscall");
|
||||
}
|
||||
|
||||
int
|
||||
sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact)
|
||||
{
|
||||
struct vlibc_kernel_sigaction kact;
|
||||
struct vlibc_kernel_sigaction kold;
|
||||
struct vlibc_kernel_sigaction *kactp;
|
||||
long r;
|
||||
|
||||
if (act != NULL)
|
||||
{
|
||||
/* Marshal the public struct into the kernel layout (see top). */
|
||||
kactp = &kact;
|
||||
kact.vlibc_handler = act->sa_handler;
|
||||
kact.vlibc_flags = (unsigned long)act->sa_flags;
|
||||
kact.vlibc_restorer = NULL;
|
||||
kact.vlibc_mask = (unsigned long)act->sa_mask;
|
||||
if (act->sa_handler != SIG_DFL && act->sa_handler != SIG_IGN)
|
||||
{
|
||||
kact.vlibc_flags |= (unsigned long)SA_RESTORER;
|
||||
kact.vlibc_restorer = vlibc_restore_rt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kactp = NULL;
|
||||
}
|
||||
|
||||
r = __syscall4(SYS_rt_sigaction, sig, (long)kactp, oact != NULL ? (long)&kold : 0L,
|
||||
(long)sizeof(sigset_t));
|
||||
if (r < 0)
|
||||
{
|
||||
return syscall_ret(r);
|
||||
}
|
||||
|
||||
if (oact != NULL)
|
||||
{
|
||||
/* Marshal the kernel layout back into the public struct. */
|
||||
oact->sa_handler = kold.vlibc_handler;
|
||||
oact->sa_mask = (sigset_t)kold.vlibc_mask;
|
||||
oact->sa_flags = (int)(kold.vlibc_flags & ~(unsigned long)SA_RESTORER);
|
||||
oact->sa_restorer = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* sigaltstack: install or query the alternate signal stack over
|
||||
* SYS_sigaltstack (ss, oss). The kernel's stack_t — base pointer, flags
|
||||
* int, size — is byte-identical to the public stack_t, so it passes
|
||||
* through unmodified. A handler installed with SA_ONSTACK runs on the
|
||||
* alternate stack; the kernel restores the ordinary stack on return.
|
||||
*/
|
||||
|
||||
int
|
||||
sigaltstack(const stack_t *restrict ss, stack_t *restrict oss)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_sigaltstack, (long)ss, (long)oss));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* siginterrupt: toggle the SA_RESTART bit of sig's disposition.
|
||||
*
|
||||
* sig is the XSI way to switch between System V semantics (flag nonzero:
|
||||
* interrupted syscalls return EINTR — SA_RESTART cleared) and BSD
|
||||
* semantics (flag zero: syscalls restart — SA_RESTART set) for one
|
||||
* signal. The current action is read, its SA_RESTART bit updated, and the
|
||||
* action reinstalled through sigaction, which re-attaches the return
|
||||
* trampoline as needed.
|
||||
*/
|
||||
|
||||
int
|
||||
siginterrupt(int sig, int flag)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
if (sigaction(sig, NULL, &sa) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (flag != 0)
|
||||
{
|
||||
sa.sa_flags &= ~SA_RESTART;
|
||||
}
|
||||
else
|
||||
{
|
||||
sa.sa_flags |= SA_RESTART;
|
||||
}
|
||||
return sigaction(sig, &sa, NULL);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
/*
|
||||
* signal: the System V convenience, defined on top of sigaction with BSD
|
||||
* restart semantics. It installs handler for sig with SA_RESTART (syscalls
|
||||
* interrupted by the signal are restarted) and an empty supplementary
|
||||
* mask, then returns the previous disposition. SIG_DFL/SIG_IGN pass
|
||||
* through unchanged. Errors are reported as SIG_ERR with errno set.
|
||||
*/
|
||||
|
||||
void (*signal(int sig, void (*handler)(int)))(int)
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct sigaction old;
|
||||
|
||||
sa.sa_handler = handler;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
if (sigaction(sig, &sa, &old) == -1)
|
||||
{
|
||||
return SIG_ERR;
|
||||
}
|
||||
return old.sa_handler;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigprocmask/sigpending: the blocked-mask pair over the rt_* syscalls.
|
||||
*
|
||||
* sigprocmask rides SYS_rt_sigprocmask (how, set, oldset, sigsetsize).
|
||||
* The kernel reads the one-word sigset when set is non-NULL and applies
|
||||
* it per how; a NULL set with SIG_BLOCK is the POSIX-blessed way to query
|
||||
* the current mask (how is then ignored). sigpending rides SYS_rt_sigpending
|
||||
* (set, sigsetsize) and reports the signals that are BOTH blocked and
|
||||
* pending. Both are pure pass-throughs: syscall_ret turns the kernel's
|
||||
* -errno convention into -1 + errno.
|
||||
*/
|
||||
|
||||
int
|
||||
sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset)
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall4(SYS_rt_sigprocmask, how, (long)set, (long)oldset, (long)sizeof(sigset_t)));
|
||||
}
|
||||
|
||||
int
|
||||
sigpending(sigset_t *set)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_rt_sigpending, (long)set, (long)sizeof(sigset_t)));
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigqueue: send sig with an attached sigval payload to process pid over
|
||||
* SYS_rt_sigqueueinfo (pid, sig, siginfo *).
|
||||
*
|
||||
* The kernel validates and forwards a full 128-byte siginfo; the sender
|
||||
* must place the payload where the kernel's SI_QUEUE union reads it:
|
||||
* si_code at offset 8 (a negative value — SI_QUEUE is -1 — because the
|
||||
* kernel refuses si_code >= 0 with EPERM: only the kernel may send with a
|
||||
* positive code) and the sigval at offset 24 (after the pid/uid words the
|
||||
* kernel reserves for the receiving side). The private struct below pins
|
||||
* exactly that; si_signo is set for completeness though the kernel
|
||||
* overwrites it with the sig argument.
|
||||
*/
|
||||
struct vlibc_rt_sigqueue_info
|
||||
{
|
||||
int si_signo; /* 0 */
|
||||
int si_errno; /* 4 */
|
||||
int si_code; /* 8 */
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
pid_t si_pid; /* 16 */
|
||||
uid_t si_uid; /* 20 */
|
||||
union sigval si_value; /* 24 */
|
||||
};
|
||||
int vlibc_pad[28]; /* union sized 112 so the struct stays 128 */
|
||||
};
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct vlibc_rt_sigqueue_info) == 128,
|
||||
"rt_sigqueueinfo needs a full 128-byte siginfo");
|
||||
_Static_assert(offsetof(struct vlibc_rt_sigqueue_info, si_code) == 8,
|
||||
"si_code must sit at offset 8");
|
||||
|
||||
int
|
||||
sigqueue(pid_t pid, int sig, const union sigval value)
|
||||
{
|
||||
struct vlibc_rt_sigqueue_info si;
|
||||
|
||||
si.si_signo = sig;
|
||||
si.si_errno = 0;
|
||||
si.si_code = -1; /* SI_QUEUE; negative per the kernel's from-user check */
|
||||
si.si_pid = 0;
|
||||
si.si_uid = 0;
|
||||
si.si_value = value;
|
||||
|
||||
return syscall_ret(__syscall3(SYS_rt_sigqueueinfo, (long)pid, sig, (long)&si));
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* sigemptyset/sigfillset/sigaddset/sigdelset/sigismember: the sigset_t
|
||||
* manipulation API over the single 64-bit word kernel sigset (signal N
|
||||
* is bit N-1). No syscalls are involved; only the out-of-range checks
|
||||
* report EINVAL. Signals 1..64 are addressable (SIGRTMAX == 64), so the
|
||||
* shift for the highest signal is 1UL << 63.
|
||||
*/
|
||||
|
||||
/* True when sig names a usable signal (1..NSIG-1, i.e. 1..64). */
|
||||
static int
|
||||
vlibc_sig_valid(int sig)
|
||||
{
|
||||
return sig >= 1 && sig < NSIG;
|
||||
}
|
||||
|
||||
int
|
||||
sigemptyset(sigset_t *set)
|
||||
{
|
||||
*set = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigfillset(sigset_t *set)
|
||||
{
|
||||
*set = ~(sigset_t)0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigaddset(sigset_t *set, int sig)
|
||||
{
|
||||
if (!vlibc_sig_valid(sig))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*set |= (sigset_t)1 << (sig - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigdelset(sigset_t *set, int sig)
|
||||
{
|
||||
if (!vlibc_sig_valid(sig))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*set &= ~((sigset_t)1 << (sig - 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigismember(const sigset_t *set, int sig)
|
||||
{
|
||||
if (!vlibc_sig_valid(sig))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return (int)((*set >> (sig - 1)) & 1);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigsuspend: atomically swap in mask and sleep until a signal is caught.
|
||||
*
|
||||
* SYS_rt_sigsuspend (mask, sigsetsize) installs mask for the duration of
|
||||
* the wait and restores the caller's previous mask when the catching
|
||||
* handler returns; it always returns -EINTR then (it is never restarted),
|
||||
* which syscall_ret reports as -1 with errno EINTR. A signal whose
|
||||
* default action terminates or stops the process makes sigsuspend never
|
||||
* return, exactly as POSIX requires.
|
||||
*/
|
||||
|
||||
int
|
||||
sigsuspend(const sigset_t *mask)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_rt_sigsuspend, (long)mask, (long)sizeof(sigset_t)));
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigwait/sigwaitinfo/sigtimedwait: synchronous signal consumption over
|
||||
* SYS_rt_sigtimedwait (set, siginfo *, timeout, sigsetsize). The syscall
|
||||
* dequeues one of the signals in set (consuming it, so no handler ever
|
||||
* runs for it), returns its number, and — when info is non-NULL — copies
|
||||
* the full 128-byte kernel siginfo through it.
|
||||
*
|
||||
* The kernel's timespec timeout is relative; NULL waits indefinitely.
|
||||
* EINTR (a handler for some OTHER signal ran) is retried by sigwait —
|
||||
* whose contract is to return 0 with the signal stored, or an error
|
||||
* NUMBER (never -1/errno). sigwaitinfo/sigtimedwait instead surface the
|
||||
* kernel result directly via syscall_ret: the signal number, or -1 with
|
||||
* errno set (EAGAIN on timeout).
|
||||
*/
|
||||
|
||||
int
|
||||
sigwait(const sigset_t *restrict set, int *restrict sig)
|
||||
{
|
||||
long r;
|
||||
|
||||
do
|
||||
{
|
||||
r = __syscall4(SYS_rt_sigtimedwait, (long)set, 0L, 0L, (long)sizeof(sigset_t));
|
||||
} while (r == -EINTR);
|
||||
|
||||
if (r < 0)
|
||||
{
|
||||
return (int)-r; /* error number, per the sigwait contract */
|
||||
}
|
||||
*sig = (int)r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigwaitinfo(const sigset_t *restrict set, siginfo_t *restrict info)
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall4(SYS_rt_sigtimedwait, (long)set, (long)info, 0L, (long)sizeof(sigset_t)));
|
||||
}
|
||||
|
||||
int
|
||||
sigtimedwait(const sigset_t *restrict set, siginfo_t *restrict info,
|
||||
const struct timespec *restrict timeout)
|
||||
{
|
||||
return syscall_ret(__syscall4(SYS_rt_sigtimedwait, (long)set, (long)info, (long)timeout,
|
||||
(long)sizeof(sigset_t)));
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* ualarm: schedule a SIGALRM in usecs microseconds, repeating every
|
||||
* interval microseconds (0 = one-shot), over SYS_setitimer(ITIMER_REAL).
|
||||
*
|
||||
* The kernel itimerval layout matches vlibc's struct itimerval from
|
||||
* <sys/time.h> exactly (two timevals of long seconds + long microseconds),
|
||||
* so the public struct passes through unmodified. The previous alarm is
|
||||
* returned in microseconds, converted from the old it_value the kernel
|
||||
* reports; -1 surfaces syscall failures (useconds_t is unsigned, so -1 is
|
||||
* the all-ones value, the standard error spelling for this XSI call).
|
||||
*/
|
||||
|
||||
useconds_t
|
||||
ualarm(useconds_t usecs, useconds_t interval)
|
||||
{
|
||||
struct itimerval new_timer;
|
||||
struct itimerval old_timer;
|
||||
|
||||
new_timer.it_interval.tv_sec = (time_t)(interval / 1000000);
|
||||
new_timer.it_interval.tv_usec = (suseconds_t)(interval % 1000000);
|
||||
new_timer.it_value.tv_sec = (time_t)(usecs / 1000000);
|
||||
new_timer.it_value.tv_usec = (suseconds_t)(usecs % 1000000);
|
||||
|
||||
if (syscall_ret(__syscall3(SYS_setitimer, ITIMER_REAL, (long)&new_timer, (long)&old_timer)) ==
|
||||
-1)
|
||||
{
|
||||
return (useconds_t)-1;
|
||||
}
|
||||
return (useconds_t)old_timer.it_value.tv_sec * 1000000 + (useconds_t)old_timer.it_value.tv_usec;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* vlibc — signal handling test (todo 28).
|
||||
*
|
||||
* Exercises the whole <signal.h> surface end to end:
|
||||
*
|
||||
* 1. sigaction installs a SIGUSR1 handler; raise() delivers it; the
|
||||
* handler runs exactly once and the process resumes after it (the
|
||||
* SA_RESTORER return trampoline works). Reading the action back with
|
||||
* act == NULL returns the same handler with SA_RESTORER masked out.
|
||||
* 2. sigprocmask SIG_BLOCK + raise makes the signal pending;
|
||||
* sigpending/sigismember report it; SIG_UNBLOCK delivers it to the
|
||||
* handler.
|
||||
* 3. sigwait consumes a blocked+pending SIGUSR1 (handler never runs).
|
||||
* 4. sigqueue(self, SIGUSR2, value) with an SA_SIGINFO handler: the
|
||||
* 3-arg handler runs and sees si_code == SI_QUEUE (-1).
|
||||
* 5. alarm(1) + pause(): the SIGALRM handler runs and pause returns -1.
|
||||
* 6. signal() installs a BSD (SA_RESTART) handler and returns the
|
||||
* previous sigaction-installed handler.
|
||||
* 7. abort() in a forked child kills it with SIGABRT (raw fork/wait4,
|
||||
* independent of the parallel fork todo).
|
||||
* 8. kill(pid, SIGKILL) on a sleeping child: reaped WIFSIGNALED with
|
||||
* WTERMSIG == SIGKILL.
|
||||
*
|
||||
* Level-2 gated section (compile with -DVLIBC_LEVEL=2):
|
||||
* 9. sigaltstack installs an alternate stack and an SA_ONSTACK SIGUSR2
|
||||
* handler provably runs on it (its locals land inside the stack).
|
||||
* 10. ualarm fires SIGALRM into the installed handler.
|
||||
* 11. siginterrupt clears and restores SA_RESTART.
|
||||
* 12. psignal/psiginfo write their stderr diagnostics.
|
||||
*
|
||||
* The -f mode runs the failure shapes — sigaction with sig -1/0/SIGKILL,
|
||||
* sigprocmask with an invalid how, sigaddset/sigismember with out-of-range
|
||||
* signals, killpg with a negative pgrp — asserting return values only
|
||||
* (errno is never read: vlibc's errno slot collides with the host TCB,
|
||||
* so every library errno write is bracketed with a save/restore of the
|
||||
* slot, house pattern). -f exits via raw SYS_exit_group.
|
||||
*
|
||||
* No host libc headers are included (the -Iinclude path would shadow
|
||||
* GCC's internal headers); diagnostics go through raw SYS_write. Not part
|
||||
* of the library proper; compiled manually for this todo (the tests/ +
|
||||
* make check wiring is owned by a later todo).
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "../src/internal/syscall.h"
|
||||
|
||||
/* Kernel timespec (vlibc <time.h> is not included by this TU). */
|
||||
struct t28_timespec
|
||||
{
|
||||
long tv_sec;
|
||||
long tv_nsec;
|
||||
};
|
||||
|
||||
static int failures;
|
||||
|
||||
static volatile sig_atomic_t usr1_count;
|
||||
static volatile sig_atomic_t alarm_count;
|
||||
static volatile sig_atomic_t bsd_count;
|
||||
static volatile sig_atomic_t usr2_count;
|
||||
static volatile int usr2_code;
|
||||
static volatile sig_atomic_t astk_fired;
|
||||
static volatile unsigned long astk_local_addr;
|
||||
|
||||
/* Write a NUL-terminated string to fd via the raw syscall layer. The
|
||||
* optimize attribute keeps GCC from lowering the length loop into a
|
||||
* strlen call, which would leave a vlibc-owned symbol undefined in this
|
||||
* host-linked standalone binary (house idiom, see src/string). */
|
||||
static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void
|
||||
say(int fd, const char *s)
|
||||
{
|
||||
long n = 0;
|
||||
|
||||
while (s[n] != '\0')
|
||||
{
|
||||
n++;
|
||||
}
|
||||
__syscall3(SYS_write, fd, (long)s, n);
|
||||
}
|
||||
|
||||
static void
|
||||
check(int cond, const char *what)
|
||||
{
|
||||
if (cond)
|
||||
{
|
||||
say(1, "PASS: ");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAIL: ");
|
||||
failures++;
|
||||
}
|
||||
say(1, what);
|
||||
say(1, "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Host-TCB slot-1 bracket: the library's errno write on a negative path
|
||||
* lands at %fs:0+8, glibc's dtv pointer. Save and restore it around each
|
||||
* such call; only vlibc/raw-syscall code runs in between (task 13
|
||||
* technique).
|
||||
*/
|
||||
static unsigned long
|
||||
tcb_slot1(void)
|
||||
{
|
||||
return *(unsigned long *)((char *)__builtin_thread_pointer() + 8);
|
||||
}
|
||||
|
||||
static void
|
||||
tcb_slot1_set(unsigned long v)
|
||||
{
|
||||
*(unsigned long *)((char *)__builtin_thread_pointer() + 8) = v;
|
||||
}
|
||||
|
||||
/* Terminate the current thread via the raw syscall (never returns). */
|
||||
static void
|
||||
t28_child_exit(int code)
|
||||
{
|
||||
__syscall1(SYS_exit, code);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void
|
||||
h_usr1(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
usr1_count++;
|
||||
}
|
||||
|
||||
static void
|
||||
h_alarm(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
alarm_count++;
|
||||
}
|
||||
|
||||
static void
|
||||
h_bsd(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
bsd_count++;
|
||||
}
|
||||
|
||||
/* SA_SIGINFO handler: records si_code (kernel SI_QUEUE == -1 for sigqueue). */
|
||||
static void
|
||||
h_usr2_info(int sig, siginfo_t *si, void *ctx)
|
||||
{
|
||||
(void)sig;
|
||||
(void)ctx;
|
||||
usr2_count++;
|
||||
usr2_code = si->si_code;
|
||||
}
|
||||
|
||||
/* 1. sigaction install + raise + readback. */
|
||||
static void
|
||||
scenario_sigaction_raise(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
struct sigaction old;
|
||||
|
||||
usr1_count = 0;
|
||||
act.sa_handler = h_usr1;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
check(sigaction(SIGUSR1, &act, NULL) == 0, "sigaction installs a SIGUSR1 handler");
|
||||
check(raise(SIGUSR1) == 0, "raise(SIGUSR1) delivers synchronously");
|
||||
check(usr1_count == 1, "SIGUSR1 handler ran exactly once and returned (resumed)");
|
||||
check(sigaction(SIGUSR1, NULL, &old) == 0 && old.sa_handler == h_usr1 &&
|
||||
(old.sa_flags & SA_RESTORER) == 0,
|
||||
"sigaction act==NULL reads back the handler, SA_RESTORER masked out");
|
||||
}
|
||||
|
||||
/* 2. Block, pend, inspect, unblock-deliver. */
|
||||
static void
|
||||
scenario_sigprocmask_pending(void)
|
||||
{
|
||||
sigset_t block;
|
||||
sigset_t pend;
|
||||
sig_atomic_t before = usr1_count;
|
||||
|
||||
sigemptyset(&block);
|
||||
sigaddset(&block, SIGUSR1);
|
||||
check(sigprocmask(SIG_BLOCK, &block, NULL) == 0, "sigprocmask SIG_BLOCK SIGUSR1");
|
||||
check(raise(SIGUSR1) == 0, "raise while blocked succeeds (signal pends)");
|
||||
sigemptyset(&pend);
|
||||
check(sigpending(&pend) == 0, "sigpending reports pending signals");
|
||||
check(sigismember(&pend, SIGUSR1) == 1, "sigpending contains SIGUSR1");
|
||||
check(sigismember(&pend, SIGALRM) == 0, "sigpending holds no unrelated signal");
|
||||
check(sigprocmask(SIG_UNBLOCK, &block, NULL) == 0,
|
||||
"sigprocmask SIG_UNBLOCK runs the pending handler");
|
||||
check(usr1_count == before + 1, "the pending SIGUSR1 was delivered on unblock");
|
||||
}
|
||||
|
||||
/* 3. sigwait consumes a blocked signal; the handler never runs. */
|
||||
static void
|
||||
scenario_sigwait(void)
|
||||
{
|
||||
sigset_t set;
|
||||
int got = -1;
|
||||
sig_atomic_t before = usr1_count;
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGUSR1);
|
||||
check(sigprocmask(SIG_BLOCK, &set, NULL) == 0, "sigwait: SIGUSR1 blocked");
|
||||
check(raise(SIGUSR1) == 0, "sigwait: pending SIGUSR1 raised");
|
||||
check(sigwait(&set, &got) == 0, "sigwait returns 0 (signal consumed)");
|
||||
check(got == SIGUSR1, "sigwait reports SIGUSR1");
|
||||
check(usr1_count == before, "sigwait consumed the signal: handler did not run");
|
||||
check(sigprocmask(SIG_UNBLOCK, &set, NULL) == 0, "sigwait: mask restored after");
|
||||
}
|
||||
|
||||
/* 4. sigqueue + SA_SIGINFO delivery (payload union + kernel siginfo). */
|
||||
static void
|
||||
scenario_sigqueue(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
union sigval val;
|
||||
long self = __syscall0(SYS_getpid);
|
||||
|
||||
usr2_count = 0;
|
||||
usr2_code = 0;
|
||||
act.sa_sigaction = h_usr2_info;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
check(sigaction(SIGUSR2, &act, NULL) == 0, "install an SA_SIGINFO SIGUSR2 handler");
|
||||
val.sival_int = 42;
|
||||
check(sigqueue((pid_t)self, SIGUSR2, val) == 0, "sigqueue(self, SIGUSR2) delivers");
|
||||
check(usr2_count == 1, "SA_SIGINFO handler ran once (3-arg form)");
|
||||
check(usr2_code == -1, "SA_SIGINFO handler saw si_code SI_QUEUE (-1)");
|
||||
}
|
||||
|
||||
/* 5. alarm(1) + pause(): the handler runs, pause returns -1/EINTR. */
|
||||
static void
|
||||
scenario_alarm_pause(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
unsigned long saved;
|
||||
|
||||
alarm_count = 0;
|
||||
act.sa_handler = h_alarm;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
check(sigaction(SIGALRM, &act, NULL) == 0, "install a SIGALRM handler");
|
||||
check(alarm(1) == 0, "alarm(1) arms with no previous alarm");
|
||||
saved = tcb_slot1(); /* pause() reports via errno EINTR (library write) */
|
||||
check(pause() == -1, "pause() returns -1 after the SIGALRM handler ran");
|
||||
tcb_slot1_set(saved);
|
||||
check(alarm_count == 1, "SIGALRM handler ran during pause");
|
||||
alarm(0); /* cancel any leftover of the 1 s alarm */
|
||||
}
|
||||
|
||||
/* 6. signal(): BSD handler install, returns the previous disposition. */
|
||||
static void
|
||||
scenario_signal_fn(void)
|
||||
{
|
||||
void (*prev)(int);
|
||||
struct sigaction cur;
|
||||
|
||||
bsd_count = 0;
|
||||
prev = signal(SIGUSR1, h_bsd);
|
||||
check(prev == h_usr1, "signal() returns the previous (sigaction) handler");
|
||||
check(raise(SIGUSR1) == 0, "signal()-installed handler receives raise()");
|
||||
check(bsd_count == 1, "signal() BSD handler ran once");
|
||||
check(sigaction(SIGUSR1, NULL, &cur) == 0 && (cur.sa_flags & SA_RESTART) != 0,
|
||||
"signal() installed with SA_RESTART (BSD semantics)");
|
||||
}
|
||||
|
||||
/* 7. abort() in a forked child: dies by SIGABRT (raw fork + wait4). */
|
||||
static void
|
||||
scenario_abort_child(void)
|
||||
{
|
||||
long pid = __syscall0(SYS_fork);
|
||||
int st = 0;
|
||||
long r;
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
abort();
|
||||
t28_child_exit(0);
|
||||
}
|
||||
if (pid > 0)
|
||||
{
|
||||
r = __syscall4(SYS_wait4, pid, (long)&st, 0, 0);
|
||||
check(r == pid && WIFSIGNALED(st) && WTERMSIG(st) == SIGABRT,
|
||||
"abort() kills the child with SIGABRT");
|
||||
}
|
||||
else
|
||||
{
|
||||
check(0, "fork for the abort child");
|
||||
}
|
||||
}
|
||||
|
||||
/* 8. kill(pid, SIGKILL) on a sleeping child: reaped WIFSIGNALED. */
|
||||
static void
|
||||
scenario_kill_child(void)
|
||||
{
|
||||
long pid = __syscall0(SYS_fork);
|
||||
int st = 0;
|
||||
long r;
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
struct t28_timespec ts = {.tv_sec = 10, .tv_nsec = 0};
|
||||
|
||||
__syscall2(SYS_nanosleep, (long)&ts, 0);
|
||||
t28_child_exit(0);
|
||||
}
|
||||
if (pid > 0)
|
||||
{
|
||||
check(kill((pid_t)pid, SIGKILL) == 0, "kill(pid, SIGKILL) succeeds");
|
||||
r = __syscall4(SYS_wait4, pid, (long)&st, 0, 0);
|
||||
check(r == pid && WIFSIGNALED(st) && WTERMSIG(st) == SIGKILL,
|
||||
"SIGKILLed child is reaped WIFSIGNALED WTERMSIG 9");
|
||||
}
|
||||
else
|
||||
{
|
||||
check(0, "fork for the SIGKILL child");
|
||||
}
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): sigaltstack / ualarm / siginterrupt / psignal. */
|
||||
|
||||
static char t28_alt_stack[SIGSTKSZ * 2];
|
||||
|
||||
/* SA_ONSTACK handler: records where its own frame landed. */
|
||||
static void
|
||||
h_astk(int sig)
|
||||
{
|
||||
volatile unsigned long local = 0;
|
||||
|
||||
(void)sig;
|
||||
astk_local_addr = (unsigned long)&local;
|
||||
astk_fired = 1;
|
||||
}
|
||||
|
||||
/* 9. Alternate stack: an SA_ONSTACK handler provably runs on it. */
|
||||
static void
|
||||
scenario_sigaltstack(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
stack_t ss;
|
||||
stack_t cur;
|
||||
stack_t disable;
|
||||
unsigned long base = (unsigned long)t28_alt_stack;
|
||||
int on_range;
|
||||
|
||||
astk_fired = 0;
|
||||
astk_local_addr = 0;
|
||||
ss.ss_sp = t28_alt_stack;
|
||||
ss.ss_flags = 0;
|
||||
ss.ss_size = sizeof t28_alt_stack;
|
||||
check(sigaltstack(&ss, NULL) == 0, "sigaltstack installs an alternate stack");
|
||||
check(sigaltstack(NULL, &cur) == 0, "sigaltstack reads the current stack back");
|
||||
check(cur.ss_sp == t28_alt_stack && cur.ss_flags == 0 && cur.ss_size == sizeof t28_alt_stack,
|
||||
"sigaltstack readback matches the installed descriptor");
|
||||
|
||||
act.sa_handler = h_astk;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_ONSTACK;
|
||||
check(sigaction(SIGUSR2, &act, NULL) == 0, "install an SA_ONSTACK SIGUSR2 handler");
|
||||
check(raise(SIGUSR2) == 0, "raise SIGUSR2 with the alternate stack armed");
|
||||
on_range =
|
||||
astk_fired == 1 && astk_local_addr >= base && astk_local_addr < base + sizeof t28_alt_stack;
|
||||
check(on_range, "SA_ONSTACK handler ran with its frame on the alternate stack");
|
||||
|
||||
disable.ss_sp = NULL;
|
||||
disable.ss_flags = SS_DISABLE;
|
||||
disable.ss_size = 0;
|
||||
check(sigaltstack(&disable, NULL) == 0, "sigaltstack disables the alternate stack");
|
||||
act.sa_handler = SIG_DFL;
|
||||
act.sa_flags = 0;
|
||||
sigaction(SIGUSR2, &act, NULL);
|
||||
}
|
||||
|
||||
/* 10. ualarm fires SIGALRM into the installed handler. */
|
||||
static void
|
||||
scenario_ualarm(void)
|
||||
{
|
||||
int i;
|
||||
int fired = 0;
|
||||
|
||||
alarm_count = 0;
|
||||
check(ualarm(200000, 0) != (useconds_t)-1, "ualarm(200 ms, 0) arms an itimer");
|
||||
for (i = 0; i < 5000 && alarm_count == 0; i++)
|
||||
{
|
||||
struct t28_timespec ts = {.tv_sec = 0, .tv_nsec = 1000000L}; /* 1 ms */
|
||||
|
||||
__syscall2(SYS_nanosleep, (long)&ts, 0);
|
||||
}
|
||||
fired = alarm_count > 0;
|
||||
check(fired, "ualarm fired SIGALRM within 5 s");
|
||||
ualarm(0, 0); /* cancel the timer */
|
||||
}
|
||||
|
||||
/* 11. siginterrupt toggles SA_RESTART on the current action. */
|
||||
static void
|
||||
scenario_siginterrupt(void)
|
||||
{
|
||||
struct sigaction cur;
|
||||
|
||||
check(siginterrupt(SIGUSR1, 1) == 0, "siginterrupt(sig, 1) succeeds");
|
||||
check(sigaction(SIGUSR1, NULL, &cur) == 0 && (cur.sa_flags & SA_RESTART) == 0,
|
||||
"siginterrupt(sig, 1) cleared SA_RESTART");
|
||||
check(siginterrupt(SIGUSR1, 0) == 0, "siginterrupt(sig, 0) succeeds");
|
||||
check(sigaction(SIGUSR1, NULL, &cur) == 0 && (cur.sa_flags & SA_RESTART) != 0,
|
||||
"siginterrupt(sig, 0) restored SA_RESTART");
|
||||
}
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/* The failure scenarios, run alone under -f (writes errno; see top). */
|
||||
static int
|
||||
failure_scenarios(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
sigset_t set;
|
||||
unsigned long saved = tcb_slot1();
|
||||
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
check(sigaction(-1, &act, NULL) == -1, "-f: sigaction(-1, ...) returns -1");
|
||||
check(sigaction(0, &act, NULL) == -1, "-f: sigaction(0, ...) returns -1");
|
||||
check(sigaction(SIGKILL, &act, NULL) == -1, "-f: sigaction(SIGKILL, catch) returns -1");
|
||||
sigemptyset(&set);
|
||||
check(sigprocmask(99, &set, NULL) == -1, "-f: sigprocmask(invalid how) returns -1");
|
||||
check(sigaddset(&set, 0) == -1, "-f: sigaddset(sig 0) returns -1");
|
||||
check(sigaddset(&set, NSIG) == -1, "-f: sigaddset(sig NSIG) returns -1");
|
||||
check(sigismember(&set, 0) == -1, "-f: sigismember(sig 0) returns -1");
|
||||
check(killpg(-5, SIGUSR1) == -1, "-f: killpg(negative pgrp) returns -1");
|
||||
|
||||
tcb_slot1_set(saved);
|
||||
return failures;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f')
|
||||
{
|
||||
/*
|
||||
* The failure scenarios write errno inside the library; under the
|
||||
* host libc that slot is glibc's private TLS state, so leave via
|
||||
* the raw syscall without running host cleanup.
|
||||
*/
|
||||
int rc = failure_scenarios();
|
||||
|
||||
__syscall1(SYS_exit_group, rc);
|
||||
return rc; /* not reached */
|
||||
}
|
||||
|
||||
scenario_sigaction_raise();
|
||||
scenario_sigprocmask_pending();
|
||||
scenario_sigwait();
|
||||
scenario_sigqueue();
|
||||
scenario_alarm_pause();
|
||||
scenario_signal_fn();
|
||||
scenario_abort_child();
|
||||
scenario_kill_child();
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
scenario_sigaltstack();
|
||||
scenario_ualarm();
|
||||
scenario_siginterrupt();
|
||||
psignal(SIGUSR1, "t28 psignal");
|
||||
psiginfo(NULL, "t28 psiginfo(NULL)");
|
||||
#endif
|
||||
|
||||
if (failures > 0)
|
||||
{
|
||||
say(2, "FAILED (");
|
||||
say(2, "some checks failed");
|
||||
say(2, ")\n");
|
||||
return 1;
|
||||
}
|
||||
say(1, "all signal tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user