| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to You 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. | |
| */ | |
| #if (defined(WIN32) || defined(_WIN32)) && !defined(_WIN32_WCE) | |
| #include <windows.h> | |
| #include <Heapapi.h> | |
| #include <log4cxx/nt/nteventlogappender.h> | |
| #include <log4cxx/spi/loggingevent.h> | |
| #include <log4cxx/helpers/loglog.h> | |
| #include <log4cxx/level.h> | |
| #include <log4cxx/helpers/stringhelper.h> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #include <log4cxx/helpers/pool.h> | |
| #include <log4cxx/private/appenderskeleton_priv.h> | |
| using namespace LOG4CXX_NS; | |
| using namespace LOG4CXX_NS::spi; | |
| using namespace LOG4CXX_NS::helpers; | |
| using namespace LOG4CXX_NS::nt; | |
| #define priv static_cast<NTEventLogAppenderPrivate*>(m_priv.get()) | |
| struct NTEventLogAppender::NTEventLogAppenderPrivate : public AppenderSkeleton::AppenderSkeletonPrivate { | |
| NTEventLogAppenderPrivate() : | |
| hEventLog(nullptr), | |
| pCurrentUserSID(nullptr) {} | |
| NTEventLogAppenderPrivate( LayoutPtr layout ) : | |
| AppenderSkeletonPrivate(layout), | |
| hEventLog(nullptr), | |
| pCurrentUserSID(nullptr) {} | |
| // Data | |
| LogString server; | |
| LogString log; | |
| LogString source; | |
| HANDLE hEventLog; | |
| SID* pCurrentUserSID; | |
| void close(); | |
| }; | |
| class CCtUserSIDHelper | |
| { | |
| public: | |
| static bool FreeSid(SID* pSid) | |
| { | |
| return ::HeapFree(GetProcessHeap(), 0, (LPVOID)pSid) != 0; | |
| } | |
| static bool CopySid(SID * * ppDstSid, SID* pSrcSid) | |
| { | |
| bool bSuccess = false; | |
| DWORD dwLength = ::GetLengthSid(pSrcSid); | |
| *ppDstSid = (SID*) ::HeapAlloc(GetProcessHeap(), | |
| HEAP_ZERO_MEMORY, dwLength); | |
| if (::CopySid(dwLength, *ppDstSid, pSrcSid)) | |
| { | |
| bSuccess = true; | |
| } | |
| else | |
| { | |
| FreeSid(*ppDstSid); | |
| } | |
| return bSuccess; | |
| } | |
| static bool GetCurrentUserSID(SID * * ppSid) | |
| { | |
| bool bSuccess = false; | |
| // Pseudohandle so don't need to close it | |
| HANDLE hProcess = ::GetCurrentProcess(); | |
| HANDLE hToken = NULL; | |
| if (::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) | |
| { | |
| // Get the required size | |
| DWORD tusize = 0; | |
| GetTokenInformation(hToken, TokenUser, NULL, 0, &tusize); | |
| TOKEN_USER* ptu = (TOKEN_USER*)new BYTE[tusize]; | |
| if (GetTokenInformation(hToken, TokenUser, (LPVOID)ptu, tusize, &tusize)) | |
| { | |
| bSuccess = CopySid(ppSid, (SID*)ptu->User.Sid); | |
| } | |
| CloseHandle(hToken); | |
| delete [] ptu; | |
| } | |
| return bSuccess; | |
| } | |
| }; | |
| IMPLEMENT_LOG4CXX_OBJECT(NTEventLogAppender) | |
| NTEventLogAppender::NTEventLogAppender() : | |
| AppenderSkeleton(std::make_unique<NTEventLogAppenderPrivate>()) | |
| { | |
| } | |
| NTEventLogAppender::NTEventLogAppender(const LogString& server, const LogString& log, const LogString& source, const LayoutPtr& layout) | |
| : AppenderSkeleton(std::make_unique<NTEventLogAppenderPrivate>(layout)) | |
| { | |
| priv->server = server; | |
| priv->log = log; | |
| priv->source = source; | |
| activateOptions(); | |
| } | |
| NTEventLogAppender::~NTEventLogAppender() | |
| { | |
| if (priv->setClosed()) | |
| priv->close(); | |
| } | |
| void NTEventLogAppender::close() | |
| { | |
| if (priv->setClosed()) | |
| priv->close(); | |
| } | |
| void NTEventLogAppender::NTEventLogAppenderPrivate::close() | |
| { | |
| if (this->hEventLog != NULL) | |
| { | |
| ::DeregisterEventSource(this->hEventLog); | |
| this->hEventLog = NULL; | |
| } | |
| if (this->pCurrentUserSID != NULL) | |
| { | |
| CCtUserSIDHelper::FreeSid((::SID*) this->pCurrentUserSID); | |
| this->pCurrentUserSID = NULL; | |
| } | |
| } | |
| void NTEventLogAppender::setOption(const LogString& option, const LogString& value) | |
| { | |
| if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SERVER"), LOG4CXX_STR("server"))) | |
| { | |
| priv->server = value; | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOG"), LOG4CXX_STR("log"))) | |
| { | |
| priv->log = value; | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SOURCE"), LOG4CXX_STR("source"))) | |
| { | |
| priv->source = value; | |
| } | |
| else | |
| { | |
| AppenderSkeleton::setOption(option, value); | |
| } | |
| } | |
| void NTEventLogAppender::activateOptions( ) | |
| { | |
| if (priv->source.empty()) | |
| { | |
| LogLog::warn( | |
| ((LogString) LOG4CXX_STR("Source option not set for appender [")) | |
| + this->m_priv->name + LOG4CXX_STR("].")); | |
| return; | |
| } | |
| if (priv->log.empty()) | |
| { | |
| priv->log = LOG4CXX_STR("Application"); | |
| } | |
| priv->close(); | |
| // current user security identifier | |
| CCtUserSIDHelper::GetCurrentUserSID((::SID**) &priv->pCurrentUserSID); | |
| addRegistryInfo(); | |
| LOG4CXX_ENCODE_WCHAR(wsource, priv->source); | |
| LOG4CXX_ENCODE_WCHAR(wserver, priv->server); | |
| priv->hEventLog = ::RegisterEventSourceW( | |
| wserver.empty() ? NULL : wserver.c_str(), | |
| wsource.c_str()); | |
| if (priv->hEventLog == NULL) | |
| { | |
| LogString msg(LOG4CXX_STR("Cannot register NT EventLog -- server: '")); | |
| msg.append(priv->server); | |
| msg.append(LOG4CXX_STR("' source: '")); | |
| msg.append(priv->source); | |
| LogLog::error(msg); | |
| LogLog::error(getErrorString(LOG4CXX_STR("RegisterEventSource"))); | |
| } | |
| } | |
| void NTEventLogAppender::append( const spi::LoggingEventPtr& event ) | |
| { | |
| if (priv->hEventLog == NULL) | |
| { | |
| LogLog::warn(LOG4CXX_STR("NT EventLog not opened.")); | |
| return; | |
| } | |
| LogString oss; | |
| this->m_priv->layout->format(oss, event); | |
| auto msgs = oss.c_str(); | |
| #if LOG4CXX_LOGCHAR_IS_WCHAR | |
| BOOL bSuccess = ::ReportEventW( | |
| priv->hEventLog, | |
| getEventType(event), | |
| getEventCategory(event), | |
| 0x1000, | |
| priv->pCurrentUserSID, | |
| 1, | |
| 0, | |
| (LPCWSTR*) &msgs, | |
| NULL); | |
| #elif LOG4CXX_LOGCHAR_IS_UTF8 | |
| BOOL bSuccess = ::ReportEventA( | |
| priv->hEventLog, | |
| getEventType(event), | |
| getEventCategory(event), | |
| 0x1000, | |
| priv->pCurrentUserSID, | |
| 1, | |
| 0, | |
| (LPCSTR*)&msgs, | |
| NULL); | |
| #endif | |
| if (!bSuccess) | |
| { | |
| LogLog::error(getErrorString(LOG4CXX_STR("ReportEvent"))); | |
| } | |
| } | |
| /* | |
| * Add this source with appropriate configuration keys to the registry. | |
| */ | |
| void NTEventLogAppender::addRegistryInfo() | |
| { | |
| DWORD disposition = 0; | |
| ::HKEY hkey = 0; | |
| LogString subkey(LOG4CXX_STR("SYSTEM\\CurrentControlSet\\Services\\EventLog\\")); | |
| subkey.append(priv->log); | |
| subkey.append(1, (logchar) 0x5C /* '\\' */); | |
| subkey.append(priv->source); | |
| LOG4CXX_ENCODE_WCHAR(wsubkey, subkey); | |
| long stat = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wsubkey.c_str(), 0, NULL, | |
| REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, | |
| &hkey, &disposition); | |
| if (stat == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY) | |
| { | |
| HMODULE hmodule = GetModuleHandleW(L"log4cxx"); | |
| if (hmodule == NULL) | |
| { | |
| hmodule = GetModuleHandleW(0); | |
| } | |
| wchar_t modpath[_MAX_PATH]; | |
| DWORD modlen = GetModuleFileNameW(hmodule, modpath, _MAX_PATH - 1); | |
| if (modlen > 0) | |
| { | |
| modpath[modlen] = 0; | |
| RegSetValueExW(hkey, L"EventMessageFile", 0, REG_SZ, | |
| (LPBYTE) modpath, (DWORD)(wcslen(modpath) * sizeof(wchar_t))); | |
| RegSetValueExW(hkey, L"CategoryMessageFile", 0, REG_SZ, | |
| (LPBYTE) modpath, (DWORD)(wcslen(modpath) * sizeof(wchar_t))); | |
| DWORD typesSupported = 7; | |
| DWORD categoryCount = 6; | |
| RegSetValueExW(hkey, L"TypesSupported", 0, REG_DWORD, | |
| (LPBYTE)&typesSupported, sizeof(DWORD)); | |
| RegSetValueExW(hkey, L"CategoryCount", 0, REG_DWORD, | |
| (LPBYTE)&categoryCount, sizeof(DWORD)); | |
| } | |
| } | |
| RegCloseKey(hkey); | |
| return; | |
| } | |
| WORD NTEventLogAppender::getEventType(const LoggingEventPtr& event) | |
| { | |
| int priority = event->getLevel()->toInt(); | |
| WORD type = EVENTLOG_SUCCESS; | |
| if (priority >= Level::INFO_INT) | |
| { | |
| type = EVENTLOG_INFORMATION_TYPE; | |
| if (priority >= Level::WARN_INT) | |
| { | |
| type = EVENTLOG_WARNING_TYPE; | |
| if (priority >= Level::ERROR_INT) | |
| { | |
| type = EVENTLOG_ERROR_TYPE; | |
| } | |
| } | |
| } | |
| return type; | |
| } | |
| WORD NTEventLogAppender::getEventCategory(const LoggingEventPtr& event) | |
| { | |
| int priority = event->getLevel()->toInt(); | |
| WORD category = 1; | |
| if (priority >= Level::DEBUG_INT) | |
| { | |
| category = 2; | |
| if (priority >= Level::INFO_INT) | |
| { | |
| category = 3; | |
| if (priority >= Level::WARN_INT) | |
| { | |
| category = 4; | |
| if (priority >= Level::ERROR_INT) | |
| { | |
| category = 5; | |
| if (priority >= Level::FATAL_INT) | |
| { | |
| category = 6; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return category; | |
| } | |
| LogString NTEventLogAppender::getErrorString(const LogString& function) | |
| { | |
| enum { MSGSIZE = 5000 }; | |
| Pool p; | |
| wchar_t* lpMsgBuf = (wchar_t*) p.palloc(MSGSIZE * sizeof(wchar_t)); | |
| DWORD dw = GetLastError(); | |
| FormatMessageW( | |
| FORMAT_MESSAGE_FROM_SYSTEM, | |
| NULL, | |
| dw, | |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
| lpMsgBuf, | |
| MSGSIZE, NULL ); | |
| LogString msg(function); | |
| msg.append(LOG4CXX_STR(" failed with error ")); | |
| StringHelper::toString((size_t) dw, msg); | |
| msg.append(LOG4CXX_STR(": ")); | |
| Transcoder::decode(lpMsgBuf, msg); | |
| return msg; | |
| } | |
| void NTEventLogAppender::setSource(const LogString& source) | |
| { | |
| priv->source.assign(source); | |
| } | |
| const LogString& NTEventLogAppender::getSource() const | |
| { | |
| return priv->source; | |
| } | |
| void NTEventLogAppender::setLog(const LogString& log) | |
| { | |
| priv->log.assign(log); | |
| } | |
| const LogString& NTEventLogAppender::getLog() const | |
| { | |
| return priv->log; | |
| } | |
| void NTEventLogAppender::setServer(const LogString& server) | |
| { | |
| priv->server.assign(server); | |
| } | |
| const LogString& NTEventLogAppender::getServer() const | |
| { | |
| return priv->server; | |
| } | |
| #endif // WIN32 |