#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;

#
# Turn on/off admin mode for nodes and optionally reboot/wait.
#
sub usage()
{
    print STDOUT "Usage: node_admin [-h] [-n | -w] <on | off> [node ....]\n";
    print STDOUT "       node_admin [-h] [-n | -w] -e pid,eid <on | off>\n";
    print STDOUT "-h     This message\n";
    print STDOUT "-n     Do not reboot node\n";
    print STDOUT "-w     Wait for node to come back up if rebooted\n";
    print STDOUT "-e     Operate on all nodes in an experiment\n";
    print STDOUT "-c cmd Run command in MFS and wait for completion\n".
		 "       (-n and -w apply after the command is run).\n";
    exit(-1);
}
my $optlist  = "hnwe:c:";
my $waitmode = 0;
my $reboot   = 1;
my $runcmd   = "";

#
# Configure variables
#
my $TB		= "@prefix@";

#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
use libadminmfs;

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

#
# Untaint the path
# 
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

#
# 
#
my $nodereboot	= "$TB/bin/node_reboot";
my $osselect    = "$TB/bin/os_select";
my @nodes       = ();

#
# 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{h})) {
    usage();
}
if (defined($options{"n"})) {
    $reboot = 0;
}
if (defined($options{"w"})) {
    $waitmode = 1;
}

my $onoff;

if (defined($options{"c"})) {
    $runcmd = $options{"c"};
    $onoff = "on";
} else {
    if (!@ARGV) {
	usage();
    }
    $onoff  = shift(@ARGV);
}

if ($onoff ne "on" && $onoff ne "off") {
    usage();
}

if (defined($options{"e"})) {
    #
    # Reboot all nodes in an experiment
    #
    if (@ARGV) {
	usage();
    }

    my $eidmode = $options{"e"};
    my $pid;
    my $eid;
    
    if ($eidmode =~ /([-\w]*),([-\w]*)/) {
	$pid = $1;
	$eid = $2;
    }
    else {
	die("*** $0:\n".
	    "    Invalid argument to -e option: $eidmode\n");
    }

    #
    # Verify permission to muck with this experiment. This is to head off
    # permission problems early; the nodes are indvidually checked later
    # in the library.
    #
    if ($UID && !TBAdmin($UID) &&
	! TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_MODIFY)) {
	die("*** $0:\n".
	    "    You not have permission to reboot nodes in $pid/$eid!\n");
    }

    my $query_result =
	DBQueryFatal("select node_id from reserved where ".
		     "pid='$pid' and eid='$eid'");

    if ($query_result->numrows == 0) {
	die("*** $0:\n".
            "    There are no nodes reserved in pid/eid $pid/$eid\n");
    }
    
    while (my ($nodeid) = $query_result->fetchrow_array()) {
	push(@nodes, $nodeid);
    }
}
else {
    if (! @ARGV) {
	usage();
    }

    # Untaint the nodes.
    foreach my $node ( @ARGV ) {
	if ($node =~ /^([-\w]+)$/) {
	    $node = $1;
	}
	else {
	    die("*** Tainted node name: $node\n");
	}
	if (!TBValidNodeName($node)) {
	    die("*** $0:\n".
		"    Node does not exist: $node\n");
	}
	push(@nodes, $node);
    }

    #
    # Root and admin types can do whatever they want. Normal users
    # can only run this on nodes in their own experiments.
    #
    if ($UID && !TBAdmin($UID)) {
	if (! TBNodeAccessCheck($UID, TB_NODEACCESS_LOADIMAGE, @nodes)) {
	    die("*** $0:\n".
		"    You are not allowed to put some nodes into admin mode\n");
	}
    }
}

my @bad;
my %args;

if ($runcmd ne "") {
    #
    # Reboot into admin more and run the command
    #
    %args = ();
    $args{'name'} = $0;
    $args{'command'} = $runcmd;
    if (TBAdminMfsRunCmd(\%args, undef, @nodes)) {
	die("*** $0:\n".
	    "    Failed to run '$runcmd' on some of @nodes!\n");
    }

    #
    # Turn admin mode back off and optionally reboot back to the old OS
    #
    %args = ();
    $args{'name'} = $0;
    $args{'on'} = 0;
    $args{'clearall'} = 0;
    if (TBAdminMfsSelect(\%args, \@bad, @nodes)) {
	die("*** $0:\n".
	    "    Could not turn admin mode off for @bad!\n");
    }

    if ($reboot) {
	%args = ();
	$args{'name'} = $0;
	$args{'on'} = 0;
	$args{'reboot'} = $reboot;
	$args{'wait'} = $waitmode;
	if (TBAdminMfsBoot(\%args, \@bad, @nodes)) {
	    die("*** $0:\n".
		"    Did not properly reboot @bad after command!\n");
	}
    }
    exit(0);
}

$args{'name'} = $0;
$args{'on'} = ($onoff eq "on");
$args{'clearall'} = 0;
if (TBAdminMfsSelect(\%args, \@bad, @nodes)) {
    die("*** $0:\n".
	"    Could not turn admin mode $onoff for @bad!\n");
}
if ($reboot) {
    %args = ();
    $args{'name'} = $0;
    $args{'on'} = ($onoff eq "on");
    $args{'reboot'} = $reboot;
    $args{'wait'} = $waitmode;
    if (TBAdminMfsBoot(\%args, \@bad, @nodes)) {
	die("*** $0:\n".
	    "    Did not properly reboot @bad!\n");
    }
}
exit(0);
