/* * Copyright (C) 2017 Aaron M. D. Jones * * 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() */ #include /* errno */ #include /* SIGCHLD, SIGHUP, SIGINT, SIGQUIT, SIGTERM */ #include /* strerror() */ #include /* waitpid(), WNOHANG */ #include /* 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; } }