X-Git-Url: https://git.ucc.asn.au/?p=matches%2Fswarm.git;a=blobdiff_plain;f=src%2Foptions.c;h=a230fa997d9004812cd2cad85fc04aa34626a09e;hp=ddd2ddc1e612a2f40af186c9f319565e119a9ad3;hb=HEAD;hpb=4e2127d6576cea3f54c619d0bb20a22006567206 diff --git a/src/options.c b/src/options.c index ddd2ddc..a230fa9 100644 --- a/src/options.c +++ b/src/options.c @@ -1,148 +1,174 @@ -#include "options.h" +#define _XOPEN_SOURCE 700 + +/** + * @file options.c + * @purpose Implementations of functions for populating Options struct + * Also implements helpers and minor cleanup functions + * @author Sam Moore (matches@ucc.asn.au) + */ + +// --- external includes --- // #include #include #include #include -#include "log.h" #include #include #include #include -#include "daemon.h" #include +#include + +// --- local includes --- // +#include "options.h" +#include "log.h" +#include "daemon.h" -void remove_command() +/** + * @funct cleanup_command + * @purpose Removes the COMMAND_FILE used for one off command parsing + * Added to exit table only if needed + */ +void cleanup_command() { remove(COMMAND_FILE); - } -void remove_daemon() +/** + * @funct cleanup_daemon + * @purpose Removes files and fifo's used by daemon + * Added to exit table only if needed + */ +void cleanup_daemon() { remove(DAEMON_PID_FILE); remove(DAEMON_FIFO); remove(DAEMON_BARRIER_FIFO); } -void close_err() -{ - fclose(stderr); -} -void close_out() +/** + * @funct Initialise + * @purpose Setup default options, call parse_args, do basic setup and initialisation + * @param argc - From main + * @param argv - From main + * @param o - Options struct to setup + */ +void options_setup(int argc, char ** argv, Options * o) { - fclose(stdout); -} - -char name[BUFSIZ]; + srand(time(NULL)); // rand is used in a few places, so seed the RNG -void Initialise(int argc, char ** argv, Options * o) -{ - srand(time(NULL)); + // Setup default options o->program = argv[0]; o->master_addr = NULL; - o->shell = "bash"; // choosing other shells seems to not work... for some reason + o->shell = "bash"; // choosing other shells will not end well o->logfile = NULL; o->outfile = NULL; - o->verbosity = 2; + o->verbosity = LOGNOTE; // default log level o->port = 0; o->append = NULL; o->prepend = NULL; - o->end = "\a\a\a"; o->nCPU = sysconf( _SC_NPROCESSORS_ONLN ); o->daemon = false; - o->encrypt = true; o->interactive = true; + o->encrypt = true; - gethostname(name, sizeof(name)); - o->name = strdup(name); + // Parse arguments + options_parse_args(argc, argv, o); - o->master_pid = getpid(); + // Initialisation behaviour that depends on option values - ParseArguments(argc, argv, o); - - o->endlen = (o->end != NULL) ? strlen(o->end) : 0; - - if (!o->daemon) + if (!o->daemon) // if we are not a daemon... { FILE * f = fopen(DAEMON_PID_FILE, "r"); if (f != NULL) { + //... but the DAEMON_PID_FILE exists ... int daemon_pid; - fscanf(f, "%d", &daemon_pid); + fscanf(f, "%d", &daemon_pid); // get its PID fclose(f); - if (kill(daemon_pid, 0) != 0) + if (kill(daemon_pid, 0) != 0) // Check it's running { error("Initialise", "There was a daemon [%d] running here, but it's gone for some reason.", daemon_pid); } + // We are a daemon wrapper o->daemon_wrapper = true; } } else + { + // We are not a daemon wrapper o->daemon_wrapper = false; + } if (o->logfile != NULL) { - if (o->logfile[0] == '+') + // A logfile was given; freopen on stderr + if (o->logfile[0] == '+') // option to append freopen(o->logfile+1, "a", stderr); else freopen(o->logfile, "w", stderr); setbuf(stderr, NULL); - atexit(close_err); } if (o->outfile != NULL) { - if (o->outfile[0] == '+') + // An outfile was given; freopen on stdout + if (o->outfile[0] == '+') // option to append freopen(o->outfile+1, "a", stdout); else freopen(o->outfile, "w", stdout); - atexit(close_out); } - if (o->verbosity >= 3) + // If the verbosity is high enough, display a welcome message + if (o->verbosity >= LOGINFO) { char buffer[BUFSIZ]; getcwd(buffer, BUFSIZ); char * type = (o->daemon) ? "daemon" : (o->daemon_wrapper) ? "wrapper" : (o->master_addr != NULL) ? "slave" : "interactive"; - log_print(3, "Initialise", "Directory %s; type of instance: %s", buffer, type); + log_print(LOGINFO, "options_setup", "Directory %s; type of instance: %s", buffer, type); } + + // If we are a daemon wrapper, we can't be interactive + // This is purely to avoid confusing people; it is trivial to be both a daemon wrapper and interactive + // but they'll never see any outputs if (o->daemon_wrapper && o->interactive) { - log_print(1, "Initialise", "There is a daemonised swarm [%d] running in this directory."); - log_print(1, "Initialise", "You can only pass commands to the daemon by invoking %s -c [command]", options.program); - log_print(1, "Initialise", "Running `swarm -c \"#EXIT#\"' will quit the daemon, unless it is waiting on a BARRIER"); - error("Initialise", "Can't run an interactive wrapper to a daemon"); + log_print(LOGWARN, "options_setup", "There is a daemonised swarm [%d] running in this directory."); + log_print(LOGWARN, "options_setup", "You can only pass commands to the daemon by invoking %s -c [command]", o->program); + log_print(LOGWARN, "options_setup", "Running `swarm -c \"#EXIT#\"' will quit the daemon, unless it is waiting on a BARRIER"); + error("options_setup", "Can't run an interactive wrapper to a daemon"); } } -void ParseArguments(int argc, char ** argv, Options * o) +/** + * @funct options_parse_args + * @purpose Parse arguments and setup options accordingly + * @param argc - From main via Initialise + * @param argv - From main via Initialise + * @param o - Options struct to setup + */ +void options_parse_args(int argc, char ** argv, Options * o) { + // Go through all arguments for (int i = 1; i < argc; ++i) { + // Most switches always start with '-' and have a single character if (argv[i][0] == '-' && argv[i][2] == '\0') { - - - - - - if (argv[i][1] == 'p') - { - if (i >= argc-1) - error("ParseArguments", "No argument following %s switch", argv[i]); - o->port = atoi(argv[++i]); - } - else if (argv[i][1] == 'n') + // Set the number of local shells + if (argv[i][1] == 'n') { if (i >= argc-1) error("ParseArguments", "No argument following %s switch", argv[i]); + o->nCPU = atoi(argv[++i]); + fprintf(stderr, "nCPU is %s (%d)\n", argv[i], o->nCPU); } - else if (argv[i][1] == 'r') + else if (argv[i][1] == 'r') // specify that this instance is remote { if (i >= argc-1) error("ParseArguments", "No argument following %s switch", argv[i]); @@ -154,7 +180,7 @@ void ParseArguments(int argc, char ** argv, Options * o) o->port = atoi(p); } } - else if (argv[i][1] == 'c') + else if (argv[i][1] == 'c') // Give a one off command { if (i >= argc-1) @@ -169,16 +195,16 @@ void ParseArguments(int argc, char ** argv, Options * o) fprintf(f, "%s\n", argv[++i]); fclose(f); dup2(open(COMMAND_FILE, O_RDONLY), fileno(stdin)); - atexit(remove_command); + atexit(cleanup_command); } - else if (argv[i][1] == 's') + else if (argv[i][1] == 's') // Specify the shell... TODO: Remove? { if (i >= argc-1) error("ParseArguments", "No argument following %s switch", argv[i]); o->shell = argv[++i]; // obviously this breaks things } - else if (argv[i][1] == 'l') + else if (argv[i][1] == 'l') // Specify logfile and/or level { if (i >= argc-1) error("ParseArguments", "No argument following %s switch", argv[i]); @@ -194,17 +220,13 @@ void ParseArguments(int argc, char ** argv, Options * o) o->logfile = argv[i]; } - else if (argv[i][1] == 'o') + else if (argv[i][1] == 'o') // Specify output file { if (i >= argc-1) error("ParseArguments", "No argument following %s switch", argv[i]); o->outfile = argv[++i]; } - else if (argv[i][1] == 'e') - o->encrypt = true; - else if (argv[i][1] == 'u') - o->encrypt = false; else { fprintf(stderr, "%s : Unrecognised switch \"%s\"\n", argv[0], argv[i]); @@ -213,18 +235,22 @@ void ParseArguments(int argc, char ** argv, Options * o) } else if (strcmp(argv[i], "--daemon") == 0) { + // The --daemon switch is special because we want people to know exactly what it does + // Except it isn't really a "true" daemon, but whatever + + // First check if there is already a daemon FILE * f = fopen(DAEMON_PID_FILE, "r"); if (f != NULL) { int daemon_pid; fscanf(f, "%d\n", &daemon_pid); fclose(f); - - if (kill(daemon_pid, 0) != 0) + // There is already a daemon... + if (kill(daemon_pid, 0) != 0) // is it running? { if (errno == ESRCH) { - log_print(0, "ParseArguments", "It looks like a daemon [%d] failed to exit cleanly. Starting a new daemon.", daemon_pid); - remove_daemon(); + log_print(LOGERR, "ParseArguments", "It looks like a daemon [%d] failed to exit cleanly. Starting a new daemon.", daemon_pid); + cleanup_daemon(); //TODO: Exit and leave it up to the user to remove files instead? } else { @@ -232,41 +258,46 @@ void ParseArguments(int argc, char ** argv, Options * o) } } else + { + // Don't rain on the existing daemon's parade error("ParseArguments", "A daemon is already running!"); + } } int pid = fork(); if (pid != 0) { - //setbuf(stdout, NULL); - //fprintf(stdout, "%d\n", pid); exit(EXIT_SUCCESS); // fork off into daemon } - atexit(remove_daemon); + atexit(cleanup_daemon); f = fopen(DAEMON_PID_FILE, "w"); if (f == NULL) error("ParseArguments", "Couldn't open %s : %s", DAEMON_PID_FILE, strerror(errno)); - fprintf(f, "%d", getpid()); fclose(f); + fprintf(f, "%d", getpid()); fclose(f); // print daemon's PID to file - fprintf(stdout, "%d\n", getpid()); - freopen("/dev/null", "w", stdout); + fprintf(stdout, "%d\n", getpid()); // ... and to stdout, because this is helpful for bash scripting + // (in case you didn't realise, this program is meant to be used in bash scripts) + freopen("/dev/null", "w", stdout); // freopen to a dark void freopen("/dev/null", "w", stderr); + // NOTE: If the user manually specified output and log files, these streams get freopen'd again later + // Slightly inefficient to do twice, but I don't care + // Create a fifo to use for stdin mkfifo(DAEMON_FIFO, 0600); mkfifo(DAEMON_BARRIER_FIFO, 0600); - freopen(DAEMON_FIFO, "r", stdin); - + freopen(DAEMON_FIFO, "r", stdin); // NOTE: Daemon will actually block here + // until a daemon wrapper is run, and opens the fifo for writing o->daemon = true; } - else if (o->interactive) + else if (o->interactive) // a script was given and we can run it { o->interactive = false; dup2(open(argv[i], O_RDONLY), fileno(stdin)); // replace stdin } - else + else // Too many arguments were given { fprintf(stderr, "%s : Usage %s [options] [script]\n", argv[0], argv[0]); fprintf(stderr, "%s : (extra argv[%d] %s)\n", argv[0], i, argv[i]); @@ -275,7 +306,15 @@ void ParseArguments(int argc, char ** argv, Options * o) } } - +/** + * @funct strdup + * @purpose Implement the stdlib function strdup + * Because I couldn't get things to compile without this + * @param str - The string to copy + * @returns char* to allocated memory containing a copy of str + * NOTE: The memory needs to be free(3)'d + */ +// TODO: Remove? char * strdup(const char * str) { int n = strlen(str) + 1; @@ -287,4 +326,27 @@ char * strdup(const char * str) return dup; } +/** + * @funct sigchld_respond + * @purpose Print messages in response to SIGCHLD... also may SIGKILL the program + */ +void sigchld_respond(int s, char * name, int id) +{ + fprintf(stderr, "Unexpected exit of slave %s:%d", name, id); + if (WIFSIGNALED(s)) + { + int sig = WTERMSIG(s); + fprintf(stderr, " due to signal %s", strsignal(sig)); + if (sig == SIGKILL) + { + fprintf(stderr," - committing suicide\n"); + kill(getpid(), sig); + } + } + else + { + fprintf(stderr, " return code %d.",s); + } + fprintf(stderr, " Starting replacement.\n"); +}