/*
 * signal.c  -  Netboot signal handling
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: signal.c,v 1.10 2007/01/06 18:31:39 gkminix Exp $
 */

#include <common.h>
#include <signal.h>
#include <nblib.h>
#include "privlib.h"



/*
 * Define SIGCHLD signal
 */
#ifndef SIGCHLD
# define SIGCHLD	SIGCLD
#endif



/*
 * Global variables
 */
volatile int nblib_sigchld;		/* number of SIGCHLD signals */



/*
 * Local variables
 */
static int isinit = FALSE;		/* initialization flag */
static int procsignal;			/* signal processing flag */
static int signalcount;			/* signal counter */
static int lastsignal;			/* last signal caught */



/*
 * Terminate program upon signal reception
 */
static void dosignal __F((signum), int signum)
{
  /* Restore all signals to their default action */
  nblib_restore_signal();

  /* Let the user know about the signal */
  if (!quiet && signum > 0)
	fprintf(stderr, "\nInterrupted by signal %d\n", signum);

  /* Terminate the program by re-raising the signal */
  if (signum > 0) {
	(void)nblib_exit_cleanup();
#ifdef HAVE_RAISE
	(void)raise(signum);
#else
# ifdef HAVE_GSIGNAL
	(void)gsignal(signum);
# else
	(void)kill(getpid(), signum);
# endif
#endif
	/* Just to make the compiler happy */
	exit(EXIT_SIGNAL);
  }
  nbexit(EXIT_SIGNAL);
}



/*
 * Handle signals
 */
static RETSIGTYPE sighandler __F((signum), int signum)
{
  static volatile int inhandler;

  if (!inhandler) {
	inhandler = TRUE;
	lastsignal = signum;
	if (signum == SIGCHLD)
		nblib_sigchld++;
	else if (procsignal)
		dosignal(signum);
	else
		signalcount++;
	inhandler = FALSE;
  }
  (void)signal(signum, &sighandler);
#if RETSIGTYPE != void
  return((RETSIGTYPE)0);
#endif
}



/*
 * Stop signal processing
 */
void signal_stop __F_NOARGS
{
  signalcount = 0;
  procsignal = FALSE;
}



/*
 * Resume signal processing
 */
void signal_resume __F_NOARGS
{
  if (!procsignal) {
	procsignal = TRUE;
	if (signalcount > 0)
		dosignal(lastsignal);
  }
}



/*
 * Initialize signal handling
 */
void nblib_init_signal __F_NOARGS
{
  /* Initialize global variables */
  procsignal = TRUE;
  signalcount = 0;
  lastsignal = 0;

  /* Check if signals already initialized */
  if (isinit)
	return;

  /* Assign signal handlers */
#ifdef SIGHUP
  (void)signal(SIGHUP, &sighandler);
#endif
#ifdef SIGPIPE
  (void)signal(SIGPIPE, &sighandler);
#endif
  (void)signal(SIGINT, &sighandler);
  (void)signal(SIGQUIT, &sighandler);
  (void)signal(SIGTERM, &sighandler);
  (void)signal(SIGCHLD, &sighandler);
#ifdef SIGUSR1
  (void)signal(SIGUSR1, SIG_IGN);
#endif
#ifdef SIGUSR2
  (void)signal(SIGUSR2, SIG_IGN);
#endif

  /* Mark signals initialized */
  isinit = TRUE;
}



/*
 * Restore all signal handlers
 */
void nblib_restore_signal __F_NOARGS
{
  /* Check if anything to do */
  if (!isinit)
	return;

  /* Stop any signal processing */
  procsignal = FALSE;

  /* Restore all signals to their default value */
#ifdef SIGHUP
  (void)signal(SIGHUP, SIG_DFL);
#endif
#ifdef SIGPIPE
  (void)signal(SIGPIPE, SIG_DFL);
#endif
  (void)signal(SIGINT, SIG_DFL);
  (void)signal(SIGQUIT, SIG_DFL);
  (void)signal(SIGTERM, SIG_DFL);
  (void)signal(SIGCHLD, SIG_DFL);
#ifdef SIGUSR1
  (void)signal(SIGUSR1, SIG_DFL);
#endif
#ifdef SIGUSR2
  (void)signal(SIGUSR2, SIG_DFL);
#endif

  /* Mark signals not initialized */
  isinit = FALSE;
}

