#!/afs/athena/contrib/perl/perl

### This file has been automatically generated by compactify.pl.
push(@RCSID, 'post-processing: $Id: compactify.pl,v 1.1 1996/05/03 16:23:46 bert Exp $');
### Do not edit by hand; edit source files instead.
### (List of source files included in this program is appended at the end.)

#*#*#*# (top level)     #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#

#
# a tool for moving all volumes from an AFS partition or an entire
# server to another partition
#
# by bert Dvornik <bert@mit.edu>, Watchmaker Zone Night Patrol
#
# $Id: volmove.pl,v 1.6 1996/04/30 02:08:28 bert Exp $

unshift(@RCSID, '$Id: volmove.pl,v 1.6 1996/04/30 02:08:28 bert Exp $');

### use other relevant files

#*#*#*# (disabled by compactify.pl)
# unshift(@INC, '/afs/athena/user/b/e/bert/project/volmove');
#*#*#*#

#*#*#*# include level 1 #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#
$compactify'result = eval <<'END_OF_AGETOPT_PL';
#*#*#*# agetopt.pl, require'd from ./volmove.pl line 15
#
# AFS-style command line parsing
#
# by bert Dvornik <bert@mit.edu>, Watchmaker Zone Night Patrol
#
# $Id: agetopt.pl,v 1.2 1996/04/19 17:58:43 bert Exp $

unshift(@RCSID, '$Id: agetopt.pl,v 1.2 1996/04/19 17:58:43 bert Exp $');

package agetopt;

$debug = 0;			# debugging level
$delim = "\000";		# delimiter for embedding stuff in strings
$valid_positional = "^[^-]";	# regexp for deciding if a positional arg is ok

%explicit = ();			# information on explicit options
%implicit = ();			# information on substring options
%ambiguous = ();		# set if multiple substring options overlap

@options = ();			# list of arg-taking opts in positional order
%nargs = ();			# number of args for each option


sub main'agetopt {	# ("-opt1", "-opt2:", ...)
    local(@optdesc);
    for $_ (@_) {
	/^([^:]*)(:*)$/ || warn "Invalid agetopt syntax: `$_'\n";
	&parse_optpair($1, length($2));
    }

    &agetopt_optdesc();
}

sub agetopt_optdesc {	# ("-opt1", nargs1, "-opt2", nargs2, ...)
    if (@_) {
	&parse_optdesc(@_);
    }
    &dump_rules if ($debug>1);

    &extract_options(@ARGV);
}

sub parse_optdesc {	# ("-opt1", nargs1, "-opt2", nargs2, ...)
    local(@optdesc) = @_;
    local($opts, $num);

    warn "Multiple calls to agetopt'parse_optdesc!\ncontinuing" if $parsed;
    $parsed++;

    while(@optdesc) {
	$opts = shift(@optdesc);
	$num = shift(@optdesc);

	&parse_optpair($opts, $num);
    }
}

sub parse_optpair {	# ("-option", nargs)
    local($opts, $num) = @_;
    local(@opts, $opt, $i);

    @opts = split(/$delim/, $opts);

    # Add $opts[0] to the positional list of options, record number of args.
    # (If an option starts with $delim, it means "not positional".)
    if ($opts[0]) {
	push (@options, $opts[0]) if $num;
    } else {
	shift(@opts);
    }
    $nargs{$opts[0]} = $num;

    for $opt (@opts) {
	# label $opt as an explicit option.
	&make_explicit($opt, $opts[0]);

	# label substrings of $opt as implicit option.
      SUBSTRING:
	for ($i=length($opt)-1; $i>0; $i--) {
#	    printf ">>> %-10s %3d %3d\n", substr($opt,0,$i), $i, $num;
	    (&make_implicit(substr($opt,0,$i), $opts[0]))|| last SUBSTRING;
	}
    }
}

sub make_explicit {	# ("-option", value)
    local($opt, $val) = @_;

    if ($explicit{$opt}) {
	die "Option `$opt' has been defined more than once.\naborting";
    }
    if ($implicit{$opt}) {
	print STDERR "Warning: option `$opt' could be confused with `",
	    $implicit{$opt}, "'.\n"  if $debug;
    }
    $explicit{$opt} = $val;
    1;
}

