diff --git a/cdrom/waboot/emulab.cdkey b/cdrom/waboot/emulab.cdkey
new file mode 100644
index 0000000000000000000000000000000000000000..88c6d2bc0c2cf18d3525aa08fd8e2870e27de1db
--- /dev/null
+++ b/cdrom/waboot/emulab.cdkey
@@ -0,0 +1 @@
+afec7ac472aab9ee9d03a9a510ef5746
diff --git a/cdrom/waboot/emulabboot.sh b/cdrom/waboot/emulabboot.sh
new file mode 100755
index 0000000000000000000000000000000000000000..56d06f764ed3223f4862d54828d35484f6f0bff3
--- /dev/null
+++ b/cdrom/waboot/emulabboot.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2000-2002 University of Utah and the Flux Group.
+# All rights reserved.
+#
+
+#
+# This script goes in /usr/local/etc/rc.d on the disk image.
+#
+# Set the magic bit that said we booted from the disk okay. Otherwise
+# if the CDROM boots and this has not been done, the CDROM assumes the
+# disk is scrogged.
+# 
+. /etc/rc.conf.local
+
+#
+# rawbootdisk is set in rc.conf.local by the CDROM boot image.
+#
+case "$1" in
+start)
+	if [ -f /usr/site/sbin/tbbootconfig ]; then
+		/usr/site/sbin/tbbootconfig -c 1 $rawbootdisk
+
+		case $? in
+		0)
+		    exit 0
+		    ;;
+		*)
+		    echo 'Error in testbed boot header program'
+		    echo 'Reboot failed. HELP!'
+		    exit 1
+		    ;;
+		esac
+	fi
+	;;
+stop)
+	;;
+*)
+	echo "Usage: `basename $0` {start|stop}" >&2
+	exit 1
+	;;
+esac
diff --git a/cdrom/waboot/rc.emulab b/cdrom/waboot/rc.emulab
new file mode 100755
index 0000000000000000000000000000000000000000..7257a115baafa300e6c1cda26accfa38955c6f8f
--- /dev/null
+++ b/cdrom/waboot/rc.emulab
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2000-2002 University of Utah and the Flux Group.
+# All rights reserved.
+#
+
+#
+# The script goes in /etc on the CDROM. Must edit /etc/rc to invoke it
+# before doing the network setup. 
+# 
+
+RCCONFLOCAL=/etc/rc.conf.local
+
+#
+# This script runs the testbed IP configuration stuff. It will generate the
+# above named file, which we then suck in to define the IP config so that
+# network configuration will proceed properly. 
+#
+# We also define a routine to call at the end of /etc/rc to change the
+# boot header.
+#
+EmulabCheckIPConfig()
+{
+	if [ -r /usr/site/sbin/waipconfig.pl ]; then
+		/usr/site/sbin/waipconfig.pl
+
+		case $? in
+	        0)
+		       ;;
+		*)
+			echo 'Error in testbed configuration program'
+			#reboot
+			echo 'Reboot failed. HELP!'
+			exit 1
+			;;
+		esac
+
+		if [ -r $RCCONFLOCAL ]; then
+			. $RCCONFLOCAL
+		else
+			echo '$RCCONFLOCAL does not exist!'
+			#reboot
+			echo 'Reboot failed. HELP!'
+			exit 1
+		fi
+	fi
+}
diff --git a/cdrom/waboot/register.pl b/cdrom/waboot/register.pl
new file mode 100755
index 0000000000000000000000000000000000000000..d47676825ebb05511268fe93f93e6d57c6eef23d
--- /dev/null
+++ b/cdrom/waboot/register.pl
@@ -0,0 +1,980 @@
+#!/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;
+
+#
+#
+#
+sub usage()
+{
+    print("Usage: register.pl <bootdisk>\n");
+    exit(-1);
+}
+my  $optlist = "";
+
+#
+# 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'};
+
+#
+# Locals
+#
+#my $WWW        = "https://www.emulab.net";
+#my $WWW        = "http://golden-gw.ballmoss.com:8080/~stoller/testbed";
+my $WWW         = "https://www.emulab.net/~stoller/www";
+my $etcdir	= "/etc";
+my $cdkeyfile	= "$etcdir/emulab.cdkey";
+my $setupconfig	= "$etcdir/emulab-setup.txt";
+my $softconfig	= "$etcdir/emulab-soft.txt";
+my $privkeyfile = "$etcdir/emulab.pkey";
+my $tbboot	= "tbbootconfig";
+my $wget	= "wget";
+my $tempdevice  = "s2c";
+my $rootdevice  = "s1a";
+my $slicexpart  = "e";
+my $slicexdev;
+my $logfile     = "/tmp/register.log";
+my $needstore   = 0;
+my $fromscratch = 0;
+my $needumount  = 0;
+my $cdkey;
+my $privkey;
+my %slicesizes;
+	
+my %config = ( privkey        => undef,
+	       fdisk          => undef,
+	       slice1_image   => undef,
+	       slice1_md5     => undef,
+	       slice3_image   => undef,
+	       slice3_md5     => undef,
+	       slice4_image   => undef,
+	       slice4_md5     => undef,
+	       slicex_mount   => undef,
+	       slicex_tarball => undef,
+	       slicex_md5     => undef,
+	     );
+
+# These errors must match what the web page returns.
+my $CDROMSTATUS_OKAY	 =	0;
+my $CDROMSTATUS_MISSINGARGS =	100;
+my $CDROMSTATUS_INVALIDARGS =	101;
+my $CDROMSTATUS_BADCDKEY =	102;
+my $CDROMSTATUS_BADPRIVKEY =	103;
+my $CDROMSTATUS_BADIPADDR =	104;
+my $CDROMSTATUS_BADREMOTEIP =	105;
+my $CDROMSTATUS_IPADDRINUSE =	106;
+my $CDROMSTATUS_OTHER =		199;
+
+my %weberrors =
+    ( $CDROMSTATUS_MISSINGARGS  => "Missing Page Arguments",
+      $CDROMSTATUS_INVALIDARGS  => "Invalid Page Arguments",
+      $CDROMSTATUS_BADCDKEY     => "Invalid CD Key",
+      $CDROMSTATUS_BADPRIVKEY   => "Invalid Private Key",
+      $CDROMSTATUS_BADIPADDR    => "IP address does not match key",
+      $CDROMSTATUS_BADREMOTEIP  => "IP address does not match remote IP",
+      $CDROMSTATUS_IPADDRINUSE  => "IP address already registered at Netbed",
+      $CDROMSTATUS_OTHER        => "Unknown Error",
+    );
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+%options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (@ARGV != 1) {
+    usage();
+}
+my $rawbootdisk = $ARGV[0];
+
+#
+# Untaint the arguments.
+#
+if ($rawbootdisk =~ /^([\w\/]+)$/) {
+    $rawbootdisk = $1;
+}
+else {
+    fatal("Tainted argument $rawbootdisk!");
+}
+
+#
+# See if we want to continue. Useful for debugging.
+# 
+if (! (Prompt("Dance with Netbed?", "Yes", 10) =~ /yes/i)) {
+    exit(0);
+}
+
+#
+# Get the plain device names (ad0, rad0).
+#
+my $rawdevice   = substr($rawbootdisk, rindex($rawbootdisk, '/') + 1);
+my $blockdevice = substr($rawdevice, 1);
+
+#
+# See if this is the first time. Look for the magic boot header.
+#
+$fromscratch = mysystem("$tbboot -v $rawbootdisk");
+
+#
+# We need our IP.
+# 
+my $hostname = `hostname`;
+if (!defined($hostname)) {
+    fatal("No hostname!");
+}
+# Untaint and strip newline.
+if ($hostname =~ /^([-\w\.]+)$/) {
+    $hostname = $1;
+}
+else {
+    fatal("Tainted argument $hostname!\n");
+}
+
+my (undef,undef,undef,undef,@ipaddrs) = gethostbyname($hostname);
+if (!defined(@ipaddrs)) {
+    fatal("Could not map $hostname to IP address!");
+}
+my $IP = inet_ntoa($ipaddrs[0]);
+
+GetInstructions();
+
+#
+# Do we even need a temp store? After the initial setup, we won't
+# need it unless updating the disk. See Checkin() for the variable.
+#
+if ($needstore) {
+    #
+    # If the disk is raw, we need to get the fdisk params and lay it down.
+    #
+    if ($fromscratch) {
+	my $fdisk  = $config{fdisk};
+	my $adjust = 0;
+	my $FDSK;
+
+	fatal("Testbed Central did not tell us what fdisk table to use!")
+	    if (!defined($fdisk));
+
+	print "Creating a temporary filesystem on slice 2 to hold images.\n";
+	
+	#
+	# First time through we need to lay down a DOS partition table.
+	#
+	mysystem("dd if=/dev/zero of=${rawbootdisk} bs=1k count=1024");
+	fatal("Failed to clear disk of old goop!")
+	    if ($?);
+	
+	mysystem("fdisk -I $rawbootdisk");
+	fatal("Failed to initialize DOS partition table!")
+	    if ($?);
+
+	#
+	# Read the summary info to see if we are going to have a problem
+	# with fdisk changing the sizes!
+	#
+	my $summary = `fdisk -s $rawbootdisk | head -1`;
+	if ($summary =~ /^.*cyl (\d*) hd (\d*) sec/) {
+	    if ($1 != 255) {
+		$adjust = $1 * $2;
+	    }
+	}
+
+	#
+	# Get the fdisk table. 
+	# 
+	if ($fdisk =~ /^http:.*$/ || $fdisk =~ /^ftp:.*$/) {
+	    mysystem("$wget -nv -O /tmp/fdisk.table $fdisk");
+	    fatal("Failed to transfer fdisk table: $fdisk!")
+		if ($?);
+	    
+	    $fdisk = "/tmp/fdisk.table";
+	}
+	else {
+	    $fdisk = "/${fdisk}";
+	}
+
+	#
+	# We are going to feed the fdisk table one line at a time
+	# so we can apply the adjustment. Yuck.
+	# 
+	if (! ($FDSK = mypopen("| fdisk -f - $rawbootdisk"))) {
+	    fatal("Could not start fdisk on $rawbootdisk!");
+	}
+	foreach my $line (`cat $fdisk`) {
+	    chomp($line);
+	    my ($key, $part, $sysid, $start, $size) = split(' ', $line);
+
+	    if (defined($start)) {
+		$size += $adjust;
+		if ($part != 1) {
+		    $start += $adjust;
+		}
+		print $FDSK "$key $part $sysid $start $size\n";
+	    }
+	    else {
+		print $FDSK "$key $part\n";
+	    }
+	}
+	if (!close($FDSK)) {
+	    fatal("Failed to lay down initial DOS partition table!");
+	}
+
+	#
+	# Clear old disklabels to avoid errors.
+	#
+
+	#
+	# Give the boot slice an auto label to prevent a bunch of warnings.
+	# Its going to get overwritten anyway. 
+	# 
+	mysystem("disklabel -w -r ${blockdevice}s1 auto");
+
+    }
+    
+    #
+    # Turn slice2 into a filesystem we can stash the image on.
+    # This slice is reserved forever! But its only 1gig.
+    #
+    mysystem("disklabel -w -r ${blockdevice}s2 auto");
+    fatal("Failed to put a disklabel on ${blockdevice}s2!")
+	if ($?);
+	
+    mysystem("newfs /dev/${rawdevice}${tempdevice}");
+    fatal("Failed to put a filesystem on /dev/${rawdevice}${tempdevice}!")
+	if ($?);
+
+    mysystem("mount /dev/${blockdevice}${tempdevice} /mnt");
+    fatal("Failed to mount /dev/${blockdevice}${tempdevice}! on /mnt!")
+	if ($?);
+    $needumount = 1;
+
+    #
+    # Find out the actual slice sizes so that we can modify the C
+    # partitions of images we lay down. Why? Cause if fdisk modified the
+    # size, then the kernel is going to complain all the time that the
+    # size of the slice does not match the size of the C partition.
+    # 
+    foreach my $line (`fdisk -s $rawbootdisk`) {
+	chomp($line);
+	if ($line =~ /\s*(\d):\s*\d*\s*(\d*)/) {
+	    $slicesizes{$1} = $2;
+	}
+    }
+}
+
+#
+# Now go through each possible slice except slice 2, since we use slice 2
+# as temp storage when first creating the disk. The fdisk table we got from
+# Emulab will have reserved that slice. 
+#
+for ($i = 1; $i <= 4; $i++) {
+    my $ED;
+    
+    if ($i == 2) {
+	next;
+    }
+    my $image = $config{"slice${i}_image"};
+    my $hash  = $config{"slice${i}_md5"};
+    
+    if (!defined($image) || $image eq "skipit") {
+	next;
+    }
+
+    #
+    # If told to load a local image from the CDROM, its really easy!
+    # 
+    if (! ($image =~ /^http:.*$/ || $image =~ /^ftp:.*$/)) {
+	print "Loading image for slice $i from $image. Be patient!\n";
+	
+	mysystem("imageunzip -s $i -d /$image $rawbootdisk");
+	fatal("Failed to lay down image /$image!")
+	    if ($?);
+	goto finished;
+    }
+
+    #
+    # Transfer the image to the temp store and then unzip to the slice.
+    #
+    if ($image =~ /^http:.*$/ || $image =~ /^ftp:.*$/) {
+      again:
+	print "Transferring image to temp storage for slice $i from:\n".
+	      "    $image \n".
+	      "    Please be patient! \n";
+	
+	mysystem("$wget -nv -O /mnt/image.ndz $image");
+	fatal("Failed to transfer image!")
+	    if ($?);
+
+	#
+	# If an md5 hash was provided, compare it.
+	#
+	if (defined($hash)) {
+	    print "Checking MD5 hash of the image. Please be patient!\n";
+	    my $hval = `md5 -q /mnt/image.ndz`;
+	    chomp($hval);
+
+	    if ($hash ne $hval) {
+		print "*** Invalid hash! Getting the image again.\n";
+		sleep(60);
+		goto again;
+	    }
+	}
+
+	print "Writing image from temp storage to slice $i.\n";
+	print "This could take several minutes. Please be *very* patient!\n";
+	
+	mysystem("imageunzip -s $i -o /mnt/image.ndz $rawbootdisk");
+	fatal("Failed to lay down image!")
+	    if ($?);
+
+	unlink("/mnt/image.ndz") or
+	    fatal("Failed to unlink /mnt/image.ndz!");
+    }
+  finished:
+    #
+    # Update the the label, changing the C partition to match the actual
+    # slice size.
+    #
+    $ENV{'EDITOR'} = "ed";
+    if (! ($ED = mypopen("| disklabel -e -r ${rawdevice}s${i}"))) {
+	fatal("Could not edit label on ${rawdevice}s${i}!");
+    }
+    print $ED "/^  c: /\n";
+    print $ED "t\n";
+    print $ED "s/c: [0-9]*/c: $slicesizes{$i}/\n";
+    print $ED "w\n";
+    print $ED "q\n";
+    if (!close($ED)) {
+	fatal("Error editing disklabel on ${rawdevice}s${i}!");
+    }
+}
+
+#
+# Unmount the temp store.
+#
+if ($needumount) {
+    mysystem("umount /mnt");
+    $needumount = 0;
+}
+#
+# Create extra FS.
+# 
+if (defined($config{"slicex_mount"})) {
+    my $slicex_mount = $config{"slicex_mount"};
+    
+    if ($fromscratch) {
+	#
+	# Easy. Just make it and remember the device for fstab.
+	#
+	if (MakeFS($slicex_mount, \$slicexdev) < 0) {
+	    fatal("Error creating extra filesystem");
+	}
+    }
+    else {
+	#
+	# Hard. We need to find out if that filesystem exists and where
+	# so that we can write fstab.
+	#
+	my $i;
+	for ($i = 1; $i <= 3; $i++) {
+	    my $image = $config{"slice${i}_image"};
+
+	    # Last slice we were told to write. 
+	    if (!defined($image)) {
+		last;
+	    }
+	}
+	#
+	# Check the next slice (after the last slice we were told to
+	# write originally) and see if there is a filesystem on it. 
+	#
+	mysystem("dd ${rawbootdisk}s${i}${slicexpart}");
+	if ($?) {
+	    print "Cannot find slice/part for $slicex_mount!\n";
+	}
+	else {
+	    $slicexdev = "s${i}${slicexpart}";
+	}
+    }
+}
+
+#
+# Localize the root filesystem (might not be needed of course).
+# 
+LocalizeRoot();
+WritePrivKey();
+
+#
+# Wow, made it. Now write the current configuration back to the magic
+# header and set things up to reboot from the disk instead of the CDROM.
+#
+WriteConfigBlock();
+
+#
+# Let Emulab know we made it through
+#
+FinishedInstructions();
+
+#
+# One last chance to hold things up.
+# 
+if (Prompt("Reboot from ${rawbootdisk}?", "Yes", 10) =~ /yes/i) {
+    mysystem("reboot");
+    fatal("Failed to reboot!")
+	if ($?);
+    sleep(100000);
+}
+exit 0;
+
+#
+# Check in with emulab for instructions.
+#
+sub GetInstructions()
+{
+    my $path  = $setupconfig;
+
+    #
+    # Talk to emulab and see what to do. We use wget to invoke a secure
+    # script on the webserver, passing it the cdkey.
+    # We give it our IP as an argument plus our private key.
+    #
+    if (! -s $path) {
+      again:
+	$path = "/tmp/emulab-config.$$";
+	undef($privkey);
+	
+	#
+	# The cdkey describes the cd.
+	# 
+	if (! -s $cdkeyfile) {
+	    fatal("No CD key on the CD!");
+	}
+	$cdkey = `cat $cdkeyfile`;
+	chomp($cdkey);
+	
+	#
+	# If not from scratch, then get the current privkey.
+	# If from scratch, then prompt user for the privkey, which had to
+	# come from emulab beforehand.
+	#
+	if (! $fromscratch) {
+	    $privkey = `$tbboot -e - $rawbootdisk`;
+	    chomp($privkey);
+	}
+	else {
+	    while (!defined($privkey)) {
+		$privkey = Prompt("Please enter you CD password?", undef);
+	    }
+	}
+
+	while (1) {
+	    print "Checking in at Netbed Central for instructions ...\n";
+	    
+	    mysystem("$wget -q -O $path ".
+		     "'${WWW}/cdromcheckin.php3".
+		     "?cdkey=$cdkey&IP=$IP&privkey=$privkey'");
+	    if (!$?) {
+		last;
+	    }
+	    print "Error checking in. Will try again in one minute ...\n";
+	    sleep(60);
+	}
+	
+	if (! -s $path) {
+	    fatal("Could not get a valid setup configuration from $WWW!");
+	}
+    }
+
+    #
+    # Read the output file. It will tell us what to do.
+    #
+    if (! open(INSTR, $path)) {
+	fatal("$path could not be opened for reading: $!");
+    }
+    while (<INSTR>) {
+	chomp();
+        SWITCH1: {
+	    /^emulab_status=(.*)$/ && do {
+		$config{emulab_status} = $1;
+		last SWITCH1;
+	    };
+	    /^privkey=(.*)$/ && do {
+		$config{privkey} = $1;
+		last SWITCH1;
+	    };
+	    /^fdisk=(.*)$/ && do {
+		$config{fdisk} = $1;
+		last SWITCH1;
+	    };
+	    /^slice(\d)_image=(.*)$/ && do {
+		$config{"slice${1}_image"} = $2;
+		$needstore++;
+		last SWITCH1;
+	    };
+	    /^slice(\d)_md5=(.*)$/ && do {
+		$config{"slice${1}_md5"} = $2;
+		last SWITCH1;
+	    };
+	    /^slicex_mount=(.*)$/ && do {
+		$config{"slicex_mount"} = $1;
+		last SWITCH1;
+	    };
+	    /^slicex_tarball=(.*)$/ && do {
+		$config{"slicex_tarball"} = $1;
+		last SWITCH1;
+	    };
+	    /^slicex_md5=(.*)$/ && do {
+		$config{"slicex_md5"} = $1;
+		last SWITCH1;
+	    };
+	    print STDERR "Invalid instruction: $_\n";
+	}
+    }
+    close(INSTR);
+
+    if (defined($config{"emulab_status"}) &&
+	$config{"emulab_status"} != $CDROMSTATUS_OKAY) {
+
+	my $err = $config{"emulab_status"};
+	my $msg = $weberrors{$err};
+
+	if ($err == $CDROMSTATUS_BADPRIVKEY &&
+	    Prompt("Invalid CD password. Try again?", "Yes") =~ /yes/i) {
+	    delete($config{"emulab_status"});
+	    goto again;
+	}
+	fatal("Netbed Checkin Error: '$msg'");
+    }
+    return 0;
+}
+
+#
+# Tell Emulab we finished its instruction.
+#
+sub FinishedInstructions()
+{
+    $path = "/tmp/emulab-finished.$$";
+    
+    #
+    # No need to do this when operating off local instructions.
+    # 
+    if (-s $setupconfig) {
+	return 0;
+    }
+
+  again:
+    while (1) {
+	print "Letting Netbed Central know we finished ...\n";
+	    
+	mysystem("$wget -q -O $path ".
+		 "'${WWW}/cdromcheckin.php3".
+		 "?cdkey=$cdkey&IP=$IP&privkey=$privkey&updated=1'");
+	if (!$?) {
+	    last;
+	}
+	print "Error checking in. Will try again in one minute ...\n";
+	sleep(60);
+    }
+    if (! -s $path) {
+	fatal("Could not update Netbed Central with status!");
+    }
+
+    #
+    # Read the output file to see if there was an error.
+    #
+    if (! open(INSTR, $path)) {
+	fatal("$path could not be opened for reading: $!");
+    }
+    while (<INSTR>) {
+	chomp();
+        SWITCH1: {
+	    /^emulab_status=(.*)$/ && do {
+		$config{emulab_status} = $1;
+		last SWITCH1;
+	    };
+	    fatal("Invalid Response: $_");
+	}
+    }
+    close(INSTR);
+
+    if ($config{"emulab_status"} != $CDROMSTATUS_OKAY) {
+	my $status = $config{"emulab_status"};
+	my $msg    = $weberrors{$status};
+
+	fatal("Netbed Update Error: '$msg'");
+    }
+    return 0;
+}
+
+#
+# Create a filesystem on the last unused slice. 
+#
+sub MakeFS($$)
+{
+    my ($mntpoint, $partition) = @_;
+    my $ED;
+    my $foo;
+
+    print "Looking for a DOS partition that can be resized ...\n";
+
+    my $pline = `growdisk -f $rawbootdisk`;
+    if ($?) {
+	print STDERR "*** Oops, no extra DOS slices to use!\n";
+	return 1;
+    }
+    chomp($pline);
+    my (undef, $slice, $type, $start, $size) = split(/\s/, $pline);
+
+    mysystem("echo \"$pline\" | fdisk -f - $rawbootdisk");
+    if ($?) {
+	print STDERR "*** Oops, could not setup extra DOS slice $slice!\n";
+	return -1;
+    }
+
+    mysystem("disklabel -w -r ${rawdevice}s${slice} auto");
+    if ($?) {
+	print STDERR "*** Oops, could not disklabel ${rawdevice}s${slice}!\n";
+	return -1;
+    }
+
+    # Make sure the kernel really has the new label!
+    mysystem("disklabel -r ${rawdevice}s${slice} > /tmp/disklabel");
+    mysystem("disklabel -R -r ${rawdevice}s${slice} /tmp/disklabel");
+
+    $ENV{'EDITOR'} = "ed";
+    if (! ($ED = mypopen("| disklabel -e -r ${rawdevice}s${slice}"))) {
+	print STDERR
+	    "*** Oops, could not edit label on ${rawdevice}s${slice}!\n";
+	return -1;
+    }
+    print $ED "/^  c: /\n";
+    print $ED "t\n";
+    print $ED "s/c: /${slicexpart}: /\n";
+    print $ED "w\n";
+    print $ED "q\n";
+    if (!close($ED)) {
+	print STDERR
+	    "*** Oops, error editing label on ${rawdevice}s${slice}!\n";
+	return -1;
+    }
+
+    print "Creating filesystem on $mntpoint (${rawdevice}s${slice}e).\n";
+    mysystem("newfs -U ${rawdevice}s${slice}e");
+    if ($?) {
+	print STDERR "*** Oops, could not newfs ${rawdevice}s${slice}e!\n";
+	return -1;
+    }
+    $foo = "s${slice}${slicexpart}";
+	
+    #
+    # If there was a tarball then mount up the filesystem and unpack it.
+    # We have to drag it across, and I assume that the FS is big enough
+    # to hold the tarball and the unpacked contents.
+    #
+    if ($config{"slicex_tarball"}) {
+	my $tarball = $config{"slicex_tarball"};
+	    
+	mysystem("mount /dev/${blockdevice}${foo} /mnt");
+	fatal("Failed to mount /dev/${blockdevice}${foo} on /mnt!")
+	    if ($?);
+	$needumount = 1;
+
+	chdir("/mnt") or
+	    fatal("Could not chdir to /mnt!");
+
+	#
+	# If told to use a local image from the CDROM, its really easy!
+	# 
+	if (! ($tarball =~ /^http:.*$/ || $tarball =~ /^ftp:.*$/)) {
+	    print "Unpacking $tarball to $mntpoint.\n".
+		"    Please be patient!\n";
+	
+	    mysystem("tar -zxf /$tarball");
+	    fatal("Failed to untar /$tarball!")
+		if ($?);
+	}
+	else {
+	    #
+	    # Transfer the image to the temp store and then unzip to the slice.
+	    #
+	    print "Transferring tarball for $mntpoint from:\n".
+	      "    $tarball\n".
+	      "    Please be patient! \n";
+	
+	    mysystem("$wget -nv -O /mnt/slicex.tar.gz $tarball");
+	    fatal("Failed to transfer tarball!")
+		if ($?);
+
+	    print "Unpacking tarball. Please be patient!\n";
+	    mysystem("tar -zxf /mnt/slicex.tar.gz");
+	    fatal("Failed to untar /mnt/slicex.tar.gz!")
+		if ($?);
+	}
+	
+	chdir("/") or
+	    fatal("Could not chdir to /!");
+	
+	mysystem("umount /mnt");
+	fatal("Failed to unmount /mnt!")
+	    if ($?);
+	$needumount = 0;
+    }
+    
+    $$partition = $foo;
+    return 0;
+}
+
+#
+# Localize the root filesystem by putting out a proper rc.conf.local,
+# resolv.conf, fstab, etc.
+#
+sub LocalizeRoot()
+{
+    my $root = "${blockdevice}${rootdevice}";
+    my $ED;
+	
+    if (! defined($config{"slice1_image"})) {
+	return 0;
+    }
+
+    print "Localizing root filesystem on $root ...\n";
+    MountRoot();
+
+    #
+    # Copy over the easy ones.
+    #
+    mysystem("cp -p /etc/rc.conf.local /mnt/etc");
+    fatal("Failed to copy /etc/rc.conf.local to /mnt/etc!")
+	if ($?);
+    mysystem("cp -p /etc/resolv.conf /mnt/etc");
+    fatal("Failed to copy /etc/resolv.conf to /mnt/etc!")
+	if ($?);
+
+    #
+    # Edit fstab, replacing the s1 entries with the current device.
+    #
+    chmod(0644, "/mnt/etc/fstab");
+    
+    if (! ($ED = mypopen("| ed /mnt/etc/fstab "))) {
+	fatal("Could not edit /mnt/etc/fstab!");
+    }
+#   print $ED "1,\$s;/dev/.*\\(s1[a-z]*\\);/dev/${blockdevice}\\1;p\n";
+    print $ED "1,\$s;/dev/[a-z]*[0-9]\\(.*[a-z]\\);/dev/${blockdevice}\\1;p\n";
+
+    #
+    # Append a mountpoint for the extra fs.
+    #
+    if (defined($config{"slicex_mount"}) && defined($slicexdev)) {
+	my $slicex_mount = $config{"slicex_mount"};
+
+	print $ED "\$\n";
+	print $ED "a\n";
+	print $ED "/dev/${blockdevice}${slicexdev}\t\t$slicex_mount\t\t".
+	    "ufs\trw\t0\t2\n";
+	print $ED ".\n";
+    }
+    
+    print $ED "w\n";
+    print $ED "q\n";
+    if (!close($ED)) {
+	fatal("Error editing /mnt/etc/fstab!");
+    }
+
+    chdir("/mnt/dev") or
+	fatal("Could not chdir to /mnt/dev!");
+    mysystem("./MAKEDEV ${blockdevice}${slicexdev}");
+    chdir("/") or
+	fatal("Could not chdir to /!");
+    
+    mysystem("umount /mnt");
+    $needumount = 0;
+
+    return 0;
+}
+
+#
+# Write out the privkey
+#
+sub WritePrivKey()
+{
+    if (! $needumount) {
+	MountRoot();
+    }
+    
+    if (defined($config{"privkey"})) {
+	my $key = $config{"privkey"};
+	
+	mysystem("echo \"$key\" > /mnt/${privkeyfile}");
+	fatal("Could not create /mnt/${privkeyfile}")
+	    if ($?);
+	chmod(0640, "/mnt/${privkeyfile}") or
+	    fatal("Could not chmod 0640 /mnt/${privkeyfile}");
+    }
+    mysystem("umount /mnt");
+    $needumount = 0;
+    
+    return 0;
+}
+
+#
+# Mount up the root partition.
+#
+sub MountRoot()
+{
+    my $root  = "${blockdevice}${rootdevice}";
+    my $rroot = "${rawdevice}${rootdevice}";
+
+    #
+    # Need to fsck it. If its dirty, the mount will fail and we will
+    # be hosed. If it fails fsck, well we are hosed anyway!
+    #
+    mysystem("fsck -y /dev/${rroot}");
+    fatal("Could not fsck root filesystem (/dev/${rroot}!")
+	if ($?);
+
+    mysystem("mount /dev/${root} /mnt");
+    fatal("Failed to mount /dev/${root}! on /mnt!")
+	if ($?);
+    $needumount = 1;
+
+    return 0;
+}
+
+#
+# When running from the CDROM write the config info to the magic spot.
+# 
+sub WriteConfigBlock()
+{
+    my $cmd = "$tbboot -f -d -w $softconfig -k 1 -c 0 ";
+    if (defined($config{privkey})) {
+	$cmd .= "-e $config{privkey} ";
+    }
+    $cmd .= "  $rawbootdisk";
+
+    mysystem("$cmd");
+    fatal("Failed to write header block info!")
+	if ($?);
+
+    return 0;
+}
+    
+#
+# Print error and exit.
+#
+sub fatal($)
+{
+    my ($msg) = @_;
+
+    if ($needumount) {
+	chdir("/");
+	mysystem("umount /mnt");
+    }
+    
+    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 $?;
+}
+
+sub mypopen($)
+{
+    my ($command) = @_;
+    local *FD;
+
+    if (defined($logfile)) {
+	system("echo \"$command\" >> $logfile");
+	open(FD, "$command >> $logfile 2>&1")
+	    or return undef;
+    }
+    else {
+	print "Command: '$command\'\n";
+	open(FD, "$command")
+	    or return undef;
+    }
+    return *FD;
+}
+
+#
+# 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 $_;
+}
+
diff --git a/cdrom/waboot/register.sh b/cdrom/waboot/register.sh
new file mode 100755
index 0000000000000000000000000000000000000000..90336b8c4691ceebcd38ee4965174e5650660c1e
--- /dev/null
+++ b/cdrom/waboot/register.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2000-2002 University of Utah and the Flux Group.
+# All rights reserved.
+#
+
+#
+# This file goes in /usr/local/etc/rc.d on the CDROM.
+#
+# Get the rawdisk and pass that to the registration program. It does
+# all the actual work.
+# 
+
+. /etc/rc.conf.local
+
+case "$1" in
+start)
+	if [ -f /usr/site/sbin/register.pl ]; then
+		/usr/site/sbin/register.pl $rawbootdisk
+		exit $?
+	fi
+	;;
+*)
+	echo "Usage: `basename $0` {start}" >&2
+	exit 1
+	;;
+esac
diff --git a/cdrom/waboot/waipconfig.pl b/cdrom/waboot/waipconfig.pl
new file mode 100755
index 0000000000000000000000000000000000000000..e6e55adba75f787747f2c7990bb24f30b3152491
--- /dev/null
+++ b/cdrom/waboot/waipconfig.pl
@@ -0,0 +1,466 @@
+#!/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;
+
+#
+# Boot configuration for the CDROM. Determine the IP configuration, either
+# from the floopy or from the user interactively.
+#
+# This is run from /etc/rc.network on the cdrom. It will write a new
+# file of shell variables resembling what goes in /etc/rc.conf so that
+# network configuration can proceed normally. Also write the new values
+# to the file floopy data file for next time.
+#
+sub usage()
+{
+    print("Usage: waipconfig\n");
+    exit(-1);
+}
+my  $optlist = "";
+
+#
+# Turn off line buffering on output
+#
+STDOUT->autoflush(1);
+STDERR->autoflush(1);
+
+#
+# Untaint the environment.
+# 
+$ENV{'PATH'} = "/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'};
+
+#
+# The raw disk device. 
+#
+my $defrawdisk	= "/dev/rad0";
+my $rawbootdisk;
+my $etcdir	= "/etc";
+my $rcconflocal	= "$etcdir/rc.conf.local";
+my $resolveconf = "$etcdir/resolv.conf";
+my $hardconfig	= "$etcdir/emulab-hard.txt";
+my $softconfig	= "$etcdir/emulab-soft.txt";
+my $tbboot	= "tbbootconfig";
+    
+#
+# This is our configuration.
+#
+my %config = ( interface  => undef,
+	       hostname   => undef,
+	       IP         => undef,
+	       netmask    => undef,
+	       domain     => undef,
+	       nameserver => undef,
+	       gateway    => undef,
+	     );
+
+# Must be root
+if ($UID != 0) {
+    die("*** $0:\n".
+	"    Must be root to run this script!\n");
+}
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+%options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (@ARGV != 0) {
+    usage();
+}
+
+print "\n";
+print "**************************************************************\n";
+print "*                                                            *\n";
+print "* Netbed installation CD.  This program will wipe your       *\n";
+print "* hard disk, and install a fresh Netbed image.               *\n";
+print "*                                                            *\n";
+print "* If this is not what you intended, please remove the CD and *\n";
+print "* reboot.                                                    *\n";
+print "*                                                            *\n";
+print "* Before you can install this CD, you must first go to       *\n";
+print "* www.emulab.net/cdromnewkey.php for a password.             *\n";
+print "*                                                            *\n";
+print "* Then, figure out what boot disk you want to use.           *\n";
+print "* Common disks are ad0 (1st IDE disk) and da0 (1st SCSI).    *\n";
+print "*                                                            *\n";
+print "**************************************************************\n";
+print "\n";
+
+#
+# Need a raw disk device.
+#
+$rawbootdisk = WhichRawDisk();
+
+BootFromCD();
+
+# Done!
+exit(0);
+
+sub BootFromCD()
+{
+    #
+    # On the CDROM, the order is to check for a hardwired config,
+    # and then a config stashed in the boot block. If neither is there,
+    # then prompt the user. Always give the user a chance to override the
+    # the current config. 
+    # 
+    if (CheckConfigFile() != 0 && CheckConfigBlock() != 0) {
+	#
+	# Else no config found. Must wait until we get one.
+	# 
+	print "\nNo existing configuration was found!\n";
+	GetUserConfig();
+    }
+
+    #
+    # Give the user a chance to override the normal operation, and specify
+    # alternate parameters.
+    #
+    while (1) {
+	PrintConfig();
+
+	#
+	# Everything must be defined or its a big problem! Keep looping
+	# until we have everything. 
+	#
+	foreach my $key (keys(%config)) {
+	    if (!defined($config{$key})) {
+		print "Some configuration parameters are missing!\n";
+		GetUserConfig();
+		next;
+	    }
+	}
+
+	#
+	# Otherwise, one last chance to override.
+	#
+	if (Prompt("Specify Alternate Configuration?", "No", 10) =~ /no/i) {
+	    last;
+	}
+	GetUserConfig();
+    }
+
+    #
+    # Generate the rc files.
+    #
+    if (WriteRCFiles() < 0) {
+	print "Could not write rc files.\n";
+	Reboot();
+    }
+    
+    #
+    # Text version of the latest config. Used later for checking registration
+    # and the disk update.
+    # 
+    if (WriteConfigFile($softconfig) < 0) {
+	print "Could not write soft config file.\n";
+	Reboot();
+    }
+    return 0;
+}
+
+#
+# Prompt for the configuration from the user. Return -1 if something goes
+# wrong.
+#
+sub GetUserConfig()
+{
+    print "Please enter the system configuration by hand\n";
+    
+    $config{'interface'} = Prompt("Network Interface", WhichInterface());
+    $config{'hostname'}  = Prompt("Hostname (no domain)", $config{'hostname'});
+    $config{'domain'}    = Prompt("Domain", $config{'domain'});
+    $config{'IP'}        = Prompt("IP Address", $config{'IP'});
+    $config{'netmask'}   = Prompt("Netmask", $config{'netmask'});
+    $config{'nameserver'}= Prompt("Nameserver", $config{'nameserver'});
+    $config{'gateway'}   = Prompt("Gateway IP", $config{'gateway'});
+
+    return 0;
+}
+
+#
+# Check for hardwired configuration file. We do this on both the CDROM boot
+# and the disk boot.
+#
+sub CheckConfigFile(;$)
+{
+    my ($path) = @_;
+
+    if (!defined($path)) {
+	$path = "$hardconfig";
+    }
+	
+    if (! -e $path) {
+	return -1;
+    }
+    if (! open(CONFIG, $path)) {
+	print("$path could not be opened for reading: $!\n");
+	return -1;
+    }
+    while (<CONFIG>) {
+	chomp();
+	ParseConfigLine($_);
+    }
+    close(CONFIG);
+    return 0;
+}
+
+#
+# Check Config block.
+#
+sub CheckConfigBlock()
+{
+    #
+    # See if a valid header block.
+    #
+    system("$tbboot -v $rawbootdisk");
+    if ($?) {
+	return -1;
+    }
+
+    #
+    # Valid block, so read the configuration out to a temp file
+    # and then parse it normally.
+    # 
+    system("$tbboot -r /tmp/config.$$ $rawbootdisk");
+    if ($?) {
+	return -1;
+    }
+    return CheckConfigFile("/tmp/config.$$");
+}
+
+#
+# Which network interface. Just come up with a guess, and let the caller
+# spit that out in a prompt for verification.
+#
+sub WhichInterface()
+{
+    # XXX
+    if (defined($config{interface})) {
+	return $config{interface};
+    }
+    my @allifaces = split(" ", `ifconfig -l`);
+		       
+    foreach my $guess ("fxp", "de", "tx", "vx", "wx", "em", "bge", "xl") {
+	foreach my $iface (@allifaces) {
+	    $iface =~ /([a-zA-z]*)(\d*)/;
+	    if ($1 eq $guess && $2 == 0) {
+		return $iface;
+	    }
+	}
+    }
+    return "fxp0";
+}
+
+#
+# Which raw disk. Prompt if we cannot come up with a good guess.
+#
+sub WhichRawDisk()
+{
+    #
+    # Search the drives until we find a valid header.
+    # 
+    foreach my $disk ("rad", "rda", "raacd") {
+	foreach my $drive (0) {
+	    my $guess = "/dev/${disk}${drive}";
+
+	    system("$tbboot -v $guess");
+	    if (! $?) {
+		#
+		# Allow for overiding the guess, with short timeout.
+		#
+		$rawbootdisk =
+		    Prompt("Which Raw Disk Device is the boot device?",
+			   "$guess", 10);
+		goto gotone;
+	    }
+	}
+    }
+
+    #
+    # Okay, no candidates. Lets find the first real disk. Use dd
+    # to see if the drive is configured.
+    #
+    foreach my $disk ("rad0", "rda0", "raacd0") {
+	my $guess = "/dev/${disk}";
+
+	system("dd if=$guess of=/dev/null bs=512 count=32 >/dev/null 2>&1");
+	if (! $?) {
+	    #
+	    # Allow for overiding the guess, with short timeout.
+	    #
+	    $rawbootdisk =
+		Prompt("Which Raw Disk Device is the boot device?",
+		       "$guess", 10);
+	    goto gotone;
+	}
+    }
+  gotone:
+    
+    #
+    # If still not defined, then loop forever.
+    # 
+    while (!defined($rawbootdisk) || ! -e $rawbootdisk) {
+	$rawbootdisk = 
+	    Prompt("Which Raw Disk Device is the boot device?", $defrawdisk);
+    }
+    return $rawbootdisk;
+}
+
+#
+# Write a config file suitable for input to the testbed boot header program.
+#
+sub WriteConfigFile($)
+{
+    my ($path) = @_;
+
+    print "Writing $path\n";
+    if (! open(CONFIG, "> $path")) {
+	print("$path could not be opened for writing: $!\n");
+	return -1;
+    }
+    foreach my $key (keys(%config)) {
+	my $val = $config{$key};
+
+	print CONFIG "$key=$val\n";
+    }
+    close(CONFIG);
+    return 0;
+}
+
+sub PrintConfig()
+{
+    print "\nCurrent Configuration:\n";
+    foreach my $key (keys(%config)) {
+	my $val = $config{$key};
+
+	if (!defined($val)) {
+	    $val = "*** undefined ***";
+	}
+
+	print "  $key=$val\n";
+    }
+    print "\n";
+    return 0;
+}
+
+#
+# Write an rc.conf style file which can be included by sh. This is based
+# on the info we get. We also write a resolve.conf
+#
+sub WriteRCFiles()
+{
+    my $path = "$rcconflocal";
+
+    if (! open(CONFIG, "> $path")) {
+	print("$path could not be opened for writing: $!\n");
+	return -1;
+    }
+
+    print "Writing $path\n";
+    print CONFIG "#\n";
+    print CONFIG "# DO NOT EDIT! This file is autogenerated at reboot.\n";
+    print CONFIG "#\n";
+    print CONFIG "hostname=\"$config{hostname}.$config{domain}\"\n";
+    print CONFIG "ifconfig_$config{interface}=\"".
+	         "inet $config{IP} netmask $config{netmask}\"\n";
+    print CONFIG "defaultrouter=\"$config{gateway}\"\n";
+    print CONFIG "rawbootdisk=\"$rawbootdisk\"\n";
+    print CONFIG "# EOF\n";
+    close(CONFIG);
+
+    $path = $resolveconf;
+    print "Writing $path\n";
+    if (! open(CONFIG, "> $path")) {
+	print("$path could not be opened for writing: $!\n");
+	return -1;
+    }
+    print CONFIG "search $config{domain}\n";
+    print CONFIG "nameserver $config{nameserver}\n";
+    close(CONFIG);
+    return 0;
+}
+
+#
+# Parse config lines. Kinda silly.
+#
+sub ParseConfigLine($)
+{
+    my($line) = @_;
+
+    if ($line =~ /(.*)="(.+)"/ ||
+	$line =~ /^(.*)=(.+)$/) {
+	print "$1 $2\n";
+	exists($config{$1}) and
+	    $config{$1} = $2;
+    }
+}
+
+#
+# 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 $_;
+}
+
+sub Reboot()
+{
+    print "Rebooting ... \n";
+    #system("/sbin/reboot");
+    exit(-1);
+}