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_backend.cpp
9  * \author Andrey Semashev
10  * \date   07.11.2008
11  *
12  * \brief  A logging sink backend that uses Windows NT event log API
13  *         for signalling application events.
14  */
15 
16 #ifndef BOOST_LOG_WITHOUT_EVENT_LOG
17 
18 #include <boost/log/detail/config.hpp>
19 #include <string>
20 #include <vector>
21 #include <ostream>
22 #include <stdexcept>
23 #include <boost/scoped_array.hpp>
24 #include <boost/system/windows_error.hpp>
25 #include <boost/log/exceptions.hpp>
26 #include <boost/log/sinks/event_log_backend.hpp>
27 #include <boost/log/sinks/event_log_constants.hpp>
28 #include <boost/log/utility/once_block.hpp>
29 #include <boost/log/detail/cleanup_scope_guard.hpp>
30 #include <boost/log/detail/attachable_sstream_buf.hpp>
31 #include <boost/log/detail/code_conversion.hpp>
32 #include <boost/log/utility/formatting_ostream.hpp>
33 #include <windows.h>
34 #include <psapi.h>
35 #include "unique_ptr.hpp"
36 #include "windows/event_log_registry.hpp"
37 #include "windows/simple_event_log.h"
38 #include <boost/log/detail/header.hpp>
39 
40 namespace boost {
41 
42 BOOST_LOG_OPEN_NAMESPACE
43 
44 namespace sinks {
45 
46 namespace event_log {
47 
48     //! The function constructs log record level from an integer
make_event_type(unsigned short lev)49     BOOST_LOG_API event_type make_event_type(unsigned short lev)
50     {
51         switch (lev)
52         {
53         case success: return success;
54         case warning: return warning;
55         case error: return error;
56         default:
57             BOOST_THROW_EXCEPTION(std::out_of_range("Windows NT event type is out of range"));
58             BOOST_LOG_UNREACHABLE_RETURN(info);
59         case info: return info;
60         }
61     }
62 
63 } // namespace event_log
64 
65 BOOST_LOG_ANONYMOUS_NAMESPACE {
66 
67 #ifdef BOOST_LOG_USE_CHAR
68     //! A simple forwarder to the ReportEvent API
69     inline BOOL report_event(
70         HANDLE hEventLog,
71         WORD wType,
72         WORD wCategory,
73         DWORD dwEventID,
74         PSID lpUserSid,
75         WORD wNumStrings,
76         DWORD dwDataSize,
77         const char** lpStrings,
78         LPVOID lpRawData)
79     {
80         return ReportEventA(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData);
81     }
82     //! A simple forwarder to the GetModuleFileName API
83     inline DWORD get_module_file_name(HMODULE hModule, char* lpFilename, DWORD nSize)
84     {
85         return GetModuleFileNameA(hModule, lpFilename, nSize);
86     }
87     //! A simple forwarder to the RegisterEventSource API
88     inline HANDLE register_event_source(const char* lpUNCServerName, const char* lpSourceName)
89     {
90         return RegisterEventSourceA(lpUNCServerName, lpSourceName);
91     }
92     //! The function completes default source name for the sink backend
93     inline void complete_default_simple_event_log_source_name(std::string& name)
94     {
95         name += " simple event source";
96     }
97     //! The function completes default source name for the sink backend
98     inline void complete_default_event_log_source_name(std::string& name)
99     {
100         name += " event source";
101     }
102 #endif // BOOST_LOG_USE_CHAR
103 
104 #ifdef BOOST_LOG_USE_WCHAR_T
105     //! A simple forwarder to the ReportEvent API
106     inline BOOL report_event(
107         HANDLE hEventLog,
108         WORD wType,
109         WORD wCategory,
110         DWORD dwEventID,
111         PSID lpUserSid,
112         WORD wNumStrings,
113         DWORD dwDataSize,
114         const wchar_t** lpStrings,
115         LPVOID lpRawData)
116     {
117         return ReportEventW(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData);
118     }
119     //! A simple forwarder to the GetModuleFileName API
120     inline DWORD get_module_file_name(HMODULE hModule, wchar_t* lpFilename, DWORD nSize)
121     {
122         return GetModuleFileNameW(hModule, lpFilename, nSize);
123     }
124     //! A simple forwarder to the RegisterEventSource API
125     inline HANDLE register_event_source(const wchar_t* lpUNCServerName, const wchar_t* lpSourceName)
126     {
127         return RegisterEventSourceW(lpUNCServerName, lpSourceName);
128     }
129     //! The function completes default source name for the sink backend
130     inline void complete_default_simple_event_log_source_name(std::wstring& name)
131     {
132         name += L" simple event source";
133     }
134     //! The function completes default source name for the sink backend
135     inline void complete_default_event_log_source_name(std::wstring& name)
136     {
137         name += L" event source";
138     }
139 #endif // BOOST_LOG_USE_WCHAR_T
140 
141     //! The function finds the handle for the current module
142     void init_self_module_handle(HMODULE& handle)
143     {
144         // Acquire all modules of the current process
145         HANDLE hProcess = GetCurrentProcess();
146         std::vector< HMODULE > modules;
147         DWORD module_count = 1024;
148         do
149         {
150             modules.resize(module_count, HMODULE(0));
151             BOOL res = EnumProcessModules(
152                 hProcess,
153                 &modules[0],
154                 static_cast< DWORD >(modules.size() * sizeof(HMODULE)),
155                 &module_count);
156             module_count /= sizeof(HMODULE);
157 
158             if (!res)
159             {
160                 DWORD err = GetLastError();
161                 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not enumerate process modules", (err));
162             }
163         }
164         while (module_count > modules.size());
165         modules.resize(module_count, HMODULE(0));
166 
167         // Now find the current module among them
168         void* p = (void*)&init_self_module_handle;
169         for (std::size_t i = 0, n = modules.size(); i < n; ++i)
170         {
171             MODULEINFO info;
172             if (!GetModuleInformation(hProcess, modules[i], &info, sizeof(info)))
173             {
174                 DWORD err = GetLastError();
175                 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not acquire module information", (err));
176             }
177 
178             if (info.lpBaseOfDll <= p && (static_cast< unsigned char* >(info.lpBaseOfDll) + info.SizeOfImage) > p)
179             {
180                 // Found it
181                 handle = modules[i];
182                 break;
183             }
184         }
185 
186         if (!handle)
187             BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not find self module information", (boost::system::windows_error::invalid_handle));
188     }
189 
190     //! Retrieves the full name of the current module (be that dll or exe)
191     template< typename CharT >
192     std::basic_string< CharT > get_current_module_name()
193     {
194         static HMODULE hSelfModule = 0;
195 
196         BOOST_LOG_ONCE_BLOCK()
197         {
198             init_self_module_handle(hSelfModule);
199         }
200 
201         // Get the module file name
202         CharT buf[MAX_PATH];
203         DWORD size = get_module_file_name(hSelfModule, buf, sizeof(buf) / sizeof(*buf));
204         if (size == 0)
205         {
206             DWORD err = GetLastError();
207             BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not get module file name", (err));
208         }
209 
210         return std::basic_string< CharT >(buf, buf + size);
211     }
212 
213 } // namespace
214 
215 //////////////////////////////////////////////////////////////////////////
216 //  Simple event log backend implementation
217 //////////////////////////////////////////////////////////////////////////
218 //! Sink backend implementation
219 template< typename CharT >
220 struct basic_simple_event_log_backend< CharT >::implementation
221 {
222     //! A handle for the registered event provider
223     HANDLE m_SourceHandle;
224     //! A level mapping functor
225     event_type_mapper_type m_LevelMapper;
226 
implementationboost::sinks::basic_simple_event_log_backend::implementation227     implementation() : m_SourceHandle(0)
228     {
229     }
230 };
231 
232 //! Default constructor. Registers event source Boost.Log <Boost version> in the Application log.
233 template< typename CharT >
basic_simple_event_log_backend()234 BOOST_LOG_API basic_simple_event_log_backend< CharT >::basic_simple_event_log_backend()
235 {
236     construct(log::aux::empty_arg_list());
237 }
238 
239 //! Destructor
240 template< typename CharT >
~basic_simple_event_log_backend()241 BOOST_LOG_API basic_simple_event_log_backend< CharT >::~basic_simple_event_log_backend()
242 {
243     DeregisterEventSource(m_pImpl->m_SourceHandle);
244     delete m_pImpl;
245 }
246 
247 //! Constructs backend implementation
248 template< typename CharT >
construct(string_type const & target,string_type const & log_name,string_type const & source_name,event_log::registration_mode reg_mode)249 BOOST_LOG_API void basic_simple_event_log_backend< CharT >::construct(
250     string_type const& target, string_type const& log_name, string_type const& source_name, event_log::registration_mode reg_mode)
251 {
252     if (reg_mode != event_log::never)
253     {
254         aux::registry_params< char_type > reg_params;
255         reg_params.event_message_file = get_current_module_name< char_type >();
256         reg_params.types_supported = DWORD(
257             EVENTLOG_SUCCESS |
258             EVENTLOG_INFORMATION_TYPE |
259             EVENTLOG_WARNING_TYPE |
260             EVENTLOG_ERROR_TYPE);
261         aux::init_event_log_registry(log_name, source_name, reg_mode == event_log::forced, reg_params);
262     }
263 
264     log::aux::unique_ptr< implementation > p(new implementation());
265 
266     const char_type* target_unc = NULL;
267     if (!target.empty())
268         target_unc = target.c_str();
269 
270     HANDLE hSource = register_event_source(target_unc, source_name.c_str());
271     if (!hSource)
272     {
273         const DWORD err = GetLastError();
274         BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not register event source", (err));
275     }
276 
277     p->m_SourceHandle = hSource;
278 
279     m_pImpl = p.release();
280 }
281 
282 //! Returns default log name
283 template< typename CharT >
284 BOOST_LOG_API typename basic_simple_event_log_backend< CharT >::string_type
get_default_log_name()285 basic_simple_event_log_backend< CharT >::get_default_log_name()
286 {
287     return aux::registry_traits< char_type >::make_default_log_name();
288 }
289 
290 //! Returns default source name
291 template< typename CharT >
292 BOOST_LOG_API typename basic_simple_event_log_backend< CharT >::string_type
get_default_source_name()293 basic_simple_event_log_backend< CharT >::get_default_source_name()
294 {
295     string_type source_name = aux::registry_traits< char_type >::make_default_source_name();
296     complete_default_simple_event_log_source_name(source_name);
297     return source_name;
298 }
299 
300 //! The method installs the function object that maps application severity levels to WinAPI event types
301 template< typename CharT >
set_event_type_mapper(event_type_mapper_type const & mapper)302 BOOST_LOG_API void basic_simple_event_log_backend< CharT >::set_event_type_mapper(event_type_mapper_type const& mapper)
303 {
304     m_pImpl->m_LevelMapper = mapper;
305 }
306 
307 //! The method puts the formatted message to the event log
308 template< typename CharT >
consume(record_view const & rec,string_type const & formatted_message)309 BOOST_LOG_API void basic_simple_event_log_backend< CharT >::consume(record_view const& rec, string_type const& formatted_message)
310 {
311     const char_type* message = formatted_message.c_str();
312     event_log::event_type evt_type = event_log::info;
313     if (!m_pImpl->m_LevelMapper.empty())
314         evt_type = m_pImpl->m_LevelMapper(rec);
315 
316     DWORD event_id;
317     switch (evt_type)
318     {
319     case event_log::success:
320         event_id = BOOST_LOG_MSG_DEBUG; break;
321     case event_log::warning:
322         event_id = BOOST_LOG_MSG_WARNING; break;
323     case event_log::error:
324         event_id = BOOST_LOG_MSG_ERROR; break;
325     default:
326         event_id = BOOST_LOG_MSG_INFO; break;
327     }
328 
329     report_event(
330         m_pImpl->m_SourceHandle,        // Event log handle.
331         static_cast< WORD >(evt_type),  // Event type.
332         0,                              // Event category.
333         event_id,                       // Event identifier.
334         NULL,                           // No user security identifier.
335         1,                              // Number of substitution strings.
336         0,                              // No data.
337         &message,                       // Pointer to strings.
338         NULL);                          // No data.
339 }
340 
341 //////////////////////////////////////////////////////////////////////////
342 //  Customizable event log backend implementation
343 //////////////////////////////////////////////////////////////////////////
344 namespace event_log {
345 
346     template< typename CharT >
347     class basic_event_composer< CharT >::insertion_composer
348     {
349     public:
350         //! Function object result type
351         typedef void result_type;
352 
353     private:
354         //! The list of insertion composers (in backward order)
355         typedef std::vector< formatter_type > formatters;
356 
357     private:
358         //! The insertion string composers
359         formatters m_Formatters;
360 
361     public:
362         //! Default constructor
insertion_composer()363         insertion_composer() {}
364         //! Composition operator
operator ()(record_view const & rec,insertion_list & insertions) const365         void operator() (record_view const& rec, insertion_list& insertions) const
366         {
367             std::size_t size = m_Formatters.size();
368             insertions.resize(size);
369             for (std::size_t i = 0; i < size; ++i)
370             {
371                 typename formatter_type::stream_type strm(insertions[i]);
372                 m_Formatters[i](rec, strm);
373                 strm.flush();
374             }
375         }
376         //! Adds a new formatter to the list
add_formatter(formatter_type const & fmt)377         void add_formatter(formatter_type const& fmt)
378         {
379             m_Formatters.push_back(formatter_type(fmt));
380         }
381     };
382 
383     //! Default constructor
384     template< typename CharT >
basic_event_composer(event_id_mapper_type const & id_mapper)385     basic_event_composer< CharT >::basic_event_composer(event_id_mapper_type const& id_mapper) :
386         m_EventIDMapper(id_mapper)
387     {
388     }
389     //! Copy constructor
390     template< typename CharT >
basic_event_composer(basic_event_composer const & that)391     basic_event_composer< CharT >::basic_event_composer(basic_event_composer const& that) :
392         m_EventIDMapper(that.m_EventIDMapper),
393         m_EventMap(that.m_EventMap)
394     {
395     }
396     //! Destructor
397     template< typename CharT >
~basic_event_composer()398     basic_event_composer< CharT >::~basic_event_composer()
399     {
400     }
401 
402     //! Assignment
403     template< typename CharT >
operator =(basic_event_composer that)404     basic_event_composer< CharT >& basic_event_composer< CharT >::operator= (basic_event_composer that)
405     {
406         swap(that);
407         return *this;
408     }
409     //! Swapping
410     template< typename CharT >
swap(basic_event_composer & that)411     void basic_event_composer< CharT >::swap(basic_event_composer& that)
412     {
413         m_EventIDMapper.swap(that.m_EventIDMapper);
414         m_EventMap.swap(that.m_EventMap);
415     }
416     //! Creates a new entry for a message
417     template< typename CharT >
418     typename basic_event_composer< CharT >::event_map_reference
operator [](event_id id)419     basic_event_composer< CharT >::operator[] (event_id id)
420     {
421         return event_map_reference(id, *this);
422     }
423     //! Creates a new entry for a message
424     template< typename CharT >
425     typename basic_event_composer< CharT >::event_map_reference
operator [](int id)426     basic_event_composer< CharT >::operator[] (int id)
427     {
428         return event_map_reference(make_event_id(id), *this);
429     }
430 
431     //! Event composition operator
432     template< typename CharT >
operator ()(record_view const & rec,insertion_list & insertions) const433     event_id basic_event_composer< CharT >::operator() (record_view const& rec, insertion_list& insertions) const
434     {
435         event_id id = m_EventIDMapper(rec);
436         typename event_map::const_iterator it = m_EventMap.find(id);
437         if (it != m_EventMap.end())
438             it->second(rec, insertions);
439         return id;
440     }
441 
442     //! Adds a formatter to the insertion composers list
443     template< typename CharT >
444     typename basic_event_composer< CharT >::insertion_composer*
add_formatter(event_id id,insertion_composer * composer,formatter_type const & fmt)445     basic_event_composer< CharT >::add_formatter(event_id id, insertion_composer* composer, formatter_type const& fmt)
446     {
447         if (!composer)
448             composer = &m_EventMap[id];
449         composer->add_formatter(fmt);
450         return composer;
451     }
452 
453 #ifdef BOOST_LOG_USE_CHAR
454     template class BOOST_LOG_API basic_event_composer< char >;
455 #endif
456 #ifdef BOOST_LOG_USE_WCHAR_T
457     template class BOOST_LOG_API basic_event_composer< wchar_t >;
458 #endif
459 
460 } // namespace event_log
461 
462 
463 //! Backend implementation
464 template< typename CharT >
465 struct basic_event_log_backend< CharT >::implementation
466 {
467     //  NOTE: This order of data members is critical for MSVC 9.0 in debug mode,
468     //        as it ICEs if boost::functions are not the first members. Doh!
469 
470     //! An event category mapper
471     event_category_mapper_type m_CategoryMapper;
472     //! A level mapping functor
473     event_type_mapper_type m_LevelMapper;
474 
475     //! A handle for the registered event provider
476     HANDLE m_SourceHandle;
477     //! A functor that composes an event
478     event_composer_type m_EventComposer;
479     //! An array of formatted insertions
480     insertion_list m_Insertions;
481 
implementationboost::sinks::basic_event_log_backend::implementation482     implementation() : m_SourceHandle(0)
483     {
484     }
485 };
486 
487 //! Destructor
488 template< typename CharT >
~basic_event_log_backend()489 BOOST_LOG_API basic_event_log_backend< CharT >::~basic_event_log_backend()
490 {
491     DeregisterEventSource(m_pImpl->m_SourceHandle);
492     delete m_pImpl;
493 }
494 
495 //! Constructs backend implementation
496 template< typename CharT >
construct(filesystem::path const & message_file_name,string_type const & target,string_type const & log_name,string_type const & source_name,event_log::registration_mode reg_mode)497 BOOST_LOG_API void basic_event_log_backend< CharT >::construct(
498     filesystem::path const& message_file_name,
499     string_type const& target,
500     string_type const& log_name,
501     string_type const& source_name,
502     event_log::registration_mode reg_mode)
503 {
504     if (reg_mode != event_log::never)
505     {
506         if (message_file_name.empty())
507             BOOST_THROW_EXCEPTION(std::invalid_argument("Message file name not specified."));
508         aux::registry_params< char_type > reg_params;
509         string_type file_name;
510         log::aux::code_convert(message_file_name.string(), file_name);
511         reg_params.event_message_file = file_name;
512         reg_params.types_supported = DWORD(
513             EVENTLOG_SUCCESS |
514             EVENTLOG_INFORMATION_TYPE |
515             EVENTLOG_WARNING_TYPE |
516             EVENTLOG_ERROR_TYPE);
517         aux::init_event_log_registry(log_name, source_name, reg_mode == event_log::forced, reg_params);
518     }
519 
520     log::aux::unique_ptr< implementation > p(new implementation());
521 
522     const char_type* target_unc = NULL;
523     if (!target.empty())
524         target_unc = target.c_str();
525 
526     HANDLE hSource = register_event_source(target_unc, source_name.c_str());
527     if (!hSource)
528     {
529         const DWORD err = GetLastError();
530         BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Could not register event source", (err));
531     }
532 
533     p->m_SourceHandle = hSource;
534 
535     m_pImpl = p.release();
536 }
537 
538 //! The method puts the formatted message to the event log
539 template< typename CharT >
consume(record_view const & rec)540 BOOST_LOG_API void basic_event_log_backend< CharT >::consume(record_view const& rec)
541 {
542     if (!m_pImpl->m_EventComposer.empty())
543     {
544         log::aux::cleanup_guard< insertion_list > cleaner(m_pImpl->m_Insertions);
545 
546         // Get event ID and construct insertions
547         DWORD id = m_pImpl->m_EventComposer(rec, m_pImpl->m_Insertions);
548         WORD string_count = static_cast< WORD >(m_pImpl->m_Insertions.size());
549         scoped_array< const char_type* > strings(new const char_type*[string_count]);
550         for (WORD i = 0; i < string_count; ++i)
551             strings[i] = m_pImpl->m_Insertions[i].c_str();
552 
553         // Get event type
554         WORD event_type = EVENTLOG_INFORMATION_TYPE;
555         if (!m_pImpl->m_LevelMapper.empty())
556             event_type = static_cast< WORD >(m_pImpl->m_LevelMapper(rec));
557 
558         WORD event_category = 0;
559         if (!m_pImpl->m_CategoryMapper.empty())
560             event_category = static_cast< WORD >(m_pImpl->m_CategoryMapper(rec));
561 
562         report_event(
563             m_pImpl->m_SourceHandle,       // Event log handle.
564             event_type,                    // Event type.
565             event_category,                // Event category.
566             id,                            // Event identifier.
567             NULL,                          // No user security identifier.
568             string_count,                  // Number of substitution strings.
569             0,                             // No data.
570             strings.get(),                 // Pointer to strings.
571             NULL);                         // No data.
572     }
573 }
574 
575 //! Returns default log name
576 template< typename CharT >
577 BOOST_LOG_API typename basic_event_log_backend< CharT >::string_type
get_default_log_name()578 basic_event_log_backend< CharT >::get_default_log_name()
579 {
580     return aux::registry_traits< char_type >::make_default_log_name();
581 }
582 
583 //! Returns default source name
584 template< typename CharT >
585 BOOST_LOG_API typename basic_event_log_backend< CharT >::string_type
get_default_source_name()586 basic_event_log_backend< CharT >::get_default_source_name()
587 {
588     string_type source_name = aux::registry_traits< char_type >::make_default_source_name();
589     complete_default_event_log_source_name(source_name);
590     return source_name;
591 }
592 
593 //! The method installs the function object that maps application severity levels to WinAPI event types
594 template< typename CharT >
set_event_type_mapper(event_type_mapper_type const & mapper)595 BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_type_mapper(event_type_mapper_type const& mapper)
596 {
597     m_pImpl->m_LevelMapper = mapper;
598 }
599 
600 //! The method installs the function object that extracts event category from attribute values
601 template< typename CharT >
set_event_category_mapper(event_category_mapper_type const & mapper)602 BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_category_mapper(event_category_mapper_type const& mapper)
603 {
604     m_pImpl->m_CategoryMapper = mapper;
605 }
606 
607 /*!
608  * The method installs the function object that extracts event identifier from the attributes and creates
609  * insertion strings that will replace placeholders in the event message.
610  */
611 template< typename CharT >
set_event_composer(event_composer_type const & composer)612 BOOST_LOG_API void basic_event_log_backend< CharT >::set_event_composer(event_composer_type const& composer)
613 {
614     m_pImpl->m_EventComposer = composer;
615 }
616 
617 
618 #ifdef BOOST_LOG_USE_CHAR
619 template class basic_simple_event_log_backend< char >;
620 template class basic_event_log_backend< char >;
621 #endif
622 #ifdef BOOST_LOG_USE_WCHAR_T
623 template class basic_simple_event_log_backend< wchar_t >;
624 template class basic_event_log_backend< wchar_t >;
625 #endif
626 
627 } // namespace sinks
628 
629 BOOST_LOG_CLOSE_NAMESPACE // namespace log
630 
631 } // namespace boost
632 
633 #include <boost/log/detail/footer.hpp>
634 
635 #endif // !defined(BOOST_LOG_WITHOUT_EVENT_LOG)
636