/* npth-sigev.c - signal handling interface * Copyright (C) 2011 g10 Code GmbH * * This file is part of nPth. * * nPth is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * nPth is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . */ /* This is a support interface to make it easier to handle signals. * * The interfaces here support one (and only one) thread (here called * "main thread") in the application to monitor several signals while * selecting on filedescriptors. * * First, the main thread should call npth_sigev_init. This * initializes some global data structures used to record interesting * and pending signals. * * Then, the main thread should call npth_sigev_add for every signal * it is interested in observing, and finally npth_sigev_fini. This * will block the signal in the main threads sigmask. Note that these * signals should also be blocked in all other threads. Since they * are blocked in the main thread after calling npth_sigev_add, it is * recommended to call npth_sigev_add in the main thread before * creating any threads. * * The function npth_sigev_sigmask is a convenient function that * returns the sigmask of the thread at time of npth_sigev_init, but * with all registered signals unblocked. It is recommended to do all * other changes to the main thread's sigmask before calling * npth_sigev_init, so that the return value of npth_sigev_sigmask can * be used in the npth_pselect invocation. * * In any case, the main thread should invoke npth_pselect with a * sigmask that has all signals that should be monitored unblocked. * * After npth_pselect returns, npth_sigev_get_pending can be called in * a loop until it returns 0 to iterate over the list of pending * signals. Each time a signal is returned by that function, its * status is reset to non-pending. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "npth.h" /* Record events that have been noticed. */ static sigset_t sigev_pending; /* The signal mask during normal operation. */ static sigset_t sigev_block; /* The signal mask during pselect. */ static sigset_t sigev_unblock; /* Registered signal numbers. Needed to iterate over sigset_t. Bah. */ #define SIGEV_MAX 32 static int sigev_signum[SIGEV_MAX]; static int sigev_signum_cnt; /* The internal handler which just sets a global flag. */ static void _sigev_handler (int signum) { sigaddset (&sigev_pending, signum); } /* Start setting up signal event handling. */ void npth_sigev_init (void) { sigemptyset (&sigev_pending); pthread_sigmask (SIG_SETMASK, NULL, &sigev_block); pthread_sigmask (SIG_SETMASK, NULL, &sigev_unblock); } /* Add signal SIGNUM to the list of watched signals. */ void npth_sigev_add (int signum) { struct sigaction sa; sigset_t ss; sigemptyset(&ss); assert (sigev_signum_cnt < SIGEV_MAX); sigev_signum[sigev_signum_cnt++] = signum; /* Make sure we can receive it. */ sigdelset (&sigev_unblock, signum); sigaddset (&sigev_block, signum); sa.sa_handler = _sigev_handler; sa.sa_mask = ss; sa.sa_flags = 0; /* NOT setting SA_RESTART! */ sigaction (signum, &sa, NULL); } #ifdef HAVE_PTHREAD_ATFORK /* There is non-POSIX operating system where fork is not available to applications. There, we have no pthread_atfork either. In such a case, we don't call pthread_atfork. */ static void restore_sigmask_for_child_process (void) { pthread_sigmask (SIG_SETMASK, &sigev_unblock, NULL); } #endif /* Finish the list of watched signals. This starts to block them, too. */ void npth_sigev_fini (void) { /* Block the interesting signals. */ pthread_sigmask (SIG_SETMASK, &sigev_block, NULL); #ifdef HAVE_PTHREAD_ATFORK pthread_atfork (NULL, NULL, restore_sigmask_for_child_process); #endif } /* Get the sigmask as needed for pselect. */ sigset_t * npth_sigev_sigmask (void) { return &sigev_unblock; } /* Return the next signal event that occured. Returns if none are left, 1 on success. */ int npth_sigev_get_pending (int *r_signum) { int i; for (i = 0; i < sigev_signum_cnt; i++) { int signum = sigev_signum[i]; if (sigismember (&sigev_pending, signum)) { sigdelset (&sigev_pending, signum); *r_signum = signum; return 1; } } return 0; }