register.pl 5.56 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#

#
# This file goes in /usr/site/sbin on the CDROM.
#
use English;
use Getopt::Std;
use Fcntl;
use IO::Handle;
use Socket;

#
18 19 20
# Get the instructions script from NetBed central and run it. This is
# basically wrapper that does no real work, but just downloads and
# runs a script, then reboots the nodes if that script ran okay.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
21 22 23
#
sub usage()
{
24
    print("Usage: register.pl <bootdisk> <ipaddr>\n");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
25 26 27 28
    exit(-1);
}
my  $optlist = "";

29 30 31 32 33 34 35 36 37 38
#
# Catch ^C and exit with error. This will cause the CDROM to boot to
# the login prompt, but thats okay.
# 
sub handler () {
    $SIG{INT} = 'IGNORE';
    exit(1);
}
$SIG{INT}  = \&handler;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52
#
# Turn off line buffering on output
#
STDOUT->autoflush(1);
STDERR->autoflush(1);

#
# Untaint the environment.
# 
$ENV{'PATH'} = "/tmp:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:".
    "/usr/local/bin:/usr/site/bin:/usr/site/sbin:/usr/local/etc/testbed";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

#
53
# Definitions.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
54
#
55
my $WWW         = "https://www.emulab.net";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
56
#my $WWW        = "http://golden-gw.ballmoss.com:8080/~stoller/testbed";
57 58
#my $WWW        = "https://www.emulab.net/~stoller/www";
my $cdkeyfile	= "/etc/emulab.cdkey";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
59 60
my $wget	= "wget";
my $logfile     = "/tmp/register.log";
61 62
my $scriptfile  = "/tmp/netbed-setup.pl";
my $tmpfile	= "/tmp/foo.$$";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
63

64 65 66 67
#
# Locals. 
# 
my $cdkey;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
68 69 70 71 72 73 74 75 76

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
77
if (@ARGV != 2) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
78 79 80
    usage();
}
my $rawbootdisk = $ARGV[0];
81
my $IP = $ARGV[1];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
82 83 84 85

#
# See if we want to continue. Useful for debugging.
# 
86
print "\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
87 88 89 90 91
if (! (Prompt("Dance with Netbed?", "Yes", 10) =~ /yes/i)) {
    exit(0);
}

#
92 93 94 95
# The cdkey describes the cd. We send it along in the request.
# 
if (! -s $cdkeyfile) {
    fatal("No CD key on the CD!");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
96
}
97 98 99
$cdkey = `cat $cdkeyfile`;
chomp($cdkey);
	
