1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #if (defined(WIN32) || defined(_WIN32)) && !defined(_WIN32_WCE)
19
20 #include <apr_strings.h>
21
22 #include <log4cxx/nt/nteventlogappender.h>
23 #include <log4cxx/spi/loggingevent.h>
24 #include <log4cxx/helpers/loglog.h>
25 #include <log4cxx/level.h>
26 #include <log4cxx/helpers/stringhelper.h>
27 #include <log4cxx/helpers/transcoder.h>
28 #include <log4cxx/helpers/pool.h>
29
30 using namespace log4cxx;
31 using namespace log4cxx::spi;
32 using namespace log4cxx::helpers;
33 using namespace log4cxx::nt;
34
35 class CCtUserSIDHelper
36 {
37 public:
FreeSid(SID * pSid)38 static bool FreeSid(SID* pSid)
39 {
40 return ::HeapFree(GetProcessHeap(), 0, (LPVOID)pSid) != 0;
41 }
42
CopySid(SID ** ppDstSid,SID * pSrcSid)43 static bool CopySid(SID * * ppDstSid, SID* pSrcSid)
44 {
45 bool bSuccess = false;
46
47 DWORD dwLength = ::GetLengthSid(pSrcSid);
48 *ppDstSid = (SID*) ::HeapAlloc(GetProcessHeap(),
49 HEAP_ZERO_MEMORY, dwLength);
50
51 if (::CopySid(dwLength, *ppDstSid, pSrcSid))
52 {
53 bSuccess = true;
54 }
55 else
56 {
57 FreeSid(*ppDstSid);
58 }
59
60 return bSuccess;
61 }
62
GetCurrentUserSID(SID ** ppSid)63 static bool GetCurrentUserSID(SID * * ppSid)
64 {
65 bool bSuccess = false;
66
67 // Pseudohandle so don't need to close it
68 HANDLE hProcess = ::GetCurrentProcess();
69 HANDLE hToken = NULL;
70
71 if (::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
72 {
73 // Get the required size
74 DWORD tusize = 0;
75 GetTokenInformation(hToken, TokenUser, NULL, 0, &tusize);
76 TOKEN_USER* ptu = (TOKEN_USER*)new BYTE[tusize];
77
78 if (GetTokenInformation(hToken, TokenUser, (LPVOID)ptu, tusize, &tusize))
79 {
80 bSuccess = CopySid(ppSid, (SID*)ptu->User.Sid);
81 }
82
83 CloseHandle(hToken);
84 delete [] ptu;
85 }
86
87 return bSuccess;
88 }
89 };
90
IMPLEMENT_LOG4CXX_OBJECT(NTEventLogAppender)91 IMPLEMENT_LOG4CXX_OBJECT(NTEventLogAppender)
92
93 NTEventLogAppender::NTEventLogAppender() : hEventLog(NULL), pCurrentUserSID(NULL)
94 {
95 }
96
NTEventLogAppender(const LogString & server,const LogString & log,const LogString & source,const LayoutPtr & layout)97 NTEventLogAppender::NTEventLogAppender(const LogString& server, const LogString& log, const LogString& source, const LayoutPtr& layout)
98 : server(server), log(log), source(source), hEventLog(NULL), pCurrentUserSID(NULL)
99 {
100 this->layout = layout;
101 Pool pool;
102 activateOptions(pool);
103 }
104
~NTEventLogAppender()105 NTEventLogAppender::~NTEventLogAppender()
106 {
107 finalize();
108 }
109
110
close()111 void NTEventLogAppender::close()
112 {
113 if (hEventLog != NULL)
114 {
115 ::DeregisterEventSource(hEventLog);
116 hEventLog = NULL;
117 }
118
119 if (pCurrentUserSID != NULL)
120 {
121 CCtUserSIDHelper::FreeSid((::SID*) pCurrentUserSID);
122 pCurrentUserSID = NULL;
123 }
124 }
125
setOption(const LogString & option,const LogString & value)126 void NTEventLogAppender::setOption(const LogString& option, const LogString& value)
127 {
128 if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SERVER"), LOG4CXX_STR("server")))
129 {
130 server = value;
131 }
132 else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOG"), LOG4CXX_STR("log")))
133 {
134 log = value;
135 }
136 else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SOURCE"), LOG4CXX_STR("source")))
137 {
138 source = value;
139 }
140 else
141 {
142 AppenderSkeleton::setOption(option, value);
143 }
144 }
145
activateOptions(Pool &)146 void NTEventLogAppender::activateOptions(Pool&)
147 {
148 if (source.empty())
149 {
150 LogLog::warn(
151 ((LogString) LOG4CXX_STR("Source option not set for appender ["))
152 + name + LOG4CXX_STR("]."));
153 return;
154 }
155
156 if (log.empty())
157 {
158 log = LOG4CXX_STR("Application");
159 }
160
161 close();
162
163 // current user security identifier
164 CCtUserSIDHelper::GetCurrentUserSID((::SID**) &pCurrentUserSID);
165
166 addRegistryInfo();
167
168 LOG4CXX_ENCODE_WCHAR(wsource, source);
169 LOG4CXX_ENCODE_WCHAR(wserver, server);
170 hEventLog = ::RegisterEventSourceW(
171 wserver.empty() ? NULL : wserver.c_str(),
172 wsource.c_str());
173
174 if (hEventLog == NULL)
175 {
176 LogString msg(LOG4CXX_STR("Cannot register NT EventLog -- server: '"));
177 msg.append(server);
178 msg.append(LOG4CXX_STR("' source: '"));
179 msg.append(source);
180 LogLog::error(msg);
181 LogLog::error(getErrorString(LOG4CXX_STR("RegisterEventSource")));
182 }
183 }
184
append(const LoggingEventPtr & event,Pool & p)185 void NTEventLogAppender::append(const LoggingEventPtr& event, Pool& p)
186 {
187 if (hEventLog == NULL)
188 {
189 LogLog::warn(LOG4CXX_STR("NT EventLog not opened."));
190 return;
191 }
192
193 LogString oss;
194 layout->format(oss, event, p);
195 wchar_t* msgs = Transcoder::wencode(oss, p);
196 BOOL bSuccess = ::ReportEventW(
197 hEventLog,
198 getEventType(event),
199 getEventCategory(event),
200 0x1000,
201 pCurrentUserSID,
202 1,
203 0,
204 (LPCWSTR*) &msgs,
205 NULL);
206
207 if (!bSuccess)
208 {
209 LogLog::error(getErrorString(LOG4CXX_STR("ReportEvent")));
210 }
211 }
212
213 /*
214 * Add this source with appropriate configuration keys to the registry.
215 */
addRegistryInfo()216 void NTEventLogAppender::addRegistryInfo()
217 {
218 DWORD disposition = 0;
219 ::HKEY hkey = 0;
220 LogString subkey(LOG4CXX_STR("SYSTEM\\CurrentControlSet\\Services\\EventLog\\"));
221 subkey.append(log);
222 subkey.append(1, (logchar) 0x5C /* '\\' */);
223 subkey.append(source);
224 LOG4CXX_ENCODE_WCHAR(wsubkey, subkey);
225
226 long stat = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wsubkey.c_str(), 0, NULL,
227 REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,
228 &hkey, &disposition);
229
230 if (stat == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY)
231 {
232 HMODULE hmodule = GetModuleHandleW(L"log4cxx");
233
234 if (hmodule == NULL)
235 {
236 hmodule = GetModuleHandleW(0);
237 }
238
239 wchar_t modpath[_MAX_PATH];
240 DWORD modlen = GetModuleFileNameW(hmodule, modpath, _MAX_PATH - 1);
241
242 if (modlen > 0)
243 {
244 modpath[modlen] = 0;
245 RegSetValueExW(hkey, L"EventMessageFile", 0, REG_SZ,
246 (LPBYTE) modpath, wcslen(modpath) * sizeof(wchar_t));
247 RegSetValueExW(hkey, L"CategoryMessageFile", 0, REG_SZ,
248 (LPBYTE) modpath, wcslen(modpath) * sizeof(wchar_t));
249 DWORD typesSupported = 7;
250 DWORD categoryCount = 6;
251 RegSetValueExW(hkey, L"TypesSupported", 0, REG_DWORD,
252 (LPBYTE)&typesSupported, sizeof(DWORD));
253 RegSetValueExW(hkey, L"CategoryCount", 0, REG_DWORD,
254 (LPBYTE)&categoryCount, sizeof(DWORD));
255 }
256 }
257
258 RegCloseKey(hkey);
259 return;
260 }
261
getEventType(const LoggingEventPtr & event)262 WORD NTEventLogAppender::getEventType(const LoggingEventPtr& event)
263 {
264 int priority = event->getLevel()->toInt();
265 WORD type = EVENTLOG_SUCCESS;
266
267 if (priority >= Level::INFO_INT)
268 {
269 type = EVENTLOG_INFORMATION_TYPE;
270
271 if (priority >= Level::WARN_INT)
272 {
273 type = EVENTLOG_WARNING_TYPE;
274
275 if (priority >= Level::ERROR_INT)
276 {
277 type = EVENTLOG_ERROR_TYPE;
278 }
279 }
280 }
281
282 return type;
283 }
284
getEventCategory(const LoggingEventPtr & event)285 WORD NTEventLogAppender::getEventCategory(const LoggingEventPtr& event)
286 {
287 int priority = event->getLevel()->toInt();
288 WORD category = 1;
289
290 if (priority >= Level::DEBUG_INT)
291 {
292 category = 2;
293
294 if (priority >= Level::INFO_INT)
295 {
296 category = 3;
297
298 if (priority >= Level::WARN_INT)
299 {
300 category = 4;
301
302 if (priority >= Level::ERROR_INT)
303 {
304 category = 5;
305
306 if (priority >= Level::FATAL_INT)
307 {
308 category = 6;
309 }
310 }
311 }
312 }
313 }
314
315 return category;
316 }
317
getErrorString(const LogString & function)318 LogString NTEventLogAppender::getErrorString(const LogString& function)
319 {
320 Pool p;
321 enum { MSGSIZE = 5000 };
322
323 wchar_t* lpMsgBuf = (wchar_t*) p.palloc(MSGSIZE * sizeof(wchar_t));
324 DWORD dw = GetLastError();
325
326 FormatMessageW(
327 FORMAT_MESSAGE_FROM_SYSTEM,
328 NULL,
329 dw,
330 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
331 lpMsgBuf,
332 MSGSIZE, NULL );
333
334 LogString msg(function);
335 msg.append(LOG4CXX_STR(" failed with error "));
336 StringHelper::toString((size_t) dw, p, msg);
337 msg.append(LOG4CXX_STR(": "));
338 Transcoder::decode(lpMsgBuf, msg);
339
340 return msg;
341 }
342
343 #endif // WIN32
344