#!/usr/local/bin/perl -w
#
#  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: peter $
#
#  $Id: nfsen 59 2006-08-10 17:47:53Z peter $
#
#  $LastChangedRevision: 59 $

use strict;
use warnings;
use POSIX 'setsid';
use Getopt::Std;
use Sys::Syslog; 
Sys::Syslog::setlogsock('unix');

use POSIX ':signal_h';

######################################
#
# Configuration: 
# The only parameter to set:

use lib "/usr/local/libexec/nfsen";

#
######################################

use NfConf;
use NfSen;
use NfProfile;
use Nfsources;
use NfSenRC;
use Log;

my $VERSION = '$Id: nfsen 59 2006-08-10 17:47:53Z peter $';
my $nfsen_version = "snapshot-20060810";

our(	
	$opt_a, 	# Add a profile
	$opt_c,	 	# Description
	$opt_d, 	# Delete a profile
	$opt_e, 	# Expire time
	$opt_f,		# Read filer from file
	$opt_h,		# Help
	$opt_l, 	# List profile
	$opt_m, 	# Modify a profile
	$opt_q, 	# Quiet output
	$opt_r, 	# Rebuild profile
	$opt_s, 	# Size limit of profile
	$opt_z, 	# Zero profile
	$opt_A, 	# List the name of All profiles available
	$opt_B, 	# Begin of profile
	$opt_E, 	# End of profile
	$opt_F, 	# Force option
	$opt_L, 	# Lock profile
	$opt_P, 	# List all installed frontend plugins
	$opt_R, 	# Reconfigure Nfsen
	$opt_S, 	# ':' separated sources list
	$opt_U, 	# Unlock profile
	$opt_V, 	# Version of nfsen
	$opt_X, 	# Force expire of profile
);

sub usage {

	print "$0 [options]\n",
		"\t-h This help\n",
		"\n",
		"\t-V                       Version of nfsen\n",
		"\n",
		"\t-l <name>                List profile <name>.\n",
		"\n",
		"\t-A                       List all profiles.\n",
		"\n",
		"\t-a <profile>Add new profile <profile>\n",
		"\t\t[-c description\n",
		"\t\t[-B Begin of profile.] Default: time now: continuous profile.\n",
		"\t\t\tFormat yyyy-mm-dd-HH-MM, or yyyymmddHHMM\n",
		"\t\t[-E End of profile.]   Default: continuous profile.\n",
		"\t\t\tFormat yyyy-mm-dd-HH-MM, or yyyymmddHHMM\n",
		"\t\t[-S <sourcelist>]      Default: All available sources.\n",
		"\t\t\tFormat ':' separated list of sources.\n",
		"\t\t[-e <time>] expire     Default: no profile expire time\n",
		"\t\t[-s <size>] Size       Default: no profile max size.\n",
		"\t\t[-f <filter]           Read filer from file <filter>\n",
		"\t\t'PROFILE FILTER'       Profile filter. Required unless -f <filter>\n",
		"\n",
		"\t-d <profile>             Delete profile <profile>\n",
		"\t\t[-F]                   Force to delete profile. WARNING: use with care!\n",
		"\n",
		"\t-m <profile>             Modify profile <profile>\n",
		"\t\t[-e <time>]            Expire time. Default: no profile expire time\n",
		"\t\t[-s <size>]            Max. profile size. Default: no limit.\n",
		"\t\t[-U]                   Unlock locked profile. WARNING: use with care!\n",
		"\t\t[-L]                   Lock profile. WARNING: use with care!\n",
		"\n",
		"\t[-r <profile>]           Rebuild profile. WARNING: use with care!\n",
		"\t[-X <profile>]           Force expire profile now.\n",
		"\n",
		"\t-R                       Reconfigure NfSen. Add/Delete netflow sources.\n",
		"\n",
		"\treload                   Reload nfsend process.\n",
		"\treconfig                 Reconfigure nfsen. Add/Delete netflow sources.\n",
		"\tstart                    Start nfsen.\n",
		"\tStop                     Stop nfsen.\n",
		"\n",

} # End of usage