sub make_implicit {	# ("-option", value)
    local($opt, $val) = @_;

    if ($explicit{$opt}) {
	# explicit options supersede implicit ones, so we should do
	# nothing and return 0 to exit the loop here.
	print STDERR "Warning: option `$opt' may be confused with `$val'.\n"
	    if $debug;
	0;
    } elsif ($implicit{$opt}) {
	# a conflict means that an option is ambiguous.
	# stay in the loop so that all substrings get tagged as ambiguous.
	$ambiguous{$opt}++;
	1;
    } else {
	$implicit{$opt} = $val;
	1;
    }
}

sub extract_options {	# (@ARGV)
    local(@argv) = @_;
    local($arg, $opt, $i, @arglist);
    local(%value);

    while (@argv) {
	$arg = shift(@argv);

	if ($ambiguous{$arg}) {
	    return "Ambiguous switch `$arg'";
	}

	$opt = ($explicit{$arg} || $implicit{$arg});

	if ($opt) {
	    # if the current argument is an option flag, deal with it.

	    if ($nargs{$opt}) {
		# if the option takes arg(s), make sure there's no conflict...
		if ($value{$opt}) {
		    return "Value for `$opt' is given more than once";
		}
		# ...extract the args...
		@arglist=();
		for ($i=$nargs{$opt}; $i>0; $i--) {
		    return "The field `$opt' isn't completed properly"
			unless @argv;
		    push(@arglist, shift(@argv));
		}
		# ...and set the value to their agglomeration.
		$value{$opt} = join($delim, @arglist);
	    } else {
		# if the option takes no arguments, increment its value.
		$value{$opt}++;
	    }
	} else {
	    # if the argument is not an option flag, treat it as positional.

	    return "Argument `$arg' is not a valid positional"
		if (($valid_positional && ($arg !~ /$valid_positional/)));

	    # ignore all positional arguments whose values we already know.
	    while (@options && $value{$options[0]}) {
		shift(@options);
	    }

	    # check if they're all used up...
	    return "Too many positional arguments, can't place `$arg'"
		unless @options;

	    # ...then extract the remaining args, if any...
	    @arglist=($arg);
	    for ($i=($nargs{$options[0]}-1); $i>0; $i--) {
		return "The fields `$options[0]' aren't completed properly"
		    unless @argv;
		push(@arglist, shift(@argv));
	    }
	    # ...and set the value to their agglomeration.
	    $value{$options[0]} = join($delim, @arglist);
	}
    }
    %main'aopt = %value;
    "";
}

sub dump_rules {
    print ">> EXPLICIT:\n";
    for $k (sort keys %explicit) {
	printf ">> %-20s -> `%s'\n", "`$k'", $explicit{$k};
    }
    print ">> AMBIGUOUS:\n";
    for $k (sort keys %ambiguous) {
	printf ">> %-20s -> `%s'\n", "`$k'", $ambiguous{$k} 
	    unless ($explicit{$k});
    }
    print ">> IMPLICIT:\n";
    for $k (sort keys %implicit) {
	printf ">> %-20s -> `%s'\n", "`$k'", $implicit{$k}
	    unless ($explicit{$k} || $ambiguous{$k});
    }
}


1;
#*#*#*# end of agetopt.pl
END_OF_AGETOPT_PL
die "while evaluating agetopt.pl: $@" if $@;
die "evaluating agetopt.pl did not return true value" unless $compactify'result;
#*#*#*# end of level 1  #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#
#*#*#*# include level 1 #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#
$compactify'result = eval <<'END_OF_TOKENS_PL';
#*#*#*# tokens.pl, require'd from ./volmove.pl line 16
#
# Check the remaining time on user's tokens.
#
# by bert Dvornik <bert@mit.edu>, Watchmaker Zone Night Patrol
#
# $Id: tokens.pl,v 1.2 1996/04/14 13:05:04 bert Exp $

unshift(@RCSID, '$Id: tokens.pl,v 1.2 1996/04/14 13:05:04 bert Exp $');

package tokens;

@month_name = ('Jan','Feb','Mar','Apr','May','Jun',
	       'Jul','Aug','Sep','Oct','Nov','Dec');

