File: eventlog.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (187 lines) | stat: -rw-r--r-- 6,857 bytes parent folder | download
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