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