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
11 /* INCLUDES *****************************************************************/
20 /* GLOBALS ******************************************************************/
22 static VOID CALLBACK
ServiceMain(DWORD
, LPWSTR
*);
23 static WCHAR ServiceName
[] = L
"EventLog";
24 static SERVICE_TABLE_ENTRYW ServiceTable
[2] =
26 { ServiceName
, ServiceMain
},
30 SERVICE_STATUS ServiceStatus
;
31 SERVICE_STATUS_HANDLE ServiceStatusHandle
;
33 BOOL onLiveCD
= FALSE
; // On LiveCD events will go to debug output only
35 PEVENTSOURCE EventLogSource
= NULL
;
37 /* FUNCTIONS ****************************************************************/
40 UpdateServiceStatus(DWORD dwState
)
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;
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;
55 ServiceStatus
.dwWaitHint
= 0;
57 SetServiceStatus(ServiceStatusHandle
,
62 ServiceControlHandler(DWORD dwControl
,
67 DPRINT("ServiceControlHandler() called\n");
71 case SERVICE_CONTROL_STOP
:
72 DPRINT(" SERVICE_CONTROL_STOP received\n");
74 LogfReportEvent(EVENTLOG_INFORMATION_TYPE
,
76 EVENT_EventlogStopped
, 0, NULL
, 0, NULL
);
78 /* Stop listening to incoming RPC messages */
79 RpcMgmtStopServerListening(NULL
);
80 UpdateServiceStatus(SERVICE_STOPPED
);
83 case SERVICE_CONTROL_PAUSE
:
84 DPRINT(" SERVICE_CONTROL_PAUSE received\n");
85 UpdateServiceStatus(SERVICE_PAUSED
);
88 case SERVICE_CONTROL_CONTINUE
:
89 DPRINT(" SERVICE_CONTROL_CONTINUE received\n");
90 UpdateServiceStatus(SERVICE_RUNNING
);
93 case SERVICE_CONTROL_INTERROGATE
:
94 DPRINT(" SERVICE_CONTROL_INTERROGATE received\n");
95 SetServiceStatus(ServiceStatusHandle
,
99 case SERVICE_CONTROL_SHUTDOWN
:
100 DPRINT(" SERVICE_CONTROL_SHUTDOWN received\n");
102 LogfReportEvent(EVENTLOG_INFORMATION_TYPE
,
104 EVENT_EventlogStopped
, 0, NULL
, 0, NULL
);
106 UpdateServiceStatus(SERVICE_STOPPED
);
107 return ERROR_SUCCESS
;
110 DPRINT1(" Control %lu received\n", dwControl
);
111 return ERROR_CALL_NOT_IMPLEMENTED
;
121 hThread
= CreateThread(NULL
,
123 (LPTHREAD_START_ROUTINE
)PortThreadRoutine
,
129 DPRINT("Cannot create PortThread\n");
130 return GetLastError();
133 CloseHandle(hThread
);
135 hThread
= CreateThread(NULL
,
144 DPRINT("Cannot create RpcThread\n");
145 return GetLastError();
148 CloseHandle(hThread
);
150 return ERROR_SUCCESS
;
155 ReportProductInfoEvent(VOID
)
157 OSVERSIONINFOW versionInfo
;
164 LONG lResult
= ERROR_SUCCESS
;
166 ZeroMemory(&versionInfo
, sizeof(versionInfo
));
167 versionInfo
.dwOSVersionInfoSize
= sizeof(versionInfo
);
169 /* Get version information */
170 if (!GetVersionExW(&versionInfo
))
173 ZeroMemory(szBuffer
, sizeof(szBuffer
));
175 cchRemain
= ARRAYSIZE(szBuffer
);
177 /* Write the version number into the buffer */
178 StringCchPrintfExW(str
, cchRemain
,
181 versionInfo
.dwMajorVersion
,
182 versionInfo
.dwMinorVersion
);
186 /* Write the build number into the buffer */
187 StringCchPrintfExW(str
, cchRemain
,
190 versionInfo
.dwBuildNumber
);
194 /* Write the service pack info into the buffer */
195 StringCchCopyExW(str
, cchRemain
,
196 versionInfo
.szCSDVersion
,
197 &str
, &cchRemain
, 0);
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",
207 if (lResult
== ERROR_SUCCESS
)
209 dwValueLength
= cchRemain
;
210 lResult
= RegQueryValueExW(hKey
,
220 /* Log the product information */
221 LogfReportEvent(EVENTLOG_INFORMATION_TYPE
,
223 EVENT_EventLogProductInfo
,
232 ServiceMain(DWORD argc
,
237 UNREFERENCED_PARAMETER(argc
);
238 UNREFERENCED_PARAMETER(argv
);
240 DPRINT("ServiceMain() called\n");
242 ServiceStatusHandle
= RegisterServiceCtrlHandlerExW(ServiceName
,
243 ServiceControlHandler
,
245 if (!ServiceStatusHandle
)
247 dwError
= GetLastError();
248 DPRINT1("RegisterServiceCtrlHandlerW() failed! (Error %lu)\n", dwError
);
252 UpdateServiceStatus(SERVICE_START_PENDING
);
254 dwError
= ServiceInit();
255 if (dwError
!= ERROR_SUCCESS
)
257 DPRINT("Service stopped (dwError: %lu\n", dwError
);
258 UpdateServiceStatus(SERVICE_START_PENDING
);
262 DPRINT("Service started\n");
263 UpdateServiceStatus(SERVICE_RUNNING
);
265 ReportProductInfoEvent();
267 LogfReportEvent(EVENTLOG_INFORMATION_TYPE
,
269 EVENT_EventlogStarted
,
276 DPRINT("ServiceMain() done\n");
281 LoadLogFile(HKEY hKey
, PWSTR LogName
)
283 DWORD MaxValueLen
, ValueLen
, Type
, ExpandedLen
;
284 PWSTR Buf
= NULL
, Expanded
= NULL
;
286 PLOGFILE pLogf
= NULL
;
287 UNICODE_STRING FileName
;
288 ULONG ulMaxSize
, ulRetention
;
291 DPRINT("LoadLogFile: `%S'\n", LogName
);
293 Result
= RegQueryInfoKeyW(hKey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
294 NULL
, NULL
, &MaxValueLen
, NULL
, NULL
);
295 if (Result
!= ERROR_SUCCESS
)
297 DPRINT1("RegQueryInfoKeyW failed: %lu\n", Result
);
301 MaxValueLen
= ROUND_DOWN(MaxValueLen
, sizeof(WCHAR
));
302 Buf
= HeapAlloc(GetProcessHeap(), 0, MaxValueLen
);
305 DPRINT1("Cannot allocate heap!\n");
309 ValueLen
= MaxValueLen
;
310 Result
= RegQueryValueExW(hKey
,
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.
321 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_EXPAND_SZ
&& Type
!= REG_SZ
))
323 MaxValueLen
= (wcslen(L
"%SystemRoot%\\System32\\Config\\") +
324 wcslen(LogName
) + wcslen(L
".evt") + 1) * sizeof(WCHAR
);
326 Expanded
= HeapReAlloc(GetProcessHeap(), 0, Buf
, MaxValueLen
);
329 DPRINT1("Cannot reallocate heap!\n");
330 HeapFree(GetProcessHeap(), 0, Buf
);
335 StringCbCopyW(Buf
, MaxValueLen
, L
"%SystemRoot%\\System32\\Config\\");
336 StringCbCatW(Buf
, MaxValueLen
, LogName
);
337 StringCbCatW(Buf
, MaxValueLen
, L
".evt");
339 ValueLen
= MaxValueLen
;
340 Result
= RegSetValueExW(hKey
,
346 if (Result
!= ERROR_SUCCESS
)
348 DPRINT1("RegSetValueExW failed: %lu\n", Result
);
349 HeapFree(GetProcessHeap(), 0, Buf
);
354 ExpandedLen
= ExpandEnvironmentStringsW(Buf
, NULL
, 0);
355 Expanded
= HeapAlloc(GetProcessHeap(), 0, ExpandedLen
* sizeof(WCHAR
));
358 DPRINT1("Cannot allocate heap!\n");
359 HeapFree(GetProcessHeap(), 0, Buf
);
363 ExpandEnvironmentStringsW(Buf
, Expanded
, ExpandedLen
);
365 if (!RtlDosPathNameToNtPathName_U(Expanded
, &FileName
, NULL
, NULL
))
367 DPRINT1("Cannot convert path!\n");
368 HeapFree(GetProcessHeap(), 0, Expanded
);
369 HeapFree(GetProcessHeap(), 0, Buf
);
373 DPRINT("%S -> %S\n", Buf
, Expanded
);
375 ValueLen
= sizeof(ulMaxSize
);
376 Result
= RegQueryValueExW(hKey
,
382 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
384 ulMaxSize
= 512 * 1024; /* 512 kBytes */
386 Result
= RegSetValueExW(hKey
,
394 ValueLen
= sizeof(ulRetention
);
395 Result
= RegQueryValueExW(hKey
,
399 (LPBYTE
)&ulRetention
,
401 if ((Result
!= ERROR_SUCCESS
) || (Type
!= REG_DWORD
))
403 /* On Windows 2003 it is 604800 (secs) == 7 days */
406 Result
= RegSetValueExW(hKey
,
410 (LPBYTE
)&ulRetention
,
411 sizeof(ulRetention
));
414 // TODO: Add, or use, default values for "AutoBackupLogFiles" (REG_DWORD)
415 // and "CustomSD" (REG_SZ).
417 Status
= LogfCreate(&pLogf
, LogName
, &FileName
, ulMaxSize
, ulRetention
, TRUE
, FALSE
);
418 if (!NT_SUCCESS(Status
))
420 DPRINT1("Failed to create %S! (Status %08lx)\n", Expanded
, Status
);
423 HeapFree(GetProcessHeap(), 0, Expanded
);
424 HeapFree(GetProcessHeap(), 0, Buf
);
429 LoadLogFiles(HKEY eventlogKey
)
432 DWORD MaxLognameLen
, LognameLen
;
437 Result
= RegQueryInfoKeyW(eventlogKey
, NULL
, NULL
, NULL
, NULL
, &MaxLognameLen
,
438 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
439 if (Result
!= ERROR_SUCCESS
)
441 DPRINT1("RegQueryInfoKeyW failed: %lu\n", Result
);
447 Buf
= HeapAlloc(GetProcessHeap(), 0, MaxLognameLen
* sizeof(WCHAR
));
450 DPRINT1("Error: cannot allocate heap!\n");
454 LognameLen
= MaxLognameLen
;
456 while (RegEnumKeyExW(eventlogKey
,
460 NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
)
466 Result
= RegOpenKeyExW(eventlogKey
, Buf
, 0, KEY_ALL_ACCESS
, &SubKey
);
467 if (Result
!= ERROR_SUCCESS
)
469 DPRINT1("Failed to open %S key.\n", Buf
);
470 HeapFree(GetProcessHeap(), 0, Buf
);
474 pLogFile
= LoadLogFile(SubKey
, Buf
);
475 if (pLogFile
!= NULL
)
477 DPRINT("Loaded %S\n", Buf
);
478 LoadEventSources(SubKey
, pLogFile
);
482 DPRINT1("Failed to load %S\n", Buf
);
487 LognameLen
= MaxLognameLen
;
491 HeapFree(GetProcessHeap(), 0, Buf
);
496 int wmain(int argc
, WCHAR
* argv
[])
501 WCHAR LogPath
[MAX_PATH
];
503 LogfListInitialize();
504 InitEventSourceList();
506 GetSystemWindowsDirectoryW(LogPath
, ARRAYSIZE(LogPath
));
508 if (GetDriveTypeW(LogPath
) == DRIVE_CDROM
)
510 DPRINT("LiveCD detected\n");
515 Result
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
516 L
"SYSTEM\\CurrentControlSet\\Services\\EventLog",
520 if (Result
!= ERROR_SUCCESS
)
522 DPRINT1("Fatal error: cannot open eventlog registry key.\n");
527 LoadLogFiles(elogKey
);
530 EventLogSource
= GetEventSourceByName(L
"EventLog");
533 DPRINT1("The 'EventLog' source is unavailable. The EventLog service will not be able to log its own events.\n");
536 StartServiceCtrlDispatcher(ServiceTable
);
544 VOID
PRINT_RECORD(PEVENTLOGRECORD pRec
)
548 LARGE_INTEGER SystemTime
;
551 DPRINT1("PRINT_RECORD(0x%p)\n", pRec
);
553 DbgPrint("Length = %lu\n", pRec
->Length
);
554 DbgPrint("Reserved = 0x%x\n", pRec
->Reserved
);
555 DbgPrint("RecordNumber = %lu\n", pRec
->RecordNumber
);
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
);
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
);
569 DbgPrint("EventID = %lu\n", pRec
->EventID
);
571 switch (pRec
->EventType
)
573 case EVENTLOG_ERROR_TYPE
:
574 DbgPrint("EventType = EVENTLOG_ERROR_TYPE\n");
576 case EVENTLOG_WARNING_TYPE
:
577 DbgPrint("EventType = EVENTLOG_WARNING_TYPE\n");
579 case EVENTLOG_INFORMATION_TYPE
:
580 DbgPrint("EventType = EVENTLOG_INFORMATION_TYPE\n");
582 case EVENTLOG_AUDIT_SUCCESS
:
583 DbgPrint("EventType = EVENTLOG_AUDIT_SUCCESS\n");
585 case EVENTLOG_AUDIT_FAILURE
:
586 DbgPrint("EventType = EVENTLOG_AUDIT_FAILURE\n");
589 DbgPrint("EventType = %hu\n", pRec
->EventType
);
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
);
602 i
= sizeof(EVENTLOGRECORD
);
603 DbgPrint("SourceName: %S\n", (PWSTR
)((ULONG_PTR
)pRec
+ i
));
605 i
+= (wcslen((PWSTR
)((ULONG_PTR
)pRec
+ i
)) + 1) * sizeof(WCHAR
);
606 DbgPrint("ComputerName: %S\n", (PWSTR
)((ULONG_PTR
)pRec
+ i
));
608 if (pRec
->StringOffset
< pRec
->Length
&& pRec
->NumStrings
)
610 DbgPrint("Strings:\n");
611 str
= (PWSTR
)((ULONG_PTR
)pRec
+ pRec
->StringOffset
);
612 for (i
= 0; i
< pRec
->NumStrings
; i
++)
614 DbgPrint("[%u] %S\n", i
, str
);
615 str
+= wcslen(str
) + 1;
619 DbgPrint("Length2 = %lu\n", *(PULONG
)((ULONG_PTR
)pRec
+ pRec
->Length
- 4));