• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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