#!/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: Nfsources.pm 5 2007-06-15 11:31:54Z phaag $
#
#  $LastChangedRevision: 5 $

package Nfsources;

use strict;
use warnings;
use Log;

use NfSenRC;

sub CheckReconfig {

	# profile 'live' contains by definition all netflow sources currently in use.
	my %profileinfo = NfProfile::ReadProfile('live', '.');
	if ( $profileinfo{'status'} eq 'empty' ) {
		# it's an error reading this profile
		print STDERR "Error reading profile 'live'";
		if ( defined $Log::ERROR ) {
			print STDERR ": $Log::ERROR";
		}
		print STDERR "\n";
		return 2;
	}

	my @current_sources = NfProfile::ProfileChannels(\%profileinfo);
    my %_tmp;
    @_tmp{@current_sources} = 1;

	# Building source lists
	my @AddSourceList;
	my @DeleteSourceList;
	foreach my $source ( keys %NfConf::sources ) {
		if ( exists $_tmp{$source} ) {
			delete $_tmp{$source};
		} else {
			return 0;
		}
	}

	if ( scalar keys %_tmp > 0 ) {
		return 0;
	}

	return 1;

} # End of CheckReconfig

sub Reconfig {

	# profile 'live' contains by definition all netflow sources currently in use.
	my %profileinfo = NfProfile::ReadProfile('live', '.');
	if ( $profileinfo{'status'} eq 'empty' ) {
		# it's an error reading this profile
		print STDERR "Error reading profile 'live'";
		if ( defined $Log::ERROR ) {
			print STDERR ": $Log::ERROR";
		}
		print STDERR "\n";
		return;
	}

	my @current_sources = NfProfile::ProfileChannels(\%profileinfo);

    my %_tmp;
    @_tmp{@current_sources} = 1;

	# Building source lists
	my @AddSourceList;
	my @DeleteSourceList;
	foreach my $source ( keys %NfConf::sources ) {
		if ( exists $_tmp{$source} ) {
			delete $_tmp{$source};
		} else {
			push @AddSourceList, $source;
		}
	}
	@DeleteSourceList = keys %_tmp;

	# For building new source list - start with current list
	my @NewSourceList = keys %NfConf::sources;
	foreach my $source ( @AddSourceList ) {
		push @NewSourceList, $source;
	}
    %_tmp = ();
    @_tmp{@NewSourceList} = 1;
	foreach my $source ( @DeleteSourceList ) {
		delete $_tmp{$source};
	}
	@NewSourceList = keys %_tmp;
	if ( scalar @NewSourceList == 0 ) {
		print "Empty source list does not make sense - nothing done!\n";
		$profileinfo{'locked'} = 0;
		if ( !NfProfile::WriteProfile(\%profileinfo) ) {
			print STDERR "Error writing profile 'live': $Log::ERROR\n";
		}
		return;
	}

	foreach my $source ( @NewSourceList ) {
		my $ret = NfSen::ValidFilename($source);
		if ( $ret ne "ok" ) {
			print STDERR "Error checking source name: $ret\n";
			return;
		}
	}

	# Nothing to do ?
	if ( (scalar @AddSourceList) == 0 && (scalar @DeleteSourceList) == 0 ) {
		print "Reconfig: No changes found!\n";
		$profileinfo{'locked'} = 0;
		if ( !NfProfile::WriteProfile(\%profileinfo) ) {
			print STDERR "Error writing profile 'live': $Log::ERROR\n";
		}
		return;
	}

	# Confirm add/delete sources
	print "New sources to configure : ", join(' ', @AddSourceList), "\n" if scalar @AddSourceList;
	print "Remove configured sources: ", join(' ', @DeleteSourceList), "\n" if scalar @DeleteSourceList;
	if ( NfSen::UserInput("Continue?") ne 'y') {
		print "Faire enough! - Nothing changed!\n";
		$profileinfo{'locked'} = 0;
		if ( !NfProfile::WriteProfile(\%profileinfo) ) {
			print STDERR "Error writing profile 'live': $Log::ERROR\n";
		}
		return;
	}
	print "\n";

	# lock live profile
	%profileinfo = NfProfile::LockProfile('live', '.');
	if ( $profileinfo{'status'} eq 'empty' ) {
		# profile already locked and in use
		if ( $profileinfo{'locked'} == 1 ) {
			print STDERR "Profile 'live' is locked. Try later\n";
			return;
		}
	
		# it's an error reading this profile
		if ( defined $Log::ERROR ) {
			print STDERR "Error profile 'live': $Log::ERROR\n";
			return;
		}
	}

	# Add all new netflow sources
	if ( scalar @AddSourceList > 0 ) {
		print "Add source(s): ", join(' ', @AddSourceList), ":\n";
		# Add sources
		my $now = time();
		my $tstart = $now - ( $now % 300 );
		foreach my $source ( @AddSourceList ) {
			print "Add source '$source'";
			my $ret = NfProfile::AddChannel(\%profileinfo, $source, '+', 0, $NfConf::sources{$source}{'col'}, $source, []);
			if ( $ret eq "ok" ) {
				# Start collector only, if NfSen is running at this time
				if ( -f "$NfConf::PIDDIR/nfsend.pid") {
					print "\tStart nfcapd: ";
					NfSenRC::StartCollector($source);
				}
			} else {
				print "Error while setting up channel '$source': $ret";
				print "No collector started! ";
			}
			print "\n";
		}
		print "\n";
	}

	# Delete sources, no longer configured
	if ( scalar @DeleteSourceList > 0 ) {
		print "Delete source(s): ", join(' ', @DeleteSourceList), ":\n";
		# Delete sources
		foreach my $source ( @DeleteSourceList ) {
			print "Delete source '$source' ";
			# Stop collector only, if NfSen is running at this time
			if ( -f "$NfConf::PIDDIR/nfsend.pid") {
				print "\tShutdown nfcapd: ";
				NfSenRC::StopCollector($source);
			}
			my $ret = NfProfile::DeleteChannel(\%profileinfo, $source);
			if ( $ret ne "ok" ) {
				print "\nError while removing channel '$source': $ret\n";
			}
			print " \n";
		}
	}

	# New sourcelist
	@_tmp{@NewSourceList} = 1;
	my $NewProfileList = [];
	# Remove no longer configure channels
	foreach my $channel ( keys %{$profileinfo{'channel'}} ) {
		if ( !exists $_tmp{$channel} ) {
			# configured source still valid
			delete $profileinfo{'channel'}{$channel};
		} # else channel still exists in profile
	}

	# Add new channels
	foreach my $channel ( @NewSourceList ) {
		if ( !exists $profileinfo{'channel'}{$channel} ) {
			$profileinfo{'channel'}{$channel}{'sign'}   = '+';
			$profileinfo{'channel'}{$channel}{'colour'} = '#0000ff';
			$profileinfo{'channel'}{$channel}{'order'}  = 0;
		}
	}

	# Unlock/Write profile
	$profileinfo{'locked'} = 0;
	if ( !NfProfile::WriteProfile(\%profileinfo) ) {
		print STDERR "Error writing profile 'live': $Log::ERROR\n";
	}

	print "\n";
	NfSenRC::NfSen_reload();

} # End of Reconfig

1;
