#! c:\adm\perl\bin\perl.exe
use strict;
use Win32::OLE qw(in);
use Data::Dumper;

=head1 NAME

elwatch.pl - a service which checks all incoming Eventlog entries.

=head1 VERSION

Version 1.1

=head1 AUTHOR

(c) 2003 Hannes Schulz <mail@hannes-schulz.de>

=head1 SYNOPSIS

install as a service:

  instsrv elwatch3 "C:\Program Files\Resource Kit\SRVANY.EXE"
  regedit /S elwatch3.reg
  net start elwatch3

Configure F<elwatch.cfg>. This is described in the documentation of
L<check_elwatch.pl>.

B<Additional>: If the field C<__logging> is C<true>, the event will be logged
to elwatch_elwID.log.


=head1 DESCRIPTION

This checks every event generated by the system, wether it matches anything
definition in C<elwatch.cfg> and writes a notice to elwatch.log if this is the
case. You can then check for these things by editing F<check_elwatch.cfg>.

=head1 CONFIGURATION

If paths change, please change the variables at the beginning of the script
accordingly.

=cut

BEGIN{ push @INC,'c:/adm/nagios'; }   # inc path goes here
use CfgParse;
my $configfile = 'c:\adm\nagios\elwatch.cfg';
my @config;

# and go: this reads all events and prepends them to the log file.
getEvents();    

# renewConfig: updates config from cfg-file {{{1
sub renewConfig{
	my $cfg    = new CfgParse($configfile);
	@config = @{$cfg->parse()} or warn "error parsing config file: $!";
}

# handleEvent: saves important events to log file. {{{1
sub handleEvent{
	my $event = shift();                 # this is a $wbemobj->{TargetInstance}
	my (@out,$head);
	my (%et, $etCrit, $match, $tmp, $not, %cfg,@lookfor, $l);
	# See, whether we have to log that one {{{2
	etmpl:
	foreach (@config){                     # event template
		%et = %$_;
		if($et{"__blockname"} =~ /^elwatch$/){%cfg = %et; next};
		$match = 0;
		foreach $etCrit (keys %et){          # event criteria
			next if($etCrit =~ /^__\w+$/);
			$not = ($etCrit =~ s/^!//);          # possibly negated?
			if ($not){        # stop processing if this matches (negated)
				next etmpl if($event->{$etCrit} =~ $et{$etCrit});
			}else{            # continue if this matches (not negated)
				next etmpl if($event->{$etCrit} !~ $et{$etCrit});
			}
			$match = 1;
		}
		if($match){    # We have to log that one: prepend to log {{{2
			my $log    = $cfg{"log"};
			my $minage = time-($cfg{"maxminutes"}*60);
			my $logtmp = $log."tmp";
			if($et{__number} == undef){$et{__number} = '0'}
			open LOGTMP, ">$logtmp" or warn "cannot open temp log: $!";
			open LOG, "<$log"       or warn "cannot open log: $!";
			print LOGTMP scalar time, ";;$et{__blockname};;$et{__number}\n";
			while($l = <LOG>){
				@_ = split /;;/,$l;
				last if($_[0]<$minage);   # stop if minimum age
				print LOGTMP $l;
			};
			close LOGTMP; close LOG;
			unlink $log || warn "Cannot delete orig. log: $!";
			system("move",$logtmp,$log) == 0
				or warn "Cannot delete orig. log: $!";
			if($et{__logging} and $et{__logging} =~ m/true/i){
				@out = ();
				foreach(qw(Category CategoryString ComputerName Data EventCode EventIdentifier EventType Logfile RecordNumber SourceName TimeGenerated TimeWritten Type User Message InsertionStrings)){
					$_ = $event->{$_};
					s/[\n]+/\t/g;
					s/\t+$/\t/g;
					s/ +/ /g;
					push @out, $_;
				}
				if(-e "c:\\adm\\nagios\\elwatch_".$et{__blockname}.".log"){
					$head = "";
				}
				else{
					$head = join("\t",qw(Category CategoryString ComputerName Data EventCode EventIdentifier EventType Logfile RecordNumber SourceName TimeGenerated TimeWritten Type User Message InsertionStrings)) . "\n";
				}
				open(ELOG,">>c:\\adm\\nagios\\elwatch_".$et{__blockname}.".log")
					or warn("Could not open log: $!");
				print ELOG $head;
				print ELOG join("\t",@out),"\n";
				close ELOG;
			}
		}
	}
}

# getEvents: reads the eventlog entries as they come in. {{{1
sub getEvents{
	Win32::OLE->Option("Warn"=>0);   # set this to 3 for errors
	my $query = "SELECT * FROM __InstanceCreationEvent " .
	"WHERE TargetInstance ISA 'Win32_NTLogEvent' ";
	#"AND TargetInstance.SourceName = 'snort'";

	# Get Locator
	my $wbemloc = new Win32::OLE("WbemScripting.SWbemLocator");

	# Add Privileges for reading the Security-log
	$wbemloc->{Security_}->{Privileges}->AddAsString("SeSecurityPrivilege");

	# Get Services
	my $wbemsvc = $wbemloc->ConnectServer(".", "root/cimv2");

	# Set impersonationlevel
	$wbemsvc->{Security_}->{ImpersonationLevel} = 3;

	# Get EventSource
	my $wbemevtsrc = $wbemsvc->ExecNotificationQuery($query);

	# while(1) begins an infinite loop
	while (1) {
		# Get the next event when it arrive
		my $wbemobj = $wbemevtsrc->NextEvent();
		# update config
		renewConfig();
		# handle event
		handleEvent($wbemobj->{TargetInstance});
	}
}
# vim:fdm=marker