Leigh B. Stoller's avatar
Leigh B. Stoller committed
100
#
101 102 103 104
# Get the script from netbed central. We have to be able to get it,
# otherwise we are hosed, so just keep trying. If the user hits ^C
# on the console, this script will exit and the node will try to
# go to single user mode (asking for passwword of course). 
Leigh B. Stoller's avatar
Leigh B. Stoller committed
105
#
106 107 108 109 110 111 112
while (1) {
    my $emulab_status;
    my $url;
    my $md5;

    while (1) {
	print "Checking in at Netbed Central for instructions ...\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
113
    
114 115
	mysystem("$wget -q -O $tmpfile ".
		 "'${WWW}/cdromcheckin.php3?cdkey=$cdkey&needscript=1'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
116
    
117 118
	if (!$?) {
	    last;
119
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
120

121 122
	print "Error getting instructions. Will try again in one minute ...\n";
	sleep(60);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
123
    }
124 125
    if (! -s $tmpfile) {
	fatal("Could not get valid instructions from $WWW!");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
126 127 128
    }

    #
129 130
    # Parse the response. We get back the URL and the MD5, as well as
    # a status code.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
131
    #
132 133
    if (! open(INSTR, $tmpfile)) {
	fatal("$tmpfile could not be opened for reading: $!");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
134 135 136 137 138
    }
    while (<INSTR>) {
	chomp();
        SWITCH1: {
	    /^emulab_status=(.*)$/ && do {
139
		$emulab_status = $1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
140 141
		last SWITCH1;
	    };
142 143
	    /^URL=(.*)$/ && do {
		$url = $1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
144 145
		last SWITCH1;
	    };
146 147
	    /^MD5=(.*)$/ && do {
		$md5 = $1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
148 149 150 151 152 153 154
		last SWITCH1;
	    };
	    print STDERR "Invalid instruction: $_\n";
	}
    }
    close(INSTR);

155 156 157
    if (defined($emulab_status)) {
	if ($emulab_status) {
	    fatal("Bad response code from Netbed: $emulab_status!");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
158 159
	}
    }
160 161
    else {
	fatal("Improper instructions; did not include netbed status!");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
162
    }
163 164 165 166
    fatal("Improper instructions; did not include URL!")
	if (!defined($url));
    fatal("Improper instructions; did not include MD5!")
	if (!defined($md5));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
167 168

    while (1) {
169 170 171
	print "Downloading script from Netbed Central ...\n";

	mysystem("$wget -nv -O $scriptfile $url");	
Leigh B. Stoller's avatar
Leigh B. Stoller committed
172 173 174 175
	if (!$?) {
	    last;
	}

176 177
	print "Error getting scriptfile. Will try again in one minute ...\n";
	sleep(60);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
178 179 180
    }

    #
181 182 183
    # Check the MD5.
    # 
    print "Checking MD5 hash of the scriptfile.\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
184

185 186
    my $hval = `md5 -q $scriptfile`;
    chomp($hval);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
187

188 189
    if ($md5 eq $hval) {
	last;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
190
    }
191 192
    print("Bad MD5! Will try again in one minute!\n");
    sleep(60);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
193 194 195
}

#
196
# Run the script. It must succeed or we are hosed!
Leigh B. Stoller's avatar
Leigh B. Stoller committed
197
#
198 199 200
mysystem("chmod +x $scriptfile");
system("$scriptfile $rawbootdisk $IP") == 0
    or fatal("$scriptfile failed!");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
201 202

#
203
# One last chance to hold things up.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
204
# 
205 206 207
if (Prompt("Reboot from ${rawbootdisk}?", "Yes", 10) =~ /yes/i) {
    mysystem("shutdown -r now");
    fatal("Failed to reboot!")
Leigh B. Stoller's avatar
Leigh B. Stoller committed
208
	if ($?);
209
    sleep(100000);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
210
}
211 212
exit(0);

Leigh B. Stoller's avatar
Leigh B. Stoller committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
#
# Print error and exit.
#
sub fatal($)
{
    my ($msg) = @_;

    die("*** $0:\n".
	"    $msg\n");
}

#
# Run a command string, redirecting output to a logfile.
#
sub mysystem($)
{
    my ($command) = @_;

    if (defined($logfile)) {
	system("echo \"$command\" >> $logfile");
	system("($command) >> $logfile 2>&1");
    }
    else {
	print "Command: '$command\'\n";
	system($command);
    }
    return $?;
}

#
# Spit out a prompt and a default answer. If optional timeout supplied,
# then wait that long before returning the default. Otherwise, wait forever.
#
sub Prompt($$;$)
{
    my ($prompt, $default, $timeout) = @_;

    if (!defined($timeout)) {
	$timeout = 10000000;
    }

    print "$prompt";
    if (defined($default)) {
	print " [$default]";
    }
    print ": ";

    eval {
	local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
	
	alarm $timeout;
	$_ = <STDIN>;
	alarm 0;
    };
    if ($@) {
	if ($@ ne "alarm\n") {
	    die("Unexpected interrupt in prompt\n");
	}
	#
	# Timed out.
	#
	print "\n";
	return $default;
    }
    return undef
	if (!defined($_));
	
    chomp();
    if ($_ eq "") {
	return $default;
    }

    return $_;
}