#
# Copyright (c) 1990,1991,1992,1993 The Ohio State University.
# All rights reserved.
#
# Redistribution and use in source and binary forms are permitted
# provided that: (1) source distributions retain this entire copyright
# notice and comment, and (2) distributions including binaries display
# the following acknowledgement:  ``This product includes software
# developed by The Ohio State University and its contributors''
# in the documentation or other materials provided with the distribution
# and in all advertising materials mentioning features or use of this
# software. Neither the name of the University nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#

#
# backup-dump - wrapper for doing backups with BSD dump.  Deals with dump 
# specific things so that do-backups can be more generic.
#
# History:
# 12/16/90 SMR	Created from backup-gtar.perl.
#

$chain =	0;
$level =	0;
$directory =	".";

require 'global.defs';
require 'local.defs';
require $yagrip_perl;
require $backuplib_perl;
require $unctime_perl;
require $ctime_perl;
require $lib_deadman_perl if $use_deadman_stuff;

$options = "c:l:f:d:F:Db:m:T:r:";
$usage = 
"usage: backup-dump [-c chain] [-l level] [-f host:tape] [-d debuglevel]
    [-F files] [-D] [-b buid#] [-T timeout] directory
    -c chain		chain number
    -l level		level number (or name)
    -f host:tape	host and tape to use
    -d debuglevel	print debugging messages
    -F files		where to log dump directory (NYI)
    -D			we're archiving to disk, not tape
    -b buid#		backup id number for this backup
    -T timeout		timeout for this backup (in seconds)
    -r runid		ID of this backup run
    directory		which directory to back up\n";

&init();

#
# Invoke the deadman watcher.  Use the given timeout, and die if any
# of RUNID, HOST.rRUNID or HOST.bBUID appear in $deadman_dir.
#
&deadman($timeout, $opt_r, "$hostname.r$opt_r", "$hostname.b$buid") 
  if $use_deadman_stuff;

if ($config_name ne "") {
    $dumpdates = "/etc/dumpdates.$config_name";
} else {
    $dumpdates = "/etc/dumpdates";
}

&setup_dumpdates();

    #
    # Get the info about the latest lower level dump from this dumpdates 
    # file. 
    #
($last_timestamp, $last_date, $last_level) = &last_dump($device, $level);

print "backup-dump: last level $last_level, date $last_date, ts $last_timestamp\n" 
  if $opt_d & $DEBUG_INFO;

if ($use_chain_files && 
    ! &check_funky_file($directory, $chain, $level, $last_level, $last_timestamp)) {
    &clean_up_dd();
    exit(0);
}

$now_date = &getdate();
$now_timestamp = time;

#
# Update from_date, to_date in this hosts's BU DB
#
print "backup-dump: update dates in per host backup DB\n" if $opt_d & $DEBUG_INFO;

%replace = ();
%replace = ("fromdate",	$last_timestamp,
	    "todate",	$now_timestamp);
if (&update_db($backup_host_file . $hostname, $backup_host_file . $hostname,
	       $buid,"backup_host", $DB_UPDATE, *replace, *add, *result)) {
    &sigh("$0: cannot update DB entry for $buid in backup host file for $hostname\n");
}

    #
    # How to deal with the "file" list with dump...
    #
undef $opt_F;			# ignore -F for now.
if (defined($opt_F)) {
    $toc_flag = "a";
    $toc_file = $opt_F;
} else {
    $toc_flag = "";
    $toc_file = "";
}

    #
    # Fork and execute the command string, and wait for the results.  If 
    # the command failed, die now (and don't update the tardates file).
    #

    #
    # If we are in parallel mode, save STDOUT, close it and reopen it to 
    # shove the archive through compress and into the archive file.
    #
if (defined($opt_D)) {
    open(SAVEOUT, ">&STDOUT");	# save STDOUT, 
    close(STDOUT);

    $cmd = "| $compress_prog > $opt_f.Z";
    print stderr "backup-dump: open '$cmd'\n" if $opt_d & $DEBUG_SYSTEM;

    if (! open(STDOUT, $cmd)) {
	&clean_up_dd();
	die "error (backup-dump): can't pipe archive to compress";
    }
}

    #
    # Prepare the command line for running dump.
    #
if (defined($opt_D)) {
    $archive_file = "-";
} else {
    ($tape_user, $tape_host, $tape_dev) = &splittapedev($opt_f);
    if ($tape_host eq $hostname) {
	$archive_file = $tape_dev;
    } else {
	if ($tape_user ne "" && $dump_does_remote_users) {
	    $archive_file = "$tape_user@$tape_host:$tape_dev";
	} else {
	    $archive_file = "$tape_host:$tape_dev";
	}
    }
}

    #
    # Create the command to run dump.  Use dump if we are writing to disk,
    # rdump otherwise.
    #
#if ($ultrix_dump && ! defined($opt_D)) {
#    $dump_extra_keys .= "o";
#}

$command = sprintf("%s %ddsuf%s%s%s $dump_density $dump_length $archive_file $toc_file $dump_extra_args $device",
	   (defined($opt_D) || ($tape_host eq $hostname))
		   ? $dump_prog : $rdump_prog,
	   $level,
	   $toc_flag,
	   $dump_extra_keys);

print stderr "backup-dump: system $command\n" if $opt_d & $DEBUG_SYSTEM;

$result = system($command);

if ($result != $dump_return_status) {
    printf(stderr "error (backup-dump): dump failed with status %d (%d)\n", 
	   $result, 
	   int($result/256));
    &clean_up_dd();
    exit(1);
}

    #
    # If we are in parallel mode, redirect STDOUT back to what it 
    # was before.
    #
if (defined($opt_D)) {
    close(STDOUT);
    open(STDOUT, ">&SAVEOUT");
}

    #
    # fixme: should we remove the dumpdates symlink, or leave the link alone, 
    # or what?
    #
&clean_up_dd();

exit(0);


#
# Need to clean up the dumpdates file if we are interrupted.
#

sub bd_handle_interrupts {
    local($sig) = @_;

    &clean_up_dd();
    &handle_interrupts($sig);
}

#
# Deal with command line args and do init stuff
#
sub init {

    #
    # Make sure that we clean up after ourselves if someone wants to kill us.
    #
    $SIG{'INT'} = 'bd_handle_interrupts';
    $SIG{'HUP'} = 'bd_handle_interrupts';
    $SIG{'TERM'} = 'bd_handle_interrupts';
    $SIG{'QUIT'} = 'bd_handle_interrupts';

    #
    # deal with args
    #	default for tapehost and tapedev is scarecrow and /dev/rst1 for now
    #
    &read_config();		# Read backup.config file

    &getopt($options) || die $usage;

    die $usage if $#ARGV != 0;

    if (! defined($opt_c) || ! defined($opt_l) || ! defined($opt_f) 
	|| ! defined($opt_b) || ! defined($opt_r)) {
	die "error (backup-dump): -c, -l, -f, -b and -r are required options (oxymoron?)\n";
    }

    $directory = $ARGV[0];
    $chain = $opt_c;
    $level = $opt_l;
    $buid = $opt_b;
    $ENV{"BUID"} = $buid;

    if (! defined($opt_T)) {
	$timeout = 0;
    } else {
	$timeout = $opt_T;
    }

    #
    # Convert $directory into a $device from /etc/fstab (or /etc/checklist).
    #
    if (($device = &cvt_to_device($directory)) eq "") {
	&sigh("backup-dump: can't convert '$directory' into a device name for dump\n");
    }

    print "backup-dump: directory $directory, device $device\n" if $opt_d & $DEBUG_INFO;
}

#
# setup the symlinks to the right dumpdates file, lock it.
#
sub setup_dumpdates {
    # 
    # If there's only one chain, and config_name is "", and 
    # $only_one_dumpdates is 1, then don't do anything to the 
    # dumpdates file.
    #
    return if ($num_chains == 1 && $config_name eq "" 
      && $only_one_dumpdates);

    #
    # Lock the dumpdates file, switch the links, get dumpdates.chain 
    # ready.
    #
    die "error (backup-dump): can't lock $dump_lock on $hostname from $0, timed out at"
      if &lock($dump_lock) != $LOCK_OK;

    #
    # Urch.  What a mess.  If /etc/dumpdates doesn't exist, create an empty 
    # one.  Rename it to dumpdates.save.  Create a link from dumpdates to
    # dumpdates.$chain.
    #
    if (! -f "/etc/dumpdates") {
	if (! -f "/etc/dumpdates.save") {
	    open(DD, ">/etc/dumpdates.save");
	    close(DD);
	    chmod 0755, "/etc/dumpdates.save";
	}
    } else {
	unlink("/etc/dumpdates.save");
	if (! rename("/etc/dumpdates", "/etc/dumpdates.save")) {
	    warn "warning (backup-dump): can't rename /etc/dumpdates to /etc/dumpdates.save\n";
	}
    }

    if (! -f "$dumpdates.$chain") {
	system("$cp_prog /etc/dumpdates.save $dumpdates.$chain");
    }

    if ($dumpdates_by_link) {
	$result = link("$dumpdates.$chain", "/etc/dumpdates");
    } else {
	$result = system("$cp_prog $dumpdates.$chain /etc/dumpdates");
	$result = ($result == 0);
    }

    if ($result != 1) {
	warn "error (backup-dump): can't make link /etc/dumpdates to $dumpdates.$chain\n";
	&clean_up_dd();
	exit(1);
    }
}

#
# clean up dumpdates, unlock it
#

sub clean_up_dd {
    local($result);

    # 
    # If there's only one chain, and config_name is "", and 
    # $only_one_dumpdates is 1, then don't do anything to the 
    # dumpdates file.
    #
    return if ($num_chains == 1 && $config_name eq "" 
      && $only_one_dumpdates);

    if ($dumpdates_by_link) {
        unlink("/etc/dumpdates");
        if (link("/etc/dumpdates.save", "/etc/dumpdates") != 1) {
	    warn "warning (backup-dump): couldn't restore dumpdates\n";
        }
    } else {
	$result = system("$cp_prog /etc/dumpdates $dumpdates.$chain");
	if ($result != 0) {
	    print stderr "warning (backup-dump): couldn't save dumpdates to $dumpdates.$chain\n";
	}
	$result = system("$cp_prog /etc/dumpdates.save /etc/dumpdates");
	if ($result != 0) {
	    print stderr "warning (backup-dump): couldn't restore /etc/dumpdates from /etc/dumpdates.save\n";
	}
    }

    &unlock($dump_lock);
}

#
# cvt_to_device - converts a file system name (hopefully a mount point) into 
# its associated device name using /etc/fstab.  Need to convert it into RAW 
# device name for speed and since dump maintains dumpdates with RAW name.
#

sub cvt_to_device {
    local($name) = @_;
    local($device, $directory, $type);

    if (! open(FSTAB, $fstab_file_name)) {
	warn "warning (backup-dump): cvt_to_device: can't open $fstab_file_name\n";
	return("");
    }

    while (<FSTAB>) {
	chop;
	($device, $directory, $type) = &read_fstab_entry($_);

	if ($name eq $directory) {
	    close(FSTAB);

	    $device = &cvt_raw($device);
	    return($device);
	}
    }

    close(FSTAB);

    return("");
}

#
# last_dump gets info on the latest lower level dump...
#
# returns: (last_timestamp, last_date, last_level)
#
# Read /etc/dumpdates, looking for entries that match this device and whose 
# levels are lower than $level.  Of these, pick the most recent and return the
# timestamp, date and level.
#

sub last_dump {
    local($device, $level) = @_;
    local($d_device, $d_level, @d_date, $d_date, $d_ts);
    local($latest_date, $latest_ts, $latest_level);

    $latest_ts = -1;
    $latest_date = "";
    $latest_level = -1;

    open(DUMPDATES, "/etc/dumpdates") ||
      return((-1, "", ""));

    while (<DUMPDATES>) {
	chop;
	($d_device, $d_level, @d_date) = split;

	if ($d_device eq $device && ($d_level+0 < $level+0)) {
	    $d_date = join(' ', @d_date);
	    $d_ts = &unctime($d_date);

	    if ($d_ts > $latest_ts) {
		$latest_ts = $d_ts;
		$latest_date = $d_date;
		$latest_level = $d_level;
	    }
	}
    }

    close(DUMPDATES);

    $latest_ts = 0 if $level+0 == 0;
    ($latest_ts, $latest_date, $latest_level);
}

