1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
require 'ffi'
# Puppet::Util::Windows::EventLog needs to be requirable without having loaded
# any other parts of Puppet so it can be leveraged independently by the code
# that runs Puppet as a service on Windows.
#
# For this reason we:
# - Define Puppet::Util::Windows
# - Replicate logic that exists elsewhere in puppet/util/windows
# - Raise generic RuntimeError instead of Puppet::Util::Windows::Error if its not defined
module Puppet; module Util; module Windows ; end ; end ; end
class Puppet::Util::Windows::EventLog
extend FFI::Library
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa363679(v=vs.85).aspx
EVENTLOG_ERROR_TYPE = 0x0001
EVENTLOG_WARNING_TYPE = 0x0002
EVENTLOG_INFORMATION_TYPE = 0x0004
# These are duplicate definitions from Puppet::Util::Windows::ApiTypes,
# established here so this class can be standalone from Puppet, and public so
# we can reference them in tests.
NULL_HANDLE = 0
WIN32_FALSE = 0
# Register an event log handle for the application
# @param source_name [String] the name of the event source to retrieve a handle for
# @return [void]
# @api public
def initialize(source_name = 'Puppet')
@eventlog_handle = RegisterEventSourceW(FFI::Pointer::NULL, wide_string(source_name))
if @eventlog_handle == NULL_HANDLE
#TRANSLATORS 'Windows' is the operating system and 'RegisterEventSourceW' is a API call and should not be translated
raise EventLogError.new(_("RegisterEventSourceW failed to open Windows eventlog"), FFI.errno)
end
end
# Close this instance's event log handle
# @return [void]
# @api public
def close
DeregisterEventSource(@eventlog_handle)
ensure
@eventlog_handle = nil
end
# Report an event to this instance's event log handle. Accepts a string to
# report (:data => <string>) and event type (:event_type => Integer) and id
# (:event_id => Integer) as returned by #to_native. The additional arguments to
# ReportEventW seen in this method aren't exposed - though ReportEventW
# technically can accept multiple strings as well as raw binary data to log,
# we accept a single string from Puppet::Util::Log
#
# @param args [Hash{Symbol=>Object}] options to the associated log event
# @return [void]
# @api public
def report_event(args = {})
unless args[:data].is_a?(String)
raise ArgumentError, _("data must be a string, not %{class_name}") % { class_name: args[:data].class }
end
from_string_to_wide_string(args[:data]) do |message_ptr|
FFI::MemoryPointer.new(:pointer) do |message_array_ptr|
message_array_ptr.write_pointer(message_ptr)
user_sid = FFI::Pointer::NULL
raw_data = FFI::Pointer::NULL
raw_data_size = 0
num_strings = 1
eventlog_category = 0
report_result = ReportEventW(@eventlog_handle, args[:event_type],
eventlog_category, args[:event_id], user_sid,
num_strings, raw_data_size, message_array_ptr, raw_data)
if report_result == WIN32_FALSE
#TRANSLATORS 'Windows' is the operating system and 'ReportEventW' is a API call and should not be translated
raise EventLogError.new(_("ReportEventW failed to report event to Windows eventlog"), FFI.errno)
end
end
end
end
class << self
# Feels more natural to do Puppet::Util::Window::EventLog.open("MyApplication")
alias :open :new
# Query event identifier info for a given log level
# @param level [Symbol] an event log level
# @return [Array] Win API Event ID, Puppet Event ID
# @api public
def to_native(level)
case level
when :debug,:info,:notice
[EVENTLOG_INFORMATION_TYPE, 0x01]
when :warning
[EVENTLOG_WARNING_TYPE, 0x02]
when :err,:alert,:emerg,:crit
[EVENTLOG_ERROR_TYPE, 0x03]
else
raise ArgumentError, _("Invalid log level %{level}") % { level: level }
end
end
end
private
# For the purposes of allowing this class to be standalone, the following are
# duplicate definitions from elsewhere in Puppet:
# If we're loaded via Puppet we should keep the previous behavior of raising
# Puppet::Util::Windows::Error on errors. If we aren't, at least concatenate
# the error code to the exception message to pass this information on to the
# user
if defined?(Puppet::Util::Windows::Error)
EventLogError = Puppet::Util::Windows::Error
else
class EventLogError < RuntimeError
def initialize(msg, code)
#TRANSLATORS 'Win32' is the Windows API and should not be translated
super(msg + ' ' + _("(Win32 error: %{detail})") % { detail: code})
end
end
end
# Private duplicate of Puppet::Util::Windows::String::wide_string
# Not for use outside of EventLog! - use Puppet::Util::Windows instead
# @api private
def wide_string(str)
# if given a nil string, assume caller wants to pass a nil pointer to win32
return nil if str.nil?
str.encode('UTF-16LE')
end
# Private duplicate of Puppet::Util::Windows::ApiTypes::from_string_to_wide_string
# Not for use outside of EventLog! - Use Puppet::Util::Windows instead
# @api private
def from_string_to_wide_string(str, &block)
str = wide_string(str)
FFI::MemoryPointer.from_wide_string(str) { |ptr| yield ptr }
# ptr has already had free called, so nothing to return
nil
end
ffi_convention :stdcall
# The following are typedefs in Puppet::Util::Winodws::ApiTypes, but here we
# use their original FFI counterparts:
# :uintptr_t for :handle
# :int32 for :win32_bool
# :uint16 for :word
# :uint32 for :dword
# :pointer for :lpvoid
# :uchar for :byte
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa363678(v=vs.85).aspx
# HANDLE RegisterEventSource(
# _In_ LPCTSTR lpUNCServerName,
# _In_ LPCTSTR lpSourceName
# );
ffi_lib :advapi32
attach_function :RegisterEventSourceW, [:buffer_in, :buffer_in], :uintptr_t
private :RegisterEventSourceW
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa363642(v=vs.85).aspx
# BOOL DeregisterEventSource(
# _Inout_ HANDLE hEventLog
# );
ffi_lib :advapi32
attach_function :DeregisterEventSource, [:uintptr_t], :int32
private :DeregisterEventSource
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa363679(v=vs.85).aspx
# BOOL ReportEvent(
# _In_ HANDLE hEventLog,
# _In_ WORD wType,
# _In_ WORD wCategory,
# _In_ DWORD dwEventID,
# _In_ PSID lpUserSid,
# _In_ WORD wNumStrings,
# _In_ DWORD dwDataSize,
# _In_ LPCTSTR *lpStrings,
# _In_ LPVOID lpRawData
# );
ffi_lib :advapi32
attach_function :ReportEventW, [:uintptr_t, :uint16, :uint16, :uint32, :pointer, :uint16, :uint32, :pointer, :pointer], :int32
private :ReportEventW
end
|