#!/usr/bin/perl
# NAME
# 	node_sf_collect - collect data for a new support file
# 
# SYNOPSYS
#  	node_sf_collect [-HT] [-m caller] [-C config_file] [-c collect_dir] [-P password]
#
# DESCRIPTION
#	sf_collect collects and formats data for creating node support file. 
#	Data is stored in a separate directory its subdirectories. Optionally 
#	script can do the actual zipping data into a resulting support file.
#	node_sf_collect should be provided with configuration XML file. By default
#	it reads configuration from '$APL/conf/etc/sf_collect.xml'.
#	Script should be run only from 'apl' accounts.
#
# OPTIONS
#	-c collect_dir
#		Specfy eollection directory where temporary files will be placed. Should be 
#		removed after collecting is finished
#
#	-C config_file    
#		Specify configuration file to use; the default is $APL/conf/etc/sf_collect.xml
#
#	-m caller
#	 	caller can be 'auto' or 'user'
#
#	-P password
#		Specify password for encrypting resulting zip file
#
#	-H	Generate HM images for all devices and include them in the collection
#
#	-T	Perform cleanup - terminate running node_sf_collect process, remove temporary files
#		and exit
#       -b
#               Collect in background. node_sf_collect prints name of newly created support files and
#               detaches from controlling terminal. 

use strict;
use Data::Dumper;
use Getopt::Std;
use File::Basename;
use Log::Log4perl "get_logger";
use POSIX ();
use SKM::SF;
use SKM::Common;

my $APL = $ENV{APL} || '/opt/sarch';
eval {
    require "$APL/common/bin/logger.patrol";
};

# Global vars
my $SF_COLLECT = (split(/\//,$0))[-1]; # the actual name of the prog
my $BACKGROUND_LOG = "$ENV{APL_VAR}/tmp/node_sf_collect.log";
my $dbh;
my $Log = get_logger('NEXTCAM::SDI::REV');
my $ConfigFile = "$APL/conf/etc/sf_collect.xml";
my $Background = 0;
my $Caller = 'user'; # auto/user
my $TmpDir = "$APL/var/sf/tmp/node";
my $CollectDir;
my $SFDir = "$APL/var/sf/node";
my $SFID;
my $TimeoutRoot = 60;
my $Password; # Password for zip file
my $START_TIME = time;

# Decrease process priority
my $PRIO_PROCESS = 0;
my $prio = getpriority($PRIO_PROCESS, $$);
setpriority($PRIO_PROCESS, $$, $prio + 10);

# Signal Handlers
$SIG{TERM} = sub {
    my ($signame) = (@_);
    $Log->error("Got SIG${signame}. Stopping...");
    SFExitSignal;
    exit 1;
};

# prepare()
# Make all the necessary preparations for collecting stats
sub prepare {
    # Process command line
    my %opts;
    getopts('THbm:c:C:P:', \%opts);
    $Caller = $opts{m} || 'user';
    $Background = 1 if $opts{b};
    $ConfigFile = $opts{C} if $opts{C} && (-f $opts{C});
    $CollectDir = $opts{c} if $opts{c};
    $Password = $opts{P} if defined($opts{P});
    
    # Do cleanup and exit if -T option is specified
    if($opts{T}) {
        $Log->warn("Terminate collector processes");
	my $pid = ReadPid;
	kill(15, $pid) if $pid;
	`sudo $APL/conf/bin/node_sf_collect.root -T`;
	`ps -au apl | grep node_sf_collect | grep -v grep | grep -v $$ | xargs kill -TERM 2>/dev/null`;
	# Rename all SF remained in 'inprogress' state
	select(undef,undef,undef,0.5);
	opendir(DH, $SFDir);
	my @inpr = grep {/\.INPR$/} readdir DH;
	closedir DH;
	foreach my $inpr (@inpr) {
	    my ($base) = $inpr=~/^(.+)\.INPR$/;
	    rename("$SFDir/$inpr","$SFDir/${base}.TERM") if $base;
	}
	POSIX::_exit(0);
    }
    
    # Check for running instances of node_sf_collect
    if (CheckPid) {
	$Log->error("Detected concurrent run. Exiting");
	exit 1;
    } else {
	WritePid;
    }
    
    # Create directory for storing support files if doesn't exist
    mkdir($SFDir) or die "Failed to create support files firectory $SFDir: $!" unless -d $SFDir;
    # Create directory for temporary files
    mkdir($TmpDir) or die "Failed to create collect dir $TmpDir: $!" unless -d $TmpDir;
    # Cleanup TmpDir - node_sf_collect supposed to have a single instance running
    system("rm -rf $TmpDir/*");

    SFInit(
	Log => $Log, 
	Password => $Password, 
	CollectDir => $CollectDir,
	Config => $ConfigFile,
	Caller => $Caller
    );
    $CollectDir = SFCollectDir unless $CollectDir;
    $SFID = SFID;
    
    # Daemonize if 'Background' option set
    if ($Background) {
	print "$SFID\n";
	open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
        open STDOUT, ">$BACKGROUND_LOG" or die "Can't write to $BACKGROUND_LOG: $!";
        defined(my $pid = fork)   or die "Can't fork: $!";
        POSIX::_exit(0) if $pid;
        WritePid;
        POSIX::setsid             or die "Can't start a new session: $!";
        open STDERR, '>&STDOUT'   or die "Can't dup stdout: $!";
    }
}

# collect_root()
# Starts another instance of the script to collect root-only acessible files
sub collect_root {
    my $cmd_opts = "-i $SFID -c '$CollectDir' -t $TimeoutRoot -l $SKM::SF::DULimit";
    print "sudo $APL/conf/bin/node_sf_collect.root $cmd_opts\n";
    system("sudo $APL/conf/bin/node_sf_collect.root $cmd_opts");
    my $exit_code = $? >> 8;
    SFExitIncomplete if $exit_code != 0;
}

# main()
sub main {
    $Log->info("node_sf_collect started at " . gmtime($START_TIME));
    eval {
	prepare();
	SFCollect(1);
	collect_root();
	SFPackage;
    };
    if($@) {
	$Log->error("Critical error occured($@). Script execution will be stopped");
	SFExitFail;
    }
}

main();

# Perform cleanup
END {
    SFComplete;
    RemovePid;
    $? = SFExitStatus;
}
