#!/usr/bin/perl
#
#  Copyright (c) 2004, SWITCH - Teleinformatikdienste fuer Lehre und Forschung
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#	 this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#	 this list of conditions and the following disclaimer in the documentation
#	 and/or other materials provided with the distribution.
#   * Neither the name of SWITCH 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#  POSSIBILITY OF SUCH DAMAGE.
#
#  $Author: phaag $
#
#  $Id: NfSenRC.pm 22 2007-11-20 12:27:38Z phaag $
#
#  $LastChangedRevision: 22 $

package NfSenRC;

use strict;
use warnings;
use NfSen;
use Log;

# public map for collector mappings - more may be added in future
our %CollectorMap = ( 
	'netflow'	=> 'nfcapd',
	'sflow'		=> 'sfcapd',
);

sub StartCollector {
	my $ident = shift;

	print " $ident";
	my $type		= exists $NfConf::sources{$ident}{'type'} ? $NfConf::sources{$ident}{'type'}: 'netflow';
	my $collector	= $CollectorMap{$type};
	my $port 		= $NfConf::sources{$ident}{'port'};
	if ( $port == 0 ) {
		print "[no collector]";
		return;
	}
	my $uid			= $NfConf::USER;
	my $gid 		= $NfConf::WWWGROUP  ? "$NfConf::WWWGROUP"   : "";
	my $buffer_opts = $NfConf::BUFFLEN ? "-B $NfConf::BUFFLEN"   : "";
	my $subdirlayout = $NfConf::SUBDIRLAYOUT ? "-S $NfConf::SUBDIRLAYOUT" : "";
	my $optargs     = exists $NfConf::sources{$ident}{'optarg'} ? $NfConf::sources{$ident}{'optarg'} : '';
	my $profiledir	= "$NfConf::PROFILEDATADIR/live/$ident";
	my $pidfile	 	= "$NfConf::PIDDIR/$ident.pid";

	my $pid = CollectorStatus($ident);
	if ( $pid > 0 ) {
		print ": a collector with pid[$pid] is already running";
		return;
	}
	my $args 		= "-w -D -I $ident -p $port -u $uid -g $gid $buffer_opts $subdirlayout -l $profiledir -P $pidfile $NfConf::ZIPcollected $optargs";
	#print "Run: $NfConf::PREFIX/nfcapd $args\n";

	system("$NfConf::PREFIX/$collector $args");
	my $exit_value  = $main::child_exit >> 8;
	my $signal_num  = $main::child_exit & 127;
	my $dumped_core = $main::child_exit & 128;
	if ( $exit_value != 0 ) {
		print "$collector exec error: exit: $exit_value, signal: $signal_num, coredump: $dumped_core\n";
	} else {
		my $dowait = 5;
		while ( $dowait && ! -f $pidfile ) {
			sleep 1;
			$dowait--;
		}
		if ( $dowait ) {
			$pid = CollectorStatus($ident);
			print "[$pid]";
		} else {
			print ": collector did not start - see logfile"
		}
	}

} # End of StartCollector

sub StopCollector {
	my $ident = shift;

	print " $ident:";
	my $pid = CollectorStatus($ident);
	if ( $pid == 0 ) {
		print "[no collector]";
		return;
	} 

	if ( $pid == - 1 ) {
		print "[collector died unexpectedly]";
		return;
	}

	print "[$pid]";
	kill 'TERM', $pid || warn "Can't signal nfcapd: $! ";
	my $timeout = 10;
	while ( $timeout && -f "$NfConf::PIDDIR/$ident.pid") {
		print ".";
		sleep 1;
		$timeout--;
	}
	if ( -f "$NfConf::PIDDIR/$ident.pid") {
		print " Process [$pid] does not want to terminate!\n";
	}

} # End of StopCollector

