/*
 * Copyright (c) 2002-2006 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: sighdl.c,v 1.16 2006/01/19 19:29:35 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "sm/signal.h"
#include "sm/evthr.h"
#include "evthr-int.h"
#include "log.h"

#define MAX_FAILS_T	10

/*
**  EVTHR_GOTSIGNAL -- write signal type to internal pipe to notify main loop
**
**	Parameters:
**		evthr_ctx -- evthr context
**		why -- which signal (EVTHR_signal)
**
**	Returns:
**		usual sm_error code
**
**	Called by signal handler. BE CAREFUL!
*/

static sm_ret_T
evthr_gotsignal(sm_evthr_ctx_P evthr_ctx, int why)
{
	ssize_t r;
	char c;

	SM_IS_EVTHR_CTX(evthr_ctx);

	c = (char) why;
	r = write(wrpipe(evthr_ctx), (void *) &c, 1);
	if (r != 1)
		return sm_error_perm(SM_EM_EVTHR, errno);
	return SM_SUCCESS;
}

/*
**  EVTHR_SIGNAL -- thread to deal with signals
**
**	Parameters:
**		ctx -- evthr context
**
**	Returns:
**		NULL
*/

static void *
evthr_signal(void *ctx)
{
#if WIN32
	hShutdownEvt = CreateEvent(NULL, true, false, EVTHR_SHUTDOWN_EVENT);
	if (hShutdownEvt == NULL)
		return NULL;

	if (SetConsoleCtrlHandler(et_console_handler, true) == 0)
	{
		CloseHandle(hShutdownEvt);
		hShutdownEvt = NULL;

		return NULL;
	}

	for( ; WAIT_OBJECT_0 != WaitForSingleObject(hShutdownEvt, INFINITE); )
		continue;

	CloseHandle(hShutdownEvt);
	hShutdownEvt = NULL;

	evthr_term(ctx, EVTHR_STOP);

#else /* WIN32 */
	int sig, errs;
	sigset_t set;
	sm_evthr_ctx_P evthr_ctx;

	(void) pthread_detach(pthread_self());
	sigemptyset(&set);
	sigaddset(&set, SIGHUP);
	sigaddset(&set, SIGTERM);

	/* Handle Ctrl-C gracefully for debugging */
	sigaddset(&set, SIGINT);
	sigaddset(&set, SIGUSR1);
	sigaddset(&set, SIGUSR2);
	errs = 0;
	evthr_ctx = (sm_evthr_ctx_P) ctx;
	while (true)
	{
		sig = 0;
		if (sigwait(&set, &sig) != 0)
		{
			evthr_ctx->evthr_sige_where = EVTHR_SHE_SIGWAIT;
			evthr_ctx->evthr_sige_what = errno;
			if (++errs > MAX_FAILS_T)
			{
				evthr_gotsignal(evthr_ctx, EVTHR_ABRT);
				return NULL;
			}
			evthr_gotsignal(evthr_ctx, EVTHR_ERROR);
			continue;
		}
		errs = 0;

		switch (sig)
		{
		  case SIGHUP:
		  case SIGTERM:
			evthr_gotsignal(evthr_ctx, EVTHR_STOP);
			return NULL;
		  case SIGINT:
			evthr_gotsignal(evthr_ctx, EVTHR_ABRT);
			return NULL;
		  case SIGUSR1:
			evthr_gotsignal(evthr_ctx, EVTHR_USR1);
			break;
		  case SIGUSR2:
			evthr_gotsignal(evthr_ctx, EVTHR_USR2);
			break;
		  default:
			evthr_ctx->evthr_sige_where = EVTHR_SHE_UNKSIG;
			evthr_ctx->evthr_sige_what = sig;
			break;
		}
	}
#endif /* WIN32 */
	return NULL;
}

/*
**  EVTHR_SPAWN_SIGNAL_THREAD -- spawn thread to handle signals
**
**	Parameters:
**		evthr_ctx -- evthr context
**
**	Returns:
*/

static sm_ret_T
evthr_spawn_signal_thread(sm_evthr_ctx_P evthr_ctx)
{
	pthread_t tid;
#if !WIN32
	int status;
	sigset_t set;
	struct sigaction sigact;

	/* Ignore SIGPIPE */
	sigact.sa_handler = SIG_IGN;
	sigemptyset(&sigact.sa_mask);
	sigact.sa_flags = 0;
	if (sigaction(SIGPIPE, &sigact, NULL) < 0)
	{
		status = errno;
		sm_log_write(evthr_ctx->evthr_c_lctx,
			EVTHR_LCAT_SIGNAL, EVTHR_LMOD_SIGNAL,
			SM_LOG_ERROR, 1,
			"sev=ERROR, func=evthr_spawn_signal_thread, "
			"status=could_not_ignore_PIPE_signal, stat=%d",
			status);
		return sm_error_perm(SM_EM_EVTHR, status);
	}

	/* Mask HUP and KILL signals */
	sigemptyset(&set);
	sigaddset(&set, SIGHUP);
	sigaddset(&set, SIGTERM);
	sigaddset(&set, SIGINT);
	sigaddset(&set, SIGUSR1);
	sigaddset(&set, SIGUSR2);

	status = pthread_sigmask(SIG_BLOCK, &set, NULL);
	if (status != 0)
	{
		sm_log_write(evthr_ctx->evthr_c_lctx,
			EVTHR_LCAT_SIGNAL, EVTHR_LMOD_SIGNAL,
			SM_LOG_ERROR, 1,
			"sev=ERROR, func=evthr_spawn_signal_thread, "
			"status=could_not_mask_HUP_and_KILL_signals, stat=%d",
			status);
		return sm_error_perm(SM_EM_EVTHR, status);
	}
#endif /* WIN32 */
#if WIN32
#pragma warning ( disable : 4024 4047 )
#endif /* WIN32 */

	status = pthread_create(&tid, NULL, evthr_signal, (void *)evthr_ctx);
	if (status != 0)
#if WIN32
#pragma warning ( default : 4024 4047 )
#endif /* WIN32 */
	{
		sm_log_write(evthr_ctx->evthr_c_lctx,
			EVTHR_LCAT_SIGNAL, EVTHR_LMOD_SIGNAL,
			SM_LOG_ERROR, 1,
			"sev=ERROR, func=evthr_spawn_signal_thread, "
			"status=could_not_start_signal_thread, stat=%d",
			status);
		return sm_error_perm(SM_EM_EVTHR, status);
	}
#if WIN32
	/* release the thread handle */
	_unused_proc_handle((int) thread_id);
#endif /* WIN32 */
	return SM_SUCCESS;
}

/*
**  EVTHR_SIGNAL_INIT -- startup for thread to handle signals
**
**	Parameters:
**		evthr_ctx -- context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
evthr_signal_init(sm_evthr_ctx_P evthr_ctx)
{
	sm_ret_T ret;

	/*
	**  spawn_signal_thread must happen before other threads are spawned
	**  off so that it can mask the right signals and other threads
	**  will inherit that mask.
	*/

	ret = evthr_spawn_signal_thread(evthr_ctx);
	if (sm_is_err(ret))
	{
		sm_log_write(evthr_ctx->evthr_c_lctx,
			EVTHR_LCAT_SIGNAL, EVTHR_LMOD_SIGNAL,
			SM_LOG_ERROR, 1,
			"sev=ERROR, func=evthr_signal_init, status="
			"could_not_spawn_signal_thread, stat=%d", ret);
	}
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1