Blame src/npth-sigev.c

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