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

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

Revision 1.1, Sat May 8 15:42:17 2021 UTC (3 years ago) by bountyht
Branch point for: MAIN

Initial revision

/*
 * 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 <inttypes.h> /* PRIu16 */
#include <string.h> /* strerror() */
#include <strings.h> /* strcasecmp() */
#include <unistd.h> /* getuid(), geteuid() */

#include "acopm.h" /* EVENT_LOG_* */
#include "check.h" /* acopm_check_user() */
#include "config.h" /* acopm_config_parse() */
#include "daemon.h" /* acopm_daemonise() */
#include "exec.h" /* acopm_exec_*() */
#include "options.h" /* acopm_parse_options(), ACOPM_OPT_* */
#include "signal.h" /* acopm_sighandle_*() */
#include "vars.h" /* global variable definitions */

#include "irc/client.h" /* acopm_irc_*() */
#include "dnsbl/client.h" /* acopm_dnsbl_*() */
#include "proxy/client.h" /* acopm_proxy_*(), ACOPM_PROXY_TYPE_NONE */
#include "utils/log.h" /* acopm_log_*() */
#include "utils/misc.h" /* acopm_rng_*() */
#include "utils/siphash24.h" /* acopm_siphash24_setkey() */

static char **argv_saved = NULL;
static int main_result = EXIT_FAILURE;

static const char *const defconfig = ACOPM_CONFIG_FILE_DEFAULT;

static inline bool acopm_attr_inline
acopm_siphash24_init(void)
{
	(void) acopm_log_debug("%s: initialising hash subsystem", __func__);

	unsigned char sipkey24[SIPHASH24_KEY_LENGTH];
	if (! acopm_rng_buf(sipkey24, sizeof sipkey24))
		return false;

	return acopm_siphash24_setkey(sipkey24);
}

static void
acopm_connect_cb(struct acopm_irc_client *const restrict client, const char *const restrict nickname,
                 const char *const restrict address)
{
	assert(client == acopm_irc_client);

	(void) client;
	(void) acopm_check_user(nickname, address);
}

static void
acopm_command_cb(struct acopm_irc_client *const restrict client, const size_t argc, char **const restrict argv)
{
	if ((argc == 1 || argc == 2) && strcasecmp(argv[0], "EXIT") == 0)
	{
		main_result = EXIT_SUCCESS;

		if (argc == 2)
		{
			char *endptr = NULL;
			const unsigned long int code = strtoul(argv[1], &endptr, 10);

			assert(endptr != NULL);

			if (code <= INT_MAX && endptr && ! *endptr)
				main_result = (int) code;
			else
				(void) acopm_irc_log(client, "Exit code '%s' invalid, using %d", argv[1], main_result);
		}

		(void) event_base_loopbreak(ev_base);
		return;
	}

	if (argc == 1 && strcasecmp(argv[0], "PING") == 0)
	{
		(void) acopm_irc_log(client, "PONG");
		return;
	}
}

static void acopm_attr_noreturn
acopm_restart_cb(void)
{
	(void) acopm_log_notice("Restarting");
	(void) execvp(argv_saved[0], argv_saved);
	(void) acopm_log_critical("%s: execvp: %s", __func__, strerror(errno));
	exit(1);
}

static void acopm_attr_noreturn
acopm_event_crit_cb(const int err)
{
	(void) acopm_log_critical("%s: libevent has failed with fatal error %d", __func__, err);

	acopm_restart_cb();
}

static void
acopm_event_vlog_cb(const int ret, const char *const restrict msg)
{
	switch (ret)
	{
		case EVENT_LOG_ERR:
			(void) acopm_log_error("%s: libevent: %s (%d)", __func__, msg, ret);
			return;

		case EVENT_LOG_WARN:
			(void) acopm_log_warning("%s: libevent: %s (%d)", __func__, msg, ret);
			return;

		case EVENT_LOG_MSG:
			(void) acopm_log_info("%s: libevent: %s (%d)", __func__, msg, ret);
			return;

		case EVENT_LOG_DEBUG:
			(void) acopm_log_debug("%s: libevent: %s (%d)", __func__, msg, ret);
			return;
	}
}

static inline bool acopm_attr_inline
acopm_evloop_init(void)
{
	(void) acopm_log_debug("%s: initialising event loop", __func__);

	if (! (ev_base = event_base_new()))
	{
		(void) acopm_log_critical("%s: event_base_new: %s", __func__, strerror(errno));
		return false;
	}

	(void) event_set_fatal_callback(acopm_event_crit_cb);
	(void) event_set_log_callback(acopm_event_vlog_cb);

	return true;
}

static inline void acopm_attr_inline
acopm_evloop_free(void)
{
	if (! ev_base)
		return;

	(void) acopm_log_debug("%s: destroying event loop", __func__);
	(void) event_base_free(ev_base);
	ev_base = NULL;
}

int
main(const int argc, char **const restrict argv)
{
	if (! acopm_log_init())
		goto end;

	switch (acopm_parse_options(argc, argv))
	{
		case ACOPM_OPT_PARSE_CONTINUE:
			break;

		case ACOPM_OPT_PARSE_EXIT_SUCCESS:
			return EXIT_SUCCESS;

		case ACOPM_OPT_PARSE_EXIT_FAILURE:
			return EXIT_FAILURE;
	}

	if ((getuid() == ((uid_t) 0)) || (geteuid() == ((uid_t) 0)))
	{
		(void) fprintf(stderr, "%s: do NOT run me as root!\n", argv[0]);
		return EXIT_FAILURE;
	}

	argv_saved = argv;

#if defined(PACKAGE_NAME) && defined(PACKAGE_VERSION)
	(void) acopm_log_info("This is %s version %s starting up", PACKAGE_NAME, PACKAGE_VERSION);
#endif

	if (! *config_file && ! acopm_strset_buf(config_file, defconfig))
		(void) acopm_log_warning("%s: config file '%s' too long, truncated (BUG)", __func__, defconfig);

	if (! acopm_evloop_init())
		goto end;
	if (! acopm_sighandle_init())
		goto end;
	if (! acopm_rng_init())
		goto end;
	if (! acopm_siphash24_init())
		goto end;
	if (! acopm_check_init())
		goto end;
	if (! (acopm_irc_client = acopm_irc_new(ev_base, acopm_connect_cb, acopm_command_cb, acopm_restart_cb)))
		goto end;
	if (! (acopm_dnsbl_client = acopm_dnsbl_new(ev_base)))
		goto end;
	if (! (acopm_proxy_client = acopm_proxy_new(ev_base)))
		goto end;
	if (! acopm_config_parse())
		goto end;
	if (! acopm_exec_init())
		goto end;
	if (! acopm_irc_connect(acopm_irc_client))
		goto end;
	if (! acopm_daemonise())
		goto end;

	(void) acopm_log_info("Entering event loop");
	(void) event_base_dispatch(ev_base);

	if (main_result == EXIT_SUCCESS)
		(void) acopm_log_info("Leaving event loop");
	else
		(void) acopm_log_warning("Leaving event loop");

end:
	(void) acopm_exec_free();

	if (acopm_proxy_client)
		(void) acopm_proxy_free(acopm_proxy_client);

	if (acopm_dnsbl_client)
		(void) acopm_dnsbl_free(acopm_dnsbl_client);

	if (acopm_irc_client)
		(void) acopm_irc_free(acopm_irc_client);

	(void) acopm_check_free();
	(void) acopm_rng_free();
	(void) acopm_sighandle_free();
	(void) acopm_evloop_free();
	(void) acopm_log_free();

	return main_result;
}