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