libslack(sig) - ISO C-compliant signal handling module
#include <slack/std.h>
#include <slack/sig.h>
typedef void signal_handler_t(int signo);
typedef void signal_siginfo_handler_t(int signo, siginfo_t *siginfo, void *context);
int signal_set_handler(int signo, int flags, signal_handler_t *handler);
int signal_set_siginfo_handler(int signo, int flags, signal_siginfo_handler_t *siginfo_handler);
int signal_addset(int signo_handled, int signo_blocked);
int signal_received(int signo);
int signal_raise(int signo);
int signal_handle(int signo);
void signal_handle_all(void);
This module provides functions for ISO C-compliant signal handling. ISO C-compliant signal handlers may only set a single value of type sig_atomic_t. This is a very restrictive requirement. This module allows you to specify unrestricted signal handlers while (almost) transparently enforcing ISO C compliance.
When a handled signal arrives, an ISO C-compliant signal handler is invoked to merely record the fact that the signal was received. Then, in the main thread of execution, when signal_handle(3) or signal_handle_all(3) is invoked, the client supplied signal handlers for all signals received since the last invocation of signal_handle(3) or signal_handle_all(3) are invoked.
Since the user-supplied signal handlers execute in the main thread on execution, they are not subject to the normal restrictions on signal handlers. Also, they will execute with the same signals blocked as the real signal handler.
However, this indirection doesn't apply to the more dramatic signals (i.e. SIGILL
, SIGABRT
, SIGFPE
, SIGSEGV
, SIGBUS
and SIGSYS
) whose signal handler functions are installed directly as the real signal handlers. Signal siginfo handler functions installed with signal_set_siginfo_handler(3) are installed directly as well. In general, these handler and siginfo_handler functions probably won't be ISO C-compliant signal handler functions but as long as they are POSIX-compliant signal handler functions (which is far less restrictive) it will be fine.
int signal_set_handler(int signo, int flags, signal_handler_t *handler)
Installs handler
as the signal handler function for the signal signo
. flags
is used as the sa_flags field of the struct sigaction *act
argument to sigaction(2). The actual function that is set as the signal handler is not handler
. It is an ISO C-compliant signal handler function that just records the fact that a signal was received. handler
will only be invoked when the client invokes signal_handle(3) or signal_handle_all(3) from the main thread of execution. So there are no restrictions on handler
. When handler
is invoked, the signo
signal will be blocked. Other signals can also be blocked when handler
is invoked using signal_addset(3) or sigaddset(3). Several signals do not allow such treatment. Behaviour upon return from their handler function is undefined (or defined, but not very pleasant). They are SIGILL
, SIGABRT
, SIGFPE
, SIGBUS
, SIGSEGV
and SIGSYS
. Handler function supplied for these signals are installed as the real signal handlers. On success, returns 0
. On error, returns -1
with errno
set appropriately.
int signal_set_siginfo_handler(int signo, int flags, signal_siginfo_handler_t *siginfo_handler)
Installs siginfo_handler
as the signal siginfo handler function for the signal signo
. A siginfo handler takes three arguments (int signo
, siginfo_t *siginfo
and void *context
), rather than the usual single argument (int signo
). flags
is used as the sa_flags field of the struct sigaction *act argument to sigaction(2) (combined with SA_SIGINFO
). Unlike usual signal handler
functions, signal siginfo_hndler functions are installed directly. So there are POSIX-imposed restrictions on siginfo_handler
. When siginfo_handler
is invoked, the signo
signal will be blocked. Other signals can also be blocked when siginfo_handler
is invoked using signal_addset(3) or sigaddset(3). On success, returns 0
. On error, returns -1
with errno
set appropriately.
int signal_addset(int signo_handled, int signo_blocked)
Adds signo_blocked
to the set of signals that will be blocked when the handler for signal signo_handled
is invoked. This must not be called before the call to signal_set_handler(3) for signo_handled
which initialises the signal set to include signo_handled
. On success, returns 0
. On error, returns -1
with errno
set appropriately.
int signal_received(int signo)
Returns the number of times that the signal signo
has been received since the last call to signal_handle(3) with signo
as its argument or signal_handle_all(3). On error (i.e. signo
is out of range), returns -1
and sets errno
set to EINVAL
.
int signal_raise(int signo)
Simulates the receipt of the signal specified by signo
. On success, returns the number of unhandled signo
signals (including this one). On error (i.e. if signo
is out of range), returns -1
and sets errno
to EINVAL
.
int signal_handle(int signo)
Executes the installed signal handler for the signal signo
. The signo
signal (and any others added with signal_addset(3)) is blocked during the execution of the signal handler. Clears the received status of the signo
signal. On success, returns 0
. On error, returns -1
with errno
set appropriately.
void signal_handle_all(void)
Executes the installed signal handlers for all signals that have been received since the last call to signal_handle(3) or signal_handle_all(3). During the execution of each signal handler, the corresponding signal (and possibly others) will be blocked. Clears the received status of all signals handled.
EINVAL
When a signal number argument is out of range.
Unsafe
#include <slack/std.h>
#include <slack/sig.h>
void hup(int signo) { printf("SIGHUP received\n"); }
void term(int signo) { printf("SIGTERM received\n"); exit(EXIT_SUCCESS); }
int main(int ac, char **av)
{
if (signal_set_handler(SIGHUP, 0, hup) == -1)
return EXIT_FAILURE;
if (signal_set_handler(SIGTERM, 0, term) == -1)
return EXIT_FAILURE;
for (;;)
{
char mesg[BUFSIZ];
ssize_t n;
signal_handle_all();
// Signals arriving here are lost
while ((n = read(STDIN_FILENO, mesg, BUFSIZ)) > 0)
fprintf(stderr, "%*.*s", n, n, mesg);
if (n == -1 && errno == EINTR)
continue;
exit((n == -1) ? EXIT_FAILURE : EXIT_SUCCESS);
}
return EXIT_SUCCESS;
}
libslack(3), prog(3)
20230824 raf <raf@raf.org>