sub main'get_token_lifetime {
    local($cellname) = @_;
    local($found, $expired, $lifetime, $lt);
    local($now) = time;
    local($s,$m,$h, $mday,$month) = localtime($now);
    $now -= $s;  # start at the start of minute

    open(TOKENS, "tokens |") || die 'Cannot run "tokens": $@\naborting';

    while (<TOKENS>) {
	if (/\s+tokens\s+for\s+afs@$cellname/i) {
	    $found++;
	    if (/\[\s*Expires\s+(\w+)\s+(\d+)\s+(\d+):(\d+)\s*\]/i) {
		$lt = ($4 - $m) + 60*($3 - $h);
		if ((($1 eq $month_name[$month]) && ($2 > $mday)) ||
		    (($1 ne $month_name[$month]) && ($2 < $mday))) {
		    $lt += 24*60;
		}
		$lifetime = $lt if (!defined($lifetime) || ($lt < $lifetime));
	    } else {
		$expired++;
	    }
	}
    }

    return -1 if (!$found || $expired);

    $expiration{$cellname} = $now + (60*$lifetime);
    $lifetime;
}

$user = $ENV{'USER'} || $ENV{'LOGNAME'};

sub main'token_expiration_alert {
    local($seconds, $xseconds, $cell) = @_;
    local($lifetime);

    if (! $expiration{$cell}) {
	$lifetime = 60 * &main'get_token_lifetime($cell);
    }
    $lifetime = $expiration{$cell} - time;

    if ($lifetime < $seconds) {
	if ($lifetime > 0) {
	    printf STDOUT <<"EndOfFormat",
               *** WARNING ****** WARNING ****** WARNING ***
                  Your tokens will expire in %02dh %02dm %02ds.
    You are advised to renew your tickets and tokens before proceeding.
If you don't, please let everyone know you are about to use strong language.
               *** WARNING ****** WARNING ****** WARNING ***

Press RETURN to continue.
EndOfFormat
		$lifetime/3600, ($lifetime/60)%60, $lifetime%60;
	} else {
	    print STDOUT <<"EndOfText";
               *** WARNING ****** WARNING ****** WARNING ***
      You do not seem to have valid tokens for the specified AFS cell.
Go get some before continuing!  (You may need to obtain new tickets first.)
               *** WARNING ****** WARNING ****** WARNING ***

Press RETURN to continue.
EndOfText
        }
	<STDIN>;
	&main'get_token_lifetime($cell);
	if ((($expiration{$cell} - time) < $xseconds) && $xseconds) {
	    print STDOUT <<"EndOfText";
You aren't listening?
      *** THIS WILL HURT.  YOU HAVE BEEN WARNED.  HAVE A NICE DAY. ***

Press RETURN to continue.
EndOfText
            <STDIN>;
	}
	return ($lifetime);
    }
    ();
}

1;

#*#*#*# end of tokens.pl
END_OF_TOKENS_PL
die "while evaluating tokens.pl: $@" if $@;
die "evaluating tokens.pl did not return true value" unless $compactify'result;
#*#*#*# end of level 1  #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#

### defaults

$DEFlogfile = '/usr/tmp/vos_move.log.'; # plus the PID of the current process

### sighandler...
sub handler {
    local($sig) = @_;
    print "*** CAUGHT SIG$sig: QUITTING AFTER THIS OPERATION COMPLETES ***\n";
    print LOG "(CAUGHT SIG$sig: QUITTING AFTER THIS OPERATION COMPLETES)\n";
    $QUIT++;
    # I don't think this is needed at all, but it can't hurt.
    $SIG{$sig} = 'IGNORE';
}

### exec a process so that it doesn't see SIGINT even if it installs a handler

sub protect {
    $SIG{'INT'} = $SIG{'QUIT'} = 'IGNORE'; # mostly irrelevant
    open (STDERR, ">&STDOUT");
    setpgrp(0, $$); # protect the child from console-generated SIGINT
    exec(@_) || die "can't exec '@_'";
}

### run an AFS command and log the output

sub run_cmd {
    local($cmd) = @_;
    local($last);

    $afsop++;

    print LOG "\n%%% $cmd\n";
    if ($dontdoit) {
	print "WOULD DO: $cmd\n";
    } else {
	print "\n%%% $cmd\n";

        # fork, setpgrp and exec (see camel book for details on open'ing "-|")
	open(VCMD, "-|") || do protect($cmd);

	while (<VCMD>) {
	    print LOG;
	    print if $verbose;
	    $last = $_ if (!/^\s+$/);
	}
	print $last if !$verbose;

	close(VCMD);
    }
}

### usage info

