Did some stuff
[matches/swarm.git] / src / options.c
index ddd2ddc..a230fa9 100644 (file)
-#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 ([email protected])
+ */
+
+// --- external includes --- //
 #include <string.h>
 #include <stdlib.h>
 #include <time.h>
 #include <unistd.h>
-#include "log.h"
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include "daemon.h"
 #include <signal.h>
+#include <sys/wait.h>
+
 
+// --- 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");
+}
 

UCC git Repository :: git.ucc.asn.au