1package Nagios::CheckLogfiles::Search::Eventlog; 2 3use strict; 4use Exporter; 5use File::Basename; 6use Time::Local; 7use IO::File; 8use vars qw(@ISA); 9 10use constant OK => 0; 11use constant WARNING => 1; 12use constant CRITICAL => 2; 13use constant UNKNOWN => 3; 14 15@ISA = qw(Nagios::CheckLogfiles::Search); 16 17sub new { 18 my $self = bless {}, shift; 19 return $self->init(shift); 20} 21 22sub init { 23 my $self = shift; 24 my $params = shift; 25 $self->{logfile} = '/eventlog/is/cool'; 26 $self->default_options({ winwarncrit => 0, 27 eventlogformat => '%g %i %m' }); 28 $self->SUPER::init($params); 29 if ($self->get_option('lookback')) { 30 if ($self->get_option('lookback') =~ /^(\d+)(s|m|h|d)$/) { 31 if ($2 eq 's') { 32 $self->set_option('lookback', $1); 33 } elsif ($2 eq 'm') { 34 $self->set_option('lookback', $1 * 60); 35 } elsif ($2 eq 'h') { 36 $self->set_option('lookback', $1 * 60 * 60); 37 } elsif ($2 eq 'd') { 38 $self->set_option('lookback', $1 * 60 * 60 * 24); 39 } 40 } else { 41 printf STDERR "illegal time interval (must be <number>[s|m|h|d]\n"; 42 $self = undef; 43 return undef; 44 } 45 } 46 if ($self->get_option('winwarncrit')) { 47 push(@{$self->{patterns}->{WARNING}}, "EE_WW_TT"); 48 push(@{$self->{patterns}->{CRITICAL}}, "EE_EE_TT"); 49 push(@{$self->{patternfuncs}->{WARNING}}, 50 eval "sub { local \$_ = shift; return m/EE_WW_TT/o; }"); 51 push(@{$self->{patternfuncs}->{CRITICAL}}, 52 eval "sub { local \$_ = shift; return m/EE_EE_TT/o; }"); 53 54 } 55 push(@{$self->{patterns}->{UNKNOWN}}, "EE_UU_TT"); 56 push(@{$self->{patternfuncs}->{UNKNOWN}}, 57 eval "sub { local \$_ = shift; return m/EE_UU_TT/o; }"); 58 $self->{eventlog} = { 59 # system, security, application 60 eventlog => $params->{eventlog}->{eventlog} || 'system', 61 computer => $params->{eventlog}->{computer} || Win32::NodeName(), 62 username => $params->{eventlog}->{username} || Win32::LoginName(), 63 password => $params->{eventlog}->{password} || '', 64 source => $params->{eventlog}->{source}, 65 speedup => $params->{eventlog}->{speedup} || 1, 66 include => $params->{eventlog}->{include} || {}, 67 exclude => $params->{eventlog}->{exclude} || {}, 68 }; 69 $self->resolve_macros(\$self->{eventlog}->{eventlog}); 70 $self->resolve_macros(\$self->{eventlog}->{computer}); 71 $self->resolve_macros(\$self->{eventlog}->{username}) if $self->{eventlog}->{username}; 72 $self->resolve_macros(\$self->{eventlog}->{password}) if $self->{eventlog}->{password}; 73 # computer: I changed "\\\\MYPDC" to $dc ($dc = Win32::AdminMisc::GetDC("MYDOMAIN");) 74 # keys fuer include/exclude: source,category,type,eventid 75 foreach my $item (qw(Source Category EventType EventID)) { 76 foreach (keys %{$self->{eventlog}->{include}}) { 77 if (lc $_ eq lc $item) { 78 $self->{eventlog}->{include}->{$item} = 79 lc $self->{eventlog}->{include}->{$_}; 80 delete $self->{eventlog}->{include}->{$_} if $_ ne $item; 81 } 82 } 83 foreach (keys %{$self->{eventlog}->{exclude}}) { 84 if (lc $_ eq lc $item) { 85 $self->{eventlog}->{exclude}->{$item} = 86 lc $self->{eventlog}->{exclude}->{$_}; 87 delete $self->{eventlog}->{exclude}->{$_} if $_ ne $item; 88 } 89 } 90 } 91 if (! exists $self->{eventlog}->{include}->{operation} || 92 $self->{eventlog}->{include}->{operation} ne 'or') { 93 $self->{eventlog}->{include}->{operation} = 'and' 94 } 95 if (! exists $self->{eventlog}->{exclude}->{operation} || 96 $self->{eventlog}->{exclude}->{operation} ne 'and') { 97 $self->{eventlog}->{exclude}->{operation} = 'or' 98 } 99 $self->{orschlorschknorsch} = sprintf "%s/%s.temp_evtlog2file", 100 $self->system_tempdir(), $self->{tag}; 101} 102 103sub prepare { 104 my $self = shift; 105 #$self->{options}->{nologfilenocry} = 1; 106 $self->{eventlog}->{thissecond} = time; 107 push(@{$self->{exceptions}->{CRITICAL}}, 'CHECK_LOGFILES INTERNAL ERROR'); 108 push(@{$self->{exceptions}->{WARNING}}, 'CHECK_LOGFILES INTERNAL ERROR'); 109} 110 111sub loadstate { 112 my $self = shift; 113 $self->SUPER::loadstate(); 114 # always scan the whole output. thst's what starttime is for. 115 $self->{laststate}->{logoffset} = 0; 116 # if this is the very first run, look back 5 mintes in the past. 117 # with allyoucaneat set, look back 10 years 118 $self->{laststate}->{logtime} = $self->{laststate}->{logtime} ? 119 $self->{laststate}->{logtime} : 120 $self->{options}->{allyoucaneat} ? 121 $self->{eventlog}->{thissecond} - 315360000 : 122 $self->{eventlog}->{thissecond} - 600; 123} 124 125sub savestate { 126 my $self = shift; 127 foreach (keys %{$self->{laststate}}) { 128 $self->{newstate}->{$_} = $self->{laststate}->{$_}; 129 } 130 $self->{newstate}->{logtime} = $self->{eventlog}->{thissecond}; 131 $self->SUPER::savestate(); 132} 133 134sub analyze_situation { 135 my $self = shift; 136 if ($self->{options}->{lookback}) { 137 $self->{eventlog}->{lastsecond} = time - $self->{options}->{lookback}; 138 $self->trace("looking back until %s", 139 scalar localtime $self->{eventlog}->{lastsecond}); 140 } else { 141 $self->{eventlog}->{lastsecond} = $self->{laststate}->{logtime}; 142 $self->trace("last scanned until %s", 143 scalar localtime $self->{eventlog}->{lastsecond}); 144 } 145 $self->trace(sprintf "from %s to %s", 146 scalar localtime $self->{eventlog}->{lastsecond}, 147 scalar localtime $self->{eventlog}->{thissecond}); 148 if ($self->{eventlog}->{lastsecond} < $self->{eventlog}->{thissecond}) { 149 $self->{logmodified} = 1; 150 } else { 151 # this happens if you call the plugin in too short intervals. 152 $self->trace("please wait for a second"); 153 } 154} 155 156sub collectfiles { 157 my $self = shift; 158 $self->trace(sprintf "get everything %d <= event < %d", 159 $self->{eventlog}->{lastsecond}, 160 $self->{eventlog}->{thissecond}); 161 if ($self->{logmodified}) { 162 open(*FH, ">$self->{orschlorschknorsch}"); 163 tie *FH, 'Nagios::CheckLogfiles::Search::Eventlog::Handle', 164 $self->{eventlog}, 165 $self->get_option('winwarncrit'), 166 $self->get_option('eventlogformat'), 167 $self->get_option('logfilenocry'), 168 $self->{tivoli}, 169 $self->{tracefile}; 170 push(@{$self->{relevantfiles}}, 171 { filename => "eventlog|", 172 fh => *FH, seekable => 0, statable => 1, 173 modtime => $self->{eventlog}->{thissecond}, 174 fingerprint => "0:0" }); 175 } 176} 177 178sub getfilefingerprint { 179 return 1; 180} 181 182sub finish { 183 my $self = shift; 184 foreach my $level (qw(CRITICAL WARNING UNKNOWN)) { 185 if (scalar(@{$self->{matchlines}->{$level}})) { 186 foreach my $match (@{$self->{matchlines}->{$level}}) { 187 $match =~ s/EE_WW_TT//; 188 $match =~ s/EE_EE_TT//; 189 $match =~ s/EE_UU_TT//; 190 } 191 } 192 if (exists $self->{lastmsg} && exists $self->{lastmsg}->{$level}) { 193 $self->{lastmsg}->{$level} =~ s/EE_WW_TT//; 194 $self->{lastmsg}->{$level} =~ s/EE_EE_TT//; 195 $self->{lastmsg}->{$level} =~ s/EE_UU_TT//; 196 } 197 } 198 if (-f $self->{orschlorschknorsch}) { 199 unlink $self->{orschlorschknorsch}; 200 } 201} 202 203sub rewind { 204 my $self = shift; 205 $self->loadstate(); 206 foreach (keys %{$self->{laststate}}) { 207 $self->{newstate}->{$_} = $self->{laststate}->{$_}; 208 } 209 $self->addevent(0, "reset"); 210 $self->{eventlog}->{thissecond} = 1; 211 $self->savestate(); 212 return $self; 213} 214 215 216package Nagios::CheckLogfiles::Search::Eventlog::Handle; 217 218use strict; 219use Exporter; 220use POSIX qw(strftime); 221require Tie::Handle; 222use Win32::EventLog; 223use Win32::TieRegistry (Delimiter => "/"); 224use Win32::WinError; 225use IO::File; 226use vars qw(@ISA); 227@ISA = qw(Tie::Handle Nagios::CheckLogfiles::Search::Eventlog); 228our $AUTOLOAD; 229our $tracefile; 230$Win32::EventLog::GetMessageText = 1; 231our @events = (); 232 233 234sub TIEHANDLE { 235 my $class = shift; 236 my $eventlog = shift; 237 my $winwarncrit = shift; 238 my $eventlogformat = shift; 239 my $logfilenocry = shift; 240 my $tivoli = shift; 241 $tracefile = shift; 242 my $self = {}; 243 my $oldestoffset = undef; # oldest event in the eventlog 244 my $numevents = undef; # number of events in the eventlog 245 my $newestoffset = 0; # latest event in the eventlog 246 my $save_newestoffset = 0; 247 my $seekoffset = 0; # temporary pointer 248 my $firstoffset = 0; # first event created after the last run 249 my $event = { 250 'Length' => undef, 251 'RecordNumber' => undef, 252 'TimeGenerated' => undef, 253 'Timewritten' => undef, 254 'EventID' => undef, 255 'EventType' => undef, 256 'Category' => undef, 257 'ClosingRecordNumber' => undef, 258 'Source' => undef, 259 'Computer' => undef, 260 'Strings' => undef, 261 'Data' => undef, 262 'User' => undef, 263 }; 264 @events = (); 265 my $offsetcache = {}; 266 my $mustabort = 0; 267 my $internal_error = ""; 268 my $lasterror = 0; 269 my $handle = undef; 270 my $must_close_ipc = 0; 271 272 if ($tivoli->{object}) { 273 $eventlogformat = "_tecad_win_"; 274 } 275 # 276 # Schritt 1 277 # 278 # Falls es sich um einen Remote-Rechner handelt, muss erst eine 279 # Verbindung zu dessen IPC$-Resource hergestellt werden 280 # Bei einem Server 2008 kann dies ein lokaler Benutzer sein, 281 # der zur Gruppe Ereignisprotokolleser gehoert 282 # 283 if ($eventlog->{computer} ne Win32::NodeName) { 284 my @harmlesscodes = (1219); 285 # 1219 Mehrfache Verbindungen zu einem Server oder .... 286 # net use \\remote\IPC$ /USER:Administrator adminpw 287 eval { 288 require Win32::NetResource; 289 }; 290 if ($@) { 291 $mustabort = 1; 292 $internal_error = 'Win32::NetResource not installed'; 293 } else { 294 trace(sprintf "connect to %s as %s with password ***", 295 $eventlog->{computer}, $eventlog->{username}); 296 if (Win32::NetResource::AddConnection({ 297 'Scope' => 0, 298 'Type' => 0, 299 'DisplayType' => 0, # RESOURCEDISPLAYTYPE_GENERIC 300 'Usage' => 0, 301 'RemoteName' => "\\\\".$eventlog->{computer}."\\IPC\$", 302 'LocalName' => '', 303 'Comment' => "check_logfiles", 304 #'Provider' => "Microsoft Windows Network" 305 }, $eventlog->{password}, $eventlog->{username}, 0)) { 306 trace("created ipc channel"); 307 $must_close_ipc = 1; 308 } else { 309 Win32::NetResource::GetError($lasterror); 310 if (scalar(grep { $lasterror == $_ } @harmlesscodes) == 0) { 311 $mustabort = 1; 312 $internal_error = 'IPC$ '.Win32::FormatMessage($lasterror); 313 trace("ipc channel could not be established"); 314 } else { 315 trace("ipc channel already established"); 316 } 317 } 318 } 319 } 320 # 321 # Schritt 2 322 # 323 # Oeffnen der Registry und kontrollieren, ob es das gewuenschte 324 # Eventlog ueberhaupt gibt 325 # 326 if (! $mustabort) { 327 my @haseventlogs = ("application", "system", "security"); 328 eval { 329 my $data = undef; 330 if ($eventlog->{computer} ne Win32::NodeName()) { 331 trace("looking into remote registry"); 332 $data = $Registry->Connect( $eventlog->{computer}, 333 'HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/EventLog/', 334 { Access=>Win32::TieRegistry::KEY_READ(), Delimiter => "/" } ); 335 } else { 336 trace("looking into registry"); 337 $data = $Registry->Open( 338 'HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/EventLog/', 339 { Access=>Win32::TieRegistry::KEY_READ(), Delimiter => "/" } ); 340 } 341 if ($data) { 342 push(@haseventlogs, grep { 343 my $var = $_; ! grep /^$var$/, @haseventlogs 344 } map { lc $_ } $data->SubKeyNames); 345 trace(sprintf "known eventlogs: %s", join(',', @haseventlogs)); 346 undef $data; 347 } else { 348 die "no data from HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/EventLog/"; 349 } 350 }; 351 if ($@) { 352 $mustabort = 1; 353 $internal_error = "Cannot read registry"; 354 trace(sprintf "looking into registry failed: %s", $@); 355 } elsif (! scalar(grep { lc $eventlog->{eventlog} eq $_ } @haseventlogs)) { 356 $mustabort = 1; 357 $internal_error = sprintf "Eventlog %s does not exist", 358 $eventlog->{eventlog} if $logfilenocry; 359 } 360 } 361 # 362 # Schritt 3 363 # 364 # Oeffnen des Eventlogs 365 # 366 if (! $mustabort) { 367 my @harmlesscodes = (0, 997); 368 trace(sprintf "opening handle to eventlog %s", $eventlog->{eventlog}); 369 $handle = 370 Win32::EventLog->new($eventlog->{eventlog}, $eventlog->{computer}); 371 $lasterror = Win32::GetLastError(); 372 # 0 Der Vorgang wurde erfolgreich beendet 373 # 997 überlappender E/A-Vorgang wird verarbeitet. 374 # 5 Zugriff verweigert 375 # 1722 Der RPC-Server ist nicht verfügbar. 376 377 # 1722 eventlog gueltig, ip gueltig, aber kein windows-rechner 378 # 1722 eventlog gueltig, ip nicht pingbar 379 # 5 kein secure channel zum remoterechner vorhanden 380 381 if (! $handle || scalar(grep { $lasterror == $_ } @harmlesscodes) == 0) { 382 # sinnlos, weiterzumachen 383 $mustabort = 1; 384 $internal_error = 'open Eventlog '.Win32::FormatMessage($lasterror); 385 trace("opening handle to eventlog failed"); 386 } 387 } 388 # 389 # Schritt 4 390 # 391 # Anzahl der Eintraege auslesen. 392 # Dieser Schritt dient ausserdem dazu, die Berechtigung zum Lesen 393 # zu ueberpruefen, da unter Cygwin der letzte Schritt erfolgreich 394 # ausfaellt, auch wenn keine Leseberechtigung besteht. 395 # 396 if (! $mustabort) { 397 $handle->GetOldest($oldestoffset); 398 $handle->GetNumber($numevents); 399 if (! defined $numevents) { 400 # cygwin perl sagt zu allem errorcode=0, auch wenn keine berechtigung 401 # zum zugriff vorliegt. aber ein undef ist zumindest ein indiz, dass 402 # etwas faul ist. 403 $mustabort = 1; 404 $internal_error = "Eventlog permission denied"; 405 } 406 } 407 # 408 # Schritt 5 409 # 410 # Jetzt beginnt das eigentliche Auslesen des Eventlogs 411 # 412 if (! $mustabort) { 413 if ($numevents) { 414 $newestoffset = $oldestoffset + $numevents - 1; 415 trace(sprintf "eventlog has offsets %d..%d", 416 $oldestoffset, $newestoffset); 417 if (! $eventlog->{speedup}) { 418 $firstoffset = $oldestoffset; 419 } else { 420 # new method. find the first event which lies within the range 421 # oldestoffset <= offset <= newestoffset 422 $save_newestoffset = $newestoffset; 423 $handle->Read((EVENTLOG_SEEK_READ|EVENTLOG_BACKWARDS_READ), 424 $oldestoffset, $event); 425 $offsetcache->{$oldestoffset} = $event->{Timewritten}; 426 if ($event->{Timewritten} >= $eventlog->{lastsecond}) { 427 # even the oldest record was created after the last run 428 # of check_logfiles. the log was cleared or this is the first run ever 429 $firstoffset = $oldestoffset; 430 trace(sprintf "i start from the beginning %d", $firstoffset); 431 } else { 432 $handle->Read((EVENTLOG_SEEK_READ|EVENTLOG_BACKWARDS_READ), 433 $newestoffset, $event); 434 $offsetcache->{$newestoffset} = $event->{Timewritten}; 435 if ($event->{Timewritten} >= $eventlog->{lastsecond}) { 436 # the latest event was created after the last run of check_logfiles 437 $seekoffset = $newestoffset; 438 trace(sprintf "start at offset %d", $seekoffset); 439 do { 440 # get the seekoffset's time 441 $handle->Read((EVENTLOG_SEEK_READ|EVENTLOG_BACKWARDS_READ), 442 $seekoffset, $event); 443 $offsetcache->{$seekoffset} = $event->{Timewritten}; 444 if ($event->{Timewritten} >= $eventlog->{lastsecond}) { 445 # inside the search interval. but is it the oldest? 446 if ((exists $offsetcache->{$seekoffset - 1}) && 447 ($offsetcache->{$seekoffset - 1} < 448 $eventlog->{lastsecond})) { 449 $firstoffset = $seekoffset; 450 trace(sprintf "found first offset %d (=)", $firstoffset); 451 } else { 452 $newestoffset = $seekoffset; 453 $seekoffset = 454 $oldestoffset + int (($seekoffset - $oldestoffset) / 2); 455 trace(sprintf "try offset %d (<)", $seekoffset); 456 } 457 } else { 458 # too old. but maybe the next offset? 459 if ((exists $offsetcache->{$seekoffset + 1}) && 460 ($offsetcache->{$seekoffset + 1} >= 461 $eventlog->{lastsecond})) { 462 $firstoffset = $seekoffset + 1; 463 trace(sprintf "found first offset %d (+)", $firstoffset); 464 } else { 465 $oldestoffset = $seekoffset; 466 $seekoffset = 467 $seekoffset + int (($newestoffset - $seekoffset) / 2); 468 trace(sprintf "try offset %d (>)", $seekoffset); 469 } 470 } 471 } while (! $firstoffset); 472 # now position at the first element in question 473 $handle->Read((EVENTLOG_SEEK_READ|EVENTLOG_BACKWARDS_READ), 474 $firstoffset, $event); 475 # adjust the number of elements to scan 476 $newestoffset = $save_newestoffset; 477 } else { 478 # there are no new events 479 # fake firstoffset to avoid entering the while loop 480 $firstoffset = $newestoffset + 1; 481 trace(sprintf "no new events fake %d", $firstoffset); 482 } 483 } 484 } 485 while ($firstoffset <= $newestoffset) { 486 # sequential_reads are not reliable, so better use direct access 487 $handle->Read((EVENTLOG_SEEK_READ|EVENTLOG_FORWARDS_READ), 488 $firstoffset, $event); 489 if (($event->{Timewritten} >= $eventlog->{lastsecond}) && 490 ($event->{Timewritten} < $eventlog->{thissecond})) { 491 if (included($event, $eventlog->{include}) && 492 ! excluded($event, $eventlog->{exclude})) { 493 #printf STDERR "passed filter %s\n", Data::Dumper::Dumper($event); 494 my $tmp_event = {}; 495 %{$tmp_event} = %{$event}; 496 Win32::EventLog::GetMessageText($tmp_event); 497 format_message($eventlogformat, $tmp_event); 498 if ($winwarncrit) { 499 if ($tmp_event->{EventType} == EVENTLOG_WARNING_TYPE) { 500 $tmp_event->{Message} = "EE_WW_TT".$tmp_event->{Message}; 501 } elsif ($tmp_event->{EventType} == EVENTLOG_ERROR_TYPE) { 502 $tmp_event->{Message} = "EE_EE_TT".$tmp_event->{Message}; 503 } 504 } 505 push(@events, $tmp_event); 506 } else { 507 #printf STDERR "blocked by filter %s\n", Data::Dumper::Dumper($event); 508 } 509 } 510 $firstoffset++; 511 } 512 } else { 513 #printf STDERR "0 events\n"; 514 } 515 } else { 516 my $now = time; 517 my $tmp_event = {}; 518 $tmp_event->{Message} = 519 "EE_UU_TTCHECK_LOGFILES INTERNAL ERROR ".$internal_error; 520 $tmp_event->{Message} =~ s/\0/ /g; 521 $tmp_event->{Message} =~ s/\s*$//g; 522 $tmp_event->{TimeGenerated} = $now; 523 $tmp_event->{Timewritten} = $now; 524 $tmp_event->{Source} = 'check_logfiles'; # internal usage 525 $tmp_event->{EventType} = EVENTLOG_ERROR_TYPE; # internal usage 526 $tmp_event->{EventID} = 0; 527 format_message($eventlogformat, $tmp_event); 528 push(@events, $tmp_event) if $internal_error; 529 } 530 # 531 # Aufraeumen 532 # 533 $handle->Close() if $handle; 534 if ($must_close_ipc) { 535 if (Win32::NetResource::CancelConnection( 536 "\\\\".$eventlog->{computer}."\\IPC\$", 0, 0)) { 537 trace("closed the ipc connection"); 538 } else { 539 trace("could not close the ipc connection"); 540 if (Win32::NetResource::CancelConnection( 541 "\\\\".$eventlog->{computer}."\\IPC\$", 0, 1)) { 542 trace("closed the ipc connection by force"); 543 } else { 544 trace("could not close the ipc connection even by force"); 545 } 546 } 547 } 548 bless $self, $class; 549 return $self; 550} 551 552sub SEEK { 553} 554 555sub STAT { 556} 557 558sub OPEN { 559} 560 561sub CLOSE { 562} 563 564sub GETC { 565} 566 567sub READ { 568} 569 570sub READLINE { 571 if (my $event = shift @events) { 572 return $event->{Message}; 573 } else { 574 return undef; 575 } 576} 577 578sub format_message { 579 my $eventlogformat = shift; 580 my $event = shift; 581 # formatstring: 582 # %t EventType 583 # %c Category 584 # %s Source 585 # %i EventID 586 # %m Message 587 # %w Timewritten 588 # %g Timegenerated 589 # %d Date/Time 590 # %u User # not documented @ cpan 591 if ($eventlogformat eq "_tecad_win_") { 592 $eventlogformat = "%__TiVoLi__g %C %t N/A %s %__TiVoLi__i %m"; 593 } 594 if (! $event->{Message}) { 595 $event->{Message} = $event->{Strings}; 596 $event->{Message} =~ s/\0/ /g; 597 $event->{Message} =~ s/\s*$//g; 598 } 599 $event->{Message} = 'unknown message' if ! $event->{Message}; 600 $event->{Message} =~ tr/\r\n/ /d; 601 my $tz = ''; 602 my $format = {}; 603 $format->{'%t'} = 604 ($event->{EventType} == -1) ? 605 'Internal' : 606 ($event->{EventType} == EVENTLOG_WARNING_TYPE) ? 607 'Warning' : 608 ($event->{EventType} == EVENTLOG_ERROR_TYPE) ? 609 'Error' : 610 ($event->{EventType} == EVENTLOG_INFORMATION_TYPE) ? 611 'Information' : 612 ($event->{EventType} == EVENTLOG_AUDIT_SUCCESS) ? 613 'AuditSuccess' : 614 ($event->{EventType} == EVENTLOG_AUDIT_FAILURE) ? 615 'AuditFailure' : 616 ($event->{EventType} == EVENTLOG_SUCCESS) ? 617 'Success' : 'UnknType'; 618 $format->{'%c'} = ! $event->{Category} ? 'None' : 619 join('_', split(" ", $event->{Category})); 620 $format->{'%C'} = ! $event->{Category} ? 'None' : $event->{Category}; 621 $format->{'%s'} = join('_', split(" ", $event->{Source})); 622 $format->{'%i'} = sprintf '%04d', $event->{EventID} & 0xffff; 623 $format->{'%__TiVoLi__i'} = sprintf '%s', $event->{EventID} & 0xffff; 624 $format->{'%m'} = $event->{Message}; 625 $format->{'%w'} = strftime("%Y-%m-%dT%H:%M:%S", 626 localtime($event->{Timewritten})).$tz; 627 $format->{'%g'} = strftime("%Y-%m-%dT%H:%M:%S", 628 localtime($event->{TimeGenerated})).$tz; 629 $format->{'%W'} = $event->{Timewritten}; 630 $format->{'%G'} = $event->{TimeGenerated}; 631 $format->{'%u'} = $event->{User} || 'undef'; 632 $format->{'%__TiVoLi__g'} = join(" ", (split(/\s+/, 633 scalar localtime $event->{TimeGenerated}))[1,2,3,4]); 634 # month day time and year => %t %s 635 my $message = $eventlogformat; 636 foreach (keys %{$format}) { 637 $message =~ s/$_/$format->{$_}/g; 638 } 639 while ($message =~ /%(\d+)m/) { 640 my $search = "%".$1."m"; 641 my $replace = sprintf "%.".$1."s", $event->{Message}; 642 $message =~ s/$search/$replace/g; 643 } 644 $event->{Message} = $message; 645} 646 647sub included { 648 my $event = shift; 649 my $filter = shift; 650 my $filters = 0; 651 my $matches = {}; 652 # EventCategory ist ein INTEGER!!! 653 # und ausserdem ist pro Source ein eigener Satz von Kategorien moeglich 654 # man muesste deren Bezeichnungen aus der Registry lesen. 655 # in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application 656 # stehen die Sources, die eigene Kategorien definiert haben. 657 # Im Key CategoryMessageFile ist die Datei hinterlegt, der die Kategorien 658 # entnommen werden koennen. In CategoryCount steht die Anzahl der 659 # selbstdefinierten Kategorien. 660 foreach my $attr (qw(Source Category)) { 661 $matches->{$attr} = 0; 662 if (exists $filter->{$attr}) { 663 foreach my $item (split(',', $filter->{$attr})) { 664 #printf "items: %s ? %s\n", $item, $event->{$attr}; 665 if (lc $item eq lc $event->{$attr}) { 666 #printf "-> %s eq %s\n", lc $item, lc $event->{$attr}; 667 $matches->{$attr}++; 668 } 669 } 670 } else { 671 #printf "no filter for %s\n", $attr; 672 $matches->{$attr}++; 673 } 674 } 675 foreach my $attr (qw(EventID)) { 676 $matches->{$attr} = 0; 677 if (exists $filter->{$attr}) { 678 foreach my $item (split(',', $filter->{$attr})) { 679 #printf "items: %s ? %s\n", $item, $event->{$attr}; 680 #if (lc $item eq lc ($event->{$attr} & 0xffff)) { 681 if ($item == ($event->{$attr} & 0xffff)) { 682 #printf "-> %s eq %s\n", lc $item, lc ($event->{$attr} & 0xffff); 683 $matches->{$attr}++; 684 } 685 } 686 } else { 687 #printf "no filter for %s\n", $attr; 688 $matches->{$attr}++; 689 } 690 } 691 foreach my $attr (qw(EventType)) { 692 $matches->{$attr} = 0; 693 if (exists $filter->{$attr}) { 694#printf "succ %s\n", EVENTLOG_SUCCESS; 695#printf "warn %s\n", EVENTLOG_WARNING_TYPE; 696#printf "err %s\n", EVENTLOG_ERROR_TYPE; 697#printf "info %s\n", EVENTLOG_INFORMATION_TYPE; 698#printf "audit %s\n", EVENTLOG_AUDIT_SUCCESS; 699#printf "fail %s\n", EVENTLOG_AUDIT_FAILURE; 700 foreach my $item (split(',', $filter->{$attr})) { 701 if ((lc $item =~ /^succ/ && $event->{$attr} == EVENTLOG_SUCCESS) || 702 (lc $item =~ /warn/ && $event->{$attr} == EVENTLOG_WARNING_TYPE) || 703 (lc $item =~ /err/ && $event->{$attr} == EVENTLOG_ERROR_TYPE) || 704 (lc $item =~ /info/ && $event->{$attr} == EVENTLOG_INFORMATION_TYPE) || 705 (lc $item =~ /audit.*succ/ && $event->{$attr} == EVENTLOG_AUDIT_SUCCESS) || 706 (lc $item =~ /fail/ && $event->{$attr} == EVENTLOG_AUDIT_FAILURE)) { 707 $matches->{$attr}++; 708 } 709 } 710 } else { 711 #printf "no filter for %s\n", $attr; 712 $matches->{$attr}++; 713 } 714 } 715 if ($filter->{operation} eq 'and') { 716 return (scalar(grep { $matches->{$_} } keys %{$matches}) == 4) ? 1 : 0; 717 } else { 718 return (scalar(grep { $matches->{$_} } keys %{$matches}) == 0) ? 0 : 1; 719 } 720} 721 722sub excluded { 723 my $event = shift; 724 my $filter = shift; 725 my $filters = 0; 726 my $matches = {}; 727 # EventCategory ist ein INTEGER!!! 728 foreach my $attr (qw(Source Category)) { 729 $matches->{$attr} = 0; 730 if (exists $filter->{$attr}) { 731 foreach my $item (split(',', $filter->{$attr})) { 732 #printf "items: %s ? %s\n", $item, $event->{$attr}; 733 if (lc $item eq lc $event->{$attr}) { 734 #printf "-> %s eq %s\n", lc $item, lc $event->{$attr}; 735 $matches->{$attr}++; 736 } 737 } 738 } else { 739 #printf "no filter for %s\n", $attr; 740 #$matches->{$attr}++; 741 } 742 } 743 foreach my $attr (qw(EventID)) { 744 $matches->{$attr} = 0; 745 if (exists $filter->{$attr}) { 746 foreach my $item (split(',', $filter->{$attr})) { 747 #printf "items: %s ? %s\n", $item, $event->{$attr}; 748 #if (lc $item eq lc ($event->{$attr} & 0xffff)) { 749 if ($item == ($event->{$attr} & 0xffff)) { 750 #printf "-> %s eq %s\n", lc $item, lc ($event->{$attr} & 0xffff); 751 $matches->{$attr}++; 752 } 753 } 754 } else { 755 #printf "no filter for %s\n", $attr; 756 #$matches->{$attr}++; 757 } 758 } 759 foreach my $attr (qw(EventType)) { 760 $matches->{$attr} = 0; 761 if (exists $filter->{$attr}) { 762 foreach my $item (split(',', $filter->{$attr})) { 763 if ((lc $item =~ /^succ/ && $event->{$attr} == EVENTLOG_SUCCESS) || 764 (lc $item =~ /warn/ && $event->{$attr} == EVENTLOG_WARNING_TYPE) || 765 (lc $item =~ /err/ && $event->{$attr} == EVENTLOG_ERROR_TYPE) || 766 (lc $item =~ /info/ && $event->{$attr} == EVENTLOG_INFORMATION_TYPE) || 767 (lc $item =~ /audit.*succ/ && $event->{$attr} == EVENTLOG_AUDIT_SUCCESS) || 768 (lc $item =~ /fail/ && $event->{$attr} == EVENTLOG_AUDIT_FAILURE)) { 769 #printf "type %s matched\n", $item; 770 $matches->{$attr}++; 771 } 772 } 773 } else { 774 #printf "no filter for %s\n", $attr; 775 #$matches->{$attr}++; 776 } 777 } 778 #printf "%s\n", Data::Dumper::Dumper($matches); 779 if ($filter->{operation} eq 'and') { 780 return (scalar(grep { $matches->{$_} } keys %{$matches}) == 4) ? 1 : 0; 781 } else { 782 return (scalar(grep { $matches->{$_} } keys %{$matches}) == 0) ? 0 : 1; 783 } 784} 785 786sub trace { 787 my $format = shift; 788 if (-f $tracefile) { 789 my $logfh = new IO::File; 790 $logfh->autoflush(1); 791 if ($logfh->open($tracefile, "a")) { 792 $logfh->printf("%s: ", scalar localtime); 793 $logfh->printf($format, @_); 794 $logfh->printf("\n"); 795 $logfh->close(); 796 } 797 } 798} 799 800 801sub AUTOLOAD { 802 #printf "uarghh %s\n", $AUTOLOAD; 803} 804 8051; 806