From d5ab728e85409b5074d4d61aaf5ba60862528e60 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Thu, 30 May 2019 23:33:21 +0100 Subject: Implement daemon (background) mode by default with optional foreground mode that can be specified on the command line. --- README | 8 +++++++ TODO | 2 +- blabouncer.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/README b/README index 9bee79b..c399a62 100644 --- a/README +++ b/README @@ -8,6 +8,14 @@ To compile run "make". To run from the current directory once compiled run "./blabouncer". +Running with command line arguments takes the form: +./blabouncer [-f] [-c /path/to/blabouncer.conf] + +-f sets foreground mode (without this, blabouncer will detach and run in the background as a daemon) +-c sets a path to a configuration file (this is optional) + +Arguments are all optional, but they must be specified in the order shown above. + == Configuration == An example configuration file is provided named "blabouncer.conf". diff --git a/TODO b/TODO index 7d3fd57..81e62bc 100644 --- a/TODO +++ b/TODO @@ -9,4 +9,4 @@ Might need to #include in blabouncer.c to make some operating systems Send a PING to the server before assuming a timeout is definite. -Implement daemon (background) mode with optional foreground mode. +Capture Ctrl-C/kill and exit cleanly. diff --git a/blabouncer.c b/blabouncer.c index f7e38a1..cd50a0a 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -114,6 +114,7 @@ struct settings { char basedir[PATH_MAX]; int logging; int replaylogging; + int background; // Whether or not we're running in the background (detached from the terminal as a daemon) }; // Structure of connected clients, their socket/file descriptors, their authentication status, and their OpenSSL structures @@ -2310,7 +2311,10 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { debugprint(DEBUG_FULL, "top of loop, fdmax %d.\n", fdmax); FD_ZERO(&rfds); // clear entries from fd set - FD_SET(STDIN, &rfds); // add STDIN (fd 0) to read fds to monitor + // Add STDIN (fd 0) to read fds to monitor if we're not in background/daemon mode + if (!settings->background) { + FD_SET(STDIN, &rfds); + } FD_SET(*serversockfd, &rfds); // add our server network socket to monitor FD_SET(*clientsockfd, &rfds); // add our client network socket to monitor @@ -2416,8 +2420,8 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { } } - // see if there's anything from stdin - if (FD_ISSET(STDIN, &rfds)) { + // see if there's anything from stdin (unless we're in background/daemon mode) + if (!settings->background && FD_ISSET(STDIN, &rfds)) { debugprint(DEBUG_FULL, "reading stdin!\n"); char outgoingmsg[MAXDATASIZE]; // String to send to server @@ -2577,19 +2581,43 @@ int main(int argc, char *argv[]) { // Terminate our global debug file string in case it's referenced before being read from file debugpath[0] = '\0'; - // Check to see if a configuration file was specified on the command line - if (argc == 3) { + // Assume background/daemon mode unless specified otherwise on the command line + settings.background = 1; + // Set configuration file to string to blank for now + settings.conffile[0] = '\0'; + + // Check to see what was specified on the command line + // TODO - Do better command line argument handling (no required order) + char helptext[] = "usage: %s [-f] [-c /path/to/blabouncer.conf] (-f for foreground mode)\n"; + if (argc == 2) { + if (strcmp(argv[1], "-f")) { + fprintf(stderr, helptext, argv[0]); + exit(1); + } else { + settings.background = 0; + } + } else if (argc == 3) { if (strcmp(argv[1], "-c")) { - fprintf(stderr,"usage: %s [-c /path/to/blabouncer.conf]\n", argv[0]); + fprintf(stderr, helptext, argv[0]); exit(1); } else { strcpy(settings.conffile, argv[2]); } - } else if (argc == 2 || argc > 3) { - fprintf(stderr,"usage: %s [-c /path/to/blabouncer.conf]\n", argv[0]); + } else if (argc == 4) { + if (strcmp(argv[1], "-f") || strcmp(argv[2], "-c")) { + fprintf(stderr, helptext, argv[0]); + exit(1); + } else { + settings.background = 0; + strcpy(settings.conffile, argv[3]); + } + } else if (argc > 4) { + fprintf(stderr, helptext, argv[0]); exit(1); - } else { - // If none provided, set to default + } + + // If a configuration file wasn't set on the command line, set it now + if (!settings.conffile[0]) { snprintf(settings.conffile, PATH_MAX, "%s/.blabouncer/blabouncer.conf", getenv("HOME")); // Since this is the default, it might not exist yet, so let's check... struct stat st = {0}; @@ -2777,6 +2805,32 @@ int main(int argc, char *argv[]) { debugprint(DEBUG_SOME, "Using configuration file '%s'.\n", settings.conffile); + // Unless specified otherwise on the command line, fork to background + if (settings.background) { + pid_t pid, sid; + // Fork from parent + if ((pid = fork()) < 0) { + printf("Couldn't fork, exiting.\n"); + debugprint(DEBUG_CRIT, "Couldn't fork, exiting.\n"); + exit(1); + } + // Exit parent (pid will be > 0 for the child) + if (pid > 0) { + exit(0); + } + umask(0); + // Let child become process group leader + if ((sid = setsid()) < 0) { + printf("Couldn't setsid() child, exiting.\n"); + debugprint(DEBUG_CRIT, "Couldn't setsid() child, exiting.\n"); + exit(1); + } + // In case our cwd changes + chdir("/"); + // TODO - close() STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO here, might need to change fd number + // logic/assumptions elsewhere (such as fd 3 being server, fd 4 being clients, etc.) + } + // TODO: see if any of this can be shared (i.e. 1. avoid code duplication, and 2. see if variables can be shared between client/server sockets) // I will try to keep to the notation of "server" meaning the real IRCd, "bouncer" meaning the bouncer, and "client" meaning the real IRC client -- cgit v1.2.3