From 60e7adb869d50f5e382d5d00011bd362d73f2158 Mon Sep 17 00:00:00 2001
From: Mike Hibler <mike@flux.utah.edu>
Date: Thu, 17 Mar 2005 20:18:33 +0000
Subject: [PATCH] Partial support for disk-zeroing on experiment termination.

I did the "back half" support.  If the 'mustwipe' field is non-zero
in the reserved table entry for a node then its disk must be zeroed.
How the zeroing is done, depends on the value of the mustwipe field.
Right now, '1' means pass the '-z' option to frisbee to have it zero
all non-allocated blocks.  The value '2' is reserved for enabling a
"full wipe" pass of the disk before running frisbee, which Keith Sklower
(DETER) wanted to be able to do.  Note that 1 and 2 are effectively the
same, if we are loading a full-disk image; i.e. all non-allocated blocks
from the new image are zeroed.  But if the disk were being loaded with
a single-partition image, then "frisbee -z" would only wipe unused
blocks in that partition.

The reload_daemon has been modified to extract the mustwipe info and
invoke os_load accordingly.   os_load now takes a "-z <type>" option
to enable the zeroing by setting a value in the current_reloads table.
tmcd will read and return that info to its caller in the "loadinfo" command.
Finally, the rc.frisbee script that runs in the frisbee MFS extracts the
loadinfo info and crafts the frisbee startup command.

What still needs to be done is the "front end," how the user specifies
the value and how it winds up in the DB reserved table.  This will probably
involve addition of state to the experiments table as this will likely be
a per-experiment setting.
---
 db/nfree.in              |  61 +++++++++++------------
 sql/database-create.sql  |   2 +
 sql/database-migrate.txt |   9 ++++
 tbsetup/libosload.pm.in  |  59 ++++++++++++++++++----
 tbsetup/os_load.in       |  14 ++++--
 tbsetup/reload_daemon.in | 105 ++++++++++++++++++++++++++-------------
 tmcd/freebsd/rc.frisbee  |  17 +++++--
 tmcd/tmcd.c              |  19 +++++--
 8 files changed, 199 insertions(+), 87 deletions(-)

diff --git a/db/nfree.in b/db/nfree.in
index 5b54c63a92..635b257159 100755
--- a/db/nfree.in
+++ b/db/nfree.in
@@ -50,6 +50,7 @@ my @nodes;
 my @freed_nodes=();
 my @dynanodes=();
 my $error = 0;
+my %mustzero=();
 
 $| = 1;  # Turn off line buffering on output
 
