+/**
+ * @file main.c
+ * @purpose Contains main function, definition of options global variable, and the general signal handler
+ */
+
+// --- external includes --- //
#include <stdlib.h>
#include <stdio.h>
+#include <signal.h>
+
+// --- local includes --- //
#include "options.h"
#include "master.h"
#include "slave.h"
#include "daemon.h"
-#include <signal.h>
-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);
}
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()
-{
-
-}
#include "master.h"
#include "log.h"
+#include <string.h>
#include <sys/wait.h>
#include "daemon.h"
#include <stdlib.h>
// 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);
}
}
-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;
}
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;
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
{
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);
}
{
FILE * f = fdopen(master.remote_err[i], "r+"); setbuf(f, NULL);
- fprintf(f, "exit\n");
+ fprintf(f, SHELL_EXIT_COMMAND);
fclose(f);
}
{
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
}
}
while (buffer[len-1] != '\n');
buffer[len-1] = '\0';
- newSlaves = atoi(buffer);
+
+ while (newSlaves == 0 && strcmp(buffer, "0") != 0)
+ {
+ newSlaves = atoi(buffer);
+ }
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);
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);
-#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
+ */
+
+// --- 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]);
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)
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]);
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]);
}
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
{
}
}
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]);
}
}
-
+/**
+ * @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;
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");
+}
+/**
+ * @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
+ */
+
#ifndef _OPTIONS_H
#define _OPTIONS_H
#include <stdbool.h>
#include <sys/select.h>
+// 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
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);
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);
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);
{
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;
// 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