1 /*
2 * Copyright Andrey Semashev 2007 - 2015.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
6 */
7 /*!
8 * \file event_log_registry.hpp
9 * \author Andrey Semashev
10 * \date 16.11.2008
11 *
12 * \brief This header is the Boost.Log library implementation, see the library documentation
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
14 */
15
16 #ifndef BOOST_LOG_WINDOWS_EVENT_LOG_REGISTRY_HPP_INCLUDED_
17 #define BOOST_LOG_WINDOWS_EVENT_LOG_REGISTRY_HPP_INCLUDED_
18
19 #include <boost/log/detail/config.hpp>
20 #include <cwchar>
21 #include <cstring>
22 #include <string>
23 #include <sstream>
24 #include <stdexcept>
25 #include <boost/version.hpp>
26 #include <boost/optional/optional.hpp>
27 #include <boost/log/detail/code_conversion.hpp>
28 #include <boost/log/exceptions.hpp>
29 #include <windows.h>
30 #include <boost/log/detail/header.hpp>
31
32 namespace boost {
33
34 BOOST_LOG_OPEN_NAMESPACE
35
36 namespace sinks {
37
38 namespace aux {
39
40 // MSVC versions up to 2008 (or their Platform SDKs, to be more precise) don't define LSTATUS.
41 // Perhaps, that is also the case for MinGW and Cygwin (untested).
42 typedef DWORD LSTATUS;
43
44 // Max registry string size, in characters (for security reasons)
45 const DWORD max_string_size = 64u * 1024u;
46
47 //! Helper traits to integrate with WinAPI
48 template< typename CharT >
49 struct registry_traits;
50
51 #ifdef BOOST_LOG_USE_CHAR
52 template< >
53 struct registry_traits< char >
54 {
make_event_log_keyboost::sinks::aux::registry_traits55 static std::string make_event_log_key(std::string const& log_name, std::string const& source_name)
56 {
57 return "SYSTEM\\CurrentControlSet\\Services\\EventLog\\" + log_name + "\\" + source_name;
58 }
59
make_default_log_nameboost::sinks::aux::registry_traits60 static std::string make_default_log_name()
61 {
62 return "Application";
63 }
64
make_default_source_nameboost::sinks::aux::registry_traits65 static std::string make_default_source_name()
66 {
67 char buf[MAX_PATH];
68 DWORD size = GetModuleFileNameA(NULL, buf, sizeof(buf) / sizeof(*buf));
69
70 std::string source_name(buf, buf + size);
71 if (source_name.empty())
72 {
73 // In case of error we provide artificial application name
74 std::ostringstream strm;
75 strm << "Boost.Log "
76 << static_cast< unsigned int >(BOOST_VERSION / 100000)
77 << "."
78 << static_cast< unsigned int >(BOOST_VERSION / 100 % 1000)
79 << "."
80 << static_cast< unsigned int >(BOOST_VERSION % 100);
81 source_name = strm.str();
82 }
83 else
84 {
85 // Cut off the path and extension
86 std::size_t backslash_pos = source_name.rfind('\\');
87 if (backslash_pos == std::string::npos || backslash_pos >= source_name.size() - 1)
88 backslash_pos = 0;
89 else
90 ++backslash_pos;
91 std::size_t dot_pos = source_name.rfind('.');
92 if (dot_pos == std::string::npos || dot_pos < backslash_pos)
93 dot_pos = source_name.size();
94 source_name = source_name.substr(backslash_pos, dot_pos - backslash_pos);
95 }
96
97 return source_name;
98 }
99
create_keyboost::sinks::aux::registry_traits100 static LSTATUS create_key(
101 HKEY hKey,
102 const char* lpSubKey,
103 DWORD Reserved,
104 char* lpClass,
105 DWORD dwOptions,
106 REGSAM samDesired,
107 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
108 PHKEY phkResult,
109 LPDWORD lpdwDisposition)
110 {
111 return RegCreateKeyExA(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
112 }
113
open_keyboost::sinks::aux::registry_traits114 static LSTATUS open_key(
115 HKEY hKey,
116 const char* lpSubKey,
117 DWORD dwOptions,
118 REGSAM samDesired,
119 PHKEY phkResult)
120 {
121 return RegOpenKeyExA(hKey, lpSubKey, dwOptions, samDesired, phkResult);
122 }
123
set_valueboost::sinks::aux::registry_traits124 static LSTATUS set_value(
125 HKEY hKey,
126 const char* lpValueName,
127 DWORD Reserved,
128 DWORD dwType,
129 const BYTE* lpData,
130 DWORD cbData)
131 {
132 return RegSetValueExA(hKey, lpValueName, Reserved, dwType, lpData, cbData);
133 }
134
get_valueboost::sinks::aux::registry_traits135 static LSTATUS get_value(HKEY hKey, const char* lpValueName, DWORD& value)
136 {
137 DWORD type = REG_NONE, size = sizeof(value);
138 LSTATUS res = RegQueryValueExA(hKey, lpValueName, NULL, &type, reinterpret_cast< LPBYTE >(&value), &size);
139 if (res == ERROR_SUCCESS && type != REG_DWORD && type != REG_BINARY)
140 res = ERROR_INVALID_DATA;
141 return res;
142 }
143
get_valueboost::sinks::aux::registry_traits144 static LSTATUS get_value(HKEY hKey, const char* lpValueName, std::string& value)
145 {
146 DWORD type = REG_NONE, size = 0;
147 LSTATUS res = RegQueryValueExA(hKey, lpValueName, NULL, &type, NULL, &size);
148 if (res == ERROR_SUCCESS && ((type != REG_EXPAND_SZ && type != REG_SZ) || size > max_string_size))
149 return ERROR_INVALID_DATA;
150 if (size == 0)
151 return res;
152
153 value.resize(size);
154 res = RegQueryValueExA(hKey, lpValueName, NULL, &type, reinterpret_cast< LPBYTE >(&value[0]), &size);
155 value.resize(std::strlen(value.c_str())); // remove extra terminating zero
156
157 return res;
158 }
159
get_event_message_file_param_nameboost::sinks::aux::registry_traits160 static const char* get_event_message_file_param_name() { return "EventMessageFile"; }
get_category_message_file_param_nameboost::sinks::aux::registry_traits161 static const char* get_category_message_file_param_name() { return "CategoryMessageFile"; }
get_category_count_param_nameboost::sinks::aux::registry_traits162 static const char* get_category_count_param_name() { return "CategoryCount"; }
get_types_supported_param_nameboost::sinks::aux::registry_traits163 static const char* get_types_supported_param_name() { return "TypesSupported"; }
164 };
165 #endif // BOOST_LOG_USE_CHAR
166
167 #ifdef BOOST_LOG_USE_WCHAR_T
168 template< >
169 struct registry_traits< wchar_t >
170 {
make_event_log_keyboost::sinks::aux::registry_traits171 static std::wstring make_event_log_key(std::wstring const& log_name, std::wstring const& source_name)
172 {
173 return L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\" + log_name + L"\\" + source_name;
174 }
175
make_default_log_nameboost::sinks::aux::registry_traits176 static std::wstring make_default_log_name()
177 {
178 return L"Application";
179 }
180
make_default_source_nameboost::sinks::aux::registry_traits181 static std::wstring make_default_source_name()
182 {
183 wchar_t buf[MAX_PATH];
184 DWORD size = GetModuleFileNameW(NULL, buf, sizeof(buf) / sizeof(*buf));
185
186 std::wstring source_name(buf, buf + size);
187 if (source_name.empty())
188 {
189 // In case of error we provide artificial application name
190 std::wostringstream strm;
191 strm << L"Boost.Log "
192 << static_cast< unsigned int >(BOOST_VERSION / 100000)
193 << L"."
194 << static_cast< unsigned int >(BOOST_VERSION / 100 % 1000)
195 << L"."
196 << static_cast< unsigned int >(BOOST_VERSION % 100);
197 source_name = strm.str();
198 }
199 else
200 {
201 // Cut off the path and extension
202 std::size_t backslash_pos = source_name.rfind(L'\\');
203 if (backslash_pos == std::wstring::npos || backslash_pos >= source_name.size() - 1)
204 backslash_pos = 0;
205 else
206 ++backslash_pos;
207 std::size_t dot_pos = source_name.rfind(L'.');
208 if (dot_pos == std::wstring::npos || dot_pos < backslash_pos)
209 dot_pos = source_name.size();
210 source_name = source_name.substr(backslash_pos, dot_pos - backslash_pos);
211 }
212
213 return source_name;
214 }
215
create_keyboost::sinks::aux::registry_traits216 static LSTATUS create_key(
217 HKEY hKey,
218 const wchar_t* lpSubKey,
219 DWORD Reserved,
220 wchar_t* lpClass,
221 DWORD dwOptions,
222 REGSAM samDesired,
223 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
224 PHKEY phkResult,
225 LPDWORD lpdwDisposition)
226 {
227 return RegCreateKeyExW(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
228 }
229
open_keyboost::sinks::aux::registry_traits230 static LSTATUS open_key(
231 HKEY hKey,
232 const wchar_t* lpSubKey,
233 DWORD dwOptions,
234 REGSAM samDesired,
235 PHKEY phkResult)
236 {
237 return RegOpenKeyExW(hKey, lpSubKey, dwOptions, samDesired, phkResult);
238 }
239
set_valueboost::sinks::aux::registry_traits240 static LSTATUS set_value(
241 HKEY hKey,
242 const wchar_t* lpValueName,
243 DWORD Reserved,
244 DWORD dwType,
245 const BYTE* lpData,
246 DWORD cbData)
247 {
248 return RegSetValueExW(hKey, lpValueName, Reserved, dwType, lpData, cbData);
249 }
250
get_valueboost::sinks::aux::registry_traits251 static LSTATUS get_value(HKEY hKey, const wchar_t* lpValueName, DWORD& value)
252 {
253 DWORD type = REG_NONE, size = sizeof(value);
254 LSTATUS res = RegQueryValueExW(hKey, lpValueName, NULL, &type, reinterpret_cast< LPBYTE >(&value), &size);
255 if (res == ERROR_SUCCESS && type != REG_DWORD && type != REG_BINARY)
256 res = ERROR_INVALID_DATA;
257 return res;
258 }
259
get_valueboost::sinks::aux::registry_traits260 static LSTATUS get_value(HKEY hKey, const wchar_t* lpValueName, std::wstring& value)
261 {
262 DWORD type = REG_NONE, size = 0;
263 LSTATUS res = RegQueryValueExW(hKey, lpValueName, NULL, &type, NULL, &size);
264 size /= sizeof(wchar_t);
265 if (res == ERROR_SUCCESS && ((type != REG_EXPAND_SZ && type != REG_SZ) || size > max_string_size))
266 return ERROR_INVALID_DATA;
267 if (size == 0)
268 return res;
269
270 value.resize(size);
271 res = RegQueryValueExW(hKey, lpValueName, NULL, &type, reinterpret_cast< LPBYTE >(&value[0]), &size);
272 value.resize(std::wcslen(value.c_str())); // remove extra terminating zero
273
274 return res;
275 }
276
get_event_message_file_param_nameboost::sinks::aux::registry_traits277 static const wchar_t* get_event_message_file_param_name() { return L"EventMessageFile"; }
get_category_message_file_param_nameboost::sinks::aux::registry_traits278 static const wchar_t* get_category_message_file_param_name() { return L"CategoryMessageFile"; }
get_category_count_param_nameboost::sinks::aux::registry_traits279 static const wchar_t* get_category_count_param_name() { return L"CategoryCount"; }
get_types_supported_param_nameboost::sinks::aux::registry_traits280 static const wchar_t* get_types_supported_param_name() { return L"TypesSupported"; }
281
282 };
283 #endif // BOOST_LOG_USE_WCHAR_T
284
285 //! The structure with parameters that have to be registered in the event log registry key
286 template< typename CharT >
287 struct registry_params
288 {
289 typedef std::basic_string< CharT > string_type;
290
291 optional< string_type > event_message_file;
292 optional< string_type > category_message_file;
293 optional< DWORD > category_count;
294 optional< DWORD > types_supported;
295 };
296
297 //! A simple guard that closes the registry key on destruction
298 struct auto_hkey_close
299 {
auto_hkey_closeboost::sinks::aux::auto_hkey_close300 explicit auto_hkey_close(HKEY hk) : hk_(hk) {}
~auto_hkey_closeboost::sinks::aux::auto_hkey_close301 ~auto_hkey_close() { RegCloseKey(hk_); }
302
303 private:
304 HKEY hk_;
305 };
306
307 //! The function checks if the event log is already registered
308 template< typename CharT >
verify_event_log_registry(std::basic_string<CharT> const & reg_key,bool force,registry_params<CharT> const & params)309 bool verify_event_log_registry(std::basic_string< CharT > const& reg_key, bool force, registry_params< CharT > const& params)
310 {
311 typedef std::basic_string< CharT > string_type;
312 typedef registry_traits< CharT > registry;
313
314 // Open the key
315 HKEY hkey = 0;
316 LSTATUS res = registry::open_key(
317 HKEY_LOCAL_MACHINE,
318 reg_key.c_str(),
319 REG_OPTION_NON_VOLATILE,
320 KEY_READ,
321 &hkey);
322 if (res != ERROR_SUCCESS)
323 return false;
324
325 auto_hkey_close hkey_guard(hkey);
326
327 if (force)
328 {
329 // Verify key values
330 if (!!params.event_message_file)
331 {
332 string_type module_name;
333 res = registry::get_value(hkey, registry::get_event_message_file_param_name(), module_name);
334 if (res != ERROR_SUCCESS || module_name != params.event_message_file.get())
335 return false;
336 }
337
338 if (!!params.category_message_file)
339 {
340 string_type module_name;
341 res = registry::get_value(hkey, registry::get_category_message_file_param_name(), module_name);
342 if (res != ERROR_SUCCESS || module_name != params.category_message_file.get())
343 return false;
344 }
345
346 if (!!params.category_count)
347 {
348 // Set number of categories
349 DWORD category_count = 0;
350 res = registry::get_value(hkey, registry::get_category_count_param_name(), category_count);
351 if (res != ERROR_SUCCESS || category_count != params.category_count.get())
352 return false;
353 }
354
355 if (!!params.types_supported)
356 {
357 // Set the supported event types
358 DWORD event_types = 0;
359 res = registry::get_value(hkey, registry::get_types_supported_param_name(), event_types);
360 if (res != ERROR_SUCCESS || event_types != params.types_supported.get())
361 return false;
362 }
363 }
364
365 return true;
366 }
367
368 //! The function initializes the event log registry key
369 template< typename CharT >
init_event_log_registry(std::basic_string<CharT> const & log_name,std::basic_string<CharT> const & source_name,bool force,registry_params<CharT> const & params)370 void init_event_log_registry(
371 std::basic_string< CharT > const& log_name,
372 std::basic_string< CharT > const& source_name,
373 bool force,
374 registry_params< CharT > const& params)
375 {
376 typedef std::basic_string< CharT > string_type;
377 typedef registry_traits< CharT > registry;
378 // Registry key name that contains log description
379 string_type reg_key = registry::make_event_log_key(log_name, source_name);
380
381 // First check the registry keys and values in read-only mode.
382 // This allows to avoid UAC asking for elevated permissions to modify HKLM registry when no modification is actually needed.
383 if (verify_event_log_registry(reg_key, force, params))
384 return;
385
386 // Create or open the key
387 HKEY hkey = 0;
388 DWORD disposition = 0;
389 LSTATUS res = registry::create_key(
390 HKEY_LOCAL_MACHINE,
391 reg_key.c_str(),
392 0,
393 NULL,
394 REG_OPTION_NON_VOLATILE,
395 KEY_WRITE,
396 NULL,
397 &hkey,
398 &disposition);
399 if (res != ERROR_SUCCESS)
400 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry key for the event log", (res));
401
402 auto_hkey_close hkey_guard(hkey);
403
404 if (disposition != REG_OPENED_EXISTING_KEY || force)
405 {
406 // Fill registry values
407 if (!!params.event_message_file)
408 {
409 // Set the module file name that contains event resources
410 string_type const& module_name = params.event_message_file.get();
411 res = registry::set_value(
412 hkey,
413 registry::get_event_message_file_param_name(),
414 0,
415 REG_EXPAND_SZ,
416 reinterpret_cast< LPBYTE >(const_cast< CharT* >(module_name.c_str())),
417 static_cast< DWORD >((module_name.size() + 1) * sizeof(CharT)));
418 if (res != ERROR_SUCCESS)
419 {
420 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry value "
421 + log::aux::to_narrow(string_type(registry::get_event_message_file_param_name())), (res));
422 }
423 }
424
425 if (!!params.category_message_file)
426 {
427 // Set the module file name that contains event category resources
428 string_type const& module_name = params.category_message_file.get();
429 res = registry::set_value(
430 hkey,
431 registry::get_category_message_file_param_name(),
432 0,
433 REG_SZ,
434 reinterpret_cast< LPBYTE >(const_cast< CharT* >(module_name.c_str())),
435 static_cast< DWORD >((module_name.size() + 1) * sizeof(CharT)));
436 if (res != ERROR_SUCCESS)
437 {
438 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry value "
439 + log::aux::to_narrow(string_type(registry::get_category_message_file_param_name())), (res));
440 }
441 }
442
443 if (!!params.category_count)
444 {
445 // Set number of categories
446 DWORD category_count = params.category_count.get();
447 res = registry::set_value(
448 hkey,
449 registry::get_category_count_param_name(),
450 0,
451 REG_DWORD,
452 reinterpret_cast< LPBYTE >(&category_count),
453 static_cast< DWORD >(sizeof(category_count)));
454 if (res != ERROR_SUCCESS)
455 {
456 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry value "
457 + log::aux::to_narrow(string_type(registry::get_category_count_param_name())), (res));
458 }
459 }
460
461 if (!!params.types_supported)
462 {
463 // Set the supported event types
464 DWORD event_types = params.types_supported.get();
465 res = registry::set_value(
466 hkey,
467 registry::get_types_supported_param_name(),
468 0,
469 REG_DWORD,
470 reinterpret_cast< LPBYTE >(&event_types),
471 static_cast< DWORD >(sizeof(event_types)));
472 if (res != ERROR_SUCCESS)
473 {
474 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not create registry value "
475 + log::aux::to_narrow(string_type(registry::get_types_supported_param_name())), (res));
476 }
477 }
478 }
479 }
480
481 } // namespace aux
482
483 } // namespace sinks
484
485 BOOST_LOG_CLOSE_NAMESPACE // namespace log
486
487 } // namespace boost
488
489 #include <boost/log/detail/footer.hpp>
490
491 #endif // BOOST_LOG_WINDOWS_EVENT_LOG_REGISTRY_HPP_INCLUDED_
492