* Sync up to trunk head (r64377).
[reactos.git] / base / services / eventlog / eventlog.c
1 /*
2 * PROJECT: ReactOS kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: services/eventlog/eventlog.c
5 * PURPOSE: Event logging service
6 * COPYRIGHT: Copyright 2002 Eric Kohl
7 * Copyright 2005 Saveliy Tretiakov
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include "eventlog.h"
13
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 HANDLE MyHeap = NULL;
35
36 /* FUNCTIONS ****************************************************************/
37
38 static VOID
39 UpdateServiceStatus(DWORD dwState)
40 {
41 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
42 ServiceStatus.dwCurrentState = dwState;
43 ServiceStatus.dwControlsAccepted = 0;
44 ServiceStatus.dwWin32ExitCode = 0;
45 ServiceStatus.dwServiceSpecificExitCode = 0;
46 ServiceStatus.dwCheckPoint = 0;
47
48 if (dwState == SERVICE_START_PENDING ||
49 dwState == SERVICE_STOP_PENDING ||
50 dwState == SERVICE_PAUSE_PENDING ||
51 dwState == SERVICE_CONTINUE_PENDING)
52 ServiceStatus.dwWaitHint = 10000;
53 else
54 ServiceStatus.dwWaitHint = 0;
55
56 SetServiceStatus(ServiceStatusHandle,
57 &ServiceStatus);
58 }
59
60 static DWORD WINAPI
61 ServiceControlHandler(DWORD dwControl,
62 DWORD dwEventType,
63 LPVOID lpEventData,
64 LPVOID lpContext)
65 {
66 DPRINT("ServiceControlHandler() called\n");
67
68 switch (dwControl)
69 {
70 case SERVICE_CONTROL_STOP:
71 DPRINT(" SERVICE_CONTROL_STOP received\n");
72
73 LogfReportEvent(EVENTLOG_INFORMATION_TYPE,
74 0,
75 EVENT_EventlogStopped, 0, NULL, 0, NULL);
76
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)
124 PortThreadRoutine,
125 NULL,
126 0,
127 NULL);
128
129 if (!hThread)
130 {
131 DPRINT("Can't create PortThread\n");
132 return GetLastError();
133 }
134 else
135 CloseHandle(hThread);
136
137 hThread = CreateThread(NULL,
138 0,
139 (LPTHREAD_START_ROUTINE)
140 RpcThreadRoutine,
141 NULL,
142 0,
143 NULL);
144
145 if (!hThread)
146 {
147 DPRINT("Can't create RpcThread\n");
148 return GetLastError();
149 }
150 else
151 CloseHandle(hThread);
152
153 return ERROR_SUCCESS;
154 }
155
156
157 static VOID
158 ReportProductInfoEvent(VOID)
159 {
160 OSVERSIONINFOW versionInfo;
161 WCHAR szBuffer[512];
162 DWORD dwLength;
163 HKEY hKey;
164 DWORD dwValueLength;
165 DWORD dwType;
166 LONG lResult = ERROR_SUCCESS;
167
168 ZeroMemory(&versionInfo, sizeof(OSVERSIONINFO));
169 versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
170
171 /* Get version information */
172 if (!GetVersionExW(&versionInfo))
173 return;
174
175 ZeroMemory(szBuffer, 512 * sizeof(WCHAR));
176
177 /* Write version into the buffer */
178 dwLength = swprintf(szBuffer,
179 L"%lu.%lu",
180 versionInfo.dwMajorVersion,
181 versionInfo.dwMinorVersion) + 1;
182
183 /* Write build number into the buffer */
184 dwLength += swprintf(&szBuffer[dwLength],
185 L"%lu",
186 versionInfo.dwBuildNumber) + 1;
187
188 /* Write service pack info into the buffer */
189 wcscpy(&szBuffer[dwLength], versionInfo.szCSDVersion);
190 dwLength += wcslen(versionInfo.szCSDVersion) + 1;
191
192 /* Read 'CurrentType' from the registry and write it into the buffer */
193 lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
194 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
195 0,
196 KEY_QUERY_VALUE,
197 &hKey);
198 if (lResult == ERROR_SUCCESS)
199 {
200 dwValueLength = 512 - dwLength;
201 lResult = RegQueryValueEx(hKey,
202 L"CurrentType",
203 NULL,
204 &dwType,
205 (LPBYTE)&szBuffer[dwLength],
206 &dwValueLength);
207
208 RegCloseKey(hKey);
209 }
210
211 /* Log the product information */
212 LogfReportEvent(EVENTLOG_INFORMATION_TYPE,
213 0,
214 EVENT_EventLogProductInfo,
215 4,
216 szBuffer,
217 0,
218 NULL);
219 }
220
221
222 static VOID CALLBACK
223 ServiceMain(DWORD argc,
224 LPWSTR *argv)
225 {
226 DWORD dwError;
227
228 UNREFERENCED_PARAMETER(argc);
229 UNREFERENCED_PARAMETER(argv);
230
231 DPRINT("ServiceMain() called\n");
232
233 ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
234 ServiceControlHandler,
235 NULL);
236 if (!ServiceStatusHandle)
237 {
238 dwError = GetLastError();
239 DPRINT1("RegisterServiceCtrlHandlerW() failed! (Error %lu)\n", dwError);
240 return;
241 }
242
243 UpdateServiceStatus(SERVICE_START_PENDING);
244
245 dwError = ServiceInit();
246 if (dwError != ERROR_SUCCESS)
247 {
248 DPRINT("Service stopped (dwError: %lu\n", dwError);
249 UpdateServiceStatus(SERVICE_START_PENDING);
250 }
251 else
252 {
253 DPRINT("Service started\n");
254 UpdateServiceStatus(SERVICE_RUNNING);
255
256 ReportProductInfoEvent();
257
258 LogfReportEvent(EVENTLOG_INFORMATION_TYPE,
259 0,
260 EVENT_EventlogStarted,
261 0,
262 NULL,
263 0,
264 NULL);
265 }
266
267 DPRINT("ServiceMain() done\n");
268 }
269
270
271 PLOGFILE LoadLogFile(HKEY hKey, WCHAR * LogName)
272 {
273 DWORD MaxValueLen, ValueLen, Type, ExpandedLen;
274 WCHAR *Buf = NULL, *Expanded = NULL;
275 LONG Result;
276 PLOGFILE pLogf = NULL;
277 UNICODE_STRING FileName;
278 ULONG ulMaxSize, ulRetention;
279 NTSTATUS Status;
280
281 DPRINT("LoadLogFile: %S\n", LogName);
282
283 RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
284 NULL, NULL, &MaxValueLen, NULL, NULL);
285
286 Buf = HeapAlloc(MyHeap, 0, MaxValueLen);
287 if (!Buf)
288 {
289 DPRINT1("Can't allocate heap!\n");
290 return NULL;
291 }
292
293 ValueLen = MaxValueLen;
294
295 Result = RegQueryValueEx(hKey,
296 L"File",
297 NULL,
298 &Type,
299 (LPBYTE) Buf,
300 &ValueLen);
301 if (Result != ERROR_SUCCESS)
302 {
303 DPRINT1("RegQueryValueEx failed: %lu\n", GetLastError());
304 HeapFree(MyHeap, 0, Buf);
305 return NULL;
306 }
307
308 if (Type != REG_EXPAND_SZ && Type != REG_SZ)
309 {
310 DPRINT1("%S\\File - value of wrong type %x.\n", LogName, Type);
311 HeapFree(MyHeap, 0, Buf);
312 return NULL;
313 }
314
315 ExpandedLen = ExpandEnvironmentStrings(Buf, NULL, 0);
316 Expanded = HeapAlloc(MyHeap, 0, ExpandedLen * sizeof(WCHAR));
317 if (!Expanded)
318 {
319 DPRINT1("Can't allocate heap!\n");
320 HeapFree(MyHeap, 0, Buf);
321 return NULL;
322 }
323
324 ExpandEnvironmentStrings(Buf, Expanded, ExpandedLen);
325
326 if (!RtlDosPathNameToNtPathName_U(Expanded, &FileName,
327 NULL, NULL))
328 {
329 DPRINT1("Can't convert path!\n");
330 HeapFree(MyHeap, 0, Expanded);
331 HeapFree(MyHeap, 0, Buf);
332 return NULL;
333 }
334
335 DPRINT("%S -> %S\n", Buf, Expanded);
336
337 ValueLen = sizeof(ULONG);
338 Result = RegQueryValueEx(hKey,
339 L"MaxSize",
340 NULL,
341 &Type,
342 (LPBYTE)&ulMaxSize,
343 &ValueLen);
344 if (Result != ERROR_SUCCESS)
345 ulMaxSize = 512 * 1024; /* 512 kBytes */
346
347 ValueLen = sizeof(ULONG);
348 Result = RegQueryValueEx(hKey,
349 L"Retention",
350 NULL,
351 &Type,
352 (LPBYTE)&ulRetention,
353 &ValueLen);
354 if (Result != ERROR_SUCCESS)
355 ulRetention = 0;
356
357 Status = LogfCreate(&pLogf, LogName, &FileName, ulMaxSize, ulRetention, TRUE, FALSE);
358 if (!NT_SUCCESS(Status))
359 {
360 DPRINT1("Failed to create %S! (Status %08lx)\n", Expanded, Status);
361 }
362
363 HeapFree(MyHeap, 0, Buf);
364 HeapFree(MyHeap, 0, Expanded);
365 return pLogf;
366 }
367
368 BOOL LoadLogFiles(HKEY eventlogKey)
369 {
370 LONG result;
371 DWORD MaxLognameLen, LognameLen;
372 WCHAR *Buf = NULL;
373 INT i;
374 PLOGFILE pLogFile;
375
376 RegQueryInfoKey(eventlogKey,
377 NULL, NULL, NULL, NULL,
378 &MaxLognameLen,
379 NULL, NULL, NULL, NULL, NULL, NULL);
380
381 MaxLognameLen++;
382
383 Buf = HeapAlloc(MyHeap, 0, MaxLognameLen * sizeof(WCHAR));
384
385 if (!Buf)
386 {
387 DPRINT1("Error: can't allocate heap!\n");
388 return FALSE;
389 }
390
391 i = 0;
392 LognameLen = MaxLognameLen;
393
394 while (RegEnumKeyEx(eventlogKey,
395 i,
396 Buf,
397 &LognameLen,
398 NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
399 {
400 HKEY SubKey;
401
402 DPRINT("%S\n", Buf);
403
404 result = RegOpenKeyEx(eventlogKey, Buf, 0, KEY_ALL_ACCESS, &SubKey);
405 if (result != ERROR_SUCCESS)
406 {
407 DPRINT1("Failed to open %S key.\n", Buf);
408 HeapFree(MyHeap, 0, Buf);
409 return FALSE;
410 }
411
412 pLogFile = LoadLogFile(SubKey, Buf);
413 if (pLogFile != NULL)
414 {
415 DPRINT("Loaded %S\n", Buf);
416 LoadEventSources(SubKey, pLogFile);
417 }
418 else
419 {
420 DPRINT1("Failed to load %S\n", Buf);
421 }
422
423 RegCloseKey(SubKey);
424 LognameLen = MaxLognameLen;
425 i++;
426 }
427
428 HeapFree(MyHeap, 0, Buf);
429 return TRUE;
430 }
431
432 INT wmain()
433 {
434 WCHAR LogPath[MAX_PATH];
435 INT RetCode = 0;
436 LONG result;
437 HKEY elogKey;
438
439 LogfListInitialize();
440 InitEventSourceList();
441
442 MyHeap = HeapCreate(0, 1024 * 256, 0);
443
444 if (!MyHeap)
445 {
446 DPRINT1("FATAL ERROR, can't create heap.\n");
447 RetCode = 1;
448 goto bye_bye;
449 }
450
451 GetWindowsDirectory(LogPath, MAX_PATH);
452
453 if (GetDriveType(LogPath) == DRIVE_CDROM)
454 {
455 DPRINT("LiveCD detected\n");
456 onLiveCD = TRUE;
457 }
458 else
459 {
460 result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
461 L"SYSTEM\\CurrentControlSet\\Services\\EventLog",
462 0,
463 KEY_ALL_ACCESS,
464 &elogKey);
465
466 if (result != ERROR_SUCCESS)
467 {
468 DPRINT1("Fatal error: can't open eventlog registry key.\n");
469 RetCode = 1;
470 goto bye_bye;
471 }
472
473 LoadLogFiles(elogKey);
474 }
475
476 StartServiceCtrlDispatcher(ServiceTable);
477
478 bye_bye:
479 LogfCloseAll();
480
481 if (MyHeap)
482 HeapDestroy(MyHeap);
483
484 return RetCode;
485 }
486
487 VOID EventTimeToSystemTime(DWORD EventTime, SYSTEMTIME * pSystemTime)
488 {
489 SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
490 FILETIME ftLocal;
491 union
492 {
493 FILETIME ft;
494 ULONGLONG ll;
495 } u1970, uUCT;
496
497 uUCT.ft.dwHighDateTime = 0;
498 uUCT.ft.dwLowDateTime = EventTime;
499 SystemTimeToFileTime(&st1970, &u1970.ft);
500 uUCT.ll = uUCT.ll * 10000000 + u1970.ll;
501 FileTimeToLocalFileTime(&uUCT.ft, &ftLocal);
502 FileTimeToSystemTime(&ftLocal, pSystemTime);
503 }
504
505 VOID SystemTimeToEventTime(SYSTEMTIME * pSystemTime, DWORD * pEventTime)
506 {
507 SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
508 union
509 {
510 FILETIME ft;
511 ULONGLONG ll;
512 } Time, u1970;
513
514 SystemTimeToFileTime(pSystemTime, &Time.ft);
515 SystemTimeToFileTime(&st1970, &u1970.ft);
516 *pEventTime = (DWORD)((Time.ll - u1970.ll) / 10000000ull);
517 }
518
519 VOID PRINT_HEADER(PEVENTLOGHEADER header)
520 {
521 DPRINT("HeaderSize = %lu\n", header->HeaderSize);
522 DPRINT("Signature = 0x%x\n", header->Signature);
523 DPRINT("MajorVersion = %lu\n", header->MajorVersion);
524 DPRINT("MinorVersion = %lu\n", header->MinorVersion);
525 DPRINT("StartOffset = %lu\n", header->StartOffset);
526 DPRINT("EndOffset = 0x%x\n", header->EndOffset);
527 DPRINT("CurrentRecordNumber = %lu\n", header->CurrentRecordNumber);
528 DPRINT("OldestRecordNumber = %lu\n", header->OldestRecordNumber);
529 DPRINT("MaxSize = 0x%x\n", header->MaxSize);
530 DPRINT("Retention = 0x%x\n", header->Retention);
531 DPRINT("EndHeaderSize = %lu\n", header->EndHeaderSize);
532 DPRINT("Flags: ");
533 if (header->Flags & ELF_LOGFILE_HEADER_DIRTY) DPRINT("ELF_LOGFILE_HEADER_DIRTY");
534 if (header->Flags & ELF_LOGFILE_HEADER_WRAP) DPRINT("| ELF_LOGFILE_HEADER_WRAP ");
535 if (header->Flags & ELF_LOGFILE_LOGFULL_WRITTEN) DPRINT("| ELF_LOGFILE_LOGFULL_WRITTEN ");
536 if (header->Flags & ELF_LOGFILE_ARCHIVE_SET) DPRINT("| ELF_LOGFILE_ARCHIVE_SET ");
537 DPRINT("\n");
538 }
539
540 VOID PRINT_RECORD(PEVENTLOGRECORD pRec)
541 {
542 UINT i;
543 WCHAR *str;
544 SYSTEMTIME time;
545
546 DPRINT("Length = %lu\n", pRec->Length);
547 DPRINT("Reserved = 0x%x\n", pRec->Reserved);
548 DPRINT("RecordNumber = %lu\n", pRec->RecordNumber);
549
550 EventTimeToSystemTime(pRec->TimeGenerated, &time);
551 DPRINT("TimeGenerated = %hu.%hu.%hu %hu:%hu:%hu\n",
552 time.wDay, time.wMonth, time.wYear,
553 time.wHour, time.wMinute, time.wSecond);
554
555 EventTimeToSystemTime(pRec->TimeWritten, &time);
556 DPRINT("TimeWritten = %hu.%hu.%hu %hu:%hu:%hu\n",
557 time.wDay, time.wMonth, time.wYear,
558 time.wHour, time.wMinute, time.wSecond);
559
560 DPRINT("EventID = %lu\n", pRec->EventID);
561
562 switch (pRec->EventType)
563 {
564 case EVENTLOG_ERROR_TYPE:
565 DPRINT("EventType = EVENTLOG_ERROR_TYPE\n");
566 break;
567 case EVENTLOG_WARNING_TYPE:
568 DPRINT("EventType = EVENTLOG_WARNING_TYPE\n");
569 break;
570 case EVENTLOG_INFORMATION_TYPE:
571 DPRINT("EventType = EVENTLOG_INFORMATION_TYPE\n");
572 break;
573 case EVENTLOG_AUDIT_SUCCESS:
574 DPRINT("EventType = EVENTLOG_AUDIT_SUCCESS\n");
575 break;
576 case EVENTLOG_AUDIT_FAILURE:
577 DPRINT("EventType = EVENTLOG_AUDIT_FAILURE\n");
578 break;
579 default:
580 DPRINT("EventType = %hu\n", pRec->EventType);
581 }
582
583 DPRINT("NumStrings = %hu\n", pRec->NumStrings);
584 DPRINT("EventCategory = %hu\n", pRec->EventCategory);
585 DPRINT("ReservedFlags = 0x%x\n", pRec->ReservedFlags);
586 DPRINT("ClosingRecordNumber = %lu\n", pRec->ClosingRecordNumber);
587 DPRINT("StringOffset = %lu\n", pRec->StringOffset);
588 DPRINT("UserSidLength = %lu\n", pRec->UserSidLength);
589 DPRINT("UserSidOffset = %lu\n", pRec->UserSidOffset);
590 DPRINT("DataLength = %lu\n", pRec->DataLength);
591 DPRINT("DataOffset = %lu\n", pRec->DataOffset);
592
593 DPRINT("SourceName: %S\n", (WCHAR *) (((PBYTE) pRec) + sizeof(EVENTLOGRECORD)));
594
595 i = (lstrlenW((WCHAR *) (((PBYTE) pRec) + sizeof(EVENTLOGRECORD))) + 1) *
596 sizeof(WCHAR);
597
598 DPRINT("ComputerName: %S\n", (WCHAR *) (((PBYTE) pRec) + sizeof(EVENTLOGRECORD) + i));
599
600 if (pRec->StringOffset < pRec->Length && pRec->NumStrings)
601 {
602 DPRINT("Strings:\n");
603 str = (WCHAR *) (((PBYTE) pRec) + pRec->StringOffset);
604 for (i = 0; i < pRec->NumStrings; i++)
605 {
606 DPRINT("[%u] %S\n", i, str);
607 str = str + lstrlenW(str) + 1;
608 }
609 }
610
611 DPRINT("Length2 = %lu\n", *(PDWORD) (((PBYTE) pRec) + pRec->Length - 4));
612 }