sub usage {
    local($err) = @_;
    local($prog) = $0;
    $prog =~ s@.*/@@g;
    $err = "\n$prog: $err\n" if $err;

    print <<"EndOfUsage";
$err
Usage: $prog [options] from-host[.part] to-host.part [cell [logfile]]
Options:

  -backupbyname  automatically back up all volumes that don't start in "n."
                 or end in ".nb"
                 [default: back up those which are currently backed up]

  -unreplicated  don't automatically replicate and release volumes on new site
  [-r]           [default: do replicate and release]

  -keepro        do not remove old read-only volumes
  [-o]           [default: remove old read-only volumes]

  -zephyr        send messages to the specified zephyr class,instance
                 (or class,cellname if instance is omitted)

  -noaction      just say what we WOULD do
  -verbose       verbose
  -debug         debugging output (probably useless to you)

Omitting the "from" partition means *all* partitions on that host.
Not specifying the cell may lead to authenticantion problems (if it's
something other than athena).
Logfile defaults to $DEFlogfile<pid>

Ctrl-C will abort the operation *after* the move in progress is finished.
Thus it should be safe to Ctrl-C out of $prog while it is performing
vos operations you normally can't safely Ctrl-C out of, like "vos move".

EndOfUsage
    print join("\n", @RCSID), "\n";
    exit 1;
}

### main body

# Ctrl-C exits in a more controlled fashion.
$SIG{'INT'} = $SIG{'QUIT'} = 'handler';

# arg parsing

($err = &agetopt('-backupbyname', "-unreplicated\000-r", "-keepro\000-o",
		 '-noaction', '-verbose', '-debug',
		 '-from:', '-to:', '-cell:', '-logfile:', "\000-zephyr:"))
    && &usage($err);

$debug      = $aopt{'-debug'};
$verbose    = $aopt{'-verbose'};
$dontdoit   = $aopt{'-noaction'};

$autobackup = $aopt{'-backupbyname'};
$keepro     = $aopt{'-keepro'};
$norelease  = $aopt{'-unreplicated'};

$shost = $aopt{'-from'} || &usage("Missing required field `-from'");
if ($shost =~ /^(\S+)\.(.)$/) {
    ($shost,$spart) = ($1,$2);
}

$dhost = $aopt{'-to'} || &usage("Missing required field `-to'");
if ($dhost =~ /^(\S+)\.(.)$/) {
    ($dhost,$dpart) = ($1,$2);
} else { &usage("Missing or garbled partition in required field `-to'"); }

if ($afscell = $aopt{'-cell'}) {
    $cell = "-cell $afscell";
}

if ($zephyr = $aopt{'-zephyr'}) {
    local(@z) = split(/,/, $zephyr);
    @zwrite = ('zwrite', '-c', @z[0], '-i', @z[1] ? @z[1] : $afscell);
}

$logfile = $aopt{'-logfile'} || ($DEFlogfile . $$);

# open the logfile

