From d9c5d3fcf004de1fcf8b9e1add32326de32748e0 Mon Sep 17 00:00:00 2001 From: Christopher Wellons Date: Sun, 3 Feb 2019 11:47:20 -0500 Subject: [PATCH] Allow configuration via config file (-f, SIGHUP) --- README.md | 5 ++- endlessh.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 110 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3bc850e..7a5835d 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,9 @@ trap multiple clients at a time. Usage information is printed with `-h`. ``` -Usage: endlessh [-vh] [-d MSECS] [-l LEN] [-m LIMIT] [-p PORT] +Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT] -d INT Message millisecond delay [10000] + -f Set config file [/etc/endlessh/config] -h Print this help message and exit -l INT Maximum banner line length (3-255) [32] -m INT Maximum number of clients [4096] @@ -37,3 +38,5 @@ are left in the accept queue, not rejected instantly. A SIGTERM signal will gracefully shut down the daemon, allowing it to write a complete, consistent log. + +A SIGHUP signal requests a reload of the configuration file (`-f`). diff --git a/endlessh.c b/endlessh.c index 83bc882..b63c294 100644 --- a/endlessh.c +++ b/endlessh.c @@ -20,6 +20,7 @@ #define DEFAULT_DELAY 10000 /* milliseconds */ #define DEFAULT_MAX_LINE_LENGTH 32 #define DEFAULT_MAX_CLIENTS 4096 +#define DEFAULT_CONFIG_FILE "/etc/endlessh/config" #define XSTR(s) STR(s) #define STR(s) #s @@ -245,6 +246,15 @@ sigterm_handler(int signal) running = 0; } +static volatile sig_atomic_t reload = 0; + +static void +sighup_handler(int signal) +{ + (void)signal; + reload = 1; +} + struct config { int port; int delay; @@ -319,13 +329,82 @@ config_set_max_line_length(struct config *c, const char *s, int hardfail) } } +static void +config_fail(const char *file, long lineno, int hardfail) +{ + fprintf(stderr, "%s:%ld: Expected integer\n", file, lineno); + if (hardfail) exit(EXIT_FAILURE); +} + +static void +config_load(struct config *c, const char *file, int hardfail) +{ + long lineno = 0; + FILE *f = fopen(file, "r"); + if (f) { + const char *delim = " \n"; + char line[256]; + while (fgets(line, sizeof(line), f)) { + lineno++; + char *save = 0; + char *tok = strtok_r(line, delim, &save); + if (!tok) { + continue; + } else if (!strcmp(tok, "Port")) { + tok = strtok_r(0, delim, &save); + if (tok) { + config_set_port(c, tok, hardfail); + } else { + config_fail(file, lineno, hardfail); + } + } else if (!strcmp(tok, "Delay")) { + tok = strtok_r(0, delim, &save); + if (tok) { + config_set_delay(c, tok, hardfail); + } else { + config_fail(file, lineno, hardfail); + } + } else if (!strcmp(tok, "MaxLineLength")) { + tok = strtok_r(0, delim, &save); + if (tok) { + config_set_max_line_length(c, tok, hardfail); + } else { + config_fail(file, lineno, hardfail); + } + } else if (!strcmp(tok, "MaxClients")) { + tok = strtok_r(0, delim, &save); + if (tok) { + config_set_max_line_length(c, tok, hardfail); + } else { + config_fail(file, lineno, hardfail); + } + } else { + fprintf(stderr, "%s:%ld: Unknown option '%s'\n", + file, lineno, tok); + } + } + fclose(f); + } +} + +static void +config_log(const struct config *c) +{ + logmsg(LOG_INFO, "Port %d", c->port); + logmsg(LOG_INFO, "Delay %ld", c->delay); + logmsg(LOG_INFO, "MaxLineLength %d", c->max_line_length); + logmsg(LOG_INFO, "MaxClients %d", c->max_clients); +} + static void usage(FILE *f) { - fprintf(f, "Usage: endlessh [-vh] [-d MSECS] [-l LEN] " + fprintf(f, "Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] " "[-m LIMIT] [-p PORT]\n"); fprintf(f, " -d INT Message millisecond delay [" XSTR(DEFAULT_DELAY) "]\n"); + fprintf(f, " -f Set config file [" + DEFAULT_CONFIG_FILE "]\n"); fprintf(f, " -h Print this help message and exit\n"); fprintf(f, " -l INT Maximum banner line length (3-255) [" XSTR(DEFAULT_MAX_LINE_LENGTH) "]\n"); @@ -340,13 +419,19 @@ int main(int argc, char **argv) { struct config config = CONFIG_DEFAULT; + const char *config_file = DEFAULT_CONFIG_FILE; + config_load(&config, config_file, 1); int option; - while ((option = getopt(argc, argv, "d:hl:m:p:v")) != -1) { + while ((option = getopt(argc, argv, "d:f:hl:m:p:v")) != -1) { switch (option) { case 'd': config_set_delay(&config, optarg, 1); break; + case 'f': + config_file = optarg; + config_load(&config, optarg, 1); + break; case 'h': usage(stdout); exit(EXIT_SUCCESS); @@ -371,13 +456,17 @@ main(int argc, char **argv) } /* Log configuration */ - logmsg(LOG_INFO, "Port %d", config.port); - logmsg(LOG_INFO, "Delay %ld", config.delay); - logmsg(LOG_INFO, "MaxLineLength %d", config.max_line_length); - logmsg(LOG_INFO, "MaxClients %d", config.max_clients); + config_log(&config); - struct sigaction sa = {.sa_handler = sigterm_handler}; - check(sigaction(SIGTERM, &sa, 0)); + { + struct sigaction sa = {.sa_handler = sigterm_handler}; + check(sigaction(SIGTERM, &sa, 0)); + } + + { + struct sigaction sa = {.sa_handler = sighup_handler}; + check(sigaction(SIGHUP, &sa, 0)); + } int nclients = 0; @@ -405,13 +494,21 @@ main(int argc, char **argv) srand(time(0)); while (running) { + if (reload) { + /* Configuration reload requested (SIGHUP) */ + config_load(&config, config_file, 0); + config_log(&config); + reload = 0; + } + + /* Enqueue the listening socket first */ pollvec_clear(pollvec); if (nclients < config.max_clients) pollvec_push(pollvec, server, POLLIN); else pollvec_push(pollvec, -1, POLLIN); - /* Poll clients that are due for another message */ + /* Enqueue clients that are due for another message */ int timeout = -1; long long now = uepoch(); for (struct client *c = queue->head; c; c = c->next) {