#!/usr/bin/perl
# usage:
# start daemon using /etc/packetq.conf:
#       pq_tracesplit.pl 

# stop daemon:
#       pq_tracesplit.pl stop

# run in foreground with conf file
#       pq_tracesplit.pl -f  -c my.conf

# stop with custom conf file
#       pq_tracesplit.pl -c my.conf stop




use POSIX;
use POSIX qw(setsid);
use Sys::Syslog qw(:DEFAULT setlogsock);  # default set, plus setlogsock()
use File::Path; 
use File::Copy;
use Getopt::Std;
use Data::Dumper;
use strict;
no  strict "subs";

my $conffile = "/etc/packetq.conf";
my %opts;
getopts('fc:', \%opts);
if (defined $opts{c})
{
   $conffile = $opts{c};
}
my $foreground = undef;
$foreground = 1 if (defined $opts{f});

openlog('pqcollector','pid,perror','LOG_USER');

######### read config file

my %config;
open(CONFIG,$conffile) or die "error reading config file $conffile exiting";
while (<CONFIG>) 
{
    chomp;
    next if /^\s*\#/;
    next unless /=/;
    my ($key, $variable) = split(/=/,$_,2);
    $variable =~ s/(\$(\w+))/$config{$2}/g;
    $config{$key} = $variable;
}
close CONFIG;

my $pidfile  = $config{'pidfile'};
my $logfile  = "/dev/null";

##### start daemon

if (-e $pidfile)
{
    open (PFILE, $pidfile);
    my $pidfromfile = <PFILE>;
    close PFILE;

    if (($pidfromfile =~ /[0-9]+/) && kill( 0, $pidfromfile))
    {            
        if ($ARGV[0] eq 'stop')
        {
            syslog 'info',"Stopping daemon pid: $pidfromfile\n";
            while (kill( 0, $pidfromfile))
            {
                kill( - SIGQUIT, $pidfromfile);
                sleep(1);
            }
            exit;
        }
        else
        {
            syslog LOG_INFO,"Pid file $pidfile exist and the program ($pidfromfile) is running ! exiting ...\n"; 
        }
        exit;
    }
    else 
    {
        unlink($pidfile);
    }
}

if ($ARGV[0] eq 'stop')
{
    syslog 'info',"Cannot stop packetq.pl as it's not running\n";
    exit;
}
&daemonize() unless defined $foreground;
open FILE, ">$pidfile" or die "unable to open pidfile : $pidfile $!";
print FILE $$."\n";
close FILE;

##### catch signals

my $keep_going = 1;
$SIG{HUP}  = sub { print("Caught SIGHUP:   exiting gracefully\n"); $keep_going = 0; };
$SIG{INT}  = sub { print("Caught SIGINT:   exiting gracefully\n"); $keep_going = 0; };
$SIG{QUIT} = sub { print("Caught SIGQUIT:  exiting gracefully\n"); $keep_going = 0; };
$SIG{TERM} = sub { print("Caught SIGTERM:  exiting gracefully\n"); $keep_going = 0; };

########## start collection
foreach my $k (keys %config)
{
    print $k."=".$config{$k}."\n";
    $config{$k}   =~ s/^\"(.*)\"$/$1/;
}

my $interval   = $config{'interval'};
my $interface  = $config{'interface'};
my @interfaces = split(/,/,$interface);
my $filter     = $config{'filter'};
my $server     = $config{'server'};
my $destdir    = $config{'destdir'};

my $stime = floor(time()/$interval) * $interval + $interval;

syslog LOG_INFO,"Starting packetq collector daemon (pid:".$$.") destdir: $config{'destdir'}\n";

my @tdpid;
my @tspid;
my $ifcnt = 0;
foreach my $if (@interfaces)
{
    if ($config{'bsdpromischack'} eq "YES")
    {
        my $pid;
        #my $tcpdumpcmd="$config{'tcpdump'} -i $if port 100 2>/dev/null";
        my $tcpdumpcmd="$config{'tcpdump'} -i $if port 100";
        $pid = spawn ($tcpdumpcmd);
        print "tcp pid $pid";
        if ($pid == 0)
        {
            syslog LOG_ERROR,"Cannot run $tcpdumpcmd exiting \n";
            exit;
        }
        syslog LOG_INFO,"Keeping the interface ($if) in promisc mode by letting tcpdump ($pid) listen on port 100 \n";
        @tdpid[$ifcnt] = $pid;
    }

    my $tracesplitcmd = $config{'tracesplit'}." pcapint:$if -s $stime -z $config{'compression_level'} -i $interval -f \"$filter\" pcapfile:$destdir/$server-$if";
    my $tspid = spawn($tracesplitcmd);
    print "ts pid $tspid";
    if ($tspid == 0)
    {
        syslog LOG_ERROR,"Cannot run $tracesplitcmd exiting \n";
        exit;
    }
    syslog LOG_INFO,"Starting tracesplit \"$tracesplitcmd\"(pid:$tspid)\n";
    @tspid[$ifcnt] = $tspid;

    $ifcnt++;
}

