1 | #
|
---|
2 | # EventLog.pm
|
---|
3 | #
|
---|
4 | # Creates an object oriented interface to the Windows NT Evenlog
|
---|
5 | # Written by Jesse Dougherty
|
---|
6 | #
|
---|
7 |
|
---|
8 | package Win32::EventLog;
|
---|
9 |
|
---|
10 | use strict;
|
---|
11 | use vars qw($VERSION $AUTOLOAD @ISA @EXPORT $GetMessageText);
|
---|
12 | $VERSION = '0.074';
|
---|
13 |
|
---|
14 | require Exporter;
|
---|
15 | require DynaLoader;
|
---|
16 |
|
---|
17 | die "The Win32::Eventlog module works only on Windows NT"
|
---|
18 | unless Win32::IsWinNT();
|
---|
19 |
|
---|
20 | @ISA= qw(Exporter DynaLoader);
|
---|
21 | @EXPORT = qw(
|
---|
22 | EVENTLOG_AUDIT_FAILURE
|
---|
23 | EVENTLOG_AUDIT_SUCCESS
|
---|
24 | EVENTLOG_BACKWARDS_READ
|
---|
25 | EVENTLOG_END_ALL_PAIRED_EVENTS
|
---|
26 | EVENTLOG_END_PAIRED_EVENT
|
---|
27 | EVENTLOG_ERROR_TYPE
|
---|
28 | EVENTLOG_FORWARDS_READ
|
---|
29 | EVENTLOG_INFORMATION_TYPE
|
---|
30 | EVENTLOG_PAIRED_EVENT_ACTIVE
|
---|
31 | EVENTLOG_PAIRED_EVENT_INACTIVE
|
---|
32 | EVENTLOG_SEEK_READ
|
---|
33 | EVENTLOG_SEQUENTIAL_READ
|
---|
34 | EVENTLOG_START_PAIRED_EVENT
|
---|
35 | EVENTLOG_SUCCESS
|
---|
36 | EVENTLOG_WARNING_TYPE
|
---|
37 | );
|
---|
38 |
|
---|
39 | $GetMessageText=0;
|
---|
40 |
|
---|
41 | sub AUTOLOAD {
|
---|
42 | my($constname);
|
---|
43 | ($constname = $AUTOLOAD) =~ s/.*:://;
|
---|
44 | # reset $! to zero to reset any current errors.
|
---|
45 | local $! = 0;
|
---|
46 | my $val = constant($constname, @_ ? $_[0] : 0);
|
---|
47 | if ($!) {
|
---|
48 | if ($! =~ /Invalid/) {
|
---|
49 | $AutoLoader::AUTOLOAD = $AUTOLOAD;
|
---|
50 | goto &AutoLoader::AUTOLOAD;
|
---|
51 | }
|
---|
52 | else {
|
---|
53 | my ($pack,$file,$line) = caller;
|
---|
54 | die "Unknown Win32::EventLog macro $constname, at $file line $line.\n";
|
---|
55 | }
|
---|
56 | }
|
---|
57 | eval "sub $AUTOLOAD { $val }";
|
---|
58 | goto &$AUTOLOAD;
|
---|
59 | }
|
---|
60 |
|
---|
61 | #
|
---|
62 | # new()
|
---|
63 | #
|
---|
64 | # Win32::EventLog->new("source name", "ServerName");
|
---|
65 | #
|
---|
66 | sub new {
|
---|
67 | die "usage: PACKAGE->new(SOURCENAME[, SERVERNAME])\n" unless @_ > 1;
|
---|
68 | my ($class,$source,$server) = @_;
|
---|
69 | my $handle;
|
---|
70 |
|
---|
71 | # Create new handle
|
---|
72 | if ($source !~ /\\/) {
|
---|
73 | OpenEventLog($handle, $server, $source);
|
---|
74 | }
|
---|
75 | else {
|
---|
76 | OpenBackupEventLog($handle, $server, $source);
|
---|
77 | }
|
---|
78 | return bless {handle => $handle,
|
---|
79 | Source => $source,
|
---|
80 | Computer => $server} => $class;
|
---|
81 | }
|
---|
82 |
|
---|
83 | sub DESTROY {shift->Close}
|
---|
84 |
|
---|
85 | #
|
---|
86 | # Open (the rather braindead old way)
|
---|
87 | # A variable initialized to empty must be supplied as the first
|
---|
88 | # arg, followed by whatever new() takes
|
---|
89 | #
|
---|
90 | sub Open {
|
---|
91 | $_[0] = Win32::EventLog->new($_[1],$_[2]);
|
---|
92 | }
|
---|
93 |
|
---|
94 | sub OpenBackup {
|
---|
95 | my ($class,$source,$server) = @_;
|
---|
96 | OpenBackupEventLog(my $handle, $server, $source);
|
---|
97 | return bless {handle => $handle,
|
---|
98 | Source => $source,
|
---|
99 | Computer => $server} => $class;
|
---|
100 | }
|
---|
101 |
|
---|
102 | sub Backup {
|
---|
103 | die " usage: OBJECT->Backup(FILENAME)\n" unless @_ == 2;
|
---|
104 | my ($self,$file) = @_;
|
---|
105 | return BackupEventLog($self->{handle}, $file);
|
---|
106 | }
|
---|
107 |
|
---|
108 | sub Close {
|
---|
109 | my $self = shift;
|
---|
110 | CloseEventLog($self->{handle});
|
---|
111 | $self->{handle} = 0;
|
---|
112 | }
|
---|
113 |
|
---|
114 | # Read
|
---|
115 | # Note: the EventInfo arguement requires a hash reference.
|
---|
116 | sub Read {
|
---|
117 | my $self = shift;
|
---|
118 |
|
---|
119 | die "usage: OBJECT->Read(FLAGS, RECORDOFFSET, HASHREF)\n" unless @_ == 3;
|
---|
120 |
|
---|
121 | my ($readflags,$recordoffset) = @_;
|
---|
122 | # The following is stolen shamelessly from Wyt's tests for the registry.
|
---|
123 | my $result = ReadEventLog($self->{handle}, $readflags, $recordoffset,
|
---|
124 | my $header, my $source, my $computer, my $sid,
|
---|
125 | my $data, my $strings);
|
---|
126 | my ($length,
|
---|
127 | $reserved,
|
---|
128 | $recordnumber,
|
---|
129 | $timegenerated,
|
---|
130 | $timewritten,
|
---|
131 | $eventid,
|
---|
132 | $eventtype,
|
---|
133 | $numstrings,
|
---|
134 | $eventcategory,
|
---|
135 | $reservedflags,
|
---|
136 | $closingrecordnumber,
|
---|
137 | $stringoffset,
|
---|
138 | $usersidlength,
|
---|
139 | $usersidoffset,
|
---|
140 | $datalength,
|
---|
141 | $dataoffset) = unpack('l6s4l6', $header);
|
---|
142 |
|
---|
143 | # make a hash out of the values returned from ReadEventLog.
|
---|
144 | my %h = ( Source => $source,
|
---|
145 | Computer => $computer,
|
---|
146 | Length => $datalength,
|
---|
147 | Category => $eventcategory,
|
---|
148 | RecordNumber => $recordnumber,
|
---|
149 | TimeGenerated => $timegenerated,
|
---|
150 | Timewritten => $timewritten,
|
---|
151 | EventID => $eventid,
|
---|
152 | EventType => $eventtype,
|
---|
153 | ClosingRecordNumber => $closingrecordnumber,
|
---|
154 | User => $sid,
|
---|
155 | Strings => $strings,
|
---|
156 | Data => $data,
|
---|
157 | );
|
---|
158 |
|
---|
159 | # get the text message here
|
---|
160 | if ($result and $GetMessageText) {
|
---|
161 | GetEventLogText($source, $eventid, $strings, $numstrings, my $message);
|
---|
162 | $h{Message} = $message;
|
---|
163 | }
|
---|
164 |
|
---|
165 | if (ref($_[2]) eq 'HASH') {
|
---|
166 | %{$_[2]} = %h; # this needed for Read(...,\%foo) case
|
---|
167 | }
|
---|
168 | else {
|
---|
169 | $_[2] = \%h;
|
---|
170 | }
|
---|
171 | return $result;
|
---|
172 | }
|
---|
173 |
|
---|
174 | sub GetMessageText {
|
---|
175 | my $self = shift;
|
---|
176 | local $^W;
|
---|
177 | GetEventLogText($self->{Source},
|
---|
178 | $self->{EventID},
|
---|
179 | $self->{Strings},
|
---|
180 | $self->{Strings} =~ tr/\0/\0/,
|
---|
181 | my $message);
|
---|
182 |
|
---|
183 | $self->{Message} = $message;
|
---|
184 | return $message;
|
---|
185 | }
|
---|
186 |
|
---|
187 | sub Report {
|
---|
188 | die "usage: OBJECT->Report( HASHREF )\n" unless @_ == 2;
|
---|
189 | my ($self,$EventInfo) = @_;
|
---|
190 |
|
---|
191 | die "Win32::EventLog::Report requires a hash reference as arg 2\n"
|
---|
192 | unless ref($EventInfo) eq "HASH";
|
---|
193 |
|
---|
194 | my $computer = $EventInfo->{Computer} ? $EventInfo->{Computer}
|
---|
195 | : $self->{Computer};
|
---|
196 | my $source = exists($EventInfo->{Source}) ? $EventInfo->{Source}
|
---|
197 | : $self->{Source};
|
---|
198 |
|
---|
199 | return WriteEventLog($computer, $source, $EventInfo->{EventType},
|
---|
200 | $EventInfo->{Category}, $EventInfo->{EventID}, 0,
|
---|
201 | $EventInfo->{Data}, split(/\0/, $EventInfo->{Strings}));
|
---|
202 |
|
---|
203 | }
|
---|
204 |
|
---|
205 | sub GetOldest {
|
---|
206 | my $self = shift;
|
---|
207 | die "usage: OBJECT->GetOldest( SCALAREF )\n" unless @_ == 1;
|
---|
208 | return GetOldestEventLogRecord($self->{handle},$_[0]);
|
---|
209 | }
|
---|
210 |
|
---|
211 | sub GetNumber {
|
---|
212 | my $self = shift;
|
---|
213 | die "usage: OBJECT->GetNumber( SCALARREF )\n" unless @_ == 1;
|
---|
214 | return GetNumberOfEventLogRecords($self->{handle}, $_[0]);
|
---|
215 | }
|
---|
216 |
|
---|
217 | sub Clear {
|
---|
218 | my ($self,$file) = @_;
|
---|
219 | die "usage: OBJECT->Clear( FILENAME )\n" unless @_ == 2;
|
---|
220 | return ClearEventLog($self->{handle}, $file);
|
---|
221 | }
|
---|
222 |
|
---|
223 | bootstrap Win32::EventLog;
|
---|
224 |
|
---|
225 | 1;
|
---|
226 | __END__
|
---|
227 |
|
---|
228 | =head1 NAME
|
---|
229 |
|
---|
230 | Win32::EventLog - Process Win32 Event Logs from Perl
|
---|
231 |
|
---|
232 | =head1 SYNOPSIS
|
---|
233 |
|
---|
234 | use Win32::EventLog
|
---|
235 | $handle=Win32::EventLog->new("Application");
|
---|
236 |
|
---|
237 | =head1 DESCRIPTION
|
---|
238 |
|
---|
239 | This module implements most of the functionality available from the
|
---|
240 | Win32 API for accessing and manipulating Win32 Event Logs. The access
|
---|
241 | to the EventLog routines is divided into those that relate to an
|
---|
242 | EventLog object and its associated methods and those that relate other
|
---|
243 | EventLog tasks (like adding an EventLog record).
|
---|
244 |
|
---|
245 | =head1 The EventLog Object and its Methods
|
---|
246 |
|
---|
247 | The following methods are available to open, read, close and backup
|
---|
248 | EventLogs.
|
---|
249 |
|
---|
250 | =over 4
|
---|
251 |
|
---|
252 | =item Win32::EventLog->new(SOURCENAME [,SERVERNAME]);
|
---|
253 |
|
---|
254 | The new() method creates a new EventLog object and returns a handle
|
---|
255 | to it. This hande is then used to call the methods below.
|
---|
256 |
|
---|
257 | The method is overloaded in that if the supplied SOURCENAME
|
---|
258 | argument contains one or more literal '\' characters (an illegal
|
---|
259 | character in a SOURCENAME), it assumes that you are trying to open
|
---|
260 | a backup eventlog and uses SOURCENAME as the backup eventlog to
|
---|
261 | open. Note that when opening a backup eventlog, the SERVERNAME
|
---|
262 | argument is ignored (as it is in the underlying Win32 API). For
|
---|
263 | EventLogs on remote machines, the SOURCENAME parameter must
|
---|
264 | therefore be specified as a UNC path.
|
---|
265 |
|
---|
266 | =item $handle->Backup(FILENAME);
|
---|
267 |
|
---|
268 | The Backup() method backs up the EventLog represented by $handle. It
|
---|
269 | takes a single arguemt, FILENAME. When $handle represents an
|
---|
270 | EventLog on a remote machine, FILENAME is filename on the remote
|
---|
271 | machine and cannot be a UNC path (i.e you must use F<C:\TEMP\App.EVT>).
|
---|
272 | The method will fail if the log file already exists.
|
---|
273 |
|
---|
274 | =item $handle->Read(FLAGS, OFFSET, HASHREF);
|
---|
275 |
|
---|
276 | The Read() method read an EventLog entry from the EventLog represented
|
---|
277 | by $handle.
|
---|
278 |
|
---|
279 | =item $handle->Close();
|
---|
280 |
|
---|
281 | The Close() method closes the EventLog represented by $handle. After
|
---|
282 | Close() has been called, any further attempt to use the EventLog
|
---|
283 | represented by $handle will fail.
|
---|
284 |
|
---|
285 | =item $handle->GetOldest(SCALARREF);
|
---|
286 |
|
---|
287 | The GetOldest() method number of the the oldest EventLog record in
|
---|
288 | the EventLog represented by $handle. This is required to correctly
|
---|
289 | compute the OFFSET required by the Read() method.
|
---|
290 |
|
---|
291 | =item $handle->GetNumber(SCALARREF);
|
---|
292 |
|
---|
293 | The GetNumber() method returns the number of EventLog records in
|
---|
294 | the EventLog represented by $handle. The number of the most recent
|
---|
295 | record in the EventLog is therefore computed by
|
---|
296 |
|
---|
297 | $handle->GetOldest($oldest);
|
---|
298 | $handle->GetNumber($lastRec);
|
---|
299 | $lastRecOffset=$oldest+$lastRec;
|
---|
300 |
|
---|
301 | =item $handle->Clear(FILENAME);
|
---|
302 |
|
---|
303 | The Clear() method clears the EventLog represented by $handle. If
|
---|
304 | you provide a non-null FILENAME, the EventLog will be backed up
|
---|
305 | into FILENAME before the EventLog is cleared. The method will fail
|
---|
306 | if FILENAME is specified and the file refered to exists. Note also
|
---|
307 | that FILENAME specifies a file local to the machine on which the
|
---|
308 | EventLog resides and cannot be specified as a UNC name.
|
---|
309 |
|
---|
310 | =item $handle->Report(HASHREF);
|
---|
311 |
|
---|
312 | The Report() method generates an EventLog entry. The HASHREF should
|
---|
313 | contain the following keys:
|
---|
314 |
|
---|
315 | =over 4
|
---|
316 |
|
---|
317 | =item C<Computer>
|
---|
318 |
|
---|
319 | The C<Computer> field specfies which computer you want the EventLog
|
---|
320 | entry recorded. If this key doesn't exist, the server name used to
|
---|
321 | create the $handle is used.
|
---|
322 |
|
---|
323 | =item C<Source>
|
---|
324 |
|
---|
325 | The C<Source> field specifies the source that generated the EventLog
|
---|
326 | entry. If this key doesn't exist, the source name used to create the
|
---|
327 | $handle is used.
|
---|
328 |
|
---|
329 | =item C<EventType>
|
---|
330 |
|
---|
331 | The C<EventType> field should be one of the constants
|
---|
332 |
|
---|
333 | =over 4
|
---|
334 |
|
---|
335 | =item C<EVENTLOG_ERROR_TYPE>
|
---|
336 |
|
---|
337 | An Error event is being logged.
|
---|
338 |
|
---|
339 | =item C<EVENTLOG_WARNING_TYPE>
|
---|
340 |
|
---|
341 | A Warning event is being logged.
|
---|
342 |
|
---|
343 | =item C<EVENTLOG_INFORMATION_TYPE>
|
---|
344 |
|
---|
345 | An Information event is being logged.
|
---|
346 |
|
---|
347 | =item C<EVENTLOG_AUDIT_SUCCESS>
|
---|
348 |
|
---|
349 | A Success Audit event is being logged (typically in the Security
|
---|
350 | EventLog).
|
---|
351 |
|
---|
352 | =item C<EVENTLOG_AUDIT_FAILURE>
|
---|
353 |
|
---|
354 | A Failure Audit event is being logged (typically in the Security
|
---|
355 | EventLog).
|
---|
356 |
|
---|
357 | =back
|
---|
358 |
|
---|
359 | These constants are exported into the main namespace by default.
|
---|
360 |
|
---|
361 | =item C<Category>
|
---|
362 |
|
---|
363 | The C<Category> field can have any value you want. It is specific to
|
---|
364 | the particular Source.
|
---|
365 |
|
---|
366 | =item C<EventID>
|
---|
367 |
|
---|
368 | The C<EventID> field should contain the ID of the message that this
|
---|
369 | event pertains too. This assumes that you have an associated message
|
---|
370 | file (indirectly referenced by the field C<Source>).
|
---|
371 |
|
---|
372 | =item C<Data>
|
---|
373 |
|
---|
374 | The C<Data> field contains raw data associated with this event.
|
---|
375 |
|
---|
376 | =item C<Strings>
|
---|
377 |
|
---|
378 | The C<Strings> field contains the single string that itself contains
|
---|
379 | NUL terminated sub-strings. This are used with the EventID to generate
|
---|
380 | the message as seen from (for example) the Event Viewer application.
|
---|
381 |
|
---|
382 | =back
|
---|
383 |
|
---|
384 | =back
|
---|
385 |
|
---|
386 | =head1 Other Win32::EventLog functions.
|
---|
387 |
|
---|
388 | The following functions are part of the Win32::EventLog package but
|
---|
389 | are not callable from an EventLog object.
|
---|
390 |
|
---|
391 | =over 4
|
---|
392 |
|
---|
393 | =item GetMessageText(HASHREF);
|
---|
394 |
|
---|
395 | The GetMessageText() function assumes that HASHREF was obtained by
|
---|
396 | a call to C<$handle-E<gt>Read()>. It returns the formatted string that
|
---|
397 | represents the fully resolved text of the EventLog message (such as
|
---|
398 | would be seen in the Windows NT Event Viewer). For convenience, the
|
---|
399 | key 'Message' in the supplied HASHREF is also set to the return value
|
---|
400 | of this function.
|
---|
401 |
|
---|
402 | If you set the variable $Win32::EventLog::GetMessageText to 1 then
|
---|
403 | each call to C<$handle-E<gt>Read()> will call this function automatically.
|
---|
404 |
|
---|
405 | =back
|
---|
406 |
|
---|
407 | =head1 Example 1
|
---|
408 |
|
---|
409 | The following example illustrates the way in which the EventLog module
|
---|
410 | can be used. It opens the System EventLog and reads through it from
|
---|
411 | oldest to newest records. For each record from the B<Source> EventLog
|
---|
412 | it extracts the full text of the Entry and prints the EventLog message
|
---|
413 | text out.
|
---|
414 |
|
---|
415 | use Win32::EventLog;
|
---|
416 |
|
---|
417 | $handle=Win32::EventLog->new("System", $ENV{ComputerName})
|
---|
418 | or die "Can't open Application EventLog\n";
|
---|
419 | $handle->GetNumber($recs)
|
---|
420 | or die "Can't get number of EventLog records\n";
|
---|
421 | $handle->GetOldest($base)
|
---|
422 | or die "Can't get number of oldest EventLog record\n";
|
---|
423 |
|
---|
424 | while ($x < $recs) {
|
---|
425 | $handle->Read(EVENTLOG_FORWARDS_READ|EVENTLOG_SEEK_READ,
|
---|
426 | $base+$x,
|
---|
427 | $hashRef)
|
---|
428 | or die "Can't read EventLog entry #$x\n";
|
---|
429 | if ($hashRef->{Source} eq "EventLog") {
|
---|
430 | Win32::EventLog::GetMessageText($hashRef);
|
---|
431 | print "Entry $x: $hashRef->{Message}\n";
|
---|
432 | }
|
---|
433 | $x++;
|
---|
434 | }
|
---|
435 |
|
---|
436 | =head1 Example 2
|
---|
437 |
|
---|
438 | To backup and clear the EventLogs on a remote machine, do the following :-
|
---|
439 |
|
---|
440 | use Win32::EventLog;
|
---|
441 |
|
---|
442 | $myServer="\\\\my-server"; # your servername here.
|
---|
443 | my($date)=join("-", ((split(/\s+/, scalar(localtime)))[0,1,2,4]));
|
---|
444 | my($dest);
|
---|
445 |
|
---|
446 | for my $eventLog ("Application", "System", "Security") {
|
---|
447 | $handle=Win32::EventLog->new($eventLog, $myServer)
|
---|
448 | or die "Can't open Application EventLog on $myServer\n";
|
---|
449 |
|
---|
450 | $dest="C:\\BackupEventLogs\\$eventLog\\$date.evt";
|
---|
451 | $handle->Backup($dest)
|
---|
452 | or warn "Could not backup and clear the $eventLog EventLog on $myServer ($^E)\n";
|
---|
453 |
|
---|
454 | $handle->Close;
|
---|
455 | }
|
---|
456 |
|
---|
457 | Note that only the Clear method is required. Note also that if the
|
---|
458 | file $dest exists, the function will fail.
|
---|
459 |
|
---|
460 | =head1 BUGS
|
---|
461 |
|
---|
462 | None currently known.
|
---|
463 |
|
---|
464 | The test script for 'make test' should be re-written to use the
|
---|
465 | EventLog object.
|
---|
466 |
|
---|
467 | =head1 AUTHOR
|
---|
468 |
|
---|
469 | Original code by Jesse Dougherty for HiP Communications. Additional
|
---|
470 | fixes and updates attributed to Martin Pauley
|
---|
471 | <[email protected]>) and Bret Giddings ([email protected]).
|
---|