xref: /reactos/base/services/eventlog/eventlog.c (revision 50cf16b3)
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 }
621