getopts('a:c:d:m:l:r:hqf:e:s:zALUVX:B:E:FPRS:');

my $opt_arg = shift @ARGV;

# Command lookup table
# 	Each function has too accepts $socket, \%opts as parameter
my %CMD_lookup = ( 
	'query'		=> 	{ 'run'	=>	\&NfProfile::QueryProfiles, 'DropPriv'	=>	1,	'RunAsRoot'	=>	0, 'rc_command'	=> 0 }, 
	'frontend'	=> 	{ 'run'	=>	\&NfSen::QueryFrontends, 	'DropPriv'	=>	1,	'RunAsRoot'	=>	0, 'rc_command'	=> 0 }, 
	'list'		=> 	{ 'run'	=>	\&NfProfile::ListProfile, 	'DropPriv'	=>	0,	'RunAsRoot'	=>	0, 'rc_command'	=> 0 }, 
	'add'		=> 	{ 'run'	=>	\&NfProfile::AddProfile, 	'DropPriv'	=>	1,	'RunAsRoot'	=>	0, 'rc_command'	=> 0 }, 
	'delete'	=> 	{ 'run'	=>	\&NfProfile::DeleteProfile, 'DropPriv'	=>	1,	'RunAsRoot'	=>	0, 'rc_command'	=> 0 }, 
	'modify'	=> 	{ 'run'	=>	\&NfProfile::ModifyProfile, 'DropPriv'	=>	1,	'RunAsRoot'	=>	0, 'rc_command'	=> 0 }, 
	'rebuild'	=> 	{ 'run'	=>	\&NfProfile::RebuildProfile, 'DropPriv'	=>	1,	'RunAsRoot'	=>	0, 'rc_command'	=> 0 }, 
	'expire'	=> 	{ 'run'	=>	\&NfProfile::ExpireProfile, 'DropPriv'	=>	1,	'RunAsRoot'	=>	0, 'rc_command'	=> 0 }, 
	'reconfig'	=> 	{ 'run'	=>	\&Nfsources::Reconfig,		'DropPriv'	=>	0,	'RunAsRoot'	=>	1, 'rc_command'	=> 0 }, 
	#
	'start'		=> 	{ 'run'	=>	\&NfSenRC::NfSen_start,		'DropPriv'	=>	0,	'RunAsRoot'	=>	1, 'rc_command'	=> 1 }, 
	'stop'		=> 	{ 'run'	=>	\&NfSenRC::NfSen_stop,		'DropPriv'	=>	0,	'RunAsRoot'	=>	1, 'rc_command'	=> 1 }, 
	'reload'	=> 	{ 'run'	=>	\&NfSenRC::NfSen_reload,	'DropPriv'	=>	0,	'RunAsRoot'	=>	1, 'rc_command'	=> 1 }, 

);

