From 6a2f7b87d4fb19f30f64ede4b18582eb366c8b7d Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Wed, 12 Jun 2019 23:38:36 +0100 Subject: Allow reloading the configuration file at runtime using a BLABOUNCER command or by issuing the SIGHUP signal. --- README | 15 ++++++++++++ TODO | 3 +-- blabouncer.c | 35 +++++++++++++++++++++++----- blabouncer.conf.example | 2 ++ config.c | 2 ++ functions.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ functions.h | 5 ++++ message.c | 28 +++++++++++++++++++++++ 8 files changed, 143 insertions(+), 8 deletions(-) diff --git a/README b/README index c399a62..2a861c5 100644 --- a/README +++ b/README @@ -22,12 +22,27 @@ An example configuration file is provided named "blabouncer.conf". If you don't specify one using "-c /path/to/configuration/file" then the example configuration one will be created for you in $HOME/.blabouncer/ when starting. +Certain configuration options can be changed at runtime, either at any time, or by issuing a BLABOUNCER REHASH command or by sending SIGHUP to blabouncer. + +These options can be changed at any time as they are re-read when needed: + - nick2 + - nick3 + - password + +These options can be changed by issuing a BLABOUNCER REHASH command or by sending SIGHUP to blabouncer: + - replaymode + - replayseconds + - logging + - replaylogging + - debug + == Commands == Once connected to blabouncer with a client, you can use the following commands: "BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]" (To replay a given length of time of replay log.) "BLABOUNCER QUIT [quit message]" (To quit blabouncer, optionally sending [quit message] to the server.) +"BLABOUNCER REHASH" (To reload settings from the configuration file, see above for details.) Blabouncer commands are all prefixed with BLABOUNCER which you can usually send using "/QUOTE BLABOUNCER". diff --git a/TODO b/TODO index ff3bab2..081cb0c 100644 --- a/TODO +++ b/TODO @@ -7,5 +7,4 @@ Add various auto replay options: Might need to #include in blabouncer.c to make some operating systems and/or compilers happy. -Allow reloading the configuration file while running (at least for things like replayseconds, replaymode) - BLABOUNCER command and SIGHUP? - +Load all settings from configuration file at startup instead of referring to it for certain things (password/nick2/nick3). diff --git a/blabouncer.c b/blabouncer.c index 567de57..04a87ba 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -60,8 +60,6 @@ #define ECONFINT 1 // errno value if getconfint() failed #define STDIN 0 // stdin is fd 0 -#define SIGINT 2 // SIGINT is signal 2 -#define SIGTERM 15 // SIGTERM is signal 15 // Various important limits - note that several related ones are defined in functions.h and structures.h @@ -419,13 +417,15 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { } // Let's set up signal handling stuff here since we're about to enter The Big Loop (TM) - // We'll handle SIGINT (Ctrl-C) and SIGTERM (default signal of `kill`) + // We'll handle SIGHUP (for rehashing), SIGINT (Ctrl-C), and SIGTERM (default signal of `kill`) + signal(SIGHUP, sighandler); // SIGHUP (1) signal(SIGINT, sighandler); // SIGINT (2) signal(SIGTERM, sighandler); // SIGTERM (15) - // Block those two signals + // Block those signals sigset_t sigset, oldset; sigemptyset(&sigset); + sigaddset(&sigset, SIGHUP); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGTERM); sigprocmask(SIG_BLOCK, &sigset, &oldset); @@ -460,9 +460,31 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { if (errno == EINTR) { // Signal caught, do signal handling debugprint(DEBUG_CRIT, "signal '%d' happened, exiting!\n", signum); - if (signum == SIGINT) { + if (signum == SIGHUP) { // REHASH requested + // TODO - This code is duplicated between here and BLABOUNCER REHASH handling + char outgoingmsg[MAXDATASIZE]; + char failuremsg[MAXDATASIZE]; + failuremsg[0] = '\0'; + + // Try to rehash... + if (!rehash(settings, failuremsg)) { + // ...or log and tell all clients if it failed + debugprint(DEBUG_CRIT, "SIGHUP REHASH failed: %s.\n", failuremsg); + if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :SIGHUP REHASH failed: %s.", ircdstate.ircnick, failuremsg)) { + debugprint(DEBUG_CRIT, "Error while preparing SIGHUP REHASH failure message response!\n"); + outgoingmsg[0] = '\0'; + } + sendtoallclients(clients, outgoingmsg, 0, settings); + } else { + // ...or tell all clients it worked + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :SIGUP REHASH complete!", ircdstate.ircnick); + sendtoallclients(clients, outgoingmsg, 0, settings); + } + // Then go back to the top of the loop + continue; + } else if (signum == SIGINT) { // Probably Ctrl+C cleanexit(server_ssl, clients, 0, &ircdstate, settings, "SIGINT received"); - } else if (signum == SIGTERM) { + } else if (signum == SIGTERM) { // Probably `kill` cleanexit(server_ssl, clients, 0, &ircdstate, settings, "SIGTERM received"); } else { cleanexit(server_ssl, clients, 0, &ircdstate, settings, "Unexpected signal received"); @@ -806,6 +828,7 @@ int main(int argc, char *argv[]) { } // Populate settings from configuration file + // TODO - Try to share some/all of this code with the rehash() settings loading // What is the auto replay mode? if (!getconfstr("replaymode", settings.conffile, settings.replaymode)) { diff --git a/blabouncer.conf.example b/blabouncer.conf.example index f9c09bc..ebb2f23 100644 --- a/blabouncer.conf.example +++ b/blabouncer.conf.example @@ -7,6 +7,8 @@ # # Shell expansion is not supported, so do not try and specify e.g. # "~/.blabouncer/" or "$HOME/.blabouncer/", instead use "/home/foo/.blabouncer" +# +# Some settings can be reloaded at runtime, please refer to README for details. nick = "blabounce" nick2 = "bbounce2" diff --git a/config.c b/config.c index 94831d1..faa153a 100644 --- a/config.c +++ b/config.c @@ -191,6 +191,8 @@ int createconfigfile(char *filename) { "#\n" "# Shell expansion is not supported, so do not try and specify e.g.\n" "# \"~/.blabouncer/\" or \"$HOME/.blabouncer/\", instead use \"/home/foo/.blabouncer\"\n" + "#\n" + "# Some settings can be reloaded at runtime, please refer to README for details.\n" "\n" "nick = \"blabounce\"\n" "nick2 = \"bbounce2\"\n" diff --git a/functions.c b/functions.c index 5dbeb0c..abffb0b 100644 --- a/functions.c +++ b/functions.c @@ -990,3 +990,64 @@ void cleanexit(SSL *server_ssl, struct client *clients, int sourcefd, struct irc exit(0); } + +// Re-read the configuration file, setting 'failuremsg' to a failure message on failure. +// Returns 1 on success or 0 on failure. +int rehash(struct settings *settings, char *failuremsg) { + // TODO - Try to share some/all of this code with the initial main() settings loading + + // What is the auto replay mode? + char oldreplaymode[MAXDATASIZE]; + strcpy(oldreplaymode, settings->replaymode); + if (!getconfstr("replaymode", settings->conffile, settings->replaymode)) { + strcpy(settings->replaymode, oldreplaymode); + strcpy(failuremsg, "error getting 'replaymode' from configuration file"); + return 0; + } else { + if (strcmp(settings->replaymode, "none") && strcmp(settings->replaymode, "time") && strcmp(settings->replaymode, "lastspoke")) { + strcpy(settings->replaymode, oldreplaymode); + strcpy(failuremsg, "replaymode in configuration file must be one of \"none\", \"time\", or \"lastspoke\""); + return 0; + } + } + + // How many seconds of replay log should automatically be replayed - TODO - Can we do error checking on this? + int oldreplayseconds = settings->replayseconds; + settings->replayseconds = getconfint("replayseconds", settings->conffile); + if (errno == ECONFINT) { + settings->replayseconds = oldreplayseconds; + strcpy(failuremsg, "error getting 'replayseconds' from configuration file"); + return 0; + } + + // Is logging enabled? + int oldlogging = settings->logging; + settings->logging = getconfint("logging", settings->conffile); + if (errno == ECONFINT) { + settings->logging = oldlogging; + strcpy(failuremsg, "error getting 'logging' from configuration file"); + return 0; + } + + // Is replay logging enabled? + int oldreplaylogging = settings->replaylogging; + settings->replaylogging = getconfint("replaylogging", settings->conffile); + if (errno == ECONFINT) { + settings->replaylogging = oldreplaylogging; + strcpy(failuremsg, "error getting 'replaylogging' from configuration file"); + return 0; + } + + // Is debugging enabled? + int olddebug = debug; + debug = getconfint("debug", settings->conffile); + if (errno == ECONFINT) { + debug = olddebug; + strcpy(failuremsg, "error getting 'debug' from configuration file"); + return 0; + } + + // All is good, no failure message, return 1. + failuremsg[0] = '\0'; + return 1; +} diff --git a/functions.h b/functions.h index c0e90f8..07fdf09 100644 --- a/functions.h +++ b/functions.h @@ -35,6 +35,7 @@ #include "sockets.h" #include "structures.h" #include "replay.h" +#include "config.h" // getstdin() return codes #define OK 0 @@ -156,4 +157,8 @@ void tryautonick(struct ircdstate *ircdstate); // "sourcefd" of 0 means the request didn't come from a client void cleanexit(SSL *server_ssl, struct client *clients, int sourcefd, struct ircdstate *ircdstate, struct settings *settings, char *quitmsg); +// Re-read the configuration file, setting 'failuremsg' to a failure message on failure. +// Returns 1 on success or 0 on failure. +int rehash(struct settings *settings, char *failuremsg); + #endif diff --git a/message.c b/message.c index aa0dc1c..6e716e4 100644 --- a/message.c +++ b/message.c @@ -873,6 +873,8 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]\" (To replay a given length of time of replay log.)", ircdstate->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REHASH\" (To reload settings from the configuration file, see README for which settings can be reloaded.)", ircdstate->ircnick); + sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER QUIT [quit message]\" (To quit blabouncer, optionally sending [quit message] to the server.)", ircdstate->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); @@ -1253,7 +1255,31 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int } else { cleanexit(server_ssl, clients, sourcefd, ircdstate, settings, ""); } + // REHASH received, re-read the configuration file and let rehash() to the appropriate things + } else if (strncasecmp(tokens[1], "REHASH", strlen("REHASH")) == 0) { + debugprint(DEBUG_SOME, "Client BLABOUNCER REHASH found and it is: %s with length %zd! Attempting rehash...\n", tokens[1], strlen(tokens[1])); + + // TODO - This code is duplicated between here and SIGHUP handling + + char failuremsg[MAXDATASIZE]; + failuremsg[0] = '\0'; + // Try to rehash... + if (!rehash(settings, failuremsg)) { + // ...or log and tell client if it failed + debugprint(DEBUG_CRIT, "REHASH failed: %s.\n", failuremsg); + if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :REHASH failed: %s.", ircdstate->ircnick, failuremsg)) { + debugprint(DEBUG_CRIT, "Error while preparing REHASH failure message response!\n"); + outgoingmsg[0] = '\0'; + } + sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); + } else { + // ...or tell all clients it worked + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :REHASH complete!", ircdstate->ircnick); + sendtoallclients(clients, outgoingmsg, 0, settings); + } + + return 1; // Unrecognised BLABOUNCER command received, send some help instructions } else { debugprint(DEBUG_SOME, "Client BLABOUNCER unrecognised command found and it is: %s with length %zd! Sending a help message.\n", tokens[1], strlen(tokens[1])); @@ -1261,6 +1287,8 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]\" (To replay a given length of time of replay log.)", ircdstate->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REHASH\" (To reload settings from the configuration file, see README for which settings can be reloaded.)", ircdstate->ircnick); + sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER QUIT [quit message]\" (To quit blabouncer, optionally sending [quit message] to the server.)", ircdstate->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); return 1; -- cgit v1.2.3