NAME

libslack(sig) - ISO C-compliant signal handling module

SYNOPSIS

    #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);

DESCRIPTION

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.

ERRORS

EINVAL

When a signal number argument is out of range.

MT-Level

Unsafe

EXAMPLE

    #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;
    }

SEE ALSO

libslack(3), prog(3)

AUTHOR

20210220 raf <raf@raf.org>