[BACK]Return to signal.c CVS log [TXT][DIR] Up to [local] / acopm / src

File: [local] / acopm / src / signal.c (download)

Revision 1.1.1.1 (vendor branch), Sat May 8 15:42:17 2021 UTC (3 years, 2 months ago) by bountyht
Branch: alphachat, MAIN
CVS Tags: start, HEAD
Changes since 1.1: +0 -0 lines

Initial import

/*
 * Copyright (C) 2017 Aaron M. D. Jones <aaronmdjones@gmail.com>
 *
 * 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. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
 */

#include "acopm-common.h"

#include <assert.h> /* assert() */
#include <errno.h> /* errno */
#include <signal.h> /* SIGCHLD, SIGHUP, SIGINT, SIGQUIT, SIGTERM */
#include <string.h> /* strerror() */
#include <sys/wait.h> /* waitpid(), WNOHANG */

#include <event2/event.h> /* event_*(), struct event */

#include "signal.h" /* decls for own functions */
#include "vars.h" /* global variable definitions */

#include "utils/log.h" /* acopm_log_*() */

static struct event *ev_sigchld = NULL;
static struct event *ev_sighup = NULL;
static struct event *ev_sigint = NULL;
static struct event *ev_sigquit = NULL;
static struct event *ev_sigterm = NULL;

static inline const char * acopm_attr_inline
acopm_signum_to_name(const int signum)
{
	switch (signum)
	{
		case SIGCHLD:
			return "SIGCHLD";

		case SIGHUP:
			return "SIGHUP";

		case SIGINT:
			return "SIGINT";

		case SIGQUIT:
			return "SIGQUIT";

		case SIGTERM:
			return "SIGTERM";
	}

	abort();
	return NULL;
}

static void
acopm_sighandle_callback(const int signum, const short acopm_attr_unused events,
                         void acopm_attr_unused *const restrict context)
{
	assert(signum == SIGHUP || signum == SIGINT || signum == SIGQUIT || signum == SIGTERM);

	const char *const signame = acopm_signum_to_name(signum);

	(void) acopm_log_notice("Caught signal %s", signame);
	(void) acopm_irc_disconnect(acopm_irc_client);
	(void) event_base_loopbreak(ev_base);
}

static void
acopm_sighandle_child_callback(const int signum, const short acopm_attr_unused events,
                               void acopm_attr_unused *const restrict context)
{
	assert(signum == SIGCHLD);

	const char *const signame = acopm_signum_to_name(signum);

	(void) acopm_log_debug("Caught signal %s", signame);

	int status = 0;

	const int result = waitpid(-1, &status, WNOHANG);

	if (result == -1 && errno != ECHILD)
		(void) acopm_log_error("%s: waitpid: %s", __func__, strerror(errno));

	if (result == -1 || result == 0)
		return;

	if (WIFEXITED(status) && WEXITSTATUS(status))
	{
		const int errnum = (int) WEXITSTATUS(status);

		(void) acopm_log_warning("%s: a child process exited with status code %d", __func__, errnum);

		if (*proxy_hit_log)
			(void) acopm_log_warning("%s: check the contents of '%s'", __func__, proxy_hit_log);
	}
	else if (WIFSIGNALED(status) && WTERMSIG(status))
	{
		const int errsig = (int) WTERMSIG(status);

		(void) acopm_log_warning("%s: a child process exited due to signal %d", __func__, errsig);

		if (*proxy_hit_log)
			(void) acopm_log_warning("%s: check the contents of '%s'", __func__, proxy_hit_log);
	}
	else
		(void) acopm_log_warning("%s: something strange happened to a child process", __func__);
}

static inline bool acopm_attr_inline
acopm_sighandle_install(const int signum, struct event **const restrict ev, const event_callback_fn callback_func)
{
	if (! (*ev = event_new(ev_base, signum, EV_SIGNAL, callback_func, NULL)))
	{
		(void) acopm_log_error("%s: event_new: %s", __func__, strerror(errno));
		return false;
	}

	if (event_add(*ev, NULL) != 0)
	{
		(void) acopm_log_error("%s: event_add: %s", __func__, strerror(errno));
		(void) event_free(*ev);
		*ev = NULL;
		return false;
	}

	return true;
}

bool
acopm_sighandle_init(void)
{
	if (! acopm_sighandle_install(SIGCHLD, &ev_sigchld, acopm_sighandle_child_callback))
		return false;

	if (! acopm_sighandle_install(SIGHUP, &ev_sighup, acopm_sighandle_callback))
		return false;

	if (! acopm_sighandle_install(SIGINT, &ev_sigint, acopm_sighandle_callback))
		return false;

	if (! acopm_sighandle_install(SIGQUIT, &ev_sigquit, acopm_sighandle_callback))
		return false;

	if (! acopm_sighandle_install(SIGTERM, &ev_sigterm, acopm_sighandle_callback))
		return false;

	return true;
}

void
acopm_sighandle_free(void)
{
	if (ev_sigchld)
	{
		(void) event_free(ev_sigchld);
		ev_sigchld = NULL;
	}
	if (ev_sighup)
	{
		(void) event_free(ev_sighup);
		ev_sighup = NULL;
	}
	if (ev_sigint)
	{
		(void) event_free(ev_sigint);
		ev_sigint = NULL;
	}
	if (ev_sigquit)
	{
		(void) event_free(ev_sigquit);
		ev_sigquit = NULL;
	}
	if (ev_sigterm)
	{
		(void) event_free(ev_sigterm);
		ev_sigterm = NULL;
	}
}