#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/bin/cdat/cdat-archive.pl 1.6 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2010,2011 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)20    1.6  src/bos/usr/bin/cdat/cdat-archive.pl, cdat, bos720 7/14/11 17:05:07
use strict;
use warnings;
use File::Temp;
use Getopt::Long;
use Fcntl ':flock';	# LOCK_* constants
use XML::LibXML;
use cdat;
use messages;

#
# Constants.
#
my $XMLFILE  = 'cdat.xml';
my $CDAT     = '/usr/bin/cdat';
my $TAR      = '/usr/bin/tar';
my $COMPRESS = '/usr/bin/compress';

#
# Globals.
#
my ($path, $filename);
my @collects;

######################################################################
# Function:	usage
# Purpose:	Display usage.
# Tasks:	Print usage and exit.
# Input:	None
# Output:	None
######################################################################
sub usage
{
    printf(STDERR catgets(MSG_CDAT_ARCHIVE_USAGE,
        "Usage: cdat archive -h\n".
	"       cdat archive [-f File] -p PMR\n".
	"       cdat archive -f File Id\n"));
    exit(1);
}

######################################################################
# Function:	archive_id
# Purpose:	Archive collect for the specified collect id.
# Tasks:	Archive collect.
# Input:	collect id
# Output:	None
######################################################################
sub archive_id
{
    my ($id) = @_;
    my $found = 0;

    foreach my $collect (@collects) {
	my $attr = $collect->getAttribute('id');
	if (defined($attr) && $id eq $attr) {
	    return 1 if (archive_collect($collect) != 0);
	    $found = 1;
	    last;	# there can be only 1 collect with that id
	}
    }
    if (!$found) {
	printf(catgets(MSG_NO_COLLECT_FOUND_FOR_ID,
	    "No collect found for id %s.\n"), $id);
	return 1;
    }
}

######################################################################
# Function:	archive_pmr
# Purpose:	Archive collect for the specified PMR.
# Tasks:	Archive collect.
# Input:	PMR number
# Output:	None
######################################################################
sub archive_pmr
{
    my ($pmr) = @_;
    my $found = 0;

    foreach my $collect (@collects) {
        my @pmrs = $collect->getElementsByTagName('pmr');
        next if (@pmrs == 0);	# no pmr tag for this collect, skip

	# retrieve value of first pmr tag
        my $val = $pmrs[0]->textContent;
	# skip if it does not match specified PMR number
	next if ($pmr ne $val);

        return 1 if (archive_collect($collect) != 0);
	$found = 1;
	# NB: there can be several collects for a given PMR
    }
    if (!$found) {
        printf(catgets(MSG_NO_COLLECT_FOUND_FOR_PMR,
	    "No collect found for PMR %s.\n"), $pmr);
	return 1;
    }
}

######################################################################
# Function:	archive_collect
# Purpose:	Archive a collect.
# Tasks:	Create a compressed archive of a collect.
# Input:	collect XML element
# Output:	None
######################################################################
sub archive_collect
{
    my ($collect) = @_;

    my @locations = $collect->getElementsByTagName('location');
    if (@locations) {
        my $location = $locations[0]->textContent;

	# add the collect directory to the archive
	DEBUG(1, "$TAR -rf $filename -C $path '$location'\n");
	system("$TAR -rf $filename -C $path '$location'");
	if ($? != 0) {
	    printf(STDERR catgets(MSG_CANNOT_ARCHIVE,
	        "Cannot archive %s.\n"), "$path/$location");
	    return 1;
	}
    }
}

