// Module: LOG4CPLUS // File: nteventlogappender.cxx // Created: 4/2003 // Author: Michael CATANZARITI // // Copyright 2003-2010 Michael CATANZARITI // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dcmtk/oflog/config.h" #if defined (DCMTK_LOG4CPLUS_HAVE_NT_EVENT_LOG) #include "dcmtk/oflog/ntelogap.h" #include "dcmtk/oflog/loglevel.h" #include "dcmtk/oflog/streams.h" #include "dcmtk/oflog/helpers/loglog.h" #include "dcmtk/oflog/helpers/property.h" #include "dcmtk/oflog/spi/logevent.h" #include "dcmtk/oflog/internal/internal.h" #include "dcmtk/oflog/thread/syncpub.h" #include #include namespace dcmtk { namespace log4cplus { ////////////////////////////////////////////////////////////////////////////// // File LOCAL methods ////////////////////////////////////////////////////////////////////////////// namespace { static bool copySID(SID** ppDstSid, SID* pSrcSid) { DWORD dwLength = ::GetLengthSid(pSrcSid); SID * pDstSid = OFreinterpret_cast(SID*, calloc(1, dwLength)); if (! pDstSid) return false; if (CopySid(dwLength, pDstSid, pSrcSid)) { *ppDstSid = pDstSid; return true; } else { free (pDstSid); return false; } } static bool GetCurrentUserSID(SID** ppSid) { bool bSuccess = false; TOKEN_USER * ptu = 0; DWORD tusize = 0; HANDLE hProcess = ::GetCurrentProcess(); HANDLE hToken = 0; if (! ::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) goto finish; // Get the required size if (! GetTokenInformation(hToken, TokenUser, NULL, 0, &tusize)) goto finish; ptu = OFreinterpret_cast(TOKEN_USER*, calloc(1, tusize)); if (! ptu) goto finish; if (GetTokenInformation(hToken, TokenUser, OFreinterpret_cast(LPVOID, ptu), tusize, &tusize)) bSuccess = copySID (ppSid, OFreinterpret_cast(SID*, ptu->User.Sid)); finish:; if (hToken) CloseHandle (hToken); free (ptu); return bSuccess; } static HKEY regGetKey(const tstring& subkey, DWORD* disposition) { HKEY hkey = 0; #if defined (DCMTK_OFLOG_UNICODE) RegCreateKeyEx(HKEY_LOCAL_MACHINE, #else RegCreateKeyExA(HKEY_LOCAL_MACHINE, #endif subkey.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hkey, disposition); return hkey; } static void regSetString(HKEY hkey, const tstring& name, const tstring& value) { #if defined (DCMTK_OFLOG_UNICODE) RegSetValueEx(hkey, #else RegSetValueExA(hkey, #endif name.c_str(), 0, REG_SZ, OFreinterpret_cast(BYTE const *, value.c_str()), OFstatic_cast(DWORD, value.length() * sizeof(tchar))); } static void regSetDword(HKEY hkey, const tstring& name, DWORD value) { #if defined (DCMTK_OFLOG_UNICODE) RegSetValueEx(hkey, #else RegSetValueExA(hkey, #endif name.c_str(), 0, REG_DWORD, OFreinterpret_cast(LPBYTE, &value), OFstatic_cast(DWORD, sizeof(DWORD))); } } ////////////////////////////////////////////////////////////////////////////// // NTEventLogAppender ctor and dtor ////////////////////////////////////////////////////////////////////////////// NTEventLogAppender::NTEventLogAppender(const tstring& server, const tstring& log, const tstring& source) : server(server), log(log), source(source), hEventLog(NULL), pCurrentUserSID(NULL) { init(); } NTEventLogAppender::NTEventLogAppender(const helpers::Properties & properties) : Appender(properties), server(properties.getProperty( DCMTK_LOG4CPLUS_TEXT("server") )), log(properties.getProperty( DCMTK_LOG4CPLUS_TEXT("log") )), source(properties.getProperty( DCMTK_LOG4CPLUS_TEXT("source") )), hEventLog(NULL), pCurrentUserSID(NULL) { init(); } void NTEventLogAppender::init() { if(source.empty()) { helpers::getLogLog().warn( DCMTK_LOG4CPLUS_TEXT("Source option not set for appender [") + name + DCMTK_LOG4CPLUS_TEXT("].")); return; } if(log.empty ()) { log = DCMTK_LOG4CPLUS_TEXT("Application"); } // current user security identifier GetCurrentUserSID(&pCurrentUserSID); addRegistryInfo(); #if defined (DCMTK_OFLOG_UNICODE) hEventLog = ::RegisterEventSource(server.empty () ? 0 : server.c_str(), source.c_str()); #else hEventLog = ::RegisterEventSourceA(server.empty () ? 0 : server.c_str(), source.c_str()); #endif if (! hEventLog || hEventLog == HANDLE(ERROR_INVALID_HANDLE)) helpers::getLogLog().warn ( DCMTK_LOG4CPLUS_TEXT("Event source registration failed.")); } NTEventLogAppender::~NTEventLogAppender() { destructorImpl(); if(pCurrentUserSID != NULL) { free (pCurrentUserSID); pCurrentUserSID = NULL; } } ////////////////////////////////////////////////////////////////////////////// // NTEventLogAppender public methods ////////////////////////////////////////////////////////////////////////////// void NTEventLogAppender::close() { if(hEventLog != NULL) { ::DeregisterEventSource(hEventLog); hEventLog = NULL; } closed = true; } ////////////////////////////////////////////////////////////////////////////// // NTEventLogAppender protected methods ////////////////////////////////////////////////////////////////////////////// void NTEventLogAppender::append(const spi::InternalLoggingEvent& event) { if(hEventLog == NULL) { helpers::getLogLog().warn(DCMTK_LOG4CPLUS_TEXT("NT EventLog not opened.")); return; } tstring & str = formatEvent (event); // From MSDN documentation for ReportEvent(): // Each string is limited to 31,839 characters. if (str.size () > 31839) str.resize (31839); const tchar * s = str.c_str (); #if defined (DCMTK_OFLOG_UNICODE) BOOL bSuccess = ::ReportEvent(hEventLog, #else BOOL bSuccess = ::ReportEventA(hEventLog, #endif getEventType(event), getEventCategory(event), 0x1000, pCurrentUserSID, 1, 0, &s, NULL); if(!bSuccess) { helpers::getLogLog().error( DCMTK_LOG4CPLUS_TEXT("Cannot report event in NT EventLog.")); } } WORD NTEventLogAppender::getEventType(const spi::InternalLoggingEvent& event) { WORD ret_val; LogLevel const ll = event.getLogLevel(); if (ll >= ERROR_LOG_LEVEL) // or FATAL_LOG_LEVEL ret_val = EVENTLOG_ERROR_TYPE; else if (ll >= WARN_LOG_LEVEL) ret_val = EVENTLOG_WARNING_TYPE; else // INFO_LOG_LEVEL or DEBUG_LOG_LEVEL or TRACE_LOG_LEVEL ret_val = EVENTLOG_INFORMATION_TYPE; return ret_val; } WORD NTEventLogAppender::getEventCategory(const spi::InternalLoggingEvent& event) { WORD ret_val; LogLevel const ll = event.getLogLevel(); if (ll >= FATAL_LOG_LEVEL) ret_val = 1; else if (ll >= ERROR_LOG_LEVEL) ret_val = 2; else if (ll >= WARN_LOG_LEVEL) ret_val = 3; else if (ll >= INFO_LOG_LEVEL) ret_val = 4; else if (ll >= DEBUG_LOG_LEVEL) ret_val = 5; else // TRACE_LOG_LEVEL ret_val = 6; return ret_val; } // Add this source with appropriate configuration keys to the registry. void NTEventLogAppender::addRegistryInfo() { DWORD disposition; HKEY hkey = 0; tstring subkey = DCMTK_LOG4CPLUS_TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\") + log + DCMTK_LOG4CPLUS_TEXT("\\") + source; hkey = regGetKey(subkey, &disposition); if(disposition == REG_CREATED_NEW_KEY) { regSetString(hkey, DCMTK_LOG4CPLUS_TEXT("EventMessageFile"), DCMTK_LOG4CPLUS_TEXT("NTEventLogAppender.dll")); regSetString(hkey, DCMTK_LOG4CPLUS_TEXT("CategoryMessageFile"), DCMTK_LOG4CPLUS_TEXT("NTEventLogAppender.dll")); regSetDword(hkey, DCMTK_LOG4CPLUS_TEXT("TypesSupported"), OFstatic_cast(DWORD, 7)); regSetDword(hkey, DCMTK_LOG4CPLUS_TEXT("CategoryCount"), OFstatic_cast(DWORD, 5)); } RegCloseKey(hkey); return; } } // namespace log4cplus } // end namespace dcmtk #else int oflog_ntelogap_cc_dummy_to_keep_linker_from_moaning = 0; #endif // DCMTK_LOG4CPLUS_HAVE_NT_EVENT_LOG