#
# Copyright (c) 1991,1992 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.
#

$options = "d:hp:N:aT:";
$usage = "usage: list-backups [-p pagesize] [-N daysago] [-a] 
  [-T date] [-d debuglevel] [-h] [host[:filesystem]]
    -p pagesize			how many lines on a page of output
    -N daysago			restrict listing to backups within
				this many days
    -a                          show all backups (including errors)
    -T date                     show only backups before that date
    -d debuglevel		display debugging messages
    -h				display this message
    host:filesystem             restrict display to just this host
                                (and file system if given).\n";

format top = 
    run backup chain,
     ID   ID   level   type   date                     tapes
    ---  ----  -----   -----  ------                   -----
.

format STDOUT = 
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$string
.

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

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

    # 
    # Deal with args...we define *_string things to use as args for progs 
    # called from here so that we can pass on the right values (eg, -d
    # or "", etc). 
    #
die $usage if ! &getopt($options);
die $usage if defined($opt_h);

if (defined($opt_p)) {
    $= = $opt_p + 0;		# set the page size
}

if (defined($opt_T)) {
    $upper_time = &parse_date_into_timestamp($opt_T);
} else {
    $upper_time = time;
}

if (defined($opt_N)) {
    $lower_time = $upper_time - $opt_N * $DAYS;
} else {
    $lower_time = 0;
}

    #
    # Read the backup config file...
    #
&read_config();

    #
    # We need the run database to get the chain, level numbers...
    #
&open_db(*RUNDB, $run_db_file, $db_mode) ||
  &sigh("list-backups: can't open the dbm file '$run_db_file'\n");

    #
    # open each of the per-host backup database files in turn...
    #
if ($#ARGV == -1) {
    @bu_host_files = <$backup_host_file*.$dbext>;
} else {
    while ($#ARGV > -1) {
	($host, $fsys) = split(/:/, shift(@ARGV));
	push(@bu_host_files, "$backup_host_file$host.$dbext");
	if ($fsys ne "") {
	    if (defined($file_sys_limits{$host})) {
		$file_sys_limits{$host} .= " $fsys";
	    } else {
		$file_sys_limits{$host} = $fsys;
	    }
	}
    }
}

foreach $budb_name (@bu_host_files) {
    # reset the arrays...
    %fs_list = (); %cache = (); %backup_times = (); %fs_name = ();
    %cache_online = ();

    #
    # Get the corresponding fsys limit
    #
    $fsys_limit = shift(@file_sys_limit);

    #
    # Need to nuke the trailing .$dbext to get the base db name, 
    # and get the host name for other uses.
    #
    @tmp = split(/\./, $budb_name);
    pop(@tmp);			# nuke trailing .$dbext
    $thishost = $tmp[$#tmp];	# host name is last before .$dbext
    $budb_name = join('.', @tmp);

    if (! &open_db(*LIST, $budb_name, $db_mode)) {
        printf stderr "list-backups: can't open the dbm file '$budb_name'\n";
	next;
    }

    #
    # For each backup in this database...read it, cache the printable
    # copy, and store the backup ID in the appropriate list for that 
    # file system.
    #
  bu_entry_loop:
    foreach $buid (&keys_db(*LIST)) {
	# no value - bogus entry, nuke it.
	next bu_entry_loop if ! &read_db(*LIST, $buid, *budb_value, *budb_array); 

	#
	# is it a keeper?  Only if done between lower time limit and upper time limit.
	#
	if ($budb_array{"todate"}+0 < $lower_time ||
	    $budb_array{"todate"}+0 > $upper_time) {
	    next bu_entry_loop;
	}

        #
        # get the chain and level from the run database
        #
	$run = $budb_array{"run"};
	$fs = $budb_array{"fs"};
	($chain, $level) = &get_chain_and_level($run);

	($tape_number, $file_number) = split(/\./, $budb_array{"tflist"});

	#
	# Ignore entries that have been reused or which failed...
	#
	if (! defined($opt_a) && 
	    ((! defined($budb_array{"flags"}) || $budb_array{"flags"} ne "done") &&
	     (! defined($budb_array{"d-flags"}) || $budb_array{"d-flags"} ne "done"))) {
	    next bu_entry_loop;
	}

        #
        # build the printable version
        #
	if ($budb_array{"flags"} eq "done") {
	    $tflist = $budb_array{"tflist"};
	} else {
	    $tflist = $budb_array{"flags"};
	}

	chop($date = &ctime($budb_array{"todate"}));

	$cache{$buid} = sprintf("    %4d %4d  %2d,%-2d   %-5s %s  %s\n",
		     $run,
		     $buid,
		     $chain,
		     $level,
		     $budb_array{"type"},
		     $date,
		     $tflist);

	if ($budb_array{"d-flags"} eq "done") {
	    $cache_online{$buid} =  
	      sprintf("                disk: %s\n", $budb_array{"afile"});
	}

        #
        # If we don't know about this file system, add it to %fs_list and 
        # make a name for its list of backup IDs.
        #
	if (! defined($fs_list{$fs})) {
	    $fs_list{$fs} = 1;
	    $fs_name{$fs} = sprintf("a%d", $count++);
	}

        #
        # store the todate time in the %backup_times array for quicker sorting
        #
	$backup_times{$buid} = $budb_array{"todate"} + 0;

        #
        # and store the backup ID in the appropriate array for this file 
        # system.
        #
	eval(sprintf("push(@%s, $buid)", $fs_name{$fs}));
	if ($@) {
	    warn "warning (list-backups): ", $@;
	    warn "warning: [reading backups for $thishost, as buid $buid, fsys '$fs']\n";
	}
    }

    #
    # ok, we've read through all of the backup entries and sorted them 
    # according to file system.  for each file system, in alphabetical 
    # order, sort the backup IDs into chrono order and print them...
    #
    if (defined($file_sys_limits{$thishost})) {
	@file_systems = split(/ /, $file_sys_limits{$thishost});
    } else {
	@file_systems = sort keys %fs_list;
    }

    foreach $fs (@file_systems) {
        $string = sprintf("%s:%s\n", $thishost, $fs);
	write;

	next if ! defined $fs_name{$fs};

	@backups_for_fs = eval(sprintf("@%s", $fs_name{$fs}));
	if ($@) {
	    warn "warning (list-backups): ", $@;
	    warn "warning: [while dealing with file system $fs]\n";
	}

        foreach $buid (sort chrono @backups_for_fs) {
	    $string = $cache{$buid};
	    write;

	    if (defined($cache_online{$buid})) {
		$string = $cache_online{$buid};
		write;
	    }
	}

	# free the memory used by the list of backups...
	eval(sprintf("@%s = ()", $fs_name{$fs}));
	if ($@) {
	    warn $@;
	    warn "[while freeing memory for fsys $fs]\n";
	}
    }

    &close_db(*LIST, $budb_name);
}

&close_db(*RUNDB, $run_db_file);

exit(0);


    #
    # Get chain, level for a given run ID, using a cache (%run_cache) 
    # to avoid excessive parsing of database entries.
    #
sub get_chain_and_level {
    local($runid) = @_;
    local($value, %array);

    if (! defined($run_cache{$runid})) {
	&read_db(*RUNDB, $runid, *value, *array);
	$run_cache{$runid} = join(',', $array{"chain"}, $array{"level"});
    }

    return(split(/,/, $run_cache{$runid}));
}

