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