text: /*	$OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $	*/
  
  /*
   * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   * 3. The name of the author may not be used to endorse or promote products
   *    derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  #ifndef HAVE_CONFIG_H
  #include <config.h>
  #endif
  
  #ifdef WIN32
  #define WIN32_LEAN_AND_MEAN
  #include <windows.h>
  #include <winsock2.h>
  #undef WIN32_LEAN_AND_MEAN
  #endif
  #include <sys/types.h>
  #ifdef HAVE_SYS_TIME_H
  #include <sys/time.h>
  #endif
  #include <sys/queue.h>
  #ifdef HAVE_SYS_SOCKET_H
  #include <sys/socket.h>
  #endif
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  #include <errno.h>
  #ifdef HAVE_FCNTL_H
  #include <fcntl.h>
  #endif
  #include <assert.h>
  
  #include <event.h>
  #include <event-internal.h>
  #include <evsignal.h>
  #include <evutil.h>
  #include <log.h>
  
  struct event_base *evsignal_base = NULL;
  
  static void evsignal_handler (int sig);
  
  /* Callback for when the signal handler write a byte to our signaling socket */
  static void
  evsignal_cb (int fd, short what, void *arg)
  {
    static char signals[100];
  
  #ifdef WIN32
    SSIZE_T n;
  #else
    ssize_t n;
  #endif
  
    (void) what;
    (void) arg;
  
    n = recv (fd, signals, sizeof (signals), 0);
  
    if (n == -1)
      event_err (1, "%s: read", __func__);
  }
  
  #ifdef HAVE_SETFD
  #define FD_CLOSEONEXEC(x) do { \
        if (fcntl(x, F_SETFD, 1) == -1) \
          event_warn("fcntl(%d, F_SETFD)", x); \
      } while (0)
  #else
  #define FD_CLOSEONEXEC(x)
  #endif
  
  void
  evsignal_init (struct event_base *base)
  {
    /*
     * Our signal handler is going to write to one end of the socket
     * pair to wake up our event loop.  The event loop then scans for
     * signals that got delivered.
     */
    if (evutil_socketpair (AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1)
      event_err (1, "%s: socketpair", __func__);
  
    FD_CLOSEONEXEC (base->sig.ev_signal_pair[0]);
  
    FD_CLOSEONEXEC (base->sig.ev_signal_pair[1]);
  
    base->sig.sh_old = NULL;
  
    base->sig.sh_old_max = 0;
  
    base->sig.evsignal_caught = 0;
  
    memset (&base->sig.evsigcaught, 0, sizeof (sig_atomic_t) *NSIG);
  
    evutil_make_socket_nonblocking (base->sig.ev_signal_pair[0]);
  
    event_set (&base->sig.ev_signal, base->sig.ev_signal_pair[1],
               EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
  
    base->sig.ev_signal.ev_base = base;
  
    base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
  }
  
  /* Helper: set the signal handler for evsignal to handler in base, so that
   * we can restore the original handler when we clear the current one. */
  int
  _evsignal_set_handler (struct event_base *base,
                         int evsignal, void (*handler) (int) )
  {
  #ifdef HAVE_SIGACTION
  
    struct sigaction sa;
  #else
    ev_sighandler_t sh;
  #endif
  
    struct evsignal_info *sig = &base->sig;
    void *p;
  
    /*
     * resize saved signal handler array up to the highest signal number.
     * a dynamic array is used to keep footprint on the low side.
     */
  
    if (evsignal >= sig->sh_old_max)
      {
        event_debug ( ("%s: evsignal (%d) >= sh_old_max (%d), resizing",
                       __func__, evsignal, sig->sh_old_max) );
        sig->sh_old_max = evsignal + 1;
        p = realloc (sig->sh_old, sig->sh_old_max * sizeof * sig->sh_old);
  
        if (p == NULL)
          {
            event_warn ("realloc");
            return (-1);
          }
  
        sig->sh_old = p;
      }
  
    /* allocate space for previous handler out of dynamic array */
    sig->sh_old[evsignal] = malloc (sizeof * sig->sh_old[evsignal]);
  
    if (sig->sh_old[evsignal] == NULL)
      {
        event_warn ("malloc");
        return (-1);
      }
  
    /* save previous handler and setup new handler */
  #ifdef HAVE_SIGACTION
    memset (&sa, 0, sizeof (sa) );
  
    sa.sa_handler = handler;
  
    sa.sa_flags |= SA_RESTART;
  
    sigfillset (&sa.sa_mask);
  
    if (sigaction (evsignal, &sa, sig->sh_old[evsignal]) == -1)
      {
        event_warn ("sigaction");
        free (sig->sh_old[evsignal]);
        return (-1);
      }
  
  #else
    if ( (sh = signal (evsignal, handler) ) == SIG_ERR)
      {
        event_warn ("signal");
        free (sig->sh_old[evsignal]);
        return (-1);
      }
  
    *sig->sh_old[evsignal] = sh;
  
  #endif
  
    return (0);
  }
  
  int
  evsignal_add (struct event *ev)
  {
    int evsignal;
  
    struct event_base *base = ev->ev_base;
  
    struct evsignal_info *sig = &ev->ev_base->sig;
  
    if (ev->ev_events & (EV_READ | EV_WRITE) )
      event_errx (1, "%s: EV_SIGNAL incompatible use", __func__);
  
    evsignal = EVENT_SIGNAL (ev);
  
    event_debug ( ("%s: %p: changing signal handler", __func__, ev) );
  
    if (_evsignal_set_handler (base, evsignal, evsignal_handler) == -1)
      return (-1);
  
    /* catch signals if they happen quickly */
    evsignal_base = base;
  
    if (!sig->ev_signal_added)
      {
        sig->ev_signal_added = 1;
        event_add (&sig->ev_signal, NULL);
      }
  
    return (0);
  }
  
  int
  _evsignal_restore_handler (struct event_base *base, int evsignal)
  {
    int ret = 0;
  
    struct evsignal_info *sig = &base->sig;
  #ifdef HAVE_SIGACTION
  
    struct sigaction *sh;
  #else
    ev_sighandler_t *sh;
  #endif
  
    /* restore previous handler */
    sh = sig->sh_old[evsignal];
    sig->sh_old[evsignal] = NULL;
  #ifdef HAVE_SIGACTION
  
    if (sigaction (evsignal, sh, NULL) == -1)
      {
        event_warn ("sigaction");
        ret = -1;
      }
  
  #else
    if (signal (evsignal, *sh) == SIG_ERR)
      {
        event_warn ("signal");
        ret = -1;
      }
  
  #endif
    free (sh);
  
    return ret;
  }
  
  int
  evsignal_del (struct event *ev)
  {
    event_debug ( ("%s: %p: restoring signal handler", __func__, ev) );
    return _evsignal_restore_handler (ev->ev_base, EVENT_SIGNAL (ev) );
  }
  
  static void
  evsignal_handler (int sig)
  {
    int save_errno = errno;
  
    if (evsignal_base == NULL)
      {
        event_warn (
          "%s: received signal %d, but have no base configured",
          __func__, sig);
        return;
      }
  
    evsignal_base->sig.evsigcaught[sig]++;
  
    evsignal_base->sig.evsignal_caught = 1;
  
  #ifndef HAVE_SIGACTION
    signal (sig, evsignal_handler);
  #endif
  
    /* Wake up our notification mechanism */
    send (evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
    errno = save_errno;
  }
  
  void
  evsignal_process (struct event_base *base)
  {
  
    struct event *ev;
    sig_atomic_t ncalls;
  
    base->sig.evsignal_caught = 0;
    TAILQ_FOREACH (ev, &base->sig.signalqueue, ev_signal_next)
    {
      ncalls = base->sig.evsigcaught[EVENT_SIGNAL (ev) ];
  
      if (ncalls)
        {
          if (! (ev->ev_events & EV_PERSIST) )
            event_del (ev);
  
          event_active (ev, EV_SIGNAL, ncalls);
  
          base->sig.evsigcaught[EVENT_SIGNAL (ev) ] = 0;
        }
    }
  }
  
  void
  evsignal_dealloc (struct event_base *base)
  {
    if (base->sig.ev_signal_added)
      {
        event_del (&base->sig.ev_signal);
        base->sig.ev_signal_added = 0;
      }
  
    assert (TAILQ_EMPTY (&base->sig.signalqueue) );
  
    EVUTIL_CLOSESOCKET (base->sig.ev_signal_pair[0]);
    base->sig.ev_signal_pair[0] = -1;
    EVUTIL_CLOSESOCKET (base->sig.ev_signal_pair[1]);
    base->sig.ev_signal_pair[1] = -1;
    base->sig.sh_old_max = 0;
  
    /* per index frees are handled in evsignal_del() */
    free (base->sig.sh_old);
  }
  