/* source: xiosignal.c */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* Published under the GNU General Public License V.2, see file COPYING */

/* this file contains code for handling signals (except SIGCHLD) */

#include "config.h"
#include "xioconfig.h"  /* what features are enabled */

#include "sysincludes.h"

#include "mytypes.h"
#include "compat.h"
#include "error.h"

#include "sycls.h"


#define SOCAT_MAXPIDS 4

struct socat_sig_desc {
   int sig_use;
   pid_t  sig_pids[SOCAT_MAXPIDS];
} ;

#if 0
size_t socat_sigint_use;	/* how many pids are set in following array */
static pid_t socat_sigint_pids[SOCAT_MAXPIDS];
size_t socat_sigquit_use;	/* how many pids are set in following array */
static pid_t socat_sigquit_pids[SOCAT_MAXPIDS];
#else
static struct socat_sig_desc socat_sighup;
static struct socat_sig_desc socat_sigint;
static struct socat_sig_desc socat_sigquit;
#endif


/* is async-signal-safe */
static struct socat_sig_desc *socat_get_sig_desc(int signum) {
   struct socat_sig_desc *sigdesc;
   switch (signum) {
   case SIGHUP:  sigdesc = &socat_sighup;  break;
   case SIGINT:  sigdesc = &socat_sigint;  break;
   case SIGQUIT: sigdesc = &socat_sigquit; break;
   default: sigdesc = NULL; break;
   }
   return sigdesc;
}

/* a signal handler that possibly passes the signal to sub processes */
void socatsignalpass(int sig) {
   int i;
   struct socat_sig_desc *sigdesc;
   int _errno;

   _errno = errno;
   diag_in_handler = 1;
   Notice1("socatsignalpass(sig=%d)", sig);
   if ((sigdesc = socat_get_sig_desc(sig)) == NULL) {	/* is async-signal-safe */
      diag_in_handler = 0;
      errno = _errno;
      return;
   }

  { /*debug*/
     int n = 0;
   for (i=0; i<sigdesc->sig_use; ++i) {
      if (sigdesc->sig_pids[i]) {
	 ++n;
	 if (Kill(sigdesc->sig_pids[i], sig) < 0) {
	    Warn2("kill("F_pid", %d): %m",
		  sigdesc->sig_pids[i], sig);
	 }
      }
   }
   if (n >= 0)
      Info1("socatsignalpass(): propagated signal to %d sub processes", n);
  }
#if !HAVE_SIGACTION
   Signal(sig, socatsignalpass);
#endif /* !HAVE_SIGACTION */
   Debug("socatsignalpass() ->");
   diag_in_handler = 0;
   errno = _errno;
}


/* register the sub process pid for passing of signals of type signum.
   Only for SIGHUP, SIGINT, and SIGQUIT!
   returns 0 on success or <0 if an error occurred */
int xio_opt_signal(pid_t pid, int signum) {
   struct socat_sig_desc *sigdesc;

   if ((sigdesc = socat_get_sig_desc(signum)) == NULL) {
      Error("sub process registered for unsupported signal");
      return -1;
   }

   if (sigdesc->sig_use >= SOCAT_MAXPIDS) {
      Error1("too many sub processes registered for signal %d", signum);
      return -1;
   }
   if (sigdesc->sig_use == 0) {
      /* the special signal handler has not been registered yet - do it now */
#if HAVE_SIGACTION
      struct sigaction act;
      memset(&act, 0, sizeof(struct sigaction));
      act.sa_flags   = 0/*|SA_RESTART*/;
      act.sa_handler = socatsignalpass;
      sigfillset(&act.sa_mask);
      if (Sigaction(signum, &act, NULL) < 0) {
	 /*! man does not say that errno is defined */
	 Warn3("sigaction(%d, %p, NULL): %s", signum, &act, strerror(errno));
      }
#else
      Signal(signum, socatsignalpass);
#endif /* !HAVE_SIGACTION */
   }
   sigdesc->sig_pids[sigdesc->sig_use++] = pid;
   return 0;
}