@@ -110,34 +111,20 @@ if (@ARGV) {
 	if ($n =~ /^([-\w]+)$/) { $n = $1; }
 	else { die("*** $0:\n    Bad node name: $n.\n"); }
 
-	# Shark hack
-	if ($n =~ /(sh\d+)/ ) {
-	    # It's a shark - do the whole shelf if its not done already.
-	    my $shelf = $1;
-	    if ( ! (join(",", @nodes) =~ /,$shelf-\d,/)) {
-		# Shelf hasn't been done yet...
-		foreach my $n ( 1 .. 8 ) {
-		    push(@nodes, "$shelf-$n");
-		}
-	    }
-	    # End shark hack
-	} else {
-	    # its not a shark - just add it in...
-	    push(@nodes, $n);
-
-	    # if -x was specified, remove any 
-	    # mapping to a node which has a phys_nodeid of $n.
-	    if ($freeDependantVirtuals) {
-		my $result = 
-		    DBQueryFatal("SELECT r.node_id FROM reserved AS r ".
-				 "LEFT JOIN nodes AS n ".
-				 "ON r.node_id=n.node_id ".
-				 "WHERE n.phys_nodeid='$n' AND ".
-				 "r.eid='$eid' AND r.pid='$pid'");
-		while (my ($dependantVirtual) = $result->fetchrow_array()) {
-		    if (defined $dependantVirtual && $dependantVirtual ne $n) {
-			push(@nodes, $dependantVirtual);
-		    }
+	push(@nodes, $n);
+
+	# if -x was specified, remove any 
+	# mapping to a node which has a phys_nodeid of $n.
+	if ($freeDependantVirtuals) {
+	    my $result = 
+		DBQueryFatal("SELECT r.node_id FROM reserved AS r ".
+			     "LEFT JOIN nodes AS n ".
+			     "ON r.node_id=n.node_id ".
+			     "WHERE n.phys_nodeid='$n' AND ".
+			     "r.eid='$eid' AND r.pid='$pid'");
+	    while (my ($dependantVirtual) = $result->fetchrow_array()) {
+		if (defined $dependantVirtual && $dependantVirtual ne $n) {
+		    push(@nodes, $dependantVirtual);
 		}
 	    }
 	}
@@ -183,6 +170,16 @@ foreach my $n (@nodes) {
 	next;
     }
 
+    #
+    # Remember if the node's disk must be zeroed
+    #
+    my $rowref = $result->fetchrow_hashref();
+    if ($rowref->{'mustwipe'}) {
+	$mustzero{$n} = $rowref->{'mustwipe'};
+    } else {
+	$mustzero{$n} = 0;
+    }
+
     if ( $moveToOldReserved ) {
 	# Move to holding reservation. Node is not free, but is no longer
 	# owned by the pid/eid, so cannot be mucked with.
@@ -251,6 +248,7 @@ foreach my $n (@freed_nodes) {
     if ($isvirt || !$imageable) {
 	# VIRTNODE HACK: Virtual nodes are special. Do not clean or reload.
 	$mustclean = 0;
+	$mustzero{$n} = 0;
     }
     elsif (defined($clean)) {
 	# If def_boot_osid set, then $clean is defined. Otherwise not set
@@ -354,9 +352,10 @@ foreach my $n (@freed_nodes) {
 	DBQueryFatal("select node_id,image_id from scheduled_reloads " .
 		     "where node_id='$n'");
 
-    # XXX force reload hack!
-    if ( !$TESTMODE && ((!$isvirt && $imageable) || $result->numrows() ||
-                        TBNodeType($n) eq "garcia")) { # XXX Garcia hack
+    if (!$TESTMODE &&
+	((!$isvirt && $imageable) || # XXX force reload hack!
+	 $result->numrows() || $mustzero{$n} ||
+	 TBNodeType($n) eq "garcia")) { # XXX Garcia hack
 	print "Moving $n to $reloadpid/$pendingeid.\n";
 	
 	DBQueryWarn("update reserved set pid='$reloadpid',eid='$pendingeid',".
diff --git a/sql/database-create.sql b/sql/database-create.sql
index d16835f0cd..c66c6671f4 100644
--- a/sql/database-create.sql
+++ b/sql/database-create.sql
@@ -67,6 +67,7 @@ CREATE TABLE comments (
 CREATE TABLE current_reloads (
   node_id varchar(32) NOT NULL default '',
   image_id varchar(45) NOT NULL default '',
+  mustwipe tinyint(4) NOT NULL default '0',
   PRIMARY KEY  (node_id)
 ) TYPE=MyISAM;
 
@@ -1490,6 +1491,7 @@ CREATE TABLE reserved (
   old_eid varchar(32) NOT NULL default '',
   cnet_vlan int(11) default NULL,
   inner_elab_role enum('boss','ops','node') default NULL,
+  mustwipe tinyint(4) NOT NULL default '0',
   PRIMARY KEY  (node_id),
   UNIQUE KEY vname (pid,eid,vname),
   KEY old_pid (old_pid,old_eid)
diff --git a/sql/database-migrate.txt b/sql/database-migrate.txt
index d74034e42b..aeaa2de43b 100644
--- a/sql/database-migrate.txt
+++ b/sql/database-migrate.txt
@@ -2518,3 +2518,12 @@ last_net_act,last_cpu_act,last_ext_act);
 
         alter table users add wikiname tinytext;
         alter table groups add wikiname tinytext;
+
+1.321: DB support for disk wipe-age.  If 'mustwipe' is set in the reserved
+       table entry for a node, then when nfree'd, the disk must be reloaded
+       and all free blocks zeroed to prevent information leakage.
+
+	alter table reserved add mustwipe tinyint(4) NOT NULL default '0';
+	alter table current_reloads add mustwipe \
+	    tinyint(4) NOT NULL default '0';
+      
diff --git a/tbsetup/libosload.pm.in b/tbsetup/libosload.pm.in
index 31a766e37f..39103e5d18 100755
--- a/tbsetup/libosload.pm.in
+++ b/tbsetup/libosload.pm.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2004 University of Utah and the Flux Group.
+# Copyright (c) 2000-2005 University of Utah and the Flux Group.
 # All rights reserved.
 #
 # Osload library. Basically the backend to the osload script, but also used
@@ -54,6 +54,7 @@ sub osload ($$) {
     my @nodes       = ();
     my $noreboot    = 0;
     my $asyncmode   = 0;
+    my $zerofree    = 0;
 
     # Locals
     my %retries	    = ();
@@ -84,6 +85,9 @@ sub osload ($$) {
     if (defined($args->{'asyncmode'})) {
 	$asyncmode = $args->{'asyncmode'};
     }
+    if (defined($args->{'zerofree'})) {
+	$zerofree = $args->{'zerofree'};
+    }
     
     #
     # Figure out who called us. Root and admin types can do whatever they
@@ -197,6 +201,7 @@ sub osload ($$) {
 		"*** osload ($node): No filename associated with $imageid!\n";
 	    goto failednode;
 	}
+
 	if (! -R $imagepath) {
 	    if ($ELABINELAB) {
 		#
@@ -210,6 +215,12 @@ sub osload ($$) {
 			    "Frisbee Launcher ($imageid) failed!\n";
 		    goto failednode;
 		}
+		if (! -R $imagepath) {
+		    print STDERR
+			"*** osload ($node): ".
+			    "Frisbee Launcher get not fetch $imagepath ($imageid)!\n";
+		    goto failednode;
+		}
 	    }
 	    else {
 		print STDERR 
@@ -324,6 +335,7 @@ sub osload ($$) {
 	    $reload_mode = "UISP";
 	    $reload_func = \&SetupReloadUISP;
 	    $reboot_required = 0; # We don't reboot motes to reload them
+	    $zerofree = 0; # and we don't zero "the disk"
 	} else {
 	    $reload_mode = "Frisbee";
 	    $reload_func = \&SetupReloadFrisbee;
@@ -339,13 +351,14 @@ sub osload ($$) {
 	    'func'    => $reload_func,
 	    'imageid' => $imageid,
 	    'osid'    => $defosid,
-	    'reboot'  => $reboot_required
+	    'reboot'  => $reboot_required,
+	    'zerofree'=> $zerofree
 	};
 
 	print "Setting up reload for $node (mode: $reload_mode)\n";
 
 	if (!$TESTMODE) {
-	    if (&$reload_func($node, $imageid,$defosid) < 0) {
+	    if (&$reload_func($node, $imageid, $defosid, $zerofree) < 0) {
 		print STDERR
 		"*** osload ($node): Could not set up reload. Skipping.\n";
 		goto failednode;
@@ -465,7 +478,7 @@ sub osload ($$) {
 
 		# Possible race with reboot?
 		if (&{$reload_info->{'func'}}($node, $reload_info->{'imageid'},
-		    $reload_info->{'osid'}) < 0) {
+		    $reload_info->{'osid'}, $reload_info->{'zerofree'}) < 0) {
 		    print(STDERR
 			  "*** osload ($node): ".
 			  "Could not set up reload. Skipping.\n");
@@ -529,7 +542,32 @@ sub WaitTillReloadDone($$$@)
 	sleep(5);
 	foreach my $node (@nodes) {
 	    if (! $done{$node}) {
-		my $maxwait = $maxwaits{$reload_info->{$node}{'imageid'}};
+		my $maxwait;
+
+		#
+		# If we have to zero fill free space, then the
+		# wait time has to be proportional to the disk
+		# size.  In other words, a really, really, really
+		# long time.  Lets assume 20MB/sec to blast zeros,
+		# so 50 seconds/GB.  What the heck, lets call it
+		# 1GB/minute.  Did I mention how this would take
+		# a really long time?
+		#
+		if ($reload_info->{$node}{'zerofree'}) {
+		    my $disksize;
+		    my $query_result =
+			DBQueryWarn("select HD from node_types,nodes ".
+				    "where nodes.type=node_types.type".
+				    " and node_id='$node'");
+		    if ($query_result && $query_result->numrows) {
+			($disksize) = $query_result->fetchrow_array();
+		    }
+		    $disksize = 20
+			if (!$disksize);
+		    $maxwait = ($disksize * 60);
+		} else {
+		    $maxwait = $maxwaits{$reload_info->{$node}{'imageid'}};
+		}
 		
 		my $query_result =
 		    DBQueryWarn("select * from current_reloads ".
@@ -604,9 +642,9 @@ sub WaitTillReloadDone($$$@)
 }
 
 # Setup a reload. 
-sub SetupReloadFrisbee($$$)
+sub SetupReloadFrisbee($$$$)
 {
-    my ($node, $imageid, $osid_notused) = @_;
+    my ($node, $imageid, $osid_notused, $zerofree) = @_;
     my $osid = $FRISBEEOSID;
 
     #
@@ -628,7 +666,8 @@ sub SetupReloadFrisbee($$$)
     #
     $query_result = 
 	DBQueryWarn("replace into current_reloads ".
-		    "(node_id, image_id) values ('$node', '$imageid')");
+		    "(node_id, image_id, mustwipe) values ".
+		    "('$node', '$imageid', $zerofree)");
     return -1
 	if (!$query_result);
 
@@ -650,9 +689,9 @@ sub SetupReloadFrisbee($$$)
 # this differs from a Frisbee reload in one key way - it does the reload
 # right here in this code, rather than setting up a reload for later.
 #
-sub SetupReloadUISP($$$)
+sub SetupReloadUISP($$$$)
 {
-    my ($node, $imageid, $osid) = @_;
+    my ($node, $imageid, $osid, $zerofree_unused) = @_;
 
     #
     # Get the path to the image
diff --git a/tbsetup/os_load.in b/tbsetup/os_load.in
index d50f265d96..f29a018d34 100755
--- a/tbsetup/os_load.in
+++ b/tbsetup/os_load.in
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -wT
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2004 University of Utah and the Flux Group.
+# Copyright (c) 2000-2005 University of Utah and the Flux Group.
 # All rights reserved.
 #
 use English;
@@ -24,14 +24,19 @@ sub usage()
 	  "Use -w to wait for the nodes to finish booting.\n".
 	  "Use -r to supress rebooting nodes - you'll need to to it yourself\n".
 	  "Use -e to reload all the nodes in an experiment.\n" .
-	  "Use -l to get a list of images you are permitted to load.\n");
+	  "Use -l to get a list of images you are permitted to load.\n".
+	  "Use -z <style> to zero all unallocated blocks on the disk\n".
+	  "       style==0: don't zero (same as not using -z)\n".
+	  "       style==1: let frisbee do the zeroing\n".
+	  "       style==2: zero disk before running frisbee\n");
     exit(-1);
 }
-my $optlist   = "swldi:e:p:m:r";
+my $optlist   = "swldi:e:p:m:rz:";
 my $waitmode  = 1;
 my $listonly  = 0;
 my $debug     = 0;
 my $noreboot  = 0;
+my $zerofree  = 0;
 my @nodes     = ();
 my $imagepid;
 my $imagename;
@@ -81,6 +86,8 @@ $waitmode = 2
     if (defined($options{"w"}));
 $noreboot = 1
     if (defined($options{"r"}));
+$zerofree = $options{"z"}
+    if (defined($options{"z"}));
 
 #
 # Figure out which nodes. Choice of nodes on command line, or all nodes in an
@@ -223,6 +230,7 @@ my %failednodes = ();
 $osloadargs{'debug'}    = $debug;
 $osloadargs{'waitmode'} = $waitmode;
 $osloadargs{'noreboot'} = $noreboot;
+$osloadargs{'zerofree'} = $zerofree;
 $osloadargs{'nodelist'} = [ @nodes ];
 # No imageid means to load the default image.
 $osloadargs{'imageid'}  = $imageid
diff --git a/tbsetup/reload_daemon.in b/tbsetup/reload_daemon.in
index 7f51265932..64df1f1ffb 100644
--- a/tbsetup/reload_daemon.in
+++ b/tbsetup/reload_daemon.in
@@ -64,8 +64,8 @@ my $reboot	= "$TB/bin/node_reboot";
 my $tbrsync     = "$TB/bin/tbrsync";
 my $logfile	= "$TB/log/reloadlog";
 my $debug	= 0;
-my $retry_time  = 15; # in minutes
-my $warn_time   = 30; # in minutes
+my $retry_time  = 20;              # in minutes
+my $warn_time   = $retry_time * 2; # in minutes
 my %retried     = ();
 my %warned	= ();
 my %failed	= ();
@@ -131,10 +131,14 @@ while (1) {
     # First, look for nodes that have been in the reloading experiment for
     # longer than $retry_time, and try rebooting them
     #
+    # XXX we count on mustwipe having the value 0, 1, 2 to represent
+    # ever slower forms of wipeage.  For retry_time of 20 minutes that
+    # yields waits of 20, 40 and 60 minutes.
+    #
     $query_result =
-	DBQueryWarn("select node_id from reserved where pid='$RELOADPID' " .
-		    "and eid='$RELOADEID' and " .
-		    "(CURRENT_TIMESTAMP - INTERVAL $retry_time MINUTE) ".
+	DBQueryWarn("select node_id,mustwipe from reserved " .
+		    "where pid='$RELOADPID' and eid='$RELOADEID' and " .
+		    "(CURRENT_TIMESTAMP - INTERVAL $retry_time * (mustwipe + 1) MINUTE)".
 		    "  > rsrv_time");
 
     if (! $query_result) {
@@ -142,7 +146,7 @@ while (1) {
 	next;
     }
 
-    while (($node) = $query_result->fetchrow){ 
+    while (($node, $mustwipe) = $query_result->fetchrow) {
 	$idle=0;
 	#
 	# If this was a node that failed os_load, then instead of rebooting,
@@ -150,7 +154,7 @@ while (1) {
 	# 
 	if ($failed{$node}) {
 	    print "$node failed an earlier os_load. Trying again\n";
-	    push(@retry_list, $node);
+	    push(@retry_list, [$node, $mustwipe]);
 	    delete $failed{$node};
 	    # Skip any reboots. 
 	    $retried{$node} = $time;
@@ -187,22 +191,25 @@ while (1) {
     # Next, we do the same thing for nodes in the reloading experiment for
     # longer than $warn_time, and warn the admins.
     #
+    # XXX again, we scale by the value of mustwipe.
+    #
     $query_result =
-	DBQueryWarn("select node_id from reserved where pid='$RELOADPID' " .
-		    "and eid='$RELOADEID' and " .
-		    "(CURRENT_TIMESTAMP - INTERVAL $warn_time MINUTE) > ".
-		    "   rsrv_time");
+	DBQueryWarn("select node_id,mustwipe from reserved " .
+		    "where pid='$RELOADPID' and eid='$RELOADEID' and " .
+		    "(CURRENT_TIMESTAMP - INTERVAL $warn_time * (mustwipe + 1) MINUTE)".
+		    "  > rsrv_time");
     
     if (! $query_result) {
 	print "DB Error. Waiting a bit.\n";
 	next;
     }
 
-    while (($node) = $query_result->fetchrow){ 
+    while (($node, $mustwipe) = $query_result->fetchrow) {
 	$idle=0;
 	if (!$warned{$node}) {
+	    my $toolong = $warn_time * ($mustwipe + 1);
 	    notify("Node $node has been in $RELOADPID/$RELOADEID for " .
-	    "more than $warn_time minutes");
+		   "more than $toolong minutes");
 	}
 	$warned{$node} = $time;
     }
@@ -227,7 +234,8 @@ while (1) {
     my $CLASSCLAUSE = "(n.class='pc' or n.class='pct')";
     
     $query_result =
-	DBQueryWarn("select a.node_id,b.pid,b.eid from reserved as b ".
+	DBQueryWarn("select a.node_id,b.pid,b.eid,b.mustwipe ".
+		    "from reserved as b ".
 		    "left join nodes as a on a.node_id=b.node_id ".
 		    "left join last_reservation as l on l.node_id=a.node_id ".
 		    "left join node_types as n on n.type=a.type where ".
@@ -249,18 +257,19 @@ while (1) {
 
     # Grab all the nodes that match
     my @pending_list = @retry_list;
-    while (%hrow  = $query_result->fetchhash()) {
+    while (%hrow = $query_result->fetchhash()) {
 	$node = $hrow{'node_id'};
 	$pid  = $hrow{'pid'};
 	$eid  = $hrow{'eid'};
+	$mustwipe = $hrow{'mustwipe'};
 	if ($pid eq $RELOADPID && $eid eq $PENDINGEID) {
-	    push(@pending_list,$node);
+	    push(@pending_list, [$node,$mustwipe]);
 	} else {
-	    push(@other_list,$node);
+	    push(@other_list, [$node,$mustwipe]);
 	}
     }
 
-    my $nodes = join(" ", (@pending_list, @other_list));
+    my $nodes = join(" ", map { $_->[0] } @pending_list, @other_list);
     print "Trying to reload $nodes at ".`date`;
 
     #
@@ -273,7 +282,9 @@ while (1) {
 	#
 	my %images = ();
 	my %imagenodes = ();
-	foreach $node (@pending_list) {
+	foreach $ref (@pending_list) {
+	    ($node, $mustwipe) = @{$ref};
+
 	    $query_result =
 	      DBQueryWarn("select image_id from scheduled_reloads " .
 			  "where node_id='$node'");
@@ -297,15 +308,26 @@ while (1) {
             }
             # XXX End Garcia Hack
 
+	    #
+	    # We need to divide up nodes not only by the image they are
+	    # to load (imageid) but also by if and how the disk should be
+	    # zeroed (mustzero).  So we really have a hash of hashes each
+	    # of which is an array of nodes.  However, my perl skilz are
+	    # not up to that so just combine the imageid and mustwipe into
+	    # a single hash key ('/' is illegal in both, so we use it as
+	    # the separator).
+	    #
+	    my $idid = "$imageid/$mustwipe";
+
 	    $images{$node} = $imageid;
-	    if (defined(@{$imagenodes{$imageid}})) {
-		push(@{$imagenodes{$imageid}},$node);
+	    if (defined(@{$imagenodes{$idid}})) {
+		push(@{$imagenodes{$idid}},$node);
 	    } else {
-		$imagenodes{$imageid} = [$node];
+		$imagenodes{$idid} = [$node];
 	    }
 	    if ($debug) {
-		print "$node => $images{$node} == $imageid (".
-		  join(",",@{$imagenodes{$imageid}}).")\n";
+		print "$node ($mustwipe) => $images{$node} == $imageid (".
+		  join(",",@{$imagenodes{$idid}}).")\n";
 	    }
 	}
 	
@@ -315,17 +337,19 @@ while (1) {
 	# We change the reservation EID over and fire up an os_load
 	# directly.
 	#
-	my $cond = join(" or ",map("node_id='$_'",@pending_list));
+	my $cond = "node_id in (" .
+	    join(",", map("'$_->[0]'", @pending_list)) . ")";
 	if (! DBQueryWarn("update reserved set ".
 			  "rsrv_time=now(),eid='$RELOADEID' ".
 			  "where $cond")) {
-	    print "Could not update EID for ".join(" ",@pending_list).
-	      ". Waiting a bit.\n";
+	    print "Could not update EID for " .
+		join(" ", map("$_->[0]", @pending_list)) .
+		    ". Waiting a bit.\n";
 	    next;
 	} else {
 	    print "Pending nodes moved to $RELOADEID at ".`date`;
 
-	    foreach my $n (@pending_list) {
+	    foreach my $n (map("$_->[0]", @pending_list)) {
 		TBSetNodeHistory($n, TB_NODEHISTORY_OP_MOVE, $UID,
 				 $RELOADPID, $RELOADEID);
 	    }
@@ -335,11 +359,13 @@ while (1) {
 
 	# Now run an os_load for each image
 	
-	foreach $imageid (keys %imagenodes) {
+	foreach my $idid (keys %imagenodes) {
 
-	    my $nodelist = join(" ",@{$imagenodes{$imageid}});
+	    my $nodelist = join(" ",@{$imagenodes{$idid}});
 	    my $os_load_flags = "";
 
+	    ($imageid, $mustzero) = split("/", $idid);
+
             # XXX Garcia Hack - gross..
             # We special-case garcia loading for now until the subnode->node
             # dependancies are worked out inside os_load.
@@ -351,7 +377,7 @@ while (1) {
                 if (system("$tbrsync upload $gimagepath $nodelist") == 0) {
                     if (system("$reboot $nodelist") == 0) {
                         # rsync and reboot succeeded, so free 'em up.
-                        foreach my $gnode (@{$imagenodes{$imageid}}) {
+                        foreach my $gnode (@{$imagenodes{$idid}}) {
                             freefromreloading($gnode);
                         }
                         print "garcia reload done at ".`date`;
@@ -380,7 +406,14 @@ while (1) {
 	    # the node's type
 	    #
 	    if ($imageid) {
-		$os_load_flags .= " -m $imageid ";
+		$os_load_flags .= " -m $imageid";
+	    }
+
+	    #
+	    # Handle optional zeroing of the disk
+	    #
+	    if ($mustzero) {
+		$os_load_flags .= " -z $mustzero";
 	    }
 
 	    print "Running '$os_load $os_load_flags $nodelist' at ".`date`;
@@ -396,7 +429,7 @@ while (1) {
 
 		# Record the failure list. If we get to the 15 minute
 		# retry, call os_load again instead of rebooting.
-		foreach my $node (@{$imagenodes{$imageid}}) {
+		foreach my $node (@{$imagenodes{$idid}}) {
 		    $failed{$node} = $time;		    
 		}
 	    }
@@ -407,6 +440,8 @@ while (1) {
     }
 	
     if (@other_list > 0 ) {
+	my $nodes = join(" ", map { $_->[0] } @other_list);
+
 	#
 	# Call sched_reload with the "force" option, which says that if
 	# sched_reload cannot reserve the node (cause someone just got it)
@@ -417,11 +452,11 @@ while (1) {
 	# default, and sched_reload will pick that up from the database
 	# in the absence of a -i option. 
 	#
-	if (system("$sched_reload -f @other_list")) {
+	if (system("$sched_reload -f $nodes")) {
 	    #
 	    # Could not get it. Wait and go around again.
 	    #
-	    print "$sched_reload failed on @other_list. Waiting a bit.\n";
+	    print "$sched_reload failed on $nodes. Waiting a bit.\n";
 	    next;
 	}
 
diff --git a/tmcd/freebsd/rc.frisbee b/tmcd/freebsd/rc.frisbee
index 4e8ea9615d..6516a7d56d 100755
--- a/tmcd/freebsd/rc.frisbee
+++ b/tmcd/freebsd/rc.frisbee
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2004 University of Utah and the Flux Group.
+# Copyright (c) 2000-2005 University of Utah and the Flux Group.
 # All rights reserved.
 #
 # Optional flag argument says "do not reboot"
@@ -49,6 +49,8 @@ PARTITION=${PARTITION:-'0'}
 PARTOS=`echo $LOADINFO | awk -F= '{ printf $4 }' | awk -F' ' '{ print $1 }'`
 DISK=`echo $LOADINFO | awk -F= '{ printf $5 }' | awk -F' ' '{ print $1 }'`
 DISK=${DISK:-'ad0'}
+ZFILL=`echo $LOADINFO | awk -F= '{ printf $6 }' | awk -F' ' '{ print $1 }'`
+ZFILL=${ZFILL:-'0'}
 
 if [ "$PARTITION" != "0" ]; then
 	SLICE="-s $PARTITION"
@@ -121,11 +123,20 @@ if [ x"$ADDRESS" != x ]; then
 		MCASTIF=""
 	fi
 	MCASTADDR="-m $MCAST -p $PORT"
+	#
+	# ZFILL==1: use frisbee
+	# ZFILL==2: separate disk-wipe pass (not yet implemented)
+	#
+	if [ "$ZFILL" != "0" ]; then
+	    ZFILL="-z"
+	else
+	    ZFILL=""
+	fi
 
-	echo "Running $BINDIR/frisbee $LOADIP $MEMARGS $SLICE $MCASTIF $MCASTADDR /dev/$DISK at `date`"
+	echo "Running $BINDIR/frisbee $LOADIP $MEMARGS $ZFILL $SLICE $MCASTIF $MCASTADDR /dev/$DISK at `date`"
 	$BINDIR/tmcc state RELOADING
 
-	$BINDIR/frisbee $LOADIP $MEMARGS $SLICE $MCASTIF $MCASTADDR /dev/$DISK
+	$BINDIR/frisbee $LOADIP $MEMARGS $ZFILL $SLICE $MCASTIF $MCASTADDR /dev/$DISK
 	case $? in
 	0)
 		echo "Frisbee run finished"
diff --git a/tmcd/tmcd.c b/tmcd/tmcd.c
index 0a6942adea..09971c3d1d 100644
--- a/tmcd/tmcd.c
+++ b/tmcd/tmcd.c
@@ -3259,17 +3259,17 @@ COMMAND_PROTOTYPE(doloadinfo)
 	char		buf[MYBUFSIZE];
 	char		*bufp = buf, *ebufp = &buf[sizeof(buf)];
 	char		*disktype;
-	int		disknum;
+	int		disknum, zfill;
 
 	/*
 	 * Get the address the node should contact to load its image
 	 */
-	res = mydb_query("select load_address,loadpart,OS,frisbee_pid "
+	res = mydb_query("select load_address,loadpart,OS,frisbee_pid,mustwipe "
 			 "  from current_reloads as r "
 			 "left join images as i on i.imageid = r.image_id "
 			 "left join os_info as o on i.default_osid = o.osid "
 			 "where node_id='%s'",
-			 4, reqp->nodeid);
+			 5, reqp->nodeid);
 
 	if (!res) {
 		error("doloadinfo: %s: DB Error getting loading address!\n",
@@ -3302,6 +3302,14 @@ COMMAND_PROTOTYPE(doloadinfo)
 
 	bufp += OUTPUT(bufp, ebufp - bufp,
 		       "ADDR=%s PART=%s PARTOS=%s", row[0], row[1], row[2]);
+
+	/*
+	 * Remember zero-fill free space indicator
+	 */
+	zfill = 0;
+	if (row[4] && row[4][0])
+		zfill = atoi(row[4]);
+
 	mysql_free_result(res);
 
 	/*
@@ -3326,9 +3334,10 @@ COMMAND_PROTOTYPE(doloadinfo)
 		if (row[1] && row[1][0])
 			disknum = atoi(row[1]);
 	}
-	OUTPUT(bufp, ebufp - bufp, " DISK=%s%d\n", disktype, disknum);
+	OUTPUT(bufp, ebufp - bufp, " DISK=%s%d ZFILL=%d\n",
+	       disktype, disknum, zfill);
 	mysql_free_result(res);
-	
+
 	client_writeback(sock, buf, strlen(buf), tcp);
 	if (verbose)
 		info("doloadinfo: %s", buf);
-- 
GitLab