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;
}