From: Florian Westphal Date: Fri, 10 Sep 2010 22:19:01 +0000 (+0200) Subject: Add new 'delayed' signal handlers. X-Git-Tag: rel-17-rc1~24 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=commitdiff_plain;h=1fe17e246cba4ee2f4349196c544296790ab5d55 Add new 'delayed' signal handlers. Allows to defer/queue signal processing for execution on the next event dispatch call, i.e. we can perform any signal action in normal, non-signal context. Example uses: - Reload everything on HUP without writing a global "SIGHUP_received" variable - Dump status of internal Lists on SIGUSR1, etc. --- diff --git a/configure.in b/configure.in index 7c5d0b61..b7e0ea13 100644 --- a/configure.in +++ b/configure.in @@ -159,7 +159,7 @@ AC_CHECK_FUNCS([ \ bind gethostbyaddr gethostbyname gethostname inet_ntoa \ setsid setsockopt socket strcasecmp waitpid],,AC_MSG_ERROR([required function missing!])) -AC_CHECK_FUNCS(getaddrinfo getnameinfo inet_aton isdigit sigaction snprintf \ +AC_CHECK_FUNCS(getaddrinfo getnameinfo inet_aton isdigit sigaction sigprocmask snprintf \ vsnprintf strdup strlcpy strlcat strtok_r) # -- Configuration options -- diff --git a/src/ngircd/Makefile.am b/src/ngircd/Makefile.am index 4c9d9fa0..8987c852 100644 --- a/src/ngircd/Makefile.am +++ b/src/ngircd/Makefile.am @@ -21,7 +21,7 @@ sbin_PROGRAMS = ngircd ngircd_SOURCES = ngircd.c array.c channel.c client.c conf.c conn.c conn-func.c \ conn-ssl.c conn-zip.c hash.c io.c irc.c irc-channel.c irc-info.c irc-login.c \ irc-mode.c irc-op.c irc-oper.c irc-server.c irc-write.c lists.c log.c \ - match.c op.c numeric.c pam.c parse.c proc.c rendezvous.c resolve.c + match.c op.c numeric.c pam.c parse.c proc.c rendezvous.c resolve.c sighandlers.c ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c index 78a20b05..03e2905c 100644 --- a/src/ngircd/conn.c +++ b/src/ngircd/conn.c @@ -477,11 +477,6 @@ Conn_InitListeners( void ) unsigned int created = 0; char *copy, *listen_addr; - if (!io_library_init(CONNECTION_POOL)) { - Log(LOG_EMERG, "Cannot initialize IO routines: %s", strerror(errno)); - return -1; - } - assert(Conf_ListenAddress); /* can't use Conf_ListenAddress directly, see below */ diff --git a/src/ngircd/ngircd.c b/src/ngircd/ngircd.c index 4d329d2a..de0b490d 100644 --- a/src/ngircd/ngircd.c +++ b/src/ngircd/ngircd.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -46,6 +45,8 @@ #include "lists.h" #include "log.h" #include "parse.h" +#include "sighandlers.h" +#include "io.h" #include "irc.h" #ifdef ZEROCONF @@ -56,9 +57,6 @@ #include "ngircd.h" -static void Initialize_Signal_Handler PARAMS(( void )); -static void Signal_Handler PARAMS(( int Signal )); - static void Show_Version PARAMS(( void )); static void Show_Help PARAMS(( void )); @@ -292,8 +290,15 @@ main( int argc, const char *argv[] ) * when not running in "no daemon" mode: */ if( ! NGIRCd_NoDaemon ) Log_InitErrorfile( ); #endif + if (!io_library_init(CONNECTION_POOL)) { + Log(LOG_ALERT, "Fatal: Cannot initialize IO routines: %s", strerror(errno)); + exit(1); + } - Initialize_Signal_Handler( ); + if (!Signals_Init()) { + Log(LOG_ALERT, "Fatal: Could not set up signal handlers: %s", strerror(errno)); + exit(1); + } /* * create protocol and server identification. @@ -475,80 +480,6 @@ NGIRCd_Rehash( void ) } /* NGIRCd_Rehash */ -/** - * Initialize the signal handler. - */ -static void -Initialize_Signal_Handler( void ) -{ -#ifdef HAVE_SIGACTION - struct sigaction saction; - - memset( &saction, 0, sizeof( saction )); - saction.sa_handler = Signal_Handler; -#ifdef SA_RESTART - saction.sa_flags |= SA_RESTART; -#endif -#ifdef SA_NOCLDWAIT - saction.sa_flags |= SA_NOCLDWAIT; -#endif - - sigaction(SIGINT, &saction, NULL); - sigaction(SIGQUIT, &saction, NULL); - sigaction(SIGTERM, &saction, NULL); - sigaction(SIGHUP, &saction, NULL); - sigaction(SIGCHLD, &saction, NULL); - - /* we handle write errors properly; ignore SIGPIPE */ - saction.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &saction, NULL); -#else - signal(SIGINT, Signal_Handler); - signal(SIGQUIT, Signal_Handler); - signal(SIGTERM, Signal_Handler); - signal(SIGHUP, Signal_Handler); - signal(SIGCHLD, Signal_Handler); - - signal(SIGPIPE, SIG_IGN); -#endif -} /* Initialize_Signal_Handler */ - - -/** - * Signal handler of ngIRCd. - * This function is called whenever ngIRCd catches a signal sent by the - * user and/or the system to it. For example SIGTERM and SIGHUP. - * @param Signal Number of the signal to handle. - */ -static void -Signal_Handler( int Signal ) -{ - switch( Signal ) - { - case SIGTERM: - case SIGINT: - case SIGQUIT: - /* shut down sever */ - NGIRCd_SignalQuit = true; - break; - case SIGHUP: - /* re-read configuration */ - NGIRCd_SignalRehash = true; - break; - case SIGCHLD: - /* child-process exited, avoid zombies */ - while (waitpid( -1, NULL, WNOHANG) > 0) - ; - break; -#ifdef DEBUG - default: - /* unbekanntes bzw. unbehandeltes Signal */ - Log( LOG_DEBUG, "Got signal %d! Ignored.", Signal ); -#endif - } -} /* Signal_Handler */ - - /** * Display copyright and version information of ngIRCd on the console. */ diff --git a/src/ngircd/ngircd.h b/src/ngircd/ngircd.h index b615f263..bd0aed0a 100644 --- a/src/ngircd/ngircd.h +++ b/src/ngircd/ngircd.h @@ -21,6 +21,8 @@ #include "defines.h" +#define C_ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + GLOBAL time_t NGIRCd_Start; /* Startzeitpunkt des Daemon */ GLOBAL char NGIRCd_StartStr[64]; diff --git a/src/ngircd/parse.c b/src/ngircd/parse.c index 95c49a51..479b3004 100644 --- a/src/ngircd/parse.c +++ b/src/ngircd/parse.c @@ -120,8 +120,6 @@ static bool Validate_Args PARAMS(( CONN_ID Idx, REQUEST *Req, bool *Closed )); static bool Handle_Request PARAMS(( CONN_ID Idx, REQUEST *Req )); -#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) - /** * Return the pointer to the global "IRC command structure". * This structure, an array of type "COMMAND" describes all the IRC commands @@ -397,7 +395,7 @@ Handle_Numeric(CLIENT *client, REQUEST *Req) /* This server is the target of the numeric */ num = atoi(Req->command); - for (i = 0; i < (int) ARRAY_SIZE(Numerics); i++) { + for (i = 0; i < (int) C_ARRAY_SIZE(Numerics); i++) { if (num == Numerics[i].numeric) { if (!Numerics[i].function) return CONNECTED; diff --git a/src/ngircd/proc.c b/src/ngircd/proc.c index dbcff6f1..614aef4c 100644 --- a/src/ngircd/proc.c +++ b/src/ngircd/proc.c @@ -26,6 +26,7 @@ #include "conn.h" #include "exp.h" +#include "sighandlers.h" #include "proc.h" /** @@ -67,6 +68,7 @@ Proc_Fork(PROC_STAT *proc, int *pipefds, void (*cbfunc)(int, short), int timeout return -1; case 0: /* New child process: */ + Signals_Exit(); signal(SIGTERM, Proc_GenericSignalHandler); signal(SIGALRM, Proc_GenericSignalHandler); close(pipefds[0]); diff --git a/src/ngircd/sighandlers.c b/src/ngircd/sighandlers.c new file mode 100644 index 00000000..f3ce24fe --- /dev/null +++ b/src/ngircd/sighandlers.c @@ -0,0 +1,220 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. + */ + +#include "portab.h" + +/** + * @file + * Signal Handlers: Actions to be performed when the program + * receives a signal. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "imp.h" +#include "io.h" +#include "log.h" +#include "ngircd.h" +#include "sighandlers.h" + +static int signalpipe[2]; + +static void Signal_Block(int sig) +{ +#ifdef HAVE_SIGPROCMASK + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, sig); + + sigprocmask(SIG_BLOCK, &set, NULL); +#endif +} + + +static void Signal_Unblock(int sig) +{ +#ifdef HAVE_SIGPROCMASK + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, sig); + + sigprocmask(SIG_UNBLOCK, &set, NULL); +#endif +} + + +/** + * Signal handler of ngIRCd. + * This function is called whenever ngIRCd catches a signal sent by the + * user and/or the system to it. For example SIGTERM and SIGHUP. + * + * It blocks the signal and queues it for later execution by Signal_Handler_BH. + * @param Signal Number of the signal to handle. + */ +static void Signal_Handler(int Signal) +{ + switch (Signal) { + case SIGTERM: + case SIGINT: + case SIGQUIT: + /* shut down sever */ + NGIRCd_SignalQuit = true; + return; + case SIGHUP: + /* re-read configuration */ + NGIRCd_SignalRehash = true; + return; + case SIGCHLD: + /* child-process exited, avoid zombies */ + while (waitpid( -1, NULL, WNOHANG) > 0) + ; + return; + } + + /* + * other signal: queue for later execution. + * This has the advantage that we are not restricted + * to functions that can be called safely from signal handlers. + */ + if (write(signalpipe[1], &Signal, sizeof(Signal)) != -1) + Signal_Block(Signal); +} /* Signal_Handler */ + + +/** + * Signal processing handler of ngIRCd. + * This function is called from the main conn event loop in (io_dispatch) + * whenever ngIRCd has queued a signal. + * + * This function runs in normal context, not from the real signal handler, + * thus its not necessary to only use functions that are signal safe. + * @param Signal Number of the signal that was queued. + */ +static void Signal_Handler_BH(int Signal) +{ + switch (Signal) { +#ifdef DEBUG + default: + Log(LOG_DEBUG, "Got signal %d! Ignored.", Signal); +#endif + } + Signal_Unblock(Signal); +} + +static void Sig_callback(int fd, short UNUSED what) +{ + int sig, ret; + (void) what; + + do { + ret = read(fd, &sig, sizeof(sig)); + if (ret == sizeof(int)) + Signal_Handler_BH(sig); + } while (ret == sizeof(int)); + + if (ret == -1) { + if (errno == EAGAIN || errno == EINTR) + return; + + Log(LOG_EMERG, "read from signal pipe: %s", strerror(errno)); + exit(1); + } + + Log(LOG_EMERG, "EOF on signal pipe"); + exit(1); +} + + +static const int signals_catch[] = { SIGINT, SIGQUIT, SIGTERM, SIGHUP, SIGCHLD, SIGUSR1, SIGUSR2 }; +/** + * Initialize the signal handlers, catch + * those signals we are interested in and sets SIGPIPE to be ignored. + * @return true if initialization was sucessful. + */ +bool Signals_Init(void) +{ + size_t i; +#ifdef HAVE_SIGACTION + struct sigaction saction; +#endif + + if (pipe(signalpipe)) + return false; + + if (!io_setnonblock(signalpipe[0]) || + !io_setnonblock(signalpipe[1])) + return false; + if (!io_setcloexec(signalpipe[0]) || + !io_setcloexec(signalpipe[1])) + return false; +#ifdef HAVE_SIGACTION + memset( &saction, 0, sizeof( saction )); + saction.sa_handler = Signal_Handler; +#ifdef SA_RESTART + saction.sa_flags |= SA_RESTART; +#endif +#ifdef SA_NOCLDWAIT + saction.sa_flags |= SA_NOCLDWAIT; +#endif + + for (i=0; i < C_ARRAY_SIZE(signals_catch) ; i++) + sigaction(signals_catch[i], &saction, NULL); + + /* we handle write errors properly; ignore SIGPIPE */ + saction.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &saction, NULL); +#else + for (i=0; i < C_ARRAY_SIZE(signals_catch) ; i++) + signal(signals_catch[i], Signal_Handler); + + signal(SIGPIPE, SIG_IGN); +#endif + return io_event_create(signalpipe[0], IO_WANTREAD, + Sig_callback); +} /* Initialize_Signal_Handler */ + + +/** + * Restores signals to their default behaviour. + * + * This should be called after a fork() in the new + * child prodcess, especially when we are about to call + * 3rd party code (e.g. PAM). + */ +void Signals_Exit(void) +{ + size_t i; +#ifdef HAVE_SIGACTION + struct sigaction saction; + + memset(&saction, 0, sizeof(saction)); + saction.sa_handler = SIG_DFL; + + for (i=0; i < C_ARRAY_SIZE(signals_catch) ; i++) + sigaction(signals_catch[i], &saction, NULL); + sigaction(SIGPIPE, &saction, NULL); +#else + for (i=0; i < C_ARRAY_SIZE(signals_catch) ; i++) + sigaction(signals_catch[i], &saction, NULL); + signal(SIGPIPE, SIG_DFL); +#endif + close(signalpipe[1]); + close(signalpipe[0]); +} + +/* -eof- */ diff --git a/src/ngircd/sighandlers.h b/src/ngircd/sighandlers.h new file mode 100644 index 00000000..5df8f223 --- /dev/null +++ b/src/ngircd/sighandlers.h @@ -0,0 +1,18 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. + */ + +#ifndef signals_included_ +#define signals_included_ + +#include "portab.h" + +bool Signals_Init(void); +void Signals_Exit(void); + + +#endif