#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#
use Getopt::Std;
use English;
use Errno;
use POSIX qw(strftime);

#
# Startup subnodes, if there are any!
#
# NB: This script should not be run in foreground mode on a remote node
# when booting; if boss is down the boot will hang. On local nodes, its
# okay to hang.
#
sub usage()
{
    print "Usage: bootsubnodes [-d] [-f]\n";
    exit(1);
}
my $optlist = "df";

#
# Turn off line buffering on output
#
$| = 1;

# Drag in path stuff so we can find emulab stuff.
BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; }

#
# Load the OS independent support library. It will load the OS dependent
# library and initialize itself. 
# 
use libsetup;
use libtmcc;
use libtestbed qw(TBBackGround);

# Locals
my $logname = "$LOGDIR/bootsubnodes.debug";
my $debug   = 0;
my $daemon  = 1;

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
    $debug  = 1;
}
if (defined($options{"f"})) {
    $daemon = 0;
}
if (@ARGV) {
    usage();
}

#
# Must be root.
# 
if ($UID != 0) {
    die("*** $0:\n".
	"    Must be root to run this script!\n");
}

#
# Put this into the background and log its output. We *must* do this cause
# we do not want to halt the boot if the testbed is down!
# 
if ($daemon && TBBackGround($logname)) {
    #
    # Parent exits normally
    #
    exit(0);
}

my %subnodelist;

#
# Get the current set of subnodes that are supposed to be running on
# this node.
#
my @tmccresults;

if (tmcc(TMCCCMD_SUBNODELIST, undef, \@tmccresults) < 0) {
    die("*** WARNING: Could not get subnode list from server!\n");
}
if (! @tmccresults) {
    print "No subnodes. Exiting gracefully ...\n";
    exit(0);
}

foreach my $str (@tmccresults) {
    if ($str =~ /^NODEID=([-\w]+) TYPE=([-\w]+)$/) {
	$subnodelist{$1} = $2;
    }
    else {
	warn("*** WARNING: Skipping bad subnodeid: '$str'\n");
    }
}
print "subnodelist is @{[keys(%subnodelist)]}\n";

# Pause to give hardware settle time.
sleep(2);

foreach my $subnode (keys(%subnodelist)) {
    my $type = $subnodelist{$subnode};
    
    print "Setting up subnode $subnode ($type) ...\n";
    # These should not return until the subnode is fully running.
    SWITCH: for ($type) {
	/^IXP$/i && do {
	    system("ixpboot $subnode");
	    if ($?) {
		print STDERR "$subnode boot failed with $?\n";
	    }
	    last SWITCH;
	};
	/^mote$/i && do {
	    libsetup_setvnodeid($subnode);
	    configtmcc("subnode", $subnode);
	    tmcc(TMCCCMD_STATE, "BOOTING");
	    tmcc(TMCCCMD_SUBCONFIG, undef, \@tmccresults) == 0
		or die("*** $0:\n".
		       "    Could not get subnode config from server!\n");
	    my $BAUD = 0;
	    my $CAPSERVER = "";
	    my $CAPPORT = 0;
	    foreach my $str (@tmccresults) {
		chomp($str);
	      SWITCH1: for ($str) {
		  /^TYPE=([-\w.]+)$/ && do {
		      if ($1 eq "mica2") {
			  $BAUD = 57600;
		      }
		      last SWITCH1;
		  };
		  /^CAPSERVER=([-\w.]+)$/ && do {
		      $CAPSERVER = $1;
		      last SWITCH1;
		  };
		  /^CAPPORT=(\d+)$/ && do {
		      $CAPPORT = $1;
		      last SWITCH1;
		  };
		  printf STDERR "Unknown directive: $str\n";
	      }
	    }
	    if (-x "$BINDIR/capture") {
		system("$BINDIR/capture -a -s $BAUD -u '/usr/local/bin/uisp ".
		       "-dprog=sggpio -dpart=ATmega128 --wr_fuse_e=ff ".
		       "--erase --upload if=%s' $CAPSERVER:$CAPPORT tts/2");
	    }
	    tmcc(TMCCCMD_STATE, "ISUP");
	    last SWITCH;
	};
	/^netfpga$/i && do {
	    if (-x "$BINDIR/netfpgactl") {
		system("$BINDIR/netfpgactl $subnode boot");
		if ($?) {
		    print STDERR "$subnode boot failed with $?\n";
		}
	    }
            last SWITCH;
	};
	print "Invalid subnode type: $type\n";
    }
    sleep(1);
}
exit(0);