open(LOG, ">$logfile") || die "can't open $logfile ($!)";
select((select(LOG), $|=1)[$[]);
print LOG "(not actually doing anything)\n" if $dontdoit;
$afsop = 0;

# complain if our tokens will last < 2 hours.

if (@life = &token_expiration_alert(60*60, 0, $afscell)) {
    print LOG "(token expiration in $life[0] seconds -- user notified)\n";
}

# parse the list of volumes

# this does a fork, setpgrp and exec
open(VOL, "-|") || do protect("vos listvol $shost $spart $cell");

print "Reading volume list for $shost $spart\n" if $verbose;

while (<VOL>) {
    print ">> $_" if ($verbose>1);
    chop;

    # parse the first line

    if (! m@^Total number of volumes on server (\S+) partition /vicep(.): (.*)$@) {
	print STDERR "Oops, I can't parse vos output:\n";
	print STDERR "'$_'\n";
	die 'quitting';
    }
    ($host,$part,$nn) = ($1, $2, $3);
    push(@partitions, "$host $part");

    # parse the volume names

  VOLUME:
    for ($i=0; $i<$nn; $i++) {
	($_ = <VOL>) || die "early end of output fron listvol";
	print ">> $_" if ($verbose>1);
	($vol,$id,$type) = split(/\s+/,$_,4);

	# ignore disk.*
	if ($vol =~ /^disk\./) {
	    print "Will ignore $vol.\n" if $debug;
	    next VOLUME;
	}

	# ignore **...
	if ($vol =~ /^\*\*+/) {
	    print;
	    next VOLUME;
	}

	# preserve back-up volumes
	if ($vol =~ /^(.*)\.backup$/) {
	    print "Warning: $vol is listed as '$type', not 'BK'!\n" if ($type ne 'BK');
	    print LOG "!!! $vol is listed as '$type', not 'BK'!\n" if ($type ne 'BK');
	    $rvol = $1;
	    print "Will back up $rvol.\n" if $debug;
	    $backup{$rvol}++;
	    next VOLUME;
	}
	if ($type eq 'BK') {
	    print "Warning: oops, $vol is a backup of *what*? (ignoring)\n";
	    print "!!! oops, $vol is a backup of *what*? (ignoring)\n";
	    next VOLUME;
	}

	# nuke and re-create read-only replication sites
	if ($vol =~ /^(.*)\.readonly$/) {
	    print "Warning: $vol is listed as '$type', not 'RO'!\n" if ($type ne 'RO');
	    print LOG "!!! $vol is listed as '$type', not 'RO'!\n" if ($type ne 'RO');
	    $rvol = $1;
	    print "Will re-replicate and blow away $rvol.\n" if $debug;
	    $replicated{$rvol} .= "$host $part,";
	    next VOLUME;
	}
	if ($type eq 'RO') {
	    print "Warning: oops, $vol is a replication site for *what*? (ignoring)\n";
	    print "!!! oops, $vol is a replication site for *what*? (ignoring)\n";
	    next VOLUME;
	}

	push(@move,"$vol $host $part");
    }

    # now we get a newline, a "Total onLine/offLine/busy" line, and another newline
    $_ = <VOL>;
    if (!/^\s*$/) {
	chop;
	print STDERR "Oops, I was expecting a newline and I got:\n'$_'\n";
	die 'quitting';
    }
    $_ = <VOL>;
    if (!/^Total volumes/) {
	chop;
	print STDERR "Oops, I was expecting a on-line/off-line/busy summary:\n'$_'\n";
	die 'quitting';
    }
    $_ = <VOL>;
    if (!/^\s*$/) {
	chop;
	print STDERR "Oops, I was expecting a newline and I got:\n";
	print STDERR "'$_'\n";
	die 'quitting';
    }
}

close(VOL);

# done parsing the list of volumes

# send out Zephyr notification

if (@zwrite && !$dontdoit) {
    local($spart) = $spart || '*';
    system (@zwrite, '-m',
	    "Moving volumes from $shost.$spart to $dhost.$dpart...");
}

# do work

while ($info = shift(@move)) {
    ($vol,$hostport) = split(/\s+/, $info, 2);

    # progressively complain if our tokens will last < 15 minutes.

    if (@life = &token_expiration_alert(5*60, 5*60, $afscell)) {
	print LOG "\n(token expiration in $life[0] seconds-- user notified)\n";
	$alert15 = 1;
    }
    if (!$alert15 && (@life= &token_expiration_alert(15*60, 5*60, $afscell))) {
	print LOG "(token expiration in $life[0] seconds -- user notified)\n";
	$alert15 = 1;
    }

    # create a list of what we need to do.

    local(@todo, $need_bk, $need_ro, @need_rm);

    if ((!$autobackup && $backup{$vol}) ||
	 ($autobackup && ($vol !~ /^n\./) && ($vol !~ /\.nb$/))) {
	$need_bk = 1;
	push(@todo, "$vol needs to be backed up.");
    }

    if (!$norelease && $replicated{$vol}) {
	$need_ro = 1;
	push(@todo, "New replication site for $vol needs to be added.");
	push(@todo, "$vol needs to be released.");
    }

    if (!$keepro && $replicated{$vol}) {
	local($oldsites) = $replicated{$vol};
	chop($oldsites);	# comma-separated list of old replication sites

	@need_rm = split(/,/, $oldsites);
	for $site (@need_rm) {
	    push(@todo, "RO site for $vol on $site needs to be removed.");
	}
    }

    # move the volume

    $QUIT && do wrapup("Some volumes have not been moved.");
    ($debug>2) && do prstatus("Some volumes have not been moved.");

    do run_cmd ("vos move $info $dhost $dpart $cell -verbose");

    # back it up if needed

    if ($need_bk) {
	$QUIT && do wrapup(@todo);
	($debug>1) && do prstatus(@todo);

	shift(@todo);
	delete $backup{$vol};

	do run_cmd ("vos backup $vol $cell -verbose");
    }

    # replicate and release if needed.
    if ($need_ro) {
	$QUIT && do wrapup(@todo);
	($debug>1) && do prstatus(@todo);

	shift(@todo);
	delete $replicated{$vol};

	do run_cmd ("vos addsite $dhost $dpart $vol $cell -verbose");

	$QUIT && do wrapup(@todo);
	($debug>1) && do prstatus(@todo);

	shift(@todo);

	do run_cmd ("vos release $vol $cell -verbose");
    }

    # remove old RO sites.
    while (@need_rm) {
	local($site) = shift(@need_rm);
	$QUIT && do wrapup(@todo);
	($debug>1) && do prstatus(@todo);

	shift(@todo);
	do run_cmd ("vos remove $site $vol.readonly $cell -verbose");
    }
}

# warn about "lost" back-up volumes

for $vol (keys %backup) {
    if (!$autobackup) {
	print "Warning: couldn't find $vol to back up (consistency error?)\n";
	print LOG "!!! couldn't find $vol to back up (consistency error?)\n";
    } else {
	print "Warning: $vol used to be backed up, but no longer is.\n";
	print LOG "!!! $vol used to be backed up, but no longer is.\n";
    }
}

# deal with replication sites from different partitions

for $vol (keys %replicated) {
    local(@todo, $need_ro, @need_rm);

    # create a list of what we need to do.

    if (!$norelease) {
	$need_ro = 1;
	push(@todo, "New replication site for $vol needs to be added.");
	push(@todo, "$vol needs to be released.");
    }

    if (!$keepro) {
	local($oldsites) = $replicated{$vol};
	chop($oldsites);	# comma-separated list of old replication sites

	@need_rm = split(/,/, $oldsites);
	for $site (@need_rm) {
	    push(@todo, "RO site for $vol on $site needs to be removed.");
	}
    }

    # replicate and release if needed.
    if ($need_ro) {
	$QUIT && do wrapup(@todo);
	($debug>1) && do prstatus(@todo);

	shift(@todo);
	delete $replicated{$vol};

	do run_cmd ("vos addsite $dhost $dpart $vol $cell -verbose");

	$QUIT && do wrapup(@todo);
	($debug>1) && do prstatus(@todo);

	shift(@todo);

	do run_cmd ("vos release $vol $cell -verbose");
    }

    # remove old RO sites.
    while (@need_rm) {
	local($site) = shift(@need_rm);
	$QUIT && do wrapup(@todo);
	($debug>1) && do prstatus(@todo);

	shift(@todo);
	do run_cmd ("vos remove $site $vol.readonly $cell -verbose");
    }
}

# wrap-up

do wrapup();

sub wrapup {
    local(@oops) = @_;

    if (@oops) {
	print <<"EndOfWarning";

-----------------------------------------------------
Warning: the move was interrupted before it finished.

This shouldn't be catastrophic (I waited on vos to end), but please
make sure everything is OK and happy.  In particular, you may want to
check the following list of potential problems:
-----
EndOfWarning

        print join("\n", @oops, "-----\n");

	print LOG "*** ended before everything was wrapped up.\n";
	print LOG "*** potential problem topics:\n";
        print LOG join("\n", @oops, '');
	if (@zwrite && !$dontdoit) {
	    local($spart) = $spart || '*';
	    system (@zwrite, '-m',
		    "Done moving volumes from $shost.$spart to $dhost.$dpart:".
		    " POSSIBLE PROBLEMS!");
	}
    } else {
	print "\nDone!\n";
	if (@zwrite && !$dontdoit) {
	    local($spart) = $spart || '*';
	    system (@zwrite, '-m',
		   "Done moving volumes from $shost.$spart to $dhost.$dpart.");
	}
    }

    close(LOG);
    if (!$dontdoit) {
	print "\nLog of $afsop performed operations stored in $logfile.\n";
    }
    exit(0);
}

# send out Zephyr notification

sub prstatus {
    print join("\n! ", '! ', @_, "\n");
}

#*#*#*# (top level)     #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#

### Inlined files:
###       ./volmove.pl
###       /afs/athena/user/b/e/bert/project/volmove/agetopt.pl
###       /afs/athena/user/b/e/bert/project/volmove/tokens.pl
### Generated on Fri May  3 12:24:45 US/Eastern 1996