#
# Command Decoder
#
sub DecodeCommand {
	my $opt_arg	= shift;

	my %opts;
	my $what = undef;

	# Decode rc commands first
	if ( defined $opt_arg && ($opt_arg eq 'reload' || $opt_arg eq 'start' || $opt_arg eq 'stop') )	{
		$what = $opt_arg;
	}

	# reconfigure
	if ( defined $opt_arg && ($opt_arg eq 'reconfig' )) {
		$what = 'reconfig';
		$opts{'profile'} 	= 'life';
		$opts{'quiet'}  	= 1 		if defined $opt_q;
	}


	# Compile Command
	# list
	if ( defined $opt_l ) {
		$what = 'list';
		$opts{'profile'} = $opt_l unless $opt_l eq 'any';
		$opts{'quiet'}   = 1 		if defined $opt_q;
	} 

	# list all profiles
	if ( defined $opt_A ) {
		$what = 'query';
		$opts{'quiet'}  = 1 		if defined $opt_q;
	} 

	# list all frontend plugins
	if ( defined $opt_P ) {
		$what = 'frontend';
		$opts{'quiet'}  = 1 		if defined $opt_q;
	} 

	# add
	#	-a profile
	# 	-B tstart
	#	-E tend
	#	-S sources
	#	-e expire
	#	-s maxsize
	#	-c description
	#	-f filter
	elsif ( defined $opt_a ) {
		$what = 'add';
		$opts{'profile'} 	= $opt_a;
		$opts{'tstart'}		= $opt_B 	if defined $opt_B;
		$opts{'tend'}		= $opt_E	if defined $opt_E;
		$opts{'sources'}	= $opt_S 	if defined $opt_S;
		$opts{'expire'} 	= $opt_e 	if defined $opt_e;
		$opts{'maxsize'}	= $opt_s 	if defined $opt_s;
		$opts{'quiet'}  	= 1 		if defined $opt_q;

		my $filter = $opt_arg;
		if ( defined $filter ) {
			$opts{'filter'} = $filter;
		}
		if ( defined $opt_f ) {
			$opts{'filterfile'} = $opt_f if defined $opt_f;
		}
		if ( defined $opt_c ) {
			$opts{'description'} = "# $opt_c";
		}
	}
	
	# delete
	#	-F force
	elsif ( defined $opt_d ) {
		$what = 'delete';
		$opts{'profile'} 	= $opt_d;
		$opts{'quiet'}  	= 1 		if defined $opt_q;
		$opts{'force'}		= 1			if defined $opt_F;
	}

	# modify
	#	-e expire
	#	-s maxsize
	#	-r rebuild
	#	-U lock
	#	-L lock
	elsif ( defined $opt_m ) {
		$what = 'modify';
		$opts{'profile'} 	= $opt_m;
		$opts{'quiet'}  	= 1 		if defined $opt_q;
		$opts{'expire'} 	= $opt_e 	if defined $opt_e;
		$opts{'maxsize'}	= $opt_s 	if defined $opt_s;
		$opts{'lock'}		= 0 		if defined $opt_U;
		$opts{'lock'}		= 1 		if defined $opt_L;
	}

	# rebuild
	elsif ( defined $opt_r ) {
		$what = 'rebuild';
		$opts{'profile'} 	= $opt_r;
		$opts{'quiet'}  	= 1 		if defined $opt_q;
		$opts{'zero'}		= 1			if defined $opt_z;
	}

	# expire
	elsif ( defined $opt_X ) {
		$what = 'expire';
		$opts{'profile'} 	= $opt_X;
		$opts{'quiet'}  	= 1 		if defined $opt_q;
	}

	my $socket = *STDOUT;
	if ( defined $what && exists $CMD_lookup{$what} ) {
		my $cmd = $CMD_lookup{$what}{'run'};

		if ( $0 =~ /nfsen.rc/ && $CMD_lookup{$what}{'rc_command'} == 0 ) {
			die "$0: Unknown command!\n";
		}

		if ( $CMD_lookup{$what}{'RunAsRoot'} == 1 && !NfSen::root_process() ) {
			die "$0 wants to run this command as root\n";
		}
		if ( $CMD_lookup{$what}{'DropPriv'} == 1 && !NfSen::DropPriv($NfConf::WWWUSER) ) {
			die "ERR $Log::ERROR\n";
		}

		my $ret = Nfsources::CheckReconfig();
		die if $ret == 2;
		if ( $ret == 0 && $what ne 'reconfig' ) {
			print STDERR "Configured sources do not match existing sources. Run 'nfsen reconfig' first!\n";
			return;
		}
		
		# Execute the command
		&$cmd($socket, \%opts);
	} else {
		usage();
	}

} # End of DecodeCommand

###################
# Main starts here
###################

if ( defined $opt_V ) {
	print "$0: $nfsen_version $VERSION\n";
	exit;
}

if ( defined $opt_h ) {
	usage();
	exit;
}

if ( !NfConf::LoadConfig() ) {
	die "ERR $Log::ERROR\n";
}

Log::LogInit();
syslog('info', "Starting $0.");

select(STDOUT); $| = 1;	
tie(*STDERR, 'Log', 'nfsen') if $opt_q;
DecodeCommand($opt_arg);

untie *STDERR if $opt_q;

syslog('info', "Terminating $0.");
Log::LogEnd();