sub NfSen_start {

	if ( ! -d "$NfConf::PIDDIR" ) {
		print "PIDDIR '$NfConf::PIDDIR' not found! NfSen installation problem!\n";
		return;
	}

	# Check if NfSen is already running
	if ( -f "$NfConf::PIDDIR/nfsend.pid" ) {
		open PID, "$NfConf::PIDDIR/nfsend.pid" || 
			die "Can't read pid file '$NfConf::PIDDIR/nfsend.pid': $!\n";
		my $pid = <PID>;
		chomp $pid;
		close PID;
		if ( kill( 0, $pid) == 1  ) {
			print "NfSen is already running!\n";
			return;
		} else {
			print "Unclean shutdown - run stop sequence first to clean up!\n";
			NfSen_stop();
		}
	}

	# Delete all possible remains from old runs.
	NfSen::CleanOrphans();

	print "Starting nfcapd:";
	foreach my $source ( keys %NfConf::sources ) {
		StartCollector($source);
		select(undef, undef, undef, 0.2);
	}
	print ".\n";

	print "Starting nfsend";
	system "$NfConf::BINDIR/nfsend";
	my $exit_value  = $main::child_exit >> 8;
	my $signal_num  = $main::child_exit & 127;
	my $dumped_core = $main::child_exit & 128;
	if ( $exit_value != 0 ) {
		print ": exec error: exit: $exit_value, signal: $signal_num, coredump: $dumped_core\n";
	}
	print ".\n";

} # End of NfSen_start

sub NfSen_stop {

	print "Shutdown nfcapd:";
	foreach my $source ( keys %NfConf::sources ) {
		StopCollector($source);
	}
	print ".\n";

	print "Shutdown nfsend:";
	if ( -f "$NfConf::PIDDIR/nfsend.pid" ) {
		open PID, "$NfConf::PIDDIR/nfsend.pid" || 
			die "Can't read pid file '$NfConf::PIDDIR/nfsend.pid': $!\n";
		my $pid = <PID>;
		chomp $pid;
		close PID;
		print "[$pid]";
		if ( kill( 0, $pid) == 0  ) {
			print "[No such process]";
			unlink "$NfConf::PIDDIR/nfsend.pid";
		} else {
			kill 'TERM', $pid || warn "Can't signal nfsend: $! ";
			my $timeout = 300;
			while ( $timeout && -f "$NfConf::PIDDIR/nfsend.pid") {
				print ".";
				sleep 1;
				$timeout--;
			}
		}
		if ( -f "$NfConf::PIDDIR/nfsend.pid") {
			print " Process [$pid] does not want to terminate!\n";
		}
	} else {
		print "[no pid file found!]";
	}
	print "\n";

} # End of NfSen_stop

sub NfSen_status {

	print "NfSen version: $$NfSen::hints{'version'}\n";
	if ( $NfConf::SIMmode ) {
		print "NfSen status: Simulation mode\n";
	} else {
		print "NfSen status:\n";
	}
	foreach my $source ( keys %NfConf::sources ) {
		print "Collector for '$source'\t";
		my $pid = CollectorStatus($source);
		if ( $pid == -1  ) {
			print "died for unknown reason.";
		} elsif ( $pid == 0 ) {
			print "is not running.";
		} else {
			print "is running: $pid.";
		}
		print "\n";
	}

	print "nfsen daemon: ";
	if ( -f "$NfConf::PIDDIR/nfsend.pid" ) {
		open PID, "$NfConf::PIDDIR/nfsend.pid" || 
			die "Can't read pid file '$NfConf::PIDDIR/nfsend.pid': $!\n";
		my $pid = <PID>;
		chomp $pid;
		close PID;
		print " pid: [$pid] ";
		if ( kill( 0, $pid) == 0  ) {
			print "died for unknown reason.";
			unlink "$NfConf::PIDDIR/nfsend.pid";
		} else {
				print "is running.";
		}
	} else {
		print "is not running.";
	}
	print "\n";

} # End of NfSen_status

sub CollectorStatus {
	my $ident = shift;

	my $pidfile = "$NfConf::PIDDIR/$ident.pid";
	if ( -f "$pidfile" ) {
		open PID, "$pidfile" || 
			die "Can't read pid file '$pidfile': $!\n";
		my $pid = <PID>;
		chomp $pid;
		close PID;
		if ( kill( 0, $pid) == 0  ) {
			unlink "$pidfile";
			return -1;
		} else {
			return $pid;
		}
	} else {
		return 0;
	}

} # End of CollectorStatus

sub NfSen_reload {

	if ( -f "$NfConf::PIDDIR/nfsend.pid" ) {
		print "Restart nfsend:";
		open PID, "$NfConf::PIDDIR/nfsend.pid" || 
			die "Can't read pid file '$NfConf::PIDDIR/nfsend.pid': $!\n";
		my $pid = <PID>;
		chomp $pid;
		close PID;
		print "[$pid]\n";
		kill 'USR1', $pid || warn "Can't restart nfsend: $! ";
	} else {
		print STDERR "No pid file found for nfsend - please restart manually\n";
	}

} # End of NfSen_reload

1;
