From: Sam Moore Date: Sat, 23 Feb 2013 10:34:52 +0000 (+0800) Subject: Did some stuff X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=matches%2Fswarm.git Did some stuff I think I fixed a bug, and tried to document things Basically things work, but I need to make the code nicer before I show it off to people Would be nice to be able to DEABSORB/DETACH or something from remote instances, but I think that's getting carried away Need to fix the log messages to show what host they come from though --- diff --git a/src/main.c b/src/main.c index baab2b7..536f3c7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,46 +1,75 @@ +/** + * @file main.c + * @purpose Contains main function, definition of options global variable, and the general signal handler + * @author Sam Moore (matches@ucc.asn.au) + */ + +// --- external includes --- // #include #include +#include + +// --- local includes --- // #include "options.h" #include "master.h" #include "slave.h" #include "daemon.h" -#include -Options options; +// --- variable definitions --- // +Options options; // basically all global variables are contained in this structure + + +// --- function declarations --- // +void signal_handler(int signal); + -void Cleanup(); -void Signal_handler(int signal); +// --- function definitions --- // +/** + * @funct main + * @purpose The main function; starts argument parsing function, + * starts appropriate main function based on the type of the instance (interactive, remote, daemon wrapper) + * @param argc - You should know what this is. But it's the size of the argv array. In case you didn't know. + * @param argv - You should also know what this is. It contains arguments passed to the program. + */ int main(int argc, char ** argv) { - atexit(Cleanup); - Initialise(argc, argv, &options); + options_setup(argc, argv, &options); // fill in the options - if (signal(SIGTERM, Signal_handler) == SIG_ERR) + // Setup signal handlers + if (signal(SIGTERM, signal_handler) == SIG_ERR) error("main", "Setting signal handler"); - if (signal(SIGINT, Signal_handler) == SIG_ERR) + if (signal(SIGINT, signal_handler) == SIG_ERR) error("main", "Setting signal handler"); - if (signal(SIGHUP, Signal_handler) == SIG_ERR) + if (signal(SIGHUP, signal_handler) == SIG_ERR) error("main", "Setting signal handler"); - if (signal(SIGPIPE, Signal_handler) == SIG_ERR) + if (signal(SIGPIPE, signal_handler) == SIG_ERR) error("main", "Setting signal handler"); - //if (signal(SIGSEGV, Signal_handler) == SIG_ERR) + //if (signal(SIGSEGV, signal_handler) == SIG_ERR) // error("main", "Setting signal handler"); - if (options.master_addr == NULL) + + // Start the appropriate actual main function + if (options.master_addr == NULL) // This is not a "remote" instance { if (options.daemon_wrapper) + { + // There is a daemon running; send commands to the daemon Daemon_wrapper(&options); + } else + { + // Daemons and interactive instances run this function Master_main(&options); + } } else { - fprintf(stderr, "%p %s", options.master_addr, options.master_addr); + // Remote instance Slave_main(&options); } @@ -48,17 +77,20 @@ int main(int argc, char ** argv) return 0; } -void Signal_handler(int sig) +/** + * @funct signal_handler + * @purpose Handle any signals that would cause the program to terminate + * @param sig - The signal number + */ +void signal_handler(int sig) { - - signal(sig, SIG_IGN); + // There is a bit of a race condition with the SIGCHLD handler + // We can't be sure whether this handler gets called first, or SIGCHLD (due to a child getting the signal) + // We need to ignore SIGCHLD if this handler gets called first signal(SIGCHLD, SIG_IGN); + signal(sig, SIG_IGN); // I'm not sure why we need this? //TODO: Remove? + log_print(2, "Signal_handler", "Got signal %d; exiting", sig); exit(EXIT_SUCCESS); } - -void Cleanup() -{ - -} diff --git a/src/master.c b/src/master.c index 2734d07..f5e17eb 100644 --- a/src/master.c +++ b/src/master.c @@ -2,6 +2,7 @@ #include "master.h" #include "log.h" +#include #include #include "daemon.h" #include @@ -101,7 +102,19 @@ void Master_setup(Options * o) // One slave per CPU for (int i = 0; i < master.nSlaves; ++i) { - Make_slave(i); + + + int sv[2]; + // Possibly the best function ever + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) + { + error("Make_slave", "Setting up socketpair for slave %d : %s", i, strerror(errno)); + } + master.slave[i].in = sv[1]; + master.slave[i].out = sv[1]; + master.slave[i].socket_child_end = sv[0]; + + Master_shell(i); @@ -114,23 +127,16 @@ void Master_setup(Options * o) } } -void Make_slave(int i) -{ - int sv[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) - { - error("Make_slave", "Setting up socketpair for slave %d : %s", i, strerror(errno)); - } - +void Master_shell(int i) +{ master.slave[i].pid = fork(); if (master.slave[i].pid == 0) { - dup2(sv[0],fileno(stdin)); - dup2(sv[0],fileno(stdout)); + dup2(master.slave[i].socket_child_end,fileno(stdin)); + dup2(master.slave[i].socket_child_end,fileno(stdout)); execlp(master.o->shell, master.o->shell, NULL); } - master.slave[i].in = sv[1]; - master.slave[i].out = sv[1]; + master.slave[i].running = true; } @@ -395,14 +401,19 @@ void Master_output(int i, char c) memset(t->output+(t->outlen), 0, sizeof(char) * t->outsiz - t->outlen); } - if (c == EOF || (master.o->endlen > 0 && t->outlen >= master.o->endlen - && strcmp((t->output)+(t->outlen)-(master.o->endlen), master.o->end) == 0)) + if (c == EOF + #ifdef SHELL_OUTPUT_FINISHED + || (t->outlen >= SHELL_OUTPUT_FINISHED_LENGTH + && strcmp((t->output)+(t->outlen)-(SHELL_OUTPUT_FINISHED_LENGTH), SHELL_OUTPUT_FINISHED) == 0)) + #else + ) // this is totally readable + #endif //SHELL_OUTPUT_FINISHED { if (c != EOF) { - t->output[t->outlen - master.o->endlen] = '\0'; - t->outlen -= master.o->endlen; + t->output[t->outlen - SHELL_OUTPUT_FINISHED_LENGTH] = '\0'; + t->outlen -= SHELL_OUTPUT_FINISHED_LENGTH; } master.slave[i].task = NULL; @@ -571,7 +582,7 @@ void Master_loop() if (errno != ENXIO) error("Master_loop", "Daemon trying to reopen fifo %s : %s", DAEMON_FIFO, strerror(errno)); else - log_print(2, "Master_loop", "Daemon couldn't reopen fifo %s : %s", DAEMON_FIFO, strerror(errno)); + log_print(LOGWARN, "Master_loop", "Daemon couldn't reopen fifo %s : %s", DAEMON_FIFO, strerror(errno)); } else { @@ -670,17 +681,18 @@ void Master_send() write(send_task.slave_fd, master.o->append, (len-1) * sizeof(char)); //log_print(0, "Sent append %s\n",master.o->append); } - if (master.o->end != NULL) + #ifdef SHELL_OUTPUT_FINISHED { static char * echo = ";echo -en \""; static int len = -1; if (len == -1) len = strlen(echo); write(send_task.slave_fd, echo, len*sizeof(char)); - write(send_task.slave_fd, master.o->end, (master.o->endlen) * sizeof(char)); - write(send_task.slave_fd, "\"", 1*sizeof(char)); + write(send_task.slave_fd, SHELL_OUTPUT_FINISHED, SHELL_OUTPUT_FINISHED_LENGTH * sizeof(char)); + write(send_task.slave_fd, "\"", sizeof(char)); //log_print(0, "Sent end\n"); } - write(send_task.slave_fd, "\n", 1*sizeof(char)); + #endif //SHELL_OUTPUT_FINISHED + write(send_task.slave_fd, "\n", sizeof(char)); master.commands_active++; log_print(3, "Master_sender", "Sent task %d \"%s\" on socket %d - %d tasks active", send_task.task->number, send_task.task->message, send_task.slave_fd, master.commands_active); } @@ -741,7 +753,7 @@ void Master_cleanup() { FILE * f = fdopen(master.remote_err[i], "r+"); setbuf(f, NULL); - fprintf(f, "exit\n"); + fprintf(f, SHELL_EXIT_COMMAND); fclose(f); } @@ -750,8 +762,8 @@ void Master_cleanup() { static int exitlen = -1; - if (exitlen == -1) exitlen = strlen(SHELL_EXIT_MESSAGE); - write(master.slave[i].in, SHELL_EXIT_MESSAGE, exitlen *sizeof(char)); + if (exitlen == -1) exitlen = strlen(SHELL_EXIT_COMMAND); + write(master.slave[i].in, SHELL_EXIT_COMMAND, exitlen *sizeof(char)); //usleep(0.5); //shouldn't matter too much } @@ -876,7 +888,11 @@ void Master_absorb(char * addr, int np) } while (buffer[len-1] != '\n'); buffer[len-1] = '\0'; - newSlaves = atoi(buffer); + + while (newSlaves == 0 && strcmp(buffer, "0") != 0) + { + newSlaves = atoi(buffer); + } @@ -986,34 +1002,21 @@ void sigchld_handler(int signal) return; } - fprintf(stderr, "Unexpected exit of slave %s", master.slave[i].name); - if (WIFSIGNALED(s)) - { - int sig = WTERMSIG(s); - fprintf(stderr, " due to %s", strsignal(sig)); - if (sig == SIGKILL) - { - printf(" - committing suicide\n"); - kill(getpid(), sig); - } - } - else - { - fprintf(stderr, " return code %d.",s); - } - fprintf(stderr, " Starting replacement.\n"); - Make_slave(i); + sigchld_respond(s, "local", i); + + Master_shell(i); - if (master.o->end != NULL) + #ifdef SHELL_OUTPUT_FINISHED { //log_print(1, "sigchld_handler", "Trying to convince slave %d to be nice", i); char buffer[BUFSIZ]; - sprintf(buffer, "name=%s;echo -en \"%s\"\n", master.slave[i].name, master.o->end); - if (write(master.slave[i].in, buffer, strlen(buffer)) <= 0) + int len = sprintf(buffer, "name=%s;echo -en \"%s\"\n", master.slave[i].name, SHELL_OUTPUT_FINISHED); + if (write(master.slave[i].in, buffer, len) <= 0) error("sigchld_handler", "Couldn't restart slave %d; it is unresponsive", i); } + #endif //SHELL_OUTPUT_FINISHED siglongjmp(env,1); diff --git a/src/master.h b/src/master.h index 3595e23..a3288ab 100644 --- a/src/master.h +++ b/src/master.h @@ -15,7 +15,7 @@ extern void Master_cleanup(); extern void Master_send(); extern void Master_absorb(char * addr, int np); -extern void Make_slave(int i); +extern void Master_shell(int i); extern void sigchld_handler(int signal); 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"); +} diff --git a/src/options.h b/src/options.h index 24f35eb..66fb4ef 100644 --- a/src/options.h +++ b/src/options.h @@ -1,3 +1,11 @@ +/** + * @file options.h + * @purpose Defines struct that is basically a container for global variables + * Also contains definitions for functions that initialise the struct + * Contains a few other clobal definitions + * @author Sam Moore (matches@ucc.asn.au) + */ + #ifndef _OPTIONS_H #define _OPTIONS_H @@ -6,42 +14,49 @@ #include #include +// One off commands are written to this file and then read back through a freopen of stdin #define COMMAND_FILE ".swarm.command" -#define SHELL_EXIT_MESSAGE "\nexit 666\n" -#define SHELL_EXIT_CODE 666 // make sure this matches the above +#define SHELL_EXIT_COMMAND "exit\n" +#define SHELL_OUTPUT_FINISHED "\a\a\a" // No one ever uses bell characters... right? +#define SHELL_OUTPUT_FINISHED_LENGTH 3 // WARNING: Make sure this is consistent with the above! +/** + * @struct Options + * @purpose Container for "options" (ie: arguments) to the program, as well as other global variables + */ typedef struct { - char * program; - char * name; - char * shell; - char * master_addr; - char * logfile; - char * outfile; - int verbosity; - int port; - char * prepend; - char * append; - char * end; - int endlen; - int nCPU; - int master_pid; - bool daemon; - bool daemon_wrapper; - bool encrypt; - bool interactive; - bool handle_signals; + char * program; // program name + char * name; // name of machine the program is running on + // Is populated by gethostname(2) but may be changed on remote instances + char * shell; // The shell to start; default "bash" + char * master_addr; // Name of master swarm. If this swarm *is* the master, it is NULL. + char * logfile; // Name of file to be opened after option parsing for logging messages and errors + char * outfile; // Name of file to be opened after option parsing for stdout + int verbosity; // Level at which to print log messages (see log.h) + int port; // Port for remote instance to connect to master on; has no effect if ssh tunneling is used + char * prepend; // Command to prepend to every other command //TODO: Not fully implemented + char * append; // Command to append to every other command //TODO: Not fully implemented + int nCPU; // Number of local shells to spawn; defaults to the number of cores/CPU on the machine + bool daemon; // indicates whether this instance is a daemon or not + bool daemon_wrapper; // indicates whether this instance is a daemon wrapper + bool interactive; // indicates whether this instance is interactive + bool encrypt; // indicates whether ssh tunnels will be used when absorbing remote instances + // NOTE: This cannot be set by the user + // It will automatically be set to true when #ABSORB# is used, and false when #ABSORB UNSECURE# is used. } Options; -extern Options options; +extern Options options; // single global variable + +extern void options_setup(int argc, char ** argv, Options * o); // fill options with default values, call ParseArguments +extern void options_parse_args(int argc, char ** argv, Options * o); // parse arguments and change option values -extern void Initialise(int argc, char ** argv, Options * o); -extern void ParseArguments(int argc, char ** argv, Options * o); +extern char * strdup(); //HACK: For some reason I couldn't use stdlib's strdup and wrote my own. -extern char * strdup(); +extern void sigchld_respond(int s, char * name, int id); // I don't know why this is here, but it is #endif //_OPTIONS_H diff --git a/src/slave.c b/src/slave.c index 65e11c8..622bc7b 100644 --- a/src/slave.c +++ b/src/slave.c @@ -35,13 +35,15 @@ void Slave_cleanup(); void Slave_main(Options * o) { + //fprintf(stderr, "%d\n", o->nCPU); + setbuf(stdin, NULL); setbuf(stdout, NULL); setbuf(stderr, NULL); dup2(fileno(stdout), fileno(stderr)); // yes, this works, apparently - slave = (Slave*)(calloc(o->nCPU, sizeof(slave))); + slave = (Slave*)(calloc(o->nCPU, sizeof(Slave))); atexit(Slave_cleanup); @@ -68,7 +70,8 @@ void Slave_main(Options * o) fgets(name, sizeof(name), stdin); name[strlen(name)-1] = '\0'; - //log_print(2, "Slave_main", "Got name %s", name); + //log_print(LOGINFO, "Slave_main", "Started remote swarm \"%s\"", name); + fprintf(stdout, "%d\n", o->nCPU); //log_print(2, "Slave_main", "Wrote nCPU %d", o->nCPU); @@ -160,30 +163,12 @@ void Slave_loop(Options * o) if (i >= o->nCPU) error("Slave_loop", "No child matches pid %d", p); - - - fprintf(stderr,"Unexpected exit of slave %s:%d", name, i); - if (WIFSIGNALED(s)) - { - int sig = WTERMSIG(s); - fprintf(stderr," due to %s", strsignal(sig)); - if (sig == SIGKILL) - { - fprintf(stderr," - %s committing suicide\n", name); - kill(getpid(), sig); - } - } - else - { - fprintf(stderr," return code %d.", s); - } + sigchld_respond(s, name, i); // cancel any tasks at the master for this slave - static int len = -1; - if (len < 0) - len = strlen(o->end); - write(slave[i].out, o->end, len); + + write(slave[i].out, SHELL_OUTPUT_FINISHED, SHELL_OUTPUT_FINISHED_LENGTH); Slave_shell(i, o->shell); diff --git a/src/slave.h b/src/slave.h index bf4db8f..6b66214 100644 --- a/src/slave.h +++ b/src/slave.h @@ -10,6 +10,7 @@ typedef struct { int in; int out; + int socket_child_end; // the child's end of the socket int pid; // pid of the process running the slave; == 0 for network slaves char * name; char * addr; diff --git a/src/ssh.c b/src/ssh.c index 0e1e197..4089e59 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -444,15 +444,17 @@ void ssh_exec_swarm(ssh * s, int * port, int * socket, int np) // connect secure if (port == NULL && socket != NULL) { - sprintf(buffer, "%s -r -", options.program); + int len = sprintf(buffer, "%s -r - -l :%d", options.program, options.verbosity); if (np != 0) - sprintf(buffer, " -n %d", np); + len += sprintf(buffer+len, " -n %d", np); + } else if (port != NULL && socket == NULL) { - sprintf(buffer, "%s -r $(echo $SSH_CONNECTION | awk \'{print $1}\'):%d", options.program, *port); + int len = sprintf(buffer, "%s -r $(echo $SSH_CONNECTION | awk \'{print $1}\'):%d -l :%d", options.program, *port); if (np != 0) - sprintf(buffer, " -n %d", np); + len += sprintf(buffer+len, " -n %d", np); + } else