######################################################################
# Function:	main
# Purpose:	Entry point of the archive subcommand.
# Tasks:	Parse command line and archive collects matching the
#		query.
# Input:	None
# Output:	None
######################################################################
sub main
{
    my ($rc, $pmr, $node, $id);

    # Parse command line options
    Getopt::Long::Configure('bundling', 'no_ignore_case');
    $rc = GetOptions(
	'h'   => \&usage,
	'f=s' => \$filename,
	'p=s' => \$pmr,
	);
    if (!$rc) {
	usage();
    }

    if (!defined($pmr)) {	# -p not specified
	# check that a collectid is specified
	usage if (@ARGV == 0);
	$id = shift @ARGV;

	# check that a filename is specified too
	usage if (!defined($filename));
    }
    # make sure there are no extra arguments
    usage if (@ARGV != 0);

    # validate PMR number
    if (defined($pmr) && !($pmr =~ m/\d{5,}(,[0-9A-Za-z]{3,}){2}/)) {
	printf(STDERR catgets(MSG_INVALID_PMR,
	    "Invalid PMR '%s', format should be 'PMR#,BRANCH#,COUNTRY#'.\n"), $pmr);
	exit(1);
    }

    $path = cdat::odm_get_path();
    if (!defined($path)) {
	printf(STDERR catgets(MSG_DIR_NOT_DEFINED,
	    "The cdat directory path is not defined.\n".
	    "Please, run 'cdat init' first.\n"));
	exit(2);
    }

    my $fh;
    if (!open($fh, "<", "$path/$XMLFILE")) {
	printf(STDERR catgets(MSG_CANNOT_OPEN,
	    "Cannot open %s.\n"), "$path/$XMLFILE");
	exit(2);
    }
    # Try to acquire read-only access to the XML file (but do not block)
    if (!flock($fh, LOCK_SH | LOCK_NB)) {
        printf(STDERR catgets(MSG_BUSY,
	    "Another instance of the cdat command is running.\n".
	    "Please wait until this instance terminates.\n"));
        exit(2);
    }

    my $parser = XML::LibXML->new();
    if (!defined($parser)) {
        printf(STDERR catgets(MSG_CANNOT_CREATE_XML_PARSER,
	    "Cannot create XML parser.\n"));
        exit(2);
    }

    if (!defined($filename)) {	# implies -p
    	# generate a filename based on the PMR number
        $filename = "$path/$pmr.tar";
    } else {
        # append ".tar" suffix
        $filename .= '.tar';
    }

    my $tempdir = File::Temp::tempdir(CLEANUP => 1);

    # generate the README file which is the output of "show -v"
    if (defined($pmr)) {
	system("$CDAT show -v -p $pmr > $tempdir/README");
    } else {
	system("$CDAT show -v $id > $tempdir/README");
    }
    if ($? != 0) {
	printf(STDERR catgets(MSG_CANNOT_CREATE_README,
	    "Cannot create README file.\n"));
	exit(2);
    }

    # add the README file to the archive
    DEBUG(1, "$TAR -cf $filename -C $tempdir README\n");
    system("$TAR -cf $filename -C $tempdir README");
    if ($? != 0) {
	printf(STDERR catgets(MSG_CANNOT_ARCHIVE,
	    "Cannot archive %s.\n"), "$tempdir/README");
	exit(2);
    }
    unlink("$tempdir/README");

    my $tree = $parser->parse_fh($fh);
    if (!defined($tree)) {
        printf(STDERR catgets(MSG_CANNOT_PARSE,
	    "Cannot parse %s.\n"), "$path/$XMLFILE");
	exit(2);
    }
    my $root = $tree->getDocumentElement;
    @collects = $root->getElementsByTagName('collect');

    my $status;
    if (defined($pmr)) {
        # -p is specified, archive all collects for specified PMR number
	$status = archive_pmr($pmr);
    } else {
        # archive collect by id
	$status = archive_id($id);
    }
    if ($status != 0) {
	unlink($filename);
	exit(2);
    }

    # we can release the cdat.xml lock at that point
    close($fh);

    # finally, compress the archive
    DEBUG(1, "$COMPRESS -F $filename\n");
    system("$COMPRESS -F $filename");
    if ($? != 0) {
	printf(STDERR catgets(MSG_CANNOT_COMPRESS,
	    "Cannot compress %s.\n"), "$filename");
	unlink($filename);
	exit(2);
    }

    printf(catgets(MSG_ARCHIVE_LOC,
        "Compressed archive successfully created at %s.\n"),
	"$filename.Z");
}

main;
