win32-eventlog 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +139 -0
- data/MANIFEST +22 -0
- data/README +63 -0
- data/doc/tutorial.txt +140 -0
- data/lib/win32/eventlog.rb +754 -0
- data/lib/win32/mc.rb +104 -0
- data/lib/win32/test.rb +8 -0
- data/test/foo.mc +24 -0
- data/test/tc_eventlog.rb +228 -0
- data/test/tc_mc.rb +49 -0
- data/test/ts_all.rb +7 -0
- metadata +68 -0
data/CHANGES
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
= 0.4.2 - 6-Aug-2006
|
2
|
+
* Fixed a bug in the EventLog.read method related to the
|
3
|
+
EVENTLOG_BACKWARDS_READ flag.
|
4
|
+
* Fixed a bug in the EventLog.read method where the event_type was not being
|
5
|
+
set properly. Thanks go to "TheR" for the spot and the fix.
|
6
|
+
* Fixed bugs in the EventLog#tail method where it would fail if the log was
|
7
|
+
full and dups would appear.
|
8
|
+
|
9
|
+
= 0.4.1 - 28-May-2006
|
10
|
+
* Fixed a bug in the EventLog#notify_change method where the handle would
|
11
|
+
die after five or six reads. Thanks go to botp for the spot.
|
12
|
+
|
13
|
+
= 0.4.0 - 24-May-2006
|
14
|
+
* Now pure Ruby.
|
15
|
+
* Now includes a gemspec.
|
16
|
+
* Fixed a major bug the EventLog#tail method where it was reading the log in
|
17
|
+
the wrong order (!!!).
|
18
|
+
* Fixed a bug in the EventLog#report_event with regards to long category_id's.
|
19
|
+
Actually, this wasn't so much "fixed" as "avoided" by virtue of using a
|
20
|
+
pure Ruby solution.
|
21
|
+
* Added the EventLog#full? method.
|
22
|
+
* Documentation updates and corrections, including the tutorial on creating
|
23
|
+
and writing to your own event source.
|
24
|
+
|
25
|
+
== 0.3.3 - 2-Jan-2006
|
26
|
+
* If EventLog.new fails, it now raises EventLogError instead of StandardError.
|
27
|
+
* Added documentation to the eventlog.txt file for EventLog#notify_change.
|
28
|
+
* Added inline rdoc to the C source code.
|
29
|
+
* Code cleanup.
|
30
|
+
|
31
|
+
== 0.3.2 - 26-May-2005
|
32
|
+
* Code now more Unicode friendly, and other cleanup.
|
33
|
+
* Made the .txt files rdoc friendly, and removed the .rd files.
|
34
|
+
* Minor adjustments to the test suite.
|
35
|
+
|
36
|
+
== 0.3.1 - 16-Feb-2005
|
37
|
+
* Minor modifications to the source to eliminate some warnings that appeared
|
38
|
+
in Ruby 1.8.2 or later, plus other minor tweaks.
|
39
|
+
* Renamed the sample programs and moved the 'examples' directory to the
|
40
|
+
toplevel directory.
|
41
|
+
* Made this document rdoc friendly.
|
42
|
+
|
43
|
+
== 0.3.0 - 30-Oct-2004
|
44
|
+
* Added the notify_change() and tail() methods.
|
45
|
+
* Added the write() alias for report_event().
|
46
|
+
* The read() class and instance methods now return an array of structs if
|
47
|
+
no block is given.
|
48
|
+
* Added the 'rubymsg.mc' message category file. If installed, this will
|
49
|
+
create a 'RubyMsg' event source for use with win32-service, though you
|
50
|
+
may use it for whatever you wish. See the README for more details.
|
51
|
+
* Renamed struct back to EventLogStruct - sorry, sorry - I promise I won't
|
52
|
+
change this again.
|
53
|
+
* Fixed a bug where using EventLog::SEEK_READ did not work properly (thanks
|
54
|
+
Park).
|
55
|
+
* Added the notify.rb test script under doc/examples.
|
56
|
+
|
57
|
+
== 0.2.5 - 28-Oct-2004
|
58
|
+
* Modified the open() and read() class methods to default to "Application"
|
59
|
+
for the default Event source.
|
60
|
+
* Removed the .html files. You can generate these on your own with rd2.
|
61
|
+
|
62
|
+
== 0.2.4 - 19-Oct-2004
|
63
|
+
* Fixed a bug where a segfault would result if an Event Log entry were
|
64
|
+
somehow corrupted. Thanks go again to Joey Gibson for the spot.
|
65
|
+
* Very minor doc corrections in tutorial.txt and eventlog.txt
|
66
|
+
|
67
|
+
== 0.2.3 - 14-Oct-2004
|
68
|
+
* Fixed a bug where some Event Log descriptions were returning nil instead
|
69
|
+
of the actual description. Thanks go to Joey Gibson for the spot.
|
70
|
+
* Minor test suite and doc additions, including a "future plan" that may
|
71
|
+
be of interest.
|
72
|
+
|
73
|
+
== 0.2.2 - 02-Jul-2004
|
74
|
+
* Fixed a bug in the read() method where not all the records were being
|
75
|
+
returned.
|
76
|
+
* Fixed calls to rb_time_new() - second argument appears to be mandatory as
|
77
|
+
of 1.8.2.
|
78
|
+
* Renamed struct returned by read() to "EventLog".
|
79
|
+
* Changed struct members "id" and "type" to "event_id" and "event_type",
|
80
|
+
respectively. This was to avoid any confusion with the builtin Ruby
|
81
|
+
methods of the same name.
|
82
|
+
* Replaced STR2CSTR() with StringValuePtr(), as the former is deprecated.
|
83
|
+
This means that as of this version, Ruby 1.8.0 or later is required.
|
84
|
+
* Moved the sample programs to doc/examples.
|
85
|
+
|
86
|
+
== 0.2.1 - 04-Mar-2004
|
87
|
+
* The add_event_source() and report_event() methods now accept symbols as
|
88
|
+
well as do key validation.
|
89
|
+
* Moved the EventLogError class under the Win32 module.
|
90
|
+
* Normalized the tc_eventlog.rb and tc_mc.rb scripts by making it easier to
|
91
|
+
run them standalone from another directory.
|
92
|
+
* Minor modification to the build process.
|
93
|
+
* Added a test_write.rb script for general futzing when it comes to creating
|
94
|
+
and writing events to the log.
|
95
|
+
* Doc updates, warranty information added.
|
96
|
+
|
97
|
+
== 0.2.0 - 1-Feb-2004
|
98
|
+
* Changed the Win32EventLogError class to just EventLogError. I felt the
|
99
|
+
Win32 was redundant given that the class is already under the Win32 module.
|
100
|
+
* Fixed bug where bogus error messages were returned because I was calling
|
101
|
+
GetLastError() too late.
|
102
|
+
* Fixed bug where error messages were getting cut off and/or garbled due to
|
103
|
+
\r\n character. (Thanks Park Heesob).
|
104
|
+
* Added open_backup class method.
|
105
|
+
|
106
|
+
== 0.1.4 - 16-Jan-2004
|
107
|
+
* Slightly better handling of the EventLog#close method. Adopted from code
|
108
|
+
previously submitted by Park Heesob.
|
109
|
+
* Fixed a bug in the (internal) GetUser() function that caused a segfault if
|
110
|
+
there were 250+ records. That function has been greatly simplified.
|
111
|
+
* Removed the rb_ensure() approach. What I thought was a memory leak was not,
|
112
|
+
in fact, a memory leak. Also, it was slowing down the method considerably.
|
113
|
+
* Minor test suite update.
|
114
|
+
|
115
|
+
== 0.1.3 - 7-Jan-2004
|
116
|
+
* Fixed memory leaks in read() method (Park Heesob).
|
117
|
+
* Added read() class method (Daniel Berger).
|
118
|
+
* Test suite and doc additions & updates
|
119
|
+
|
120
|
+
== 0.1.2 - 30-Nov-2003
|
121
|
+
* Added the report_event method. You can now write to the event log.
|
122
|
+
* Added the add_event_source class method, allowing you to register a custom
|
123
|
+
event source.
|
124
|
+
* Added the win32-mc package to the distribution. This is a simple
|
125
|
+
wrapper to generate .dll files from .mc files.
|
126
|
+
* Added Event Type constants
|
127
|
+
* Test suite additions
|
128
|
+
* Documentation updates and additions, including a small tutorial.
|
129
|
+
|
130
|
+
== 0.1.1 - 5-Nov-2003
|
131
|
+
* Fixed the free() call, and added implicit eventlog closing (when the
|
132
|
+
EventLog object is free'd).
|
133
|
+
* Simplified constants by removing 'EVENTLOG_' portion of name.
|
134
|
+
* Cleanup in the GetDescription() method, which was causing segfaults in
|
135
|
+
conjunction with TestUnit.
|
136
|
+
* Minor doc updates and cleanup.
|
137
|
+
|
138
|
+
== 0.1.0 - 14-Oct-2003
|
139
|
+
* Initial release
|
data/MANIFEST
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
MANIFEST
|
2
|
+
CHANGES
|
3
|
+
README
|
4
|
+
install.rb
|
5
|
+
win32-eventlog.gemspec
|
6
|
+
|
7
|
+
doc/tutorial.txt
|
8
|
+
|
9
|
+
examples/test_read.rb
|
10
|
+
examples/test_write.rb
|
11
|
+
examples/test_notify.rb
|
12
|
+
|
13
|
+
lib/win32/eventlog.rb
|
14
|
+
lib/win32/mc.rb
|
15
|
+
|
16
|
+
misc/install_msg.rb
|
17
|
+
misc/rubymsg.mc
|
18
|
+
|
19
|
+
test/foo.mc
|
20
|
+
test/ts_all.rb
|
21
|
+
test/tc_eventlog.rb
|
22
|
+
test/tc_mc.rb
|
data/README
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
= What is it?
|
2
|
+
An interface to the Windows Event Log.
|
3
|
+
|
4
|
+
= Prerequisites
|
5
|
+
Ruby 1.8.2 or later.
|
6
|
+
windows-pr 0.5.0 or later.
|
7
|
+
|
8
|
+
= Installation
|
9
|
+
== Manual install
|
10
|
+
ruby test\tc_eventlog.rb (optional)
|
11
|
+
ruby install.rb
|
12
|
+
== Gem Install
|
13
|
+
ruby win32-eventlog.gemspec
|
14
|
+
gem install win32-eventlog-X.Y.Z-mswin32.gem
|
15
|
+
|
16
|
+
This will install both the win32-eventlog and win32-mc packages. The latter
|
17
|
+
is strictly for turning .mc files into .dll files. See the mc documentation
|
18
|
+
for more details.
|
19
|
+
|
20
|
+
= Installing the 'RubyMsg' event source
|
21
|
+
If you wish to install the RubyMsg event source, run the 'install_msg.rb'
|
22
|
+
script in the 'misc' directory. This will create a 'rubymsg' directory under
|
23
|
+
your toplevel Ruby installation directory (usually C:\ruby), and create the
|
24
|
+
.dll, .h, .rc and .res files there, in addition to copying the rubymsg.mc file.
|
25
|
+
It will then install the 'RubyMsg' event source into your registry.
|
26
|
+
|
27
|
+
DO NOT MOVE THE DLL FILE ONCE IT IS INSTALLED! If you do, you will have
|
28
|
+
to delete the registry entry and reinstall it with the correct path.
|
29
|
+
|
30
|
+
Take a look at the rubymsg.mc file for the category and message values. If
|
31
|
+
you do not understand this, please read the 'tutorial.txt' file in the 'doc'
|
32
|
+
directory.
|
33
|
+
|
34
|
+
= Additional documentation
|
35
|
+
If you are unfamiliar with message files and event logging on Windows in
|
36
|
+
general, please read the 'tutorial.txt' file.
|
37
|
+
|
38
|
+
There are also a couple of sample test scripts under the 'examples'
|
39
|
+
directory if you want to futz around and get a feel for how things work.
|
40
|
+
|
41
|
+
= If the tc_mc.rb tests fail
|
42
|
+
There's a chance that you either don't have the mc, rc and/or link commands
|
43
|
+
installed or they're not in your %PATH%. If you have MSVC++, you should have
|
44
|
+
them somewhere on your system.
|
45
|
+
|
46
|
+
= Known Issues
|
47
|
+
You may see this warning during the build and/or test phase:
|
48
|
+
|
49
|
+
LINK : warning LNK4068: /MACHINE not specified; defaulting to X86
|
50
|
+
|
51
|
+
You may ignore this warning.
|
52
|
+
|
53
|
+
= License
|
54
|
+
Ruby's
|
55
|
+
|
56
|
+
= Warranty
|
57
|
+
This package is provided "as is" and without any express or
|
58
|
+
implied warranties, including, without limitation, the implied
|
59
|
+
warranties of merchantability and fitness for a particular purpose.
|
60
|
+
|
61
|
+
= Authors
|
62
|
+
Daniel J. Berger
|
63
|
+
Park Heesob
|
data/doc/tutorial.txt
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
= Information about Message Files
|
2
|
+
Each event source should register message files that contain description
|
3
|
+
strings for each event identifier, event category, and parameter. Register
|
4
|
+
these files in the EventMessageFile, CategoryMessageFile, and
|
5
|
+
ParameterMessageFile registry values for the event source.
|
6
|
+
|
7
|
+
Note: ParameterMessageFiles are not yet supported in the add_event_source
|
8
|
+
method and probably won't be unless requested.
|
9
|
+
|
10
|
+
You can create one message file that contains descriptions for the event
|
11
|
+
identifiers, categories, and parameters, or create three separate message
|
12
|
+
files. Several applications can share the same message file.
|
13
|
+
|
14
|
+
You should typically create message files as resource-only DLLs. They are
|
15
|
+
smaller and faster than ordinary DLLs.
|
16
|
+
|
17
|
+
= What does a .mc file look like?
|
18
|
+
|
19
|
+
A .mc file is just a plain text file that is parsed by the "mc" utility to
|
20
|
+
generate a header and, ultimately, a .dll file. Here is a quick sample.
|
21
|
+
Note that there must be a newline after the last '.' at the bottom.
|
22
|
+
The ';' character denotes a comment.
|
23
|
+
|
24
|
+
; foo.mc
|
25
|
+
MessageId=0x1
|
26
|
+
SymbolicName=CATEGORY_ERROR
|
27
|
+
Language=English
|
28
|
+
error
|
29
|
+
.
|
30
|
+
|
31
|
+
MessageId=0x2
|
32
|
+
SymbolicName=CATEGORY_WARNING
|
33
|
+
Language=English
|
34
|
+
warning
|
35
|
+
.
|
36
|
+
|
37
|
+
MessageId=0x3
|
38
|
+
Severity=Error
|
39
|
+
SymbolicName=FOO_ERROR
|
40
|
+
Language=English
|
41
|
+
Error: %1
|
42
|
+
.
|
43
|
+
|
44
|
+
= How to generate a .dll file from a .mc file
|
45
|
+
To turn this file into a .dll you have two options. The first is to use the
|
46
|
+
command line utilities. Follow these steps:
|
47
|
+
|
48
|
+
1) mc filename.mc
|
49
|
+
2) rc -r -fo filename.res filename.rc
|
50
|
+
3) link -dll -noentry -out:filename.dll filename.res
|
51
|
+
|
52
|
+
Your other option is to use the win32-mc package, which is a simple wrapper
|
53
|
+
for the above commands, and is included with this package. You now have a
|
54
|
+
dll that you can associate with your event source (i.e. the one you associate
|
55
|
+
with your application). You can also take a look at the C header file that
|
56
|
+
.mc generates and use that in your own extensions if you like.
|
57
|
+
|
58
|
+
After this you'll need to register your event source and associate the .dll
|
59
|
+
file with it. To do that, use the EventLog.add_event_source method. Be sure
|
60
|
+
to specifiy the number of categories manually - it is not calculated
|
61
|
+
automatically by the OS.
|
62
|
+
|
63
|
+
Returning to the .mc file, the example I used actually creates two categories,
|
64
|
+
"error" and "warning", and one event message. The numbers you assign here
|
65
|
+
create corresponding (though not identical) values in the header file that
|
66
|
+
is generated. It is the values found in the header file that you pass to the
|
67
|
+
EventLog#report_event method for the category or event id. Here's the
|
68
|
+
relevant data from the foo.h file (using foo.mc above):
|
69
|
+
|
70
|
+
#define CATEGORY_ERROR 0x00000001L
|
71
|
+
#define CATEGORY_WARNING 0x00000002L
|
72
|
+
#define FOO_ERROR 0xC0000003L
|
73
|
+
|
74
|
+
In the case of categories, that number is the name number that shows up in the
|
75
|
+
"category" field in the Event Viewer. In the case of event message files, it
|
76
|
+
is the text that shows up in the event description.
|
77
|
+
|
78
|
+
The "data" field is what replaces "%1" as an actual text string in the event
|
79
|
+
log, sort of like a printf (except that it's always a string).
|
80
|
+
|
81
|
+
= Registering an event source
|
82
|
+
First, create the .dll file from the .mc file. Then register that .dll file
|
83
|
+
for an event source we'll call "foo". You can name the .dll file anything
|
84
|
+
you like, but for sanity's sake I recommend keeping the same as the event
|
85
|
+
source name.
|
86
|
+
|
87
|
+
require "win32/eventlog"
|
88
|
+
include Win32
|
89
|
+
|
90
|
+
dll_file = "c:\\wherever\\foo.dll"
|
91
|
+
|
92
|
+
EventLog.add_event_source(
|
93
|
+
"source" => "Application",
|
94
|
+
"key_name" => "foo",
|
95
|
+
"category_count" => 2,
|
96
|
+
"event_message_file" => dll_file,
|
97
|
+
"category_message_file" => dll_file
|
98
|
+
)
|
99
|
+
|
100
|
+
After you run this, you can run regedit and see that your event source has
|
101
|
+
been inserted into the registry. You can find it under:
|
102
|
+
|
103
|
+
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application.
|
104
|
+
|
105
|
+
= Writing to the event source
|
106
|
+
Now that our event source "foo" is registered, we can begin writing event
|
107
|
+
log data for it. Here's an example of how you use it:
|
108
|
+
|
109
|
+
require "win32/eventlog"
|
110
|
+
include Win32
|
111
|
+
|
112
|
+
EventLog.open("Application") do |log|
|
113
|
+
log.report_event(
|
114
|
+
:source => "foo",
|
115
|
+
:event_type => EventLog::WARN,
|
116
|
+
:category => "0x00000002L".hex,
|
117
|
+
:event_id => "0xC0000003L".hex,
|
118
|
+
:data => "I'm warning you!"
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
Note the values used for the 'category' and 'event_id' keys. Those are the
|
123
|
+
values that were generated automatically in the foo.h file that I showed you
|
124
|
+
above. You'll have to manually inspect the header file that's generated to
|
125
|
+
determine which values you should be using.
|
126
|
+
|
127
|
+
You can now open your event log viewer and look at the message. You can get
|
128
|
+
to your event log viewer via Start -> Control Panel -> Administrative Tools ->
|
129
|
+
Event Viewer. You should see a warning message with the category "warning"
|
130
|
+
and an event id of '3'. If you right click on that entry and select
|
131
|
+
"properties", you can see the event description is "Warning: I'm warning you!".
|
132
|
+
|
133
|
+
== More Info
|
134
|
+
For more information visit the following URL's:
|
135
|
+
|
136
|
+
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tools/tools/message_text_file_syntax.asp
|
137
|
+
|
138
|
+
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/message_files.asp
|
139
|
+
|
140
|
+
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tools/tools/header_section.asp
|
@@ -0,0 +1,754 @@
|
|
1
|
+
require 'windows/error'
|
2
|
+
require 'windows/eventlog'
|
3
|
+
require 'windows/security'
|
4
|
+
require 'windows/registry'
|
5
|
+
require 'windows/system_info'
|
6
|
+
require 'windows/library'
|
7
|
+
require 'windows/synchronize'
|
8
|
+
require 'windows/handle'
|
9
|
+
|
10
|
+
class String
|
11
|
+
# Return the portion of the string up to the first NULL character. This
|
12
|
+
# was added for both speed and convenience.
|
13
|
+
def nstrip
|
14
|
+
self[ /^[^\0]*/ ]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Win32
|
19
|
+
class EventLogError < StandardError; end
|
20
|
+
class EventLog
|
21
|
+
include Windows::Error
|
22
|
+
include Windows::EventLog
|
23
|
+
include Windows::Security
|
24
|
+
include Windows::Registry
|
25
|
+
include Windows::SystemInfo
|
26
|
+
include Windows::Library
|
27
|
+
include Windows::Synchronize
|
28
|
+
include Windows::Handle
|
29
|
+
extend Windows::Error
|
30
|
+
extend Windows::Registry
|
31
|
+
|
32
|
+
VERSION = '0.4.2'
|
33
|
+
|
34
|
+
# Aliased read flags
|
35
|
+
FORWARDS_READ = EVENTLOG_FORWARDS_READ
|
36
|
+
BACKWARDS_READ = EVENTLOG_BACKWARDS_READ
|
37
|
+
SEEK_READ = EVENTLOG_SEEK_READ
|
38
|
+
SEQUENTIAL_READ = EVENTLOG_SEQUENTIAL_READ
|
39
|
+
|
40
|
+
# Aliased event types
|
41
|
+
SUCCESS = EVENTLOG_SUCCESS
|
42
|
+
ERROR = EVENTLOG_ERROR_TYPE
|
43
|
+
WARN = EVENTLOG_WARNING_TYPE
|
44
|
+
INFO = EVENTLOG_INFORMATION_TYPE
|
45
|
+
AUDIT_SUCCESS = EVENTLOG_AUDIT_SUCCESS
|
46
|
+
AUDIT_FAILURE = EVENTLOG_AUDIT_FAILURE
|
47
|
+
|
48
|
+
# These are not meant for public use
|
49
|
+
BUFFER_SIZE = 1024 * 64
|
50
|
+
MAX_SIZE = 256
|
51
|
+
MAX_STRINGS = 16
|
52
|
+
BASE_KEY = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\"
|
53
|
+
|
54
|
+
EventLogStruct = Struct.new('EventLogStruct', :record_number,
|
55
|
+
:time_generated, :time_written, :event_id, :event_type, :category,
|
56
|
+
:source, :computer, :user, :description
|
57
|
+
)
|
58
|
+
|
59
|
+
# The name of the event log source. This will typically be
|
60
|
+
# 'Application', 'System' or 'Security', but could also refer to
|
61
|
+
# a custom event log source.
|
62
|
+
#
|
63
|
+
attr_reader :source
|
64
|
+
|
65
|
+
# The name of the server which the event log is reading from.
|
66
|
+
#
|
67
|
+
attr_reader :server
|
68
|
+
|
69
|
+
# The name of the file used in the EventLog.open_backup method. This is
|
70
|
+
# set to nil if the file was not opened using the EventLog.open_backup
|
71
|
+
# method.
|
72
|
+
#
|
73
|
+
attr_reader :file
|
74
|
+
|
75
|
+
# Opens a handle to the new EventLog +source+ on +server+, or the local
|
76
|
+
# machine if no host is specified. Typically, your source will be
|
77
|
+
# 'Application, 'Security' or 'System', although you can specify a
|
78
|
+
# custom log file as well.
|
79
|
+
#
|
80
|
+
# If a custom, registered log file name cannot be found, the event
|
81
|
+
# logging service opens the 'Application' log file. This is the
|
82
|
+
# behavior of the underlying Windows function, not my own doing.
|
83
|
+
#
|
84
|
+
def initialize(source = 'Application', server = nil, file = nil)
|
85
|
+
@source = source || 'Application' # In case of explicit nil
|
86
|
+
@server = server
|
87
|
+
@file = file
|
88
|
+
|
89
|
+
# Avoid Win32API segfaults
|
90
|
+
raise TypeError unless @source.is_a?(String)
|
91
|
+
raise TypeError unless @server.is_a?(String) if @server
|
92
|
+
|
93
|
+
function = 'OpenEventLog()'
|
94
|
+
|
95
|
+
if @file.nil?
|
96
|
+
@handle = OpenEventLog(@server, @source)
|
97
|
+
else
|
98
|
+
@handle = OpenBackupEventLog(@server, @file)
|
99
|
+
function = 'OpenBackupEventLog()'
|
100
|
+
end
|
101
|
+
|
102
|
+
if @handle == 0
|
103
|
+
error = "#{function} failed: " + get_last_error
|
104
|
+
raise EventLogError, error
|
105
|
+
end
|
106
|
+
|
107
|
+
# Ensure the handle is closed at the end of a block
|
108
|
+
if block_given?
|
109
|
+
begin
|
110
|
+
yield self
|
111
|
+
ensure
|
112
|
+
close
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Class method aliases
|
118
|
+
class << self
|
119
|
+
alias :open :new
|
120
|
+
end
|
121
|
+
|
122
|
+
# Nearly identical to EventLog.open, except that the source is a backup
|
123
|
+
# file and not an event source (and there is no default).
|
124
|
+
#
|
125
|
+
def self.open_backup(file, source = 'Application', server = nil)
|
126
|
+
@file = file
|
127
|
+
@source = source
|
128
|
+
@server = server
|
129
|
+
|
130
|
+
# Avoid Win32API segfaults
|
131
|
+
raise TypeError unless @file.is_a?(String)
|
132
|
+
raise TypeError unless @source.is_a?(String)
|
133
|
+
raise TypeError unless @server.is_a?(String) if @server
|
134
|
+
|
135
|
+
self.new(source, server, file)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Adds an event source to the registry. The following are valid keys:
|
139
|
+
#
|
140
|
+
# * source - Source name. Set to "Application" by default
|
141
|
+
# * key_name - Name stored as the registry key
|
142
|
+
# * category_count - Number of supported (custom) categories
|
143
|
+
# * event_message_file - File (dll) that defines events
|
144
|
+
# * category_message_file - File (dll) that defines categories
|
145
|
+
# * supported_types - See the 'event types' constants
|
146
|
+
#
|
147
|
+
# Of these keys, only +key_name+ is mandatory. An ArgumentError is
|
148
|
+
# raised if you attempt to use an invalid key. If +supported_types+
|
149
|
+
# is not specified then the following value is used:
|
150
|
+
#
|
151
|
+
# EventLog::ERROR | EventLog::WARN | EventLog::INFO
|
152
|
+
#
|
153
|
+
# The +event_message_file+ and +category_message_file+ are typically,
|
154
|
+
# though not necessarily, the same file. See the documentation on .mc files
|
155
|
+
# for more details.
|
156
|
+
#
|
157
|
+
def self.add_event_source(args)
|
158
|
+
raise TypeError unless args.is_a?(Hash)
|
159
|
+
|
160
|
+
hkey = [0].pack('L')
|
161
|
+
|
162
|
+
valid_keys = %w/source key_name category_count event_message_file
|
163
|
+
category_message_file supported_types/
|
164
|
+
|
165
|
+
key_base = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\"
|
166
|
+
|
167
|
+
# Default values
|
168
|
+
hash = {
|
169
|
+
'source' => 'Application',
|
170
|
+
'supported_types' => ERROR | WARN | INFO
|
171
|
+
}
|
172
|
+
|
173
|
+
# Validate the keys, and convert symbols and case to lowercase strings.
|
174
|
+
args.each{ |key, val|
|
175
|
+
key = key.to_s.downcase
|
176
|
+
unless valid_keys.include?(key)
|
177
|
+
raise ArgumentError, "invalid key '#{key}'"
|
178
|
+
end
|
179
|
+
hash[key] = val
|
180
|
+
}
|
181
|
+
|
182
|
+
# The key_name must be specified
|
183
|
+
unless hash['key_name']
|
184
|
+
raise EventLogError, 'no event_type specified'
|
185
|
+
end
|
186
|
+
|
187
|
+
key = key_base << hash['source'] << "\\" << hash['key_name']
|
188
|
+
|
189
|
+
if RegCreateKey(HKEY_LOCAL_MACHINE, key, hkey) != ERROR_SUCCESS
|
190
|
+
error = 'RegCreateKey() failed: ' + get_last_error
|
191
|
+
raise EventLogError, error
|
192
|
+
end
|
193
|
+
|
194
|
+
hkey = hkey.unpack('L').first
|
195
|
+
|
196
|
+
if hash['category_count']
|
197
|
+
data = [hash['category_count']].pack('L')
|
198
|
+
|
199
|
+
rv = RegSetValueEx(
|
200
|
+
hkey,
|
201
|
+
'CategoryCount',
|
202
|
+
0,
|
203
|
+
REG_DWORD,
|
204
|
+
data,
|
205
|
+
data.size
|
206
|
+
)
|
207
|
+
|
208
|
+
if rv != ERROR_SUCCESS
|
209
|
+
error = 'RegSetValueEx() failed: ', get_last_error
|
210
|
+
RegCloseKey(hkey)
|
211
|
+
raise EventLogError, error
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
if hash['category_message_file']
|
216
|
+
data = File.expand_path(hash['category_message_file'])
|
217
|
+
|
218
|
+
rv = RegSetValueEx(
|
219
|
+
hkey,
|
220
|
+
'CategoryMessageFile',
|
221
|
+
0,
|
222
|
+
REG_EXPAND_SZ,
|
223
|
+
data,
|
224
|
+
data.size
|
225
|
+
)
|
226
|
+
|
227
|
+
if rv != ERROR_SUCCESS
|
228
|
+
error = 'RegSetValueEx() failed: ', get_last_error
|
229
|
+
RegCloseKey(hkey)
|
230
|
+
raise EventLogError, error
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
if hash['event_message_file']
|
235
|
+
data = File.expand_path(hash['event_message_file'])
|
236
|
+
|
237
|
+
rv = RegSetValueEx(
|
238
|
+
hkey,
|
239
|
+
'EventMessageFile',
|
240
|
+
0,
|
241
|
+
REG_EXPAND_SZ,
|
242
|
+
data,
|
243
|
+
data.size
|
244
|
+
)
|
245
|
+
|
246
|
+
if rv != ERROR_SUCCESS
|
247
|
+
error = 'RegSetValueEx() failed: ', get_last_error
|
248
|
+
RegCloseKey(hkey)
|
249
|
+
raise EventLogError, error
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
data = [hash['supported_types']].pack('L')
|
254
|
+
rv = RegSetValueEx(
|
255
|
+
hkey,
|
256
|
+
'TypesSupported',
|
257
|
+
0,
|
258
|
+
REG_DWORD,
|
259
|
+
data,
|
260
|
+
data.size
|
261
|
+
)
|
262
|
+
|
263
|
+
if rv != ERROR_SUCCESS
|
264
|
+
error = 'RegSetValueEx() failed: ', get_last_error
|
265
|
+
RegCloseKey(hkey)
|
266
|
+
raise EventLogError, error
|
267
|
+
end
|
268
|
+
|
269
|
+
RegCloseKey(hkey)
|
270
|
+
self
|
271
|
+
end
|
272
|
+
|
273
|
+
# Backs up the event log to +file+. Note that you cannot backup to
|
274
|
+
# a file that already exists or a EventLogError will be raised.
|
275
|
+
#
|
276
|
+
def backup(file)
|
277
|
+
raise TypeError unless file.is_a?(String)
|
278
|
+
unless BackupEventLog(@handle, file)
|
279
|
+
error = 'BackupEventLog() failed: ' + get_last_error
|
280
|
+
raise EventLogError, error
|
281
|
+
end
|
282
|
+
self
|
283
|
+
end
|
284
|
+
|
285
|
+
# Clears the EventLog. If +backup_file+ is provided, it backs up the
|
286
|
+
# event log to that file first.
|
287
|
+
#
|
288
|
+
def clear(backup_file = nil)
|
289
|
+
raise TypeError unless backup_file.is_a?(String) if backup_file
|
290
|
+
backup_file = 0 unless backup_file
|
291
|
+
|
292
|
+
unless ClearEventLog(@handle, backup_file)
|
293
|
+
error = 'ClearEventLog() failed: ' + get_last_error
|
294
|
+
raise EventLogError
|
295
|
+
end
|
296
|
+
|
297
|
+
self
|
298
|
+
end
|
299
|
+
|
300
|
+
# Closes the EventLog handle. Automatically closed for you if you use
|
301
|
+
# the block form of EventLog.new.
|
302
|
+
#
|
303
|
+
def close
|
304
|
+
CloseEventLog(@handle)
|
305
|
+
end
|
306
|
+
|
307
|
+
# Indicates whether or not the event log is full.
|
308
|
+
#
|
309
|
+
def full?
|
310
|
+
buf = [0].pack('L') # dwFull
|
311
|
+
needed = [0].pack('L')
|
312
|
+
|
313
|
+
unless GetEventLogInformation(@handle, 0, buf, buf.size, needed)
|
314
|
+
raise 'GetEventLogInformation() failed: ' + get_last_error
|
315
|
+
end
|
316
|
+
|
317
|
+
buf[0,4].unpack('L').first != 0
|
318
|
+
end
|
319
|
+
|
320
|
+
# Returns the absolute record number of the oldest record. Note that
|
321
|
+
# this is not guaranteed to be 1 because event log records can be
|
322
|
+
# overwritten.
|
323
|
+
#
|
324
|
+
def oldest_record_number
|
325
|
+
rec = [0].pack('L')
|
326
|
+
|
327
|
+
unless GetOldestEventLogRecord(@handle, rec)
|
328
|
+
error = 'GetOldestEventLogRecord() failed: ' + get_last_error
|
329
|
+
raise EventLogError, error
|
330
|
+
end
|
331
|
+
|
332
|
+
rec.unpack('L').first
|
333
|
+
end
|
334
|
+
|
335
|
+
# Returns the total number of records for the given event log.
|
336
|
+
#
|
337
|
+
def total_records
|
338
|
+
total = [0].pack('L')
|
339
|
+
|
340
|
+
unless GetNumberOfEventLogRecords(@handle, total)
|
341
|
+
error = 'GetNumberOfEventLogRecords() failed: '
|
342
|
+
error += get_last_error
|
343
|
+
raise EventLogError, error
|
344
|
+
end
|
345
|
+
|
346
|
+
total.unpack('L').first
|
347
|
+
end
|
348
|
+
|
349
|
+
# Yields an EventLogStruct every time a record is written to the event
|
350
|
+
# log. Unlike EventLog#tail, this method breaks out of the block after
|
351
|
+
# the event.
|
352
|
+
#
|
353
|
+
# Raises an EventLogError if no block is provided.
|
354
|
+
#
|
355
|
+
def notify_change(&block)
|
356
|
+
unless block_given?
|
357
|
+
raise EventLogError, 'block missing for notify_change()'
|
358
|
+
end
|
359
|
+
|
360
|
+
# Reopen the handle because the NotifyChangeEventLog() function will
|
361
|
+
# choke after five or six reads otherwise.
|
362
|
+
@handle = OpenEventLog(@server, @source)
|
363
|
+
|
364
|
+
if @handle == 0
|
365
|
+
error = "OpenEventLog() failed: " + get_last_error
|
366
|
+
raise EventLogError, error
|
367
|
+
end
|
368
|
+
|
369
|
+
event = CreateEvent(0, 0, 0, 0)
|
370
|
+
|
371
|
+
unless NotifyChangeEventLog(@handle, event)
|
372
|
+
error = 'NotifyChangeEventLog() failed: ' + get_last_error
|
373
|
+
raise EventLogError, error
|
374
|
+
end
|
375
|
+
|
376
|
+
wait_result = WaitForSingleObject(event, INFINITE)
|
377
|
+
|
378
|
+
if wait_result == WAIT_FAILED
|
379
|
+
error = 'WaitForSingleObject() failed: ' + get_last_error
|
380
|
+
CloseHandle(event)
|
381
|
+
raise EventLogError, error
|
382
|
+
else
|
383
|
+
last = read_last_event
|
384
|
+
block.call(last)
|
385
|
+
end
|
386
|
+
|
387
|
+
CloseHandle(event)
|
388
|
+
self
|
389
|
+
end
|
390
|
+
|
391
|
+
# Yields an EventLogStruct every time a record is written to the event
|
392
|
+
# log, once every +frequency+ seconds. Unlike EventLog#notify_change,
|
393
|
+
# this method does not break out of the block after the event. The read
|
394
|
+
# +frequency+ is set to 5 seconds by default.
|
395
|
+
#
|
396
|
+
# Raises an EventLogError if no block is provided.
|
397
|
+
#
|
398
|
+
# The delay between reads is due to the nature of the Windows event log.
|
399
|
+
# It is not really designed to be tailed in the manner of a Unix syslog,
|
400
|
+
# for example, in that not nearly as many events are typically recorded.
|
401
|
+
# It's just not designed to be polled that heavily.
|
402
|
+
#
|
403
|
+
def tail(frequency = 5)
|
404
|
+
unless block_given?
|
405
|
+
raise EventLogError, 'block missing for tail()'
|
406
|
+
end
|
407
|
+
|
408
|
+
old_total = total_records()
|
409
|
+
flags = FORWARDS_READ | SEEK_READ
|
410
|
+
rec_num = read_last_event.record_number
|
411
|
+
|
412
|
+
while true
|
413
|
+
new_total = total_records()
|
414
|
+
if new_total != old_total
|
415
|
+
rec_num = oldest_record_number() if full?
|
416
|
+
read(flags, rec_num).each{ |log| yield log }
|
417
|
+
old_total = new_total
|
418
|
+
rec_num = read_last_event.record_number + 1
|
419
|
+
end
|
420
|
+
sleep frequency
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# EventLog#read(flags=nil, offset=0)
|
425
|
+
# EventLog#read(flags=nil, offset=0){ |log| ... }
|
426
|
+
#
|
427
|
+
# Iterates over each record in the event log, yielding a EventLogStruct
|
428
|
+
# for each record. The offset value is only used when used in
|
429
|
+
# conjunction with the EventLog::SEEK_READ flag. Otherwise, it is
|
430
|
+
# ignored. If no flags are specified, then the default flags are:
|
431
|
+
#
|
432
|
+
# EventLog::SEQUENTIAL_READ | EventLog::FORWARDS_READ
|
433
|
+
#
|
434
|
+
# The EventLogStruct struct contains the following members:
|
435
|
+
#
|
436
|
+
# record_number # Fixnum
|
437
|
+
# time_generated # Time
|
438
|
+
# time_written # Time
|
439
|
+
# event_id # Fixnum
|
440
|
+
# event_type # String
|
441
|
+
# category # String
|
442
|
+
# source # String
|
443
|
+
# computer # String
|
444
|
+
# user # String or nil
|
445
|
+
# description # String or nil
|
446
|
+
#
|
447
|
+
# If no block is given the method returns an array of EventLogStruct's.
|
448
|
+
#
|
449
|
+
def read(flags = nil, offset = 0)
|
450
|
+
buf = 0.chr * BUFFER_SIZE # 64k buffer
|
451
|
+
size = buf.size
|
452
|
+
read = [0].pack('L')
|
453
|
+
needed = [0].pack('L')
|
454
|
+
array = []
|
455
|
+
|
456
|
+
unless flags
|
457
|
+
flags = FORWARDS_READ | SEQUENTIAL_READ
|
458
|
+
end
|
459
|
+
|
460
|
+
while ReadEventLog(@handle, flags, offset, buf, size, read, needed) ||
|
461
|
+
GetLastError() == ERROR_INSUFFICIENT_BUFFER
|
462
|
+
|
463
|
+
if GetLastError() == ERROR_INSUFFICIENT_BUFFER
|
464
|
+
buf += 0.chr * needed.unpack('L').first
|
465
|
+
ReadEventLog(@handle, flags, offset, buf, size, read, needed)
|
466
|
+
end
|
467
|
+
|
468
|
+
dwread = read.unpack('L').first
|
469
|
+
|
470
|
+
while dwread > 0
|
471
|
+
struct = EventLogStruct.new
|
472
|
+
event_source = buf[56..-1].nstrip
|
473
|
+
computer = buf[56 + event_source.length + 1..-1].nstrip
|
474
|
+
|
475
|
+
user = get_user(buf)
|
476
|
+
desc = get_description(buf, event_source)
|
477
|
+
|
478
|
+
struct.source = event_source
|
479
|
+
struct.computer = computer
|
480
|
+
struct.record_number = buf[8,4].unpack('L').first
|
481
|
+
struct.time_generated = Time.at(buf[12,4].unpack('L').first)
|
482
|
+
struct.time_written = Time.at(buf[16,4].unpack('L').first)
|
483
|
+
struct.event_id = buf[20,4].unpack('L').first & 0x0000FFFF
|
484
|
+
struct.event_type = get_event_type(buf[24,2].unpack('S').first)
|
485
|
+
struct.user = user
|
486
|
+
struct.category = buf[28,2].unpack('S').first
|
487
|
+
struct.description = desc
|
488
|
+
|
489
|
+
if block_given?
|
490
|
+
yield struct
|
491
|
+
else
|
492
|
+
array.push(struct)
|
493
|
+
end
|
494
|
+
|
495
|
+
if flags & EVENTLOG_BACKWARDS_READ > 0
|
496
|
+
offset = buf[8,4].unpack('L').first - 1
|
497
|
+
else
|
498
|
+
offset = buf[8,4].unpack('L').first + 1
|
499
|
+
end
|
500
|
+
|
501
|
+
length = buf[0,4].unpack('L').first # Length
|
502
|
+
|
503
|
+
dwread -= length
|
504
|
+
buf = buf[length..-1]
|
505
|
+
end
|
506
|
+
|
507
|
+
buf = 0.chr * BUFFER_SIZE
|
508
|
+
read = [0].pack('L')
|
509
|
+
end
|
510
|
+
|
511
|
+
block_given? ? nil : array
|
512
|
+
end
|
513
|
+
|
514
|
+
# This class method is nearly identical to the EventLog#read instance
|
515
|
+
# method, except that it takes a +source+ and +server+ as the first two
|
516
|
+
# arguments.
|
517
|
+
#
|
518
|
+
def self.read(source='Application', server=nil, flags=nil, offset=0)
|
519
|
+
self.new(source, server){ |log|
|
520
|
+
if block_given?
|
521
|
+
log.read(flags, offset){ |els| yield els }
|
522
|
+
else
|
523
|
+
return log.read(flags, offset)
|
524
|
+
end
|
525
|
+
}
|
526
|
+
end
|
527
|
+
|
528
|
+
# EventLog#report_event(key => value, ...)
|
529
|
+
#
|
530
|
+
# Writes an event to the event log. The following are valid keys:
|
531
|
+
#
|
532
|
+
# source # Event log source name. Defaults to "Application"
|
533
|
+
# event_id # Event ID (defined in event message file)
|
534
|
+
# category # Event category (defined in category message file)
|
535
|
+
# data # String that is written to the log
|
536
|
+
# event_type # Type of event, e.g. EventLog::ERROR, etc.
|
537
|
+
#
|
538
|
+
# The +event_type+ keyword is the only mandatory keyword. The others are
|
539
|
+
# optional. Although the +source+ defaults to "Application", I
|
540
|
+
# recommend that you create an application specific event source and use
|
541
|
+
# that instead. See the 'EventLog.add_event_source' method for more
|
542
|
+
# details.
|
543
|
+
#
|
544
|
+
# The +event_id+ and +category+ values are defined in the message
|
545
|
+
# file(s) that you created for your application. See the tutorial.txt
|
546
|
+
# file for more details on how to create a message file.
|
547
|
+
#
|
548
|
+
# An ArgumentError is raised if you attempt to use an invalid key.
|
549
|
+
#
|
550
|
+
def report_event(args)
|
551
|
+
raise TypeError unless args.is_a?(Hash)
|
552
|
+
|
553
|
+
valid_keys = %w/source event_id category data event_type/
|
554
|
+
num_strings = 0
|
555
|
+
|
556
|
+
# Default values
|
557
|
+
hash = {
|
558
|
+
'source' => @source,
|
559
|
+
'event_id' => 0,
|
560
|
+
'category' => 0,
|
561
|
+
'data' => 0
|
562
|
+
}
|
563
|
+
|
564
|
+
# Validate the keys, and convert symbols and case to lowercase strings.
|
565
|
+
args.each{ |key, val|
|
566
|
+
key = key.to_s.downcase
|
567
|
+
unless valid_keys.include?(key)
|
568
|
+
raise ArgumentError, "invalid key '#{key}'"
|
569
|
+
end
|
570
|
+
hash[key] = val
|
571
|
+
}
|
572
|
+
|
573
|
+
# The event_type must be specified
|
574
|
+
unless hash['event_type']
|
575
|
+
raise EventLogError, 'no event_type specified'
|
576
|
+
end
|
577
|
+
|
578
|
+
handle = RegisterEventSource(@server, hash['source'])
|
579
|
+
|
580
|
+
if handle == 0
|
581
|
+
error = 'RegisterEventSource() failed: ' + get_last_error
|
582
|
+
raise EventLogError, error
|
583
|
+
end
|
584
|
+
|
585
|
+
if hash['data'].is_a?(String)
|
586
|
+
data = hash['data'] << 0.chr
|
587
|
+
data = [data].pack('p*')
|
588
|
+
num_strings = 1
|
589
|
+
else
|
590
|
+
data = 0
|
591
|
+
num_strings = 0
|
592
|
+
end
|
593
|
+
|
594
|
+
bool = ReportEvent(
|
595
|
+
handle,
|
596
|
+
hash['event_type'],
|
597
|
+
hash['category'],
|
598
|
+
hash['event_id'],
|
599
|
+
0,
|
600
|
+
num_strings,
|
601
|
+
0,
|
602
|
+
data,
|
603
|
+
0
|
604
|
+
)
|
605
|
+
|
606
|
+
unless bool
|
607
|
+
error = 'ReportEvent() failed: ' + get_last_error
|
608
|
+
raise EventLogError, error
|
609
|
+
end
|
610
|
+
|
611
|
+
self
|
612
|
+
end
|
613
|
+
|
614
|
+
alias :write :report_event
|
615
|
+
|
616
|
+
private
|
617
|
+
|
618
|
+
# A private method that reads the last event log record.
|
619
|
+
#
|
620
|
+
def read_last_event(handle=@handle, source=@source, server=@server)
|
621
|
+
buf = 0.chr * BUFFER_SIZE # 64k buffer
|
622
|
+
read = [0].pack('L')
|
623
|
+
needed = [0].pack('L')
|
624
|
+
|
625
|
+
flags = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ
|
626
|
+
ReadEventLog(@handle, flags, 0, buf, buf.size, read, needed)
|
627
|
+
|
628
|
+
event_source = buf[56..-1].nstrip
|
629
|
+
computer = buf[56 + event_source.length + 1..-1].nstrip
|
630
|
+
event_type = get_event_type(buf[24,2].unpack('S').first)
|
631
|
+
user = get_user(buf)
|
632
|
+
desc = get_description(buf, event_source)
|
633
|
+
|
634
|
+
struct = EventLogStruct.new
|
635
|
+
struct.source = event_source
|
636
|
+
struct.computer = computer
|
637
|
+
struct.record_number = buf[8,4].unpack('L').first
|
638
|
+
struct.time_generated = Time.at(buf[12,4].unpack('L').first)
|
639
|
+
struct.time_written = Time.at(buf[16,4].unpack('L').first)
|
640
|
+
struct.event_id = buf[20,4].unpack('L').first & 0x0000FFFF
|
641
|
+
struct.event_type = event_type
|
642
|
+
struct.user = user
|
643
|
+
struct.category = buf[28,2].unpack('S').first
|
644
|
+
struct.description = desc
|
645
|
+
|
646
|
+
struct
|
647
|
+
end
|
648
|
+
|
649
|
+
# Private method that gets the event description (text) based on data
|
650
|
+
# from the EVENTLOGRECORD buffer.
|
651
|
+
#
|
652
|
+
def get_description(rec, event_source)
|
653
|
+
str = rec[ rec[36,4].unpack('L').first .. -1]
|
654
|
+
num = rec[26,2].unpack('S').first # NumStrings
|
655
|
+
hkey = [0].pack('L')
|
656
|
+
key = BASE_KEY + "#{@source}\\#{event_source}"
|
657
|
+
buf = 0.chr * 1024
|
658
|
+
|
659
|
+
if num == 0
|
660
|
+
va_list_ptr = 0.chr * 4
|
661
|
+
else
|
662
|
+
va_list = str.split(0.chr)[0...num]
|
663
|
+
va_list_ptr = va_list.map{ |x|
|
664
|
+
[x + 0.chr].pack('P').unpack('L').first
|
665
|
+
}.pack('L*')
|
666
|
+
end
|
667
|
+
|
668
|
+
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, hkey) == 0
|
669
|
+
value = 'EventMessageFile'
|
670
|
+
file = 0.chr * MAX_SIZE
|
671
|
+
hkey = hkey.unpack('L').first
|
672
|
+
size = [file.length].pack('L')
|
673
|
+
|
674
|
+
if RegQueryValueEx(hkey, value, 0, 0, file, size) == 0
|
675
|
+
file = file.nstrip
|
676
|
+
exe = 0.chr * MAX_SIZE
|
677
|
+
|
678
|
+
ExpandEnvironmentStrings(file, exe, exe.size)
|
679
|
+
exe = exe.nstrip
|
680
|
+
|
681
|
+
exe.split(';').each{ |file|
|
682
|
+
hmodule = LoadLibraryEx(file, 0, LOAD_LIBRARY_AS_DATAFILE)
|
683
|
+
event_id = rec[20,4].unpack('L').first
|
684
|
+
if hmodule != 0
|
685
|
+
FormatMessage(
|
686
|
+
FORMAT_MESSAGE_FROM_HMODULE |
|
687
|
+
FORMAT_MESSAGE_FROM_SYSTEM |
|
688
|
+
FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
689
|
+
hmodule,
|
690
|
+
event_id,
|
691
|
+
0,
|
692
|
+
buf,
|
693
|
+
buf.size,
|
694
|
+
va_list_ptr
|
695
|
+
)
|
696
|
+
FreeLibrary(hmodule)
|
697
|
+
end
|
698
|
+
}
|
699
|
+
end
|
700
|
+
|
701
|
+
RegCloseKey(hkey)
|
702
|
+
end
|
703
|
+
buf.strip
|
704
|
+
end
|
705
|
+
|
706
|
+
# Private method that retrieves the user name based on data in the
|
707
|
+
# EVENTLOGRECORD buffer.
|
708
|
+
#
|
709
|
+
def get_user(buf)
|
710
|
+
return nil if buf[40,4].unpack('L').first <= 0 # UserSidLength
|
711
|
+
|
712
|
+
name = 0.chr * MAX_SIZE
|
713
|
+
name_size = [name.size].pack('L')
|
714
|
+
domain = 0.chr * MAX_SIZE
|
715
|
+
domain_size = [domain.size].pack('L')
|
716
|
+
snu = 0.chr * 4
|
717
|
+
|
718
|
+
offset = buf[44,4].unpack('L').first # UserSidOffset
|
719
|
+
|
720
|
+
val = LookupAccountSid(
|
721
|
+
@server,
|
722
|
+
[buf].pack('P').unpack('L').first + offset,
|
723
|
+
name,
|
724
|
+
name_size,
|
725
|
+
domain,
|
726
|
+
domain_size,
|
727
|
+
snu
|
728
|
+
)
|
729
|
+
|
730
|
+
# Return nil if the lookup failed
|
731
|
+
return val ? name.nstrip : nil
|
732
|
+
end
|
733
|
+
|
734
|
+
# Private method that converts a numeric event type into a human
|
735
|
+
# readable string.
|
736
|
+
#
|
737
|
+
def get_event_type(event)
|
738
|
+
case event
|
739
|
+
when EVENTLOG_ERROR_TYPE
|
740
|
+
'error'
|
741
|
+
when EVENTLOG_WARNING_TYPE
|
742
|
+
'warning'
|
743
|
+
when EVENTLOG_INFORMATION_TYPE
|
744
|
+
'information'
|
745
|
+
when EVENTLOG_AUDIT_SUCCESS
|
746
|
+
'audit_success'
|
747
|
+
when EVENTLOG_AUDIT_FAILURE
|
748
|
+
'audit_failure'
|
749
|
+
else
|
750
|
+
nil
|
751
|
+
end
|
752
|
+
end
|
753
|
+
end
|
754
|
+
end
|