[NTOS:IO][INCLUDES][EVENTLOG]: Fix support for event logging through the "ErrorLogPor...
[reactos.git] / reactos / base / services / eventlog / eventlog.c
1 /*
2 * PROJECT: ReactOS EventLog Service
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/services/eventlog/eventlog.c
5 * PURPOSE: Event logging service
6 * COPYRIGHT: Copyright 2002 Eric Kohl
7 * Copyright 2005 Saveliy Tretiakov
8 * Hermes Belusca-Maito
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include "eventlog.h"
14 #include <stdio.h>
15 #include <netevent.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 /* GLOBALS ******************************************************************/
21
22 static VOID CALLBACK ServiceMain(DWORD, LPWSTR*);
23 static WCHAR ServiceName[] = L"EventLog";
24 static SERVICE_TABLE_ENTRYW ServiceTable[2] =
25 {
26 { ServiceName, ServiceMain },
27 { NULL, NULL }
28 };
29
30 SERVICE_STATUS ServiceStatus;
31 SERVICE_STATUS_HANDLE ServiceStatusHandle;
32
33 BOOL onLiveCD = FALSE; // On LiveCD events will go to debug output only
34
35 PEVENTSOURCE EventLogSource = NULL;
36
37 /* FUNCTIONS ****************************************************************/
38
39 static VOID
40 UpdateServiceStatus(DWORD dwState)
41 {
42 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
43 ServiceStatus.dwCurrentState = dwState;
44 ServiceStatus.dwControlsAccepted = 0;
45 ServiceStatus.dwWin32ExitCode = 0;
46 ServiceStatus.dwServiceSpecificExitCode = 0;
47 ServiceStatus.dwCheckPoint = 0;
48
49 if (dwState == SERVICE_START_PENDING ||
50 dwState == SERVICE_STOP_PENDING ||
51 dwState == SERVICE_PAUSE_PENDING ||
52 dwState == SERVICE_CONTINUE_PENDING)
53 ServiceStatus.dwWaitHint = 10000;
54 else
55 ServiceStatus.dwWaitHint = 0;
56
57 SetServiceStatus(ServiceStatusHandle,
58 &ServiceStatus);
59 }
60
61 static DWORD WINAPI
62 ServiceControlHandler(DWORD dwControl,
63 DWORD dwEventType,
64 LPVOID lpEventData,
65 LPVOID lpContext)
66 {
67 DPRINT("ServiceControlHandler() called\n");
68
69 switch (dwControl)
70 {
71 case SERVICE_CONTROL_STOP:
72 DPRINT(" SERVICE_CONTROL_STOP received\n");
73
74 LogfReportEvent(EVENTLOG_INFORMATION_TYPE,
75 0,
76 EVENT_EventlogStopped, 0, NULL, 0, NULL);
77
78 /* Stop listening to incoming RPC messages */
79 RpcMgmtStopServerListening(NULL);
80 UpdateServiceStatus(SERVICE_STOPPED);
81 return ERROR_SUCCESS;
82
83 case SERVICE_CONTROL_PAUSE:
84 DPRINT(" SERVICE_CONTROL_PAUSE received\n");
85 UpdateServiceStatus(SERVICE_PAUSED);
86 return ERROR_SUCCESS;
87
88 case SERVICE_CONTROL_CONTINUE:
89 DPRINT(" SERVICE_CONTROL_CONTINUE received\n");
90 UpdateServiceStatus(SERVICE_RUNNING);
91 return ERROR_SUCCESS;
92
93 case SERVICE_CONTROL_INTERROGATE:
94 DPRINT(" SERVICE_CONTROL_INTERROGATE received\n");
95 SetServiceStatus(ServiceStatusHandle,
96 &ServiceStatus);
97 return ERROR_SUCCESS;
98
99 case SERVICE_CONTROL_SHUTDOWN:
100 DPRINT(" SERVICE_CONTROL_SHUTDOWN received\n");
101
102 LogfReportEvent(EVENTLOG_INFORMATION_TYPE,
103 0,
104 EVENT_EventlogStopped, 0, NULL, 0, NULL);
105
106 UpdateServiceStatus(SERVICE_STOPPED);
107 return ERROR_SUCCESS;
108
109 default:
110 DPRINT1(" Control %lu received\n", dwControl);
111 return ERROR_CALL_NOT_IMPLEMENTED;
112 }
113 }
114
115
116 static DWORD
117 ServiceInit(VOID)
118 {
119 HANDLE hThread;
120
121 hThread = CreateThread(NULL,
122 0,
123 (LPTHREAD_START_ROUTINE)PortThreadRoutine,
124 NULL,
125 0,
126 NULL);
127 if (!hThread)
128 {
129 DPRINT("Cannot create PortThread\n");
130 return GetLastError();
131 }
132 else
133 CloseHandle(hThread);
134
135 hThread = CreateThread(NULL,
136 0,
137 RpcThreadRoutine,
138 NULL,
139 0,
140 NULL);
141
142 if (!hThread)
143 {
144 DPRINT("Cannot create RpcThread\n");
145 return GetLastError();
146 }
147 else
148 CloseHandle(hThread);
149
150 return ERROR_SUCCESS;
151 }
152
153
154 static VOID
155 ReportProductInfoEvent(VOID)
156 {
157 OSVERSIONINFOW versionInfo;
158 WCHAR szBuffer[512];
159 PWSTR str;
160 size_t cchRemain;
161 HKEY hKey;
162 DWORD dwValueLength;
163 DWORD dwType;
164 LONG lResult = ERROR_SUCCESS;
165
166 ZeroMemory(&versionInfo, sizeof(versionInfo));
167 versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
168
169 /* Get version information */
170 if (!GetVersionExW(&versionInfo))
171 return;
172
173 ZeroMemory(szBuffer, sizeof(szBuffer));
174 str = szBuffer;
175 cchRemain = ARRAYSIZE(szBuffer);
176
177 /* Write the version number into the buffer */
178 StringCchPrintfExW(str, cchRemain,
179 &str, &cchRemain, 0,
180 L"%lu.%lu",
181 versionInfo.dwMajorVersion,
182 versionInfo.dwMinorVersion);
183 str++;
184 cchRemain++;
185
186 /* Write the build number into the buffer */
187 StringCchPrintfExW(str, cchRemain,
188 &str, &cchRemain, 0,
189 L"%lu",
190 versionInfo.dwBuildNumber);
191 str++;
192 cchRemain++;
193
194 /* Write the service pack info into the buffer */
195 StringCchCopyExW(str, cchRemain,
196 versionInfo.szCSDVersion,
197 &str, &cchRemain, 0);
198 str++;
199 cchRemain++;
200
201 /* Read 'CurrentType' from the registry and write it into the buffer */
202 lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
203 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
204 0,
205 KEY_QUERY_VALUE,
206 &hKey);
207 if (lResult == ERROR_SUCCESS)
208 {
209 dwValueLength = cchRemain;
210 lResult = RegQueryValueExW(hKey,
211 L"CurrentType",
212 NULL,
213 &dwType,
214 (LPBYTE)str,
215 &dwValueLength);
216
217 RegCloseKey(hKey);
218 }
219
220 /* Log the product information */
221 LogfReportEvent(EVENTLOG_INFORMATION_TYPE,
222 0,
223 EVENT_EventLogProductInfo,
224 4,
225 szBuffer,
226 0,
227 NULL);
228 }
229
230
231 static VOID CALLBACK
232 ServiceMain(DWORD argc,
233 LPWSTR* argv)
234 {
235 DWORD dwError;
236
237 UNREFERENCED_PARAMETER(argc);
238 UNREFERENCED_PARAMETER(argv);
239
240 DPRINT("ServiceMain() called\n");
241
242 ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
243 ServiceControlHandler,
244 NULL);
245 if (!ServiceStatusHandle)
246 {
247 dwError = GetLastError();
248 DPRINT1("RegisterServiceCtrlHandlerW() failed! (Error %lu)\n", dwError);
249 return;
250 }
251
252 UpdateServiceStatus(SERVICE_START_PENDING);
253
254 dwError = ServiceInit();
255 if (dwError != ERROR_SUCCESS)
256 {
257 DPRINT("Service stopped (dwError: %lu\n", dwError);
258 UpdateServiceStatus(SERVICE_START_PENDING);
259 }
260 else
261 {
262 DPRINT("Service started\n");
263 UpdateServiceStatus(SERVICE_RUNNING);
264
265 ReportProductInfoEvent();
266
267 LogfReportEvent(EVENTLOG_INFORMATION_TYPE,
268 0,
269 EVENT_EventlogStarted,
270 0,
271 NULL,
272 0,
273 NULL);
274 }
275
276 DPRINT("ServiceMain() done\n");
277 }
278
279
280 static PLOGFILE
281 LoadLogFile(HKEY hKey, PWSTR LogName)
282 {
283 DWORD MaxValueLen, ValueLen, Type, ExpandedLen;
284 PWSTR Buf = NULL, Expanded = NULL;
285 LONG Result;
286 PLOGFILE pLogf = NULL;
287 UNICODE_STRING FileName;
288 ULONG ulMaxSize, ulRetention;
289 NTSTATUS Status;
290
291 DPRINT("LoadLogFile: `%S'\n", LogName);
292
293 Result = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
294 NULL, NULL, &MaxValueLen, NULL, NULL);
295 if (Result != ERROR_SUCCESS)
296 {
297 DPRINT1("RegQueryInfoKeyW failed: %lu\n", Result);
298 return NULL;
299 }
300
301 MaxValueLen = ROUND_DOWN(MaxValueLen, sizeof(WCHAR));
302 Buf = HeapAlloc(GetProcessHeap(), 0, MaxValueLen);
303 if (!Buf)
304 {
305 DPRINT1("Cannot allocate heap!\n");
306 return NULL;
307 }
308
309 ValueLen = MaxValueLen;
310 Result = RegQueryValueExW(hKey,
311 L"File",
312 NULL,
313 &Type,
314 (LPBYTE)Buf,
315 &ValueLen);
316 /*
317 * If we failed, because the registry value was inexistent
318 * or the value type was incorrect, create a new "File" value
319 * that holds the default event log path.
320 */
321 if ((Result != ERROR_SUCCESS) || (Type != REG_EXPAND_SZ && Type != REG_SZ))
322 {
323 MaxValueLen = (wcslen(L"%SystemRoot%\\System32\\Config\\") +
324 wcslen(LogName) + wcslen(L".evt") + 1) * sizeof(WCHAR);
325
326 Expanded = HeapReAlloc(GetProcessHeap(), 0, Buf, MaxValueLen);
327 if (!Expanded)
328 {
329 DPRINT1("Cannot reallocate heap!\n");
330 HeapFree(GetProcessHeap(), 0, Buf);
331 return NULL;
332 }
333 Buf = Expanded;
334
335 StringCbCopyW(Buf, MaxValueLen, L"%SystemRoot%\\System32\\Config\\");
336 StringCbCatW(Buf, MaxValueLen, LogName);
337 StringCbCatW(Buf, MaxValueLen, L".evt");
338
339 ValueLen = MaxValueLen;
340 Result = RegSetValueExW(hKey,
341 L"File",
342 0,
343 REG_EXPAND_SZ,
344 (LPBYTE)Buf,
345 ValueLen);
346 if (Result != ERROR_SUCCESS)
347 {
348 DPRINT1("RegSetValueExW failed: %lu\n", Result);
349 HeapFree(GetProcessHeap(), 0, Buf);
350 return NULL;
351 }
352 }
353
354 ExpandedLen = ExpandEnvironmentStringsW(Buf, NULL, 0);
355 Expanded = HeapAlloc(GetProcessHeap(), 0, ExpandedLen * sizeof(WCHAR));
356 if (!Expanded)
357 {
358 DPRINT1("Cannot allocate heap!\n");
359 HeapFree(GetProcessHeap(), 0, Buf);
360 return NULL;
361 }
362
363 ExpandEnvironmentStringsW(Buf, Expanded, ExpandedLen);
364
365 if (!RtlDosPathNameToNtPathName_U(Expanded, &FileName, NULL, NULL))
366 {
367 DPRINT1("Cannot convert path!\n");
368 HeapFree(GetProcessHeap(), 0, Expanded);
369 HeapFree(GetProcessHeap(), 0, Buf);
370 return NULL;
371 }
372
373 DPRINT("%S -> %S\n", Buf, Expanded);
374
375 ValueLen = sizeof(ulMaxSize);
376 Result = RegQueryValueExW(hKey,
377 L"MaxSize",
378 NULL,
379 &Type,
380 (LPBYTE)&ulMaxSize,
381 &ValueLen);
382 if ((Result != ERROR_SUCCESS) || (Type != REG_DWORD))
383 {
384 ulMaxSize = 512 * 1024; /* 512 kBytes */
385
386 Result = RegSetValueExW(hKey,
387 L"MaxSize",
388 0,
389 REG_DWORD,
390 (LPBYTE)&ulMaxSize,
391 sizeof(ulMaxSize));
392 }
393
394 ValueLen = sizeof(ulRetention);
395 Result = RegQueryValueExW(hKey,
396 L"Retention",
397 NULL,
398 &Type,
399 (LPBYTE)&ulRetention,
400 &ValueLen);
401 if ((Result != ERROR_SUCCESS) || (Type != REG_DWORD))
402 {
403 /* On Windows 2003 it is 604800 (secs) == 7 days */
404 ulRetention = 0;
405
406 Result = RegSetValueExW(hKey,
407 L"Retention",
408 0,
409 REG_DWORD,
410 (LPBYTE)&ulRetention,
411 sizeof(ulRetention));
412 }
413
414 // TODO: Add, or use, default values for "AutoBackupLogFiles" (REG_DWORD)
415 // and "CustomSD" (REG_SZ).
416
417 Status = LogfCreate(&pLogf, LogName, &FileName, ulMaxSize, ulRetention, TRUE, FALSE);
418 if (!NT_SUCCESS(Status))
419 {
420 DPRINT1("Failed to create %S! (Status %08lx)\n", Expanded, Status);
421 }
422
423 HeapFree(GetProcessHeap(), 0, Expanded);
424 HeapFree(GetProcessHeap(), 0, Buf);
425 return pLogf;
426 }
427
428 static BOOL
429 LoadLogFiles(HKEY eventlogKey)
430 {
431 LONG Result;
432 DWORD MaxLognameLen, LognameLen;
433 DWORD dwIndex;
434 PWSTR Buf = NULL;
435 PLOGFILE pLogFile;
436
437 Result = RegQueryInfoKeyW(eventlogKey, NULL, NULL, NULL, NULL, &MaxLognameLen,
438 NULL, NULL, NULL, NULL, NULL, NULL);
439 if (Result != ERROR_SUCCESS)
440 {
441 DPRINT1("RegQueryInfoKeyW failed: %lu\n", Result);
442 return FALSE;
443 }
444
445 MaxLognameLen++;
446
447 Buf = HeapAlloc(GetProcessHeap(), 0, MaxLognameLen * sizeof(WCHAR));
448 if (!Buf)
449 {
450 DPRINT1("Error: cannot allocate heap!\n");
451 return FALSE;
452 }
453
454 LognameLen = MaxLognameLen;
455 dwIndex = 0;
456 while (RegEnumKeyExW(eventlogKey,
457 dwIndex,
458 Buf,
459 &LognameLen,
460 NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
461 {
462 HKEY SubKey;
463
464 DPRINT("%S\n", Buf);
465
466 Result = RegOpenKeyExW(eventlogKey, Buf, 0, KEY_ALL_ACCESS, &SubKey);
467 if (Result != ERROR_SUCCESS)
468 {
469 DPRINT1("Failed to open %S key.\n", Buf);
470 HeapFree(GetProcessHeap(), 0, Buf);
471 return FALSE;
472 }
473
474 pLogFile = LoadLogFile(SubKey, Buf);
475 if (pLogFile != NULL)
476 {
477 DPRINT("Loaded %S\n", Buf);
478 LoadEventSources(SubKey, pLogFile);
479 }
480 else
481 {
482 DPRINT1("Failed to load %S\n", Buf);
483 }
484
485 RegCloseKey(SubKey);
486
487 LognameLen = MaxLognameLen;
488 dwIndex++;
489 }
490
491 HeapFree(GetProcessHeap(), 0, Buf);
492 return TRUE;
493 }
494
495
496 int wmain(int argc, WCHAR* argv[])
497 {
498 INT RetCode = 0;
499 LONG Result;
500 HKEY elogKey;
501 WCHAR LogPath[MAX_PATH];
502
503 LogfListInitialize();
504 InitEventSourceList();
505
506 GetSystemWindowsDirectoryW(LogPath, ARRAYSIZE(LogPath));
507
508 if (GetDriveTypeW(LogPath) == DRIVE_CDROM)
509 {
510 DPRINT("LiveCD detected\n");
511 onLiveCD = TRUE;
512 }
513 else
514 {
515 Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
516 L"SYSTEM\\CurrentControlSet\\Services\\EventLog",
517 0,
518 KEY_ALL_ACCESS,
519 &elogKey);
520 if (Result != ERROR_SUCCESS)
521 {
522 DPRINT1("Fatal error: cannot open eventlog registry key.\n");
523 RetCode = 1;
524 goto bye_bye;
525 }
526
527 LoadLogFiles(elogKey);
528 }
529
530 EventLogSource = GetEventSourceByName(L"EventLog");
531 if (!EventLogSource)
532 {
533 DPRINT1("The 'EventLog' source is unavailable. The EventLog service will not be able to log its own events.\n");
534 }
535
536 StartServiceCtrlDispatcher(ServiceTable);
537
538 bye_bye:
539 LogfCloseAll();
540
541 return RetCode;
542 }
543
544 VOID PRINT_RECORD(PEVENTLOGRECORD pRec)
545 {
546 UINT i;
547 PWSTR str;
548 LARGE_INTEGER SystemTime;
549 TIME_FIELDS Time;
550
551 DPRINT1("PRINT_RECORD(0x%p)\n", pRec);
552
553 DbgPrint("Length = %lu\n", pRec->Length);
554 DbgPrint("Reserved = 0x%x\n", pRec->Reserved);
555 DbgPrint("RecordNumber = %lu\n", pRec->RecordNumber);
556
557 RtlSecondsSince1970ToTime(pRec->TimeGenerated, &SystemTime);
558 RtlTimeToTimeFields(&SystemTime, &Time);
559 DbgPrint("TimeGenerated = %hu.%hu.%hu %hu:%hu:%hu\n",
560 Time.Day, Time.Month, Time.Year,
561 Time.Hour, Time.Minute, Time.Second);
562
563 RtlSecondsSince1970ToTime(pRec->TimeWritten, &SystemTime);
564 RtlTimeToTimeFields(&SystemTime, &Time);
565 DbgPrint("TimeWritten = %hu.%hu.%hu %hu:%hu:%hu\n",
566 Time.Day, Time.Month, Time.Year,
567 Time.Hour, Time.Minute, Time.Second);
568
569 DbgPrint("EventID = %lu\n", pRec->EventID);
570
571 switch (pRec->EventType)
572 {
573 case EVENTLOG_ERROR_TYPE:
574 DbgPrint("EventType = EVENTLOG_ERROR_TYPE\n");
575 break;
576 case EVENTLOG_WARNING_TYPE:
577 DbgPrint("EventType = EVENTLOG_WARNING_TYPE\n");
578 break;
579 case EVENTLOG_INFORMATION_TYPE:
580 DbgPrint("EventType = EVENTLOG_INFORMATION_TYPE\n");
581 break;
582 case EVENTLOG_AUDIT_SUCCESS:
583 DbgPrint("EventType = EVENTLOG_AUDIT_SUCCESS\n");
584 break;
585 case EVENTLOG_AUDIT_FAILURE:
586 DbgPrint("EventType = EVENTLOG_AUDIT_FAILURE\n");
587 break;
588 default:
589 DbgPrint("EventType = %hu\n", pRec->EventType);
590 }
591
592 DbgPrint("NumStrings = %hu\n", pRec->NumStrings);
593 DbgPrint("EventCategory = %hu\n", pRec->EventCategory);
594 DbgPrint("ReservedFlags = 0x%x\n", pRec->ReservedFlags);
595 DbgPrint("ClosingRecordNumber = %lu\n", pRec->ClosingRecordNumber);
596 DbgPrint("StringOffset = %lu\n", pRec->StringOffset);
597 DbgPrint("UserSidLength = %lu\n", pRec->UserSidLength);
598 DbgPrint("UserSidOffset = %lu\n", pRec->UserSidOffset);
599 DbgPrint("DataLength = %lu\n", pRec->DataLength);
600 DbgPrint("DataOffset = %lu\n", pRec->DataOffset);
601
602 i = sizeof(EVENTLOGRECORD);
603 DbgPrint("SourceName: %S\n", (PWSTR)((ULONG_PTR)pRec + i));
604
605 i += (wcslen((PWSTR)((ULONG_PTR)pRec + i)) + 1) * sizeof(WCHAR);
606 DbgPrint("ComputerName: %S\n", (PWSTR)((ULONG_PTR)pRec + i));
607
608 if (pRec->StringOffset < pRec->Length && pRec->NumStrings)
609 {
610 DbgPrint("Strings:\n");
611 str = (PWSTR)((ULONG_PTR)pRec + pRec->StringOffset);
612 for (i = 0; i < pRec->NumStrings; i++)
613 {
614 DbgPrint("[%u] %S\n", i, str);
615 str += wcslen(str) + 1;
616 }
617 }
618
619 DbgPrint("Length2 = %lu\n", *(PULONG)((ULONG_PTR)pRec + pRec->Length - 4));
620 }