1 #define _XOPEN_SOURCE 700
5 * @purpose Implementations of functions for populating Options struct
6 * Also implements helpers and minor cleanup functions
10 // --- external includes --- //
16 #include <sys/types.h>
23 // --- local includes --- //
29 * @funct cleanup_command
30 * @purpose Removes the COMMAND_FILE used for one off command parsing
31 * Added to exit table only if needed
33 void cleanup_command()
39 * @funct cleanup_daemon
40 * @purpose Removes files and fifo's used by daemon
41 * Added to exit table only if needed
45 remove(DAEMON_PID_FILE);
47 remove(DAEMON_BARRIER_FIFO);
53 * @purpose Setup default options, call parse_args, do basic setup and initialisation
54 * @param argc - From main
55 * @param argv - From main
56 * @param o - Options struct to setup
58 void options_setup(int argc, char ** argv, Options * o)
60 srand(time(NULL)); // rand is used in a few places, so seed the RNG
62 // Setup default options
64 o->master_addr = NULL;
65 o->shell = "bash"; // choosing other shells will not end well
68 o->verbosity = LOGNOTE; // default log level
72 o->nCPU = sysconf( _SC_NPROCESSORS_ONLN );
74 o->interactive = true;
78 options_parse_args(argc, argv, o);
80 // Initialisation behaviour that depends on option values
82 if (!o->daemon) // if we are not a daemon...
84 FILE * f = fopen(DAEMON_PID_FILE, "r");
87 //... but the DAEMON_PID_FILE exists ...
89 fscanf(f, "%d", &daemon_pid); // get its PID
91 if (kill(daemon_pid, 0) != 0) // Check it's running
93 error("Initialise", "There was a daemon [%d] running here, but it's gone for some reason.", daemon_pid);
95 // We are a daemon wrapper
96 o->daemon_wrapper = true;
102 // We are not a daemon wrapper
103 o->daemon_wrapper = false;
106 if (o->logfile != NULL)
108 // A logfile was given; freopen on stderr
109 if (o->logfile[0] == '+') // option to append
110 freopen(o->logfile+1, "a", stderr);
112 freopen(o->logfile, "w", stderr);
114 setbuf(stderr, NULL);
116 if (o->outfile != NULL)
118 // An outfile was given; freopen on stdout
119 if (o->outfile[0] == '+') // option to append
120 freopen(o->outfile+1, "a", stdout);
122 freopen(o->outfile, "w", stdout);
125 // If the verbosity is high enough, display a welcome message
126 if (o->verbosity >= LOGINFO)
128 char buffer[BUFSIZ]; getcwd(buffer, BUFSIZ);
129 char * type = (o->daemon) ? "daemon" : (o->daemon_wrapper) ? "wrapper" : (o->master_addr != NULL) ? "slave" : "interactive";
130 log_print(LOGINFO, "options_setup", "Directory %s; type of instance: %s", buffer, type);
133 // If we are a daemon wrapper, we can't be interactive
134 // This is purely to avoid confusing people; it is trivial to be both a daemon wrapper and interactive
135 // but they'll never see any outputs
136 if (o->daemon_wrapper && o->interactive)
138 log_print(LOGWARN, "options_setup", "There is a daemonised swarm [%d] running in this directory.");
139 log_print(LOGWARN, "options_setup", "You can only pass commands to the daemon by invoking %s -c [command]", o->program);
140 log_print(LOGWARN, "options_setup", "Running `swarm -c \"#EXIT#\"' will quit the daemon, unless it is waiting on a BARRIER");
141 error("options_setup", "Can't run an interactive wrapper to a daemon");
147 * @funct options_parse_args
148 * @purpose Parse arguments and setup options accordingly
149 * @param argc - From main via Initialise
150 * @param argv - From main via Initialise
151 * @param o - Options struct to setup
153 void options_parse_args(int argc, char ** argv, Options * o)
156 // Go through all arguments
157 for (int i = 1; i < argc; ++i)
159 // Most switches always start with '-' and have a single character
160 if (argv[i][0] == '-' && argv[i][2] == '\0')
162 // Set the number of local shells
163 if (argv[i][1] == 'n')
166 error("ParseArguments", "No argument following %s switch", argv[i]);
168 o->nCPU = atoi(argv[++i]);
169 fprintf(stderr, "nCPU is %s (%d)\n", argv[i], o->nCPU);
171 else if (argv[i][1] == 'r') // specify that this instance is remote
174 error("ParseArguments", "No argument following %s switch", argv[i]);
175 o->master_addr = argv[++i];
176 char * p = strstr(o->master_addr, ":");
183 else if (argv[i][1] == 'c') // Give a one off command
187 error("ParseArguments", "No argument following %s switch", argv[i]);
190 error("ParseArguments", "Can't use %s switch in combination with a script", argv[i]);
192 // insert terrible hack here
193 o->interactive = false;
194 FILE * f = fopen(COMMAND_FILE, "a");
195 fprintf(f, "%s\n", argv[++i]);
197 dup2(open(COMMAND_FILE, O_RDONLY), fileno(stdin));
198 atexit(cleanup_command);
200 else if (argv[i][1] == 's') // Specify the shell... TODO: Remove?
203 error("ParseArguments", "No argument following %s switch", argv[i]);
205 o->shell = argv[++i]; // obviously this breaks things
207 else if (argv[i][1] == 'l') // Specify logfile and/or level
210 error("ParseArguments", "No argument following %s switch", argv[i]);
212 char * l = argv[++i];
213 while (*l != '\0') if (*(l++) == ':') break;
216 o->verbosity = atoi(l);
219 if (argv[i][0] != '\0')
220 o->logfile = argv[i];
223 else if (argv[i][1] == 'o') // Specify output file
226 error("ParseArguments", "No argument following %s switch", argv[i]);
228 o->outfile = argv[++i];
232 fprintf(stderr, "%s : Unrecognised switch \"%s\"\n", argv[0], argv[i]);
236 else if (strcmp(argv[i], "--daemon") == 0)
238 // The --daemon switch is special because we want people to know exactly what it does
239 // Except it isn't really a "true" daemon, but whatever
241 // First check if there is already a daemon
242 FILE * f = fopen(DAEMON_PID_FILE, "r");
245 int daemon_pid; fscanf(f, "%d\n", &daemon_pid);
247 // There is already a daemon...
248 if (kill(daemon_pid, 0) != 0) // is it running?
252 log_print(LOGERR, "ParseArguments", "It looks like a daemon [%d] failed to exit cleanly. Starting a new daemon.", daemon_pid);
253 cleanup_daemon(); //TODO: Exit and leave it up to the user to remove files instead?
257 error("ParseArguments", "Couldn't determine whether a daemon [%d] was already running : %s", daemon_pid, strerror(errno));
262 // Don't rain on the existing daemon's parade
263 error("ParseArguments", "A daemon is already running!");
270 exit(EXIT_SUCCESS); // fork off into daemon
272 atexit(cleanup_daemon);
274 f = fopen(DAEMON_PID_FILE, "w");
276 error("ParseArguments", "Couldn't open %s : %s", DAEMON_PID_FILE, strerror(errno));
277 fprintf(f, "%d", getpid()); fclose(f); // print daemon's PID to file
279 fprintf(stdout, "%d\n", getpid()); // ... and to stdout, because this is helpful for bash scripting
280 // (in case you didn't realise, this program is meant to be used in bash scripts)
281 freopen("/dev/null", "w", stdout); // freopen to a dark void
282 freopen("/dev/null", "w", stderr);
283 // NOTE: If the user manually specified output and log files, these streams get freopen'd again later
284 // Slightly inefficient to do twice, but I don't care
286 // Create a fifo to use for stdin
287 mkfifo(DAEMON_FIFO, 0600);
288 mkfifo(DAEMON_BARRIER_FIFO, 0600);
289 freopen(DAEMON_FIFO, "r", stdin); // NOTE: Daemon will actually block here
290 // until a daemon wrapper is run, and opens the fifo for writing
295 else if (o->interactive) // a script was given and we can run it
297 o->interactive = false;
298 dup2(open(argv[i], O_RDONLY), fileno(stdin)); // replace stdin
300 else // Too many arguments were given
302 fprintf(stderr, "%s : Usage %s [options] [script]\n", argv[0], argv[0]);
303 fprintf(stderr, "%s : (extra argv[%d] %s)\n", argv[0], i, argv[i]);
311 * @purpose Implement the stdlib function strdup
312 * Because I couldn't get things to compile without this
313 * @param str - The string to copy
314 * @returns char* to allocated memory containing a copy of str
315 * NOTE: The memory needs to be free(3)'d
318 char * strdup(const char * str)
320 int n = strlen(str) + 1;
321 char * dup = (char*)(calloc(n, sizeof(char)));
330 * @funct sigchld_respond
331 * @purpose Print messages in response to SIGCHLD... also may SIGKILL the program
333 void sigchld_respond(int s, char * name, int id)
335 fprintf(stderr, "Unexpected exit of slave %s:%d", name, id);
338 int sig = WTERMSIG(s);
339 fprintf(stderr, " due to signal %s", strsignal(sig));
342 fprintf(stderr," - committing suicide\n");
348 fprintf(stderr, " return code %d.",s);
350 fprintf(stderr, " Starting replacement.\n");