/*
 * Copyright (c) 2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: stthreadssignal.c,v 1.4 2005/01/18 00:08:31 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/types.h"
#include "sm/signal.h"
#include "statethreads/st.h"

/*
**  Generic signal handler for statethreads application.
**  This module requires a signal pipe (see below) over which it will
**  send single characters that indicate which signal has been received
**  (see signal.h).
**  The application can read from the pipe and act accordingly.
**  The advantage of this approach is that the signal handler code is
**  small and "async signal safe", while the code that acts on the
**  data read from the pipe doesn't have to be and hence can use logging etc.
*/

/*
**  Signal pipe, provided by application.
**  Fixme: should this be local/static and the installation function returns
**  a pointer to the read part??
*/

extern st_netfd_t	 Sig_pipe[2];

/*
**  Callback function if write(2) fails and context for it.
**  The context doesn't seem to be necessary, it can be a global in the
**  application itself (as there is only one "global" callback function and
**  context).
*/

static sm_sigwrfail_F	 sigwrfailcb = NULL;
static void		*sigwrfailctx = NULL;


/*
**  ST_SIGNAL  -- install a single signal handler
**
**	Parameters:
**		sig -- signal number
**		handler -- handler for signal
**
**	Returns:
**		nothing.
*/

static void
st_signal(int sig, sm_sighandler_F handler)
{
	struct sigaction sa;

	sa.sa_handler = handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sigaction(sig, &sa, NULL);
}

/*
**  ST_SIGHANDLER -- Signal handler; send signal number via pipe
**
**	Parameters:
**		signo -- signal number
**
**	Returns:
**		nothing
*/

static void
st_sighandler(int signo)
{
	int err, fd;
	char sig;

	err = errno;
	fd = st_netfd_fileno(Sig_pipe[SM_WR_SIG_PIPE]);

	sig = '\0';
	switch (signo)
	{
	  case SIGHUP:
		sig = SM_SIG_HUP;
		break;
	  case SIGINT:
		sig = SM_SIG_INT;
		break;
	  case SIGALRM:
		sig = SM_SIG_ALRM;
		break;
	  case SIGCHLD:
		sig = SM_SIG_CHLD;
		break;
	  case SIGTERM:
		sig = SM_SIG_TERM;
		break;
	  case SIGUSR1:
		sig = SM_SIG_USR1;
		break;
	  case SIGUSR2:
		sig = SM_SIG_USR2;
		break;
	  case SIGPIPE:
		sig = SM_SIG_PIPE;
		break;
	  default:
		sig = SM_SIG_UNKNOWN;
		break;
	}

	/* write() is async-safe */
	if (write(fd, &sig, sizeof(char)) != sizeof(char))
	{
		if (sigwrfailcb != NULL)
			sigwrfailcb(errno, sigwrfailctx);
	}
	errno = err;
}

/*
**  ST_INSTALL_SIGHANDLERS -- install signal handlers
**
**	Parameters:
**		signalset -- set of signals to handle
**		sigwrfail -- function to invoke when write() in signal handler
**			fails
**		ctx -- context for sigwrfail
**
**	Returns:
**		usual error code
*/

sm_ret_T
st_install_sighandlers(uint signalset, sm_sigwrfail_F sigwrfail, void *ctx)
{
	sigset_t mask;
	int p[2];

	/* Create signal pipe */
	if (pipe(p) < 0)
		return sm_error_perm(SM_EM_STR, errno);

/*
	set close-on-exec flag?
	fcntl(p[0], F_SETFD, FD_CLOEXEC) < 0
	fcntl(p[1], F_SETFD, FD_CLOEXEC) < 0)
*/

	if ((Sig_pipe[SM_RD_SIG_PIPE] = st_netfd_open(p[SM_RD_SIG_PIPE]))
	    == NULL)
		return sm_error_perm(SM_EM_STTHR, errno);
	if ((Sig_pipe[SM_WR_SIG_PIPE] = st_netfd_open(p[SM_WR_SIG_PIPE]))
	    == NULL)
		return sm_error_perm(SM_EM_STTHR, errno);

	sigemptyset(&mask);

	/* Install signal handlers */
	if (SM_IS_FLAG(signalset, SM_HDL_SIG_HUP))
	{
		st_signal(SIGHUP, st_sighandler);
		sigaddset(&mask, SIGHUP);
	}
	if (SM_IS_FLAG(signalset, SM_HDL_SIG_INT))
	{
		st_signal(SIGINT, st_sighandler);
		sigaddset(&mask, SIGINT);
	}
	if (SM_IS_FLAG(signalset, SM_HDL_SIG_ALRM))
	{
		st_signal(SIGALRM, st_sighandler);
		sigaddset(&mask, SIGALRM);
	}
	if (SM_IS_FLAG(signalset, SM_HDL_SIG_CHLD))
	{
		st_signal(SIGCHLD, st_sighandler);
		sigaddset(&mask, SIGCHLD);
	}
	if (SM_IS_FLAG(signalset, SM_HDL_SIG_TERM))
	{
		st_signal(SIGTERM, st_sighandler);
		sigaddset(&mask, SIGTERM);
	}
	if (SM_IS_FLAG(signalset, SM_HDL_SIG_USR1))
	{
		st_signal(SIGUSR1, st_sighandler);
		sigaddset(&mask, SIGUSR1);
	}
	if (SM_IS_FLAG(signalset, SM_HDL_SIG_USR2))
	{
		st_signal(SIGUSR2, st_sighandler);
		sigaddset(&mask, SIGUSR2);
	}
	if (SM_IS_FLAG(signalset, SM_HDL_SIG_PIPE))
	{
		st_signal(SIGPIPE, st_sighandler);
		sigaddset(&mask, SIGPIPE);
	}

	sigprocmask(SIG_UNBLOCK, &mask, NULL);

	sigwrfailcb = sigwrfail;
	sigwrfailctx = ctx;

	return SM_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1