Index: fedkit/splitter.pl
===================================================================
--- fedkit/splitter.pl	(revision 4addf9dd2a0d02bef5fdbf050d2462a92d694ed4)
+++ fedkit/splitter.pl	(revision 9c00d41bfebe55ed619f6335dde3c538c3d811f2)
@@ -2,7 +2,42 @@
 
 use Getopt::Std;
+use IO::File;
+use IO::Dir;
+use IO::Pipe;
+use File::Copy;
 
 @scripts = ("federate.sh", "smbmount.pl");
 $local_script_dir = ".";
+
+# Parse the config file.  The format is a colon-separated parameter name
+# followed by the value of that parameter to the end of the line.  This parses
+# that format and puts the parameters into the referenced hash.  Parameter
+# names are mapped to lower case, parameter values are unchanged.  Returns 0 on
+# failure (e.g. file open) and 1 on success.
+sub parse_config {
+    my($file, $href) = @_;
+    my($fh) = new IO::File($file);
+	
+    unless ($fh) {
+	warn "Can't open $file: $!\n";
+	return 0;
+    }
+
+    while (<$fh>) {
+	next if /^\s*#/ || /^\s*$/;	# Skip comments & blanks
+	chomp;
+	/^([^:]+):\s*(.*)/ && do {
+	    my($key) = $1; 
+
+	    $key =~ tr/A-Z/a-z/;
+	    $href->{$key} = $2;
+	    next;
+	};
+	warn "Unparasble line in $file: $_\n";
+    }
+    $fh->close();   # It will close when it goes out of scope, but...
+    return 1;
+}
+
 
 # use scp to transfer a file, reporting true if successful and false otherwise.
@@ -54,4 +89,20 @@
     return 1;
 }
+
+# Ship per-testbed configuration generated by this script to the remote /proj
+# directories on the remote testbeds
+sub ship_configs {
+    my($host, $user, $src_dir, $dest_dir) = @_;	    # Where, who, where remotely
+    my($d, $f);
+
+    $d = IO::Dir->new($src_dir) || return 0;
+
+    while ( $f = $d->read()) {
+	next if $f =~ /^\./;
+	&scp_file("$src_dir/$f", "$user\@$host", $dest_dir) || return 0;
+    }
+    return 1;
+}
+
 
 
@@ -75,14 +126,16 @@
     my($proj_dir) = "/proj/$pid/exp/$eid/tmp";  # Where to stash federation stuff
     my($to_hostname) = "$proj_dir/hosts";   # remote hostnames file
+    my($status) = new IO::Pipe;		    # The pipe to get status
 
     # Determine the status of the remote experiment
-    open(STATUS, "ssh $user\@$host /usr/testbed/bin/expinfo $pid $eid|") || 
+    $status->reader("ssh $user\@$host /usr/testbed/bin/expinfo $pid $eid") || 
 	die "Can't ssh to $user\@$host:$!\n";
+
     # XXX: this is simple now.  Parsing may become more complex
-    while (<STATUS>) {
+    while (<$status>) {
 	/State: (\w+)/ && ($state = $1);
 	/No\s+such\s+experiment/ && ($state = "none");
     }
-    close(STATUS);
+    $status->close();
     print "$tb: $state\n";
 
@@ -95,4 +148,5 @@
 	    return 0;
 	&ship_scripts($host, $user, $proj_dir) || return 0;
+	&ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
 	&ssh_cmd($user, $host, "/usr/testbed/bin/modexp -r -s -w $pid " . 
 	    "$eid $tclfile", "modexp") || return 0;
@@ -107,4 +161,5 @@
 	&scp_file("./hostnames", "$user\@$host", $to_hostname) || return 0;
 	&ship_scripts($host, $user, $proj_dir) || return 0;
+	&ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
 	# Now start up
 	&ssh_cmd($user, $host, "/usr/testbed/bin/swapexp -w $pid $eid in", 
@@ -121,4 +176,5 @@
 	&scp_file("./hostnames", "$user\@$host", $to_hostname) || return 0;
 	&ship_scripts($host, $user, $proj_dir) || return 0;
+	&ship_configs($host, $user, "$tmpdir/$tb", $proj_dir) || return 0;
 	# Now start up
 	&ssh_cmd($user, $host, "/usr/testbed/bin/swapexp -w $pid $eid in", 
@@ -147,8 +203,4 @@
 }
 
-# tcl program to split experiments
-# $tcl_splitter = "/usr/testbed/lib/ns2ir/parse.tcl";
-$tcl_splitter = "/users/faber/testbed/tbsetup/ns2ir/parse.tcl";
-$tclsh = "/usr/local/bin/otclsh";   # tclsh to call directly
 
 $pid = $gid = "dummy";		    # Default project and group to pass to
@@ -157,23 +209,39 @@
 				    # use them at all, but we supply them to
 				    # keep our changes to the parser minimal.
-
 # Argument processing.
-getopts('d:c:m:e:f:nt:', \%opts);
-
-$eid = $opts{'e'};		    # Experiment ID
-$tcl = $opts{'f'} || shift;	    # The experiment description
-$master = $opts{'m'};		    # Master testbed
-$startem = $opts{'n'} ? 0 : 1;	    # If true, start the sub-experiments
-$tmpdir = $opts{'t'} || "/tmp";		    # where to collect tmp files
-$config = $opts{'c'} || "./testbeds";
-$local_script_dir = $opts{'d'};	    # Local scripts
-
+getopts('c:f:nd', \%opts);
+$splitter_config = $opts{'c'} || "./splitter.conf";
+$debug = $opts{'d'};
+&parse_config("$splitter_config", \%opts) || 
+    die "Cannot read config file $splitter_conf: $!\n";
+
+
+$startem = $opts{'n'} ? 0 : 1;		# If true, start the sub-experiments
+$eid = $opts{'experiment'};		# Experiment ID
+$tcl = $opts{'f'} || shift;		# The experiment description
+$master = $opts{'master'};		# Master testbed
+$tmpdir = $opts{'tmpdir'} || $opts{'tempdir'}|| "/tmp"; # tmp files
+$tb_config = $opts{'testbeds'} || "./testbeds";	# testbed configurations
+$local_script_dir = $opts{'scriptdir'};	# Local scripts
+# For now specify these.  We may want to generate them later.
+$gw_pubkey = $opts{'gatewaypubkey'};
+($gw_pubkey_base = $gw_pubkey) =~ s#.*/##;
+$gw_secretkey = $opts{'gatewaysecretkey'};
+($gw_secretkey_base = $gw_secretkey) =~ s#.*/##;
+
+# tcl program to split experiments (changed during devel)
+$tcl_splitter = $opts{'tclparse'} || "/usr/testbed/lib/ns2ir/parse.tcl";
+# tclsh to call directly (changed during devel)
+$tclsh = $opts{'tclsh'} || "/usr/local/bin/otclsh";
+
+# Prefix to avoid collisions
 $tmpdir .= "/split$$";
 
+# Create a workspace
 unless (-d "$tmpdir") {
     mkdir("$tmpdir") || die "Can't create $tmpdir: $!";
 }
 
-
+# Validate scripts directory
 for $s (@scripts) {
     die "$local_script_dir/$s not in local script directory. Try -d\n"
@@ -184,6 +252,7 @@
 
 # Read a hash of per-testbed parameters from the local configurations.
-open(CONF, $config) || die "can't read testbed configutions from $config: $!\n";
-while (<CONF>) {
+$conf = new IO::File($tb_config) || 
+    die "can't read testbed configutions from $tb_config: $!\n";
+while (<$conf>) {
     next if /^#/;
     chomp;
@@ -203,12 +272,14 @@
     $domain{$tb} = ".$domain{$tb}" unless $domain{$tb} =~ /^\./;
 }
-close(CONF);
+$conf->close();
 
 # Open a pipe to the splitter program and start it parsing the experiments
-open(PIPE, "$tclsh $tcl_splitter -s -m $master -p $pid $gid $eid $tcl|") || 
+$pipe = new IO::Pipe;
+$pipe->reader("$tclsh $tcl_splitter -s -m $master -p $pid $gid $eid $tcl") || 
     die "Cannot execute $tclsh $tcl_splitter -s -p $pid $gid $eid $tcl:$!\n";
 
-# Parse the splitter output.
-while (<PIPE>) {
+# Parse the splitter output.  This loop creates the sub experiments, gateway
+# configurations and hostnames file
+while (<$pipe>) {
     # Start of a sub-experiment
     /^#\s+Begin\s+Testbed\s+\((\w+)\)/ && do {
@@ -290,4 +361,5 @@
 	my($sdomain) = $domain{$gateways};	# domain for the source
 	my($ddomain) = $domain{$dtb};		# domain for the destination
+	my($sproject) = $project{$gateways};	# Project of the destination
 
 	$sdomain = ".$eid.$project{$gateways}$sdomain";
@@ -305,11 +377,29 @@
 	# Write out the file
 	open(GWCONFIG, ">$tmpdir/$gateways/$myname$sdomain.gw.conf") || 
-	    die "can't open $tmpdir/%gateways/$myname$sdomain.gw.conf: $!\n";
+	    die "can't open $tmpdir/$gateways/$myname$sdomain.gw.conf: $!\n";
 	print GWCONFIG "Active: $active\n";
 	print GWCONFIG "Type: $type\n";
 	print GWCONFIG "Peer: $desthost$ddomain\n";
-	print GWCONFIG "Pubkeys: /placeholder\n";
-	print GWCONFIG "Privkeys: /placeholder\n";
+	print GWCONFIG "Pubkeys: " . 
+	    "/proj/$sproject/exp/$eid/tmp/$gw_pubkey_base\n";
+	print GWCONFIG "Privkeys: " .
+	    "/proj/$sproject/exp/$eid/tmp/$gw_secretkey_base\n";
 	close(GWCONFIG);
+
+	# This testbed has a gateway (most will) so make a copy of the keys it
+	# needs in this testbed's subdirectory.  start_segment will transfer
+	# them.
+	unless (-r "$tmpdir/$gateways/$gw_pubkey_base" ) {
+	    copy($gw_pubkey, "$tmpdir/$gateways/$gw_pubkey_base") ||
+		die "Can't copy pubkeys ($gw_pubkey to " . 
+		    "$tmpdir/$gateways/$gw_pubkey_base): $!\n";
+	}
+	if ($active eq "true" ) {
+	    unless (-r "$tmpdir/$gateways/$gw_secretkey_base" ) {
+		copy($gw_secretkey, "$tmpdir/$gateways/$gw_secretkey_base") ||
+		    die "Can't copy secret keys ($gw_secretkey to " . 
+			"$tmpdir/$gateways/$gw_secretkey_base): $!\n";
+	    }
+	}
 
 	#done processing gateway entry, ready for next line
@@ -336,5 +426,5 @@
     print FILE;
 }
-close(PIPE);
+$pipe->close();
 die "No nodes in master testbed ($master)\n" unless $allocated{$master};
 
@@ -362,4 +452,5 @@
 }
 print "Experiment started\n";
+system("rm -rf $tmpdir") unless $debug;
 exit(0);    # set the exit value
 
@@ -372,6 +463,5 @@
 =head1 SYNOPSIS
 
-B<splitter.pl> B<-e> I<experiment> B<-m> I<master_testbed> [B<-n>] 
-    [B<-d> F<script_dir>] [B<-c> F<config_file>] [B<-f> F<experiment_tcl>]
+B<splitter.pl> [B<-nd>] [B<-c> F<config_file>] [B<-f> F<experiment_tcl>]
     [F<experiment_tcl>]
 
@@ -384,26 +474,93 @@
 
 The testbed labels are meaningful based on their presence in the testbeds file.
-that file can be specified with the B<-c> option, and defaults to
-F<./testbeds>.  The syntax is described below.
+that file can be specified in the configuration file using the B<Testbeds>
+directive, and defaults to F<./testbeds>.  The syntax is described below.
+
+Most of the intermediate files are staged in a sub-directory of a temporary
+files directory and deleted at the end of the script.  Specifying the B<-d>
+flag on the command line avoids the deletion for debbugging.  By default the
+temporary files directory is directory is F</tmp> and can be reset in the
+configuration file using the B<Tmpdir> directive.  Intermediate files are
+stored under a subdirectory formed by adding the process ID of the splitter
+process.  For example, if the temporary files directory is F</tmp> and the
+B<splitter.pl> process ID is 2323, the temporary files will be stored in
+F</tmp/split2323/>.
 
 The expreriment is split out into one experiment description per testbed in the
-current directory named as F<experiment.testbed.tcl> where the experiment is
-the argument to B<-e> and the testbed is the tb-set-node-testbed parameter for
-the nodes in the file.
+temporary directory named as F<experiment.testbed.tcl> where the experiment is
+the experiment ID given in the configuration file, and the testbed is the
+tb-set-node-testbed parameter for the nodes in the file.
 
 If the B<-n> option is absent the sub-experiments are then instantiated on
 their testbeds.  (Here B<-n> is analogous to its use in L<make(1)>).
-Per-testbed parameters are set in the configuration file.  Sub-experiments on
+Per-testbed parameters are set in the testbeds file.  Sub-experiments on
 slave testbeds are instantiated in a random order, but the master testbed is
 currently instantiated last.
 
-Scripts to start federation are copied into the local experiment's tmp file -
-e.g., F</proj/DETER/exp/simple-split/tmp>.  These are taken from the directory
-given by the B<-d> option.
+Scripts to start federation (the federation kit) are copied into the local
+experiment's tmp file - e.g., F</proj/DETER/exp/simple-split/tmp>.  These are
+taken from the directory given by the B<ScriptDir> directive in the
+configuration file.
 
 If any sub-experiment fails to instantiate, the other sub-exeriments are
 swapped out.
 
-=head2 Configuration file
+=head2 Configuration File
+
+The configuration file is a simple attribute-value pair set of colon-separated
+parameters and values.  A configuration file must be present, either specified
+in the B<-c> flag or the default F<./splitter.conf>.  All the parameter names
+are case insensitive, but should not include any whitespace.  Parameter values
+may include whitespace, but no newlines.
+
+Possible parameters are:
+
+=over 5
+
+=item Experiment
+
+The name of the experiment on the various testbeds
+
+=item Master
+
+The master testbed label from the testbeds file, described below.
+
+=item Testbeds
+
+The testbeds file described below, giving per-testbed parameters.  If this
+directive is absent the testbeds file defaults to F<./testbeds>
+
+=item ScriptDir
+
+Location of the default federation scripts, i.e. the federation kit.
+
+=item GatewayPubkey
+
+=item GatewaySecretKey
+
+The names of the files containing secret and public keys to use in setting up
+tunnels between testbeds.  These will eventually be automatically generated.
+
+=item TmpDir
+
+=item TempDir
+
+The directory where temporary files are created.  These are synonyms, but
+should both be specified, B<TmpDir> has priority.  If neither is specified,
+F</tmp> is used.
+
+=item Tclparse
+
+The pathname to the experiment parsing program.  Only developers should set
+this.
+
+=item Tclsh
+
+The pathname to the local oTcl shell.  Only developers should set
+this.
+
+=back
+
+=head2 Testbeds file
 
 The configuration file (F<./testbeds> unless overridden by B<-c>) is a
@@ -443,20 +600,24 @@
 
 The start command to run on experimental nodes when this testbed is used as a
-slave.
+slave.  In all the start commands the string FEDDIR will be replaced by the
+local experiment's federation scripts directory and the string GWCONF replaced
+by the gatway configuration file.
 
 =item gateway start (slave)
 
+The start command to run on gateway nodes when this testbed is used as a slave.
+The same string substitutions are made in this command as in experiment start.
+
+=item experiment start (master)
+
+The start command to run on experimental nodes when this testbed is used as a
+master.  The same string substitutions are made in this command as in
+experiment start.
+
+=item gateway start (master)
+
 The start command to run on gateway nodes when this testbed is used as a
-slave.
-
-=item experiment start (master)
-
-The start command to run on experimental nodes when this testbed is used as a
-master.
-
-=item gateway start (master)
-
-The start command to run on gateway nodes when this testbed is used as a
-master.
+master.  The same string substitutions are made in this command as in
+experiment start.
 
 =item gateway image
@@ -466,5 +627,5 @@
 =back
 
-The parsing of the configuration is extremely simple.  Colons separate each
+The parsing of the testbeds is extremely simple.  Colons separate each
 field and there is n provision for escaping them at this time.
 