########## infinite loop
reaper();

while($keep_going == 1)
{
    foreach my $if (@interfaces)
    {
        opendir(DIR, $destdir) or last;
        my @files; 

        while (my $file = readdir(DIR)) 
        {
            # Use a regular expression to ignore files beginning with a period
            next if ($file =~ m/^\./);
            next unless ($file =~ m/^$server-$if.*/);
            push(@files,$file);
        }
        @files= sort(@files);
        if (@files>1)
        {
            pop @files;
            #print "files: \n".join("\n",@files)."\n";
            foreach my $f (@files)
            {
                if($f =~ /^$server-$if-(.*)\.gz/)
                {
                    ##my ($sec, $min, $hour, $day,$month,$year) = (localtime($1))[0,1,2,3,4,5,6]; 
                    my ($sec, $min, $hour, $day,$month,$year) = (gmtime($1))[0,1,2,3,4,5,6]; 
                    $year+=1900;
                    $month++;
                    $sec    = "0".$sec   if $sec  <10;
                    $min    = "0".$min   if $min  <10;
                    $hour   = "0".$hour  if $hour <10;
                    $day    = "0".$day   if $day  <10;
                    $month  = "0".$month if $month<10;

                    my $file = "$server-$year$month$day-$hour$min$sec-$if.pcap.gz";
                    my $dir  = "$year/$month/$day/$hour";
                    my $cmd  = $config{command};
                    $cmd     =~ s/%F/$file/g; 
                    $cmd     =~ s/%S/$server/g; 
                    $cmd     =~ s/%I/$if/g; 
                    $cmd     =~ s/%P/$dir/g; 
                    $cmd     =~ s/%Y/$year/g; 
                    $cmd     =~ s/%M/$month/g; 
                    $cmd     =~ s/%D/$day/g; 
                    $cmd     =~ s/%h/$hour/g; 
                    $cmd     =~ s/%m/$min/g; 
                    $cmd     =~ s/%s/$sec/g; 
                    
                    #print "$cmd\n";

                    mkpath "$destdir/$dir";
                    move ("$destdir/$f","$destdir/$dir/$file");
                    spawn($cmd);
                    #print "hello $hour, $min, $sec,-- $day,$month,$year\n";
                    #print "mkdir $dir\n";
					#print "mv $destdir/$f $destdir/$dir/$file\n";
                }
            }
        }
        closedir(DIR);
    } 
    #printf("blipp\n");
    sleep(5);
}

########## exit cleanup

syslog 'info',"Shutting down collector ...\n";
foreach my $pid (@tspid)
{
	if ($pid ne 0)
	{
        print "Stopping tracesplit ($pid)\n";
	    syslog LOG_INFO,"Stopping tracesplit ($pid)\n";
	    kill( - SIGABRT, $pid);
	}
}

foreach my $pid (@tdpid)
{
	if ($pid ne 0)
	{
	    syslog LOG_INFO,"Stopping tcpdump ($pid)\n";
	    kill( - SIGABRT, $pid);
	}
}

syslog LOG_INFO,"removing pidfile\n";
unlink($pidfile);

syslog LOG_INFO,"bye bye\n";
closelog;
exit;

##########  functions

sub spawn 
{
    my $cmd = shift;
    defined(my $pid = fork)   or die "Can't fork: $!";
    if ($pid == 0)
    {
        exec $cmd;
	    syslog LOG_ERROR,"Couldn't run $cmd\n";
        die "Couldn't run $cmd";
    }
    return $pid;
}

sub daemonize 
{
    chdir '/'                 or die "Can't chdir to /: $!";
    defined(my $pid = fork)   or die "Can't fork: $!";
    exit if $pid;
    setsid                    or die "Can't start a new session: $!";
    umask 0;

    open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
    open STDOUT, ">>$logfile" or die "Can't write to $logfile: $!";
    open STDERR, ">>$logfile" or die "Can't write to $logfile: $!";
}

sub reaper {
    my $stiff;
    while ( ($stiff = waitpid(-1, &WNOHANG) ) > 0 ) 
    {
        #print "child $stiff terminated -- status $?";
    }
    $SIG{CHLD} = \&reaper;
}