Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2908 lines
95 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. evlog.cpp
  5. Abstract:
  6. !evlog using the debug engine evlog query interface
  7. Environment:
  8. User Mode
  9. --*/
  10. //
  11. // TODO: Feature to see exact formatted desc string for specific event id
  12. // TODO: Feature to see correct loaded category name for specific event id
  13. // TODO: Feature to list cat and desc strings for given msg dll(?)
  14. //
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. #include <cmnutil.hpp>
  18. #include "messages.h"
  19. //
  20. // Global display constants
  21. //
  22. const CHAR *g_pcszEventType[] = {
  23. "None", // 0
  24. "Error", // 1
  25. "Warning", // 2
  26. "",
  27. "Information", // 4
  28. "",
  29. "",
  30. "",
  31. "Success Audit", // 8
  32. "",
  33. "",
  34. "",
  35. "",
  36. "",
  37. "",
  38. "",
  39. "Failure Audit", // 16
  40. };
  41. const CHAR *g_pcszAppEventCategory[] = {
  42. "None", // 0
  43. "Devices", // 1
  44. "Disk", // 2
  45. "Printers", // 3
  46. "Services", // 4
  47. "Shell", // 5
  48. "System Event", // 6
  49. "Network", // 7
  50. };
  51. //
  52. // TODO: Really we should load the CategoryMessageFile from the registry
  53. // but that requires lots of calls to RegOpenKeyEx, RegQueryValueEx,
  54. // LoadLibrary and FormatMessage. So we just create a known static
  55. // list that works for most cases.
  56. //
  57. const CHAR *g_pcszSecEventCategory[] = {
  58. "None", // 0
  59. "System Event", // 1
  60. "Logon/Logoff", // 2
  61. "Object Access", // 3
  62. "Privilege Use", // 4
  63. "Detailed Tracking", // 5
  64. "Policy Change", // 6
  65. "Account Management", // 7
  66. "Directory Service Access", // 8
  67. "Account Logon", // 9
  68. };
  69. //
  70. // Display text for read direction
  71. //
  72. const CHAR g_cszBackwardsRead[] = "Backwards";
  73. const CHAR g_cszForwardsRead[] = "Forwards";
  74. const CHAR g_cszUnknownRead[] = "<unknown>";
  75. //
  76. // Global Variables and Constants:
  77. //
  78. // g_cdwDefaultMaxRecords:
  79. // arbitrary to prevent too much output, ctrl+c will interrupt display
  80. //
  81. // g_cdwDefaultReadFlags:
  82. // starts from beginning (FORWARDS) or end (BACKWARDS) of event log
  83. //
  84. // g_cwMaxDataDisplayWidth:
  85. // allows for 32 columns of 8 byte chunks
  86. //
  87. // g_cwDefaultDataDisplayWidth
  88. // same as event log display. Can never be < 1 or > g_cdwMaxDataDisplayWidth
  89. //
  90. const DWORD BACKWARDS_READ = EVENTLOG_BACKWARDS_READ;
  91. const DWORD FORWARDS_READ = EVENTLOG_FORWARDS_READ;
  92. const DWORD g_cdwDefaultMaxRecords = 20;
  93. const DWORD g_cdwDefaultRecordOffset = 0;
  94. const DWORD g_cdwDefaultReadFlags = BACKWARDS_READ;
  95. const WORD g_cwMaxDataDisplayWidth = 256;
  96. const BYTE g_cwDefaultDataDisplayWidth = 8;
  97. //
  98. // Global static vars
  99. //
  100. // These are used to persist settings for !evlog option command
  101. //
  102. static DWORD g_dwMaxRecords = g_cdwDefaultMaxRecords;
  103. static DWORD g_dwRecordOffsetAppEvt = g_cdwDefaultRecordOffset;
  104. static DWORD g_dwRecordOffsetSecEvt = g_cdwDefaultRecordOffset;
  105. static DWORD g_dwRecordOffsetSysEvt = g_cdwDefaultRecordOffset;
  106. static DWORD g_dwReadFlags = g_cdwDefaultReadFlags;
  107. static WORD g_wDataDisplayWidth = g_cwDefaultDataDisplayWidth;
  108. //
  109. // Macros
  110. //
  111. #define SKIP_WSPACE(s) while (*s && (*s == ' ' || *s == '\t')) {++s;}
  112. //----------------------------------------------------------------------------
  113. //
  114. // Generic support/utility functions
  115. //
  116. //----------------------------------------------------------------------------
  117. HRESULT
  118. GetEvLogNewestRecord ( const CHAR *szEventLog , OUT DWORD *pdwNewestRecord)
  119. /*++
  120. Routine Description:
  121. This function is used to retrieve the most recent event record number
  122. logged to the specified event log.
  123. Arguments:
  124. szEventLog - Supplies name of event log (Application, System,
  125. Security)
  126. pdwNewestRecord - Supplies buffer for record number
  127. Return Value:
  128. E_POINTER if either argument is NULL
  129. E_UNEXPECTED if Status is (mistakenly) not set during code execution
  130. GetLastError() converted to HRESULT otherwise
  131. --*/
  132. {
  133. HANDLE hEventLog = NULL;
  134. DWORD dwRecords = 0;
  135. DWORD dwOldestRecord = 0xFFFFFFFF;
  136. HRESULT Status = E_UNEXPECTED;
  137. if ((NULL == szEventLog) || (NULL == pdwNewestRecord))
  138. {
  139. Status = E_POINTER;
  140. ExtErr("Internal error: null event log string or null oldest record "
  141. "pointer\n");
  142. goto Exit;
  143. }
  144. // Open the event log.
  145. hEventLog = OpenEventLog(
  146. NULL, // uses local computer
  147. szEventLog); // source name
  148. if (NULL == hEventLog)
  149. {
  150. Status = HRESULT_FROM_WIN32(GetLastError());
  151. ExtErr("Unable to open '%s' event log, 0x%08X\n", szEventLog, Status);
  152. goto Exit;
  153. }
  154. // Get the number of records in the event log.
  155. if (!GetNumberOfEventLogRecords(
  156. hEventLog, // handle to event log
  157. &dwRecords)) // buffer for number of records
  158. {
  159. Status = HRESULT_FROM_WIN32(GetLastError());
  160. ExtErr("Unable to count '%s' event log records, 0x%08X\n",
  161. szEventLog, Status);
  162. goto Exit;
  163. }
  164. if (!GetOldestEventLogRecord(
  165. hEventLog, // handle to event log
  166. &dwOldestRecord)) // buffer for number of records
  167. {
  168. Status = HRESULT_FROM_WIN32(GetLastError());
  169. ExtErr("Unable to get oldest '%s' event log record, 0x%08X\n",
  170. szEventLog, Status);
  171. goto Exit;
  172. }
  173. //
  174. // If there are zero events we should have failed above
  175. // when trying to get the oldest event log record because
  176. // it does not exist.
  177. //
  178. // If there is at least one, the math should work.
  179. //
  180. // The logging should result in sequential numbers for
  181. // the events, however, the first event will not always
  182. // start at #1
  183. //
  184. *pdwNewestRecord = dwOldestRecord + dwRecords - 1;
  185. Status = S_OK;
  186. Exit:
  187. if (hEventLog)
  188. {
  189. CloseEventLog(hEventLog);
  190. }
  191. return Status;
  192. }
  193. void
  194. PrintEvLogTimeGenerated( EVENTLOGRECORD *pevlr )
  195. /*++
  196. Routine Description:
  197. This function is used to display two lines with the local date and time
  198. info from an EVENTLOGRECORD structure.
  199. Arguments:
  200. pevlr - Supplies the pointer to any EVENTLOGRECORD structure
  201. Return Value:
  202. None
  203. --*/
  204. {
  205. FILETIME FileTime, LocalFileTime;
  206. SYSTEMTIME SysTime;
  207. __int64 lgTemp;
  208. __int64 SecsTo1970 = 116444736000000000;
  209. if (NULL == pevlr)
  210. goto Exit;
  211. lgTemp = Int32x32To64(pevlr->TimeGenerated,10000000) + SecsTo1970;
  212. FileTime.dwLowDateTime = (DWORD) lgTemp;
  213. FileTime.dwHighDateTime = (DWORD)(lgTemp >> 32);
  214. // TODO: Could use GetTimeFormat to be more consistent w/Event Log
  215. // cch = GetTimeFormat(LOCALE_USER_DEFAULT,
  216. // 0,
  217. // &stGenerated,
  218. // NULL,
  219. // wszBuf,
  220. // cchBuf);
  221. FileTimeToLocalFileTime(&FileTime, &LocalFileTime);
  222. FileTimeToSystemTime(&LocalFileTime, &SysTime);
  223. ExtOut("Date:\t\t%02d/%02d/%04d\n",
  224. SysTime.wMonth,
  225. SysTime.wDay,
  226. SysTime.wYear);
  227. ExtOut("Time:\t\t%02d:%02d:%02d\n",
  228. SysTime.wHour,
  229. SysTime.wMinute,
  230. SysTime.wSecond);
  231. Exit:
  232. return;
  233. }
  234. void
  235. PrintEvLogData( EVENTLOGRECORD *pevlr )
  236. /*++
  237. Routine Description:
  238. This function is used to display an event record's data section. If there
  239. is no data to display, nothing is displayed, not even the "Data:" header.
  240. Example:
  241. ========
  242. Data: (40432 bytes)
  243. 0000: 0d 00 0a 00 0d 00 0a 00 ........
  244. 0008: 41 00 70 00 70 00 6c 00 A.p.p.l.
  245. Arguments:
  246. pevlr - Supplies the pointer to any EVENTLOGRECORD structure
  247. Return Value:
  248. None
  249. --*/
  250. {
  251. PBYTE pbData = NULL;
  252. DWORD dwDataLen = pevlr->DataLength;
  253. DWORD dwCurPos = 0;
  254. // 0000: 0d 00 0a 00 0d 00 0a 00 ........
  255. // 4 + 4 bytes for leading offset 0000: (+4 more in case bounds exceeded)
  256. // 2 bytes for ": " separator
  257. // 3 * g_cdwMaxDataDisplayWidth bytes for hex display
  258. // 2 bytes for " " separator
  259. // g_cdwMaxDataDisplayWidth bytes for trailing ASCII display
  260. // 1 byte for trailing newline
  261. // 1 byte for terminating nul '\0'
  262. // = 1042 bytes required, round up to 1280 to be safe
  263. const cDataOutputDisplayWidth =
  264. 4+4+4+2+3*g_cwMaxDataDisplayWidth+2+g_cwMaxDataDisplayWidth+1+1;
  265. CHAR szDataDisplay[cDataOutputDisplayWidth];
  266. CHAR szTempBuffer[MAX_PATH+1];
  267. if (NULL == pevlr)
  268. goto Exit;
  269. ZeroMemory(szDataDisplay, sizeof(szDataDisplay));
  270. // Only display Data section if data is present
  271. if (0 != dwDataLen)
  272. {
  273. ExtOut("Data: (%u bytes [=0x%04X])\n", dwDataLen, dwDataLen);
  274. if (dwDataLen >= g_wDataDisplayWidth)
  275. {
  276. do
  277. {
  278. unsigned int i = 0;
  279. pbData = (PBYTE)pevlr + pevlr->DataOffset + dwCurPos;
  280. //ExtOut("%04x: "
  281. // "%02x %02x %02x %02x %02x %02x %02x %02x ",
  282. // dwCurPos,
  283. // pbData[0], pbData[1], pbData[2], pbData[3],
  284. // pbData[4], pbData[5], pbData[6], pbData[7]);
  285. // Print offset for this line of data
  286. PrintString(szDataDisplay,
  287. sizeof(szDataDisplay),
  288. "%04x: ",
  289. dwCurPos);
  290. // Fill in hex values for next g_wDataDisplayWidth bytes
  291. for (i = 0; i < g_wDataDisplayWidth; i++)
  292. {
  293. PrintString(szTempBuffer,
  294. sizeof(szTempBuffer),
  295. "%02x ",
  296. pbData[i]);
  297. CatString(szDataDisplay,
  298. szTempBuffer,
  299. sizeof(szDataDisplay));
  300. }
  301. // Pad with two extra spaces
  302. CatString(szDataDisplay, " ", sizeof(szDataDisplay));
  303. for (i = 0; i < g_wDataDisplayWidth; i++)
  304. {
  305. if (isprint(pbData[i]))
  306. {
  307. PrintString(szTempBuffer,
  308. sizeof(szTempBuffer),
  309. "%c",
  310. pbData[i]);
  311. }
  312. else
  313. {
  314. PrintString(szTempBuffer, sizeof(szTempBuffer), ".");
  315. }
  316. CatString(szDataDisplay,
  317. szTempBuffer,
  318. sizeof(szDataDisplay));
  319. }
  320. CatString(szDataDisplay, "\n", sizeof(szDataDisplay));
  321. ExtOut(szDataDisplay);
  322. if (CheckControlC())
  323. {
  324. ExtOut("Terminated w/ctrl-C...\n");
  325. goto Exit;
  326. }
  327. // Display data 8 bytes at a time like event log
  328. // unless overridden by !evlog option setting
  329. dwCurPos += sizeof(BYTE) * g_wDataDisplayWidth;
  330. } while (dwCurPos < (dwDataLen - g_wDataDisplayWidth));
  331. }
  332. // Sometimes there will be fewer than 8 bytes on last line
  333. if (dwCurPos < dwDataLen)
  334. {
  335. pbData = (PBYTE)pevlr + pevlr->DataOffset + dwCurPos;
  336. ExtOut("%04x: ", dwCurPos);
  337. for (unsigned int i = 0; i < (dwDataLen - dwCurPos); i++)
  338. {
  339. ExtOut("%02x ", pbData[i]);
  340. }
  341. for (i = 0; i < g_wDataDisplayWidth - (dwDataLen - dwCurPos); i++)
  342. {
  343. ExtOut(" ");
  344. }
  345. ExtOut(" ");
  346. for (i = 0; i < (dwDataLen - dwCurPos); i++)
  347. {
  348. if (isprint(pbData[i]))
  349. ExtOut("%c", pbData[i]);
  350. else
  351. ExtOut(".");
  352. }
  353. ExtOut("\n");
  354. }
  355. }
  356. Exit:
  357. return;
  358. }
  359. void
  360. PrintEvLogDescription( EVENTLOGRECORD *pevlr )
  361. /*++
  362. Routine Description:
  363. This function is used to display an event record's description insertion
  364. strings (%1, %2, etc).
  365. Currently it does not look up the message string from the event message
  366. file.
  367. Arguments:
  368. pevlr - Supplies the pointer to any EVENTLOGRECORD structure
  369. Return Value:
  370. None
  371. --*/
  372. {
  373. DWORD dwOffset = 0;
  374. CHAR *pString = NULL;
  375. if (NULL == pevlr)
  376. goto Exit;
  377. ExtOut("Description: (%u strings)\n", pevlr->NumStrings);
  378. for (int i = 0; i < pevlr->NumStrings; i++)
  379. {
  380. // TODO: Should break this up into chunks in case it is really long
  381. // and add Ctrl+C handling
  382. pString = (CHAR *)(BYTE *)pevlr + pevlr->StringOffset + dwOffset;
  383. ExtOut("%s\n", pString);
  384. dwOffset += strlen(pString) + 1;
  385. }
  386. Exit:
  387. return;
  388. }
  389. void
  390. PrintEvLogEvent( const CHAR *szEventLog, EVENTLOGRECORD *pevlr )
  391. /*++
  392. Routine Description:
  393. This function is used to display an event record. The format used for
  394. display attempts to duplicate the format you see when you use the "copy
  395. to clipboard" button while viewing an event in eventvwr.
  396. Example:
  397. ========
  398. -------------- 01 --------------
  399. Record #: 7923
  400. Event Type: Error (1)
  401. Event Source: Userenv
  402. Event Category: None (0)
  403. Event ID: 1030 (0xC0000406)
  404. Date: 01/06/2002
  405. Time: 18:13:05
  406. Description: (0 strings)
  407. Arguments:
  408. pevlr - Supplies the pointer to any EVENTLOGRECORD structure
  409. Return Value:
  410. None
  411. --*/
  412. {
  413. const CHAR *cszCategory;
  414. const CHAR cszDefaultCategory[] = "None";
  415. if (NULL == pevlr)
  416. goto Exit;
  417. if (!_stricmp(szEventLog, "System"))
  418. {
  419. cszCategory = cszDefaultCategory;
  420. }
  421. else if (!_stricmp(szEventLog, "Security"))
  422. {
  423. if (pevlr->EventCategory <= 9)
  424. {
  425. cszCategory = g_pcszSecEventCategory[pevlr->EventCategory];
  426. }
  427. else
  428. {
  429. cszCategory = "";
  430. }
  431. }
  432. else // Application
  433. {
  434. if (pevlr->EventCategory <= 7)
  435. {
  436. cszCategory = g_pcszAppEventCategory[pevlr->EventCategory];
  437. }
  438. else
  439. {
  440. cszCategory = "";
  441. }
  442. }
  443. // Output format similar to copy to clipboard format in event viewer
  444. ExtOut("Record #: %u\n\n", pevlr->RecordNumber);
  445. ExtOut("Event Type:\t%s (%u)\n",
  446. (pevlr->EventType <= 16)
  447. ? g_pcszEventType[pevlr->EventType]
  448. : "None",
  449. pevlr->EventType);
  450. ExtOut("Event Source:\t%s\n",
  451. (CHAR *)(BYTE *)pevlr + sizeof(EVENTLOGRECORD));
  452. ExtOut("Event Category:\t%s (%u)\n",
  453. cszCategory,
  454. pevlr->EventCategory);
  455. if (pevlr->EventID > 0xFFFF)
  456. ExtOut("Event ID:\t%u (0x%08X)\n",
  457. 0xFFFF & pevlr->EventID,
  458. pevlr->EventID);
  459. else
  460. ExtOut("Event ID:\t%u\n",
  461. pevlr->EventID);
  462. PrintEvLogTimeGenerated(pevlr);
  463. PrintEvLogDescription(pevlr);
  464. PrintEvLogData(pevlr);
  465. Exit:
  466. return;
  467. }
  468. HRESULT
  469. PrintEvLogSummary ( const CHAR *szEventLog )
  470. /*++
  471. Routine Description:
  472. This function is used to display summary information about a specific
  473. event log.
  474. Example:
  475. ========
  476. --------------------------------
  477. Application Event Log:
  478. # Records : 7923
  479. Oldest Record # : 1
  480. Newest Record # : 7923
  481. Event Log Full : false
  482. --------------------------------
  483. System Event Log:
  484. # Records : 5046
  485. Oldest Record # : 1
  486. Newest Record # : 5046
  487. Event Log Full : false
  488. --------------------------------
  489. Security Event Log:
  490. # Records : 24256
  491. Oldest Record # : 15164
  492. Newest Record # : 39419
  493. Event Log Full : false
  494. --------------------------------
  495. Arguments:
  496. szEventLog - Supplies name of event log (Application, System,
  497. Security)
  498. Return Value:
  499. None
  500. --*/
  501. {
  502. HANDLE hEventLog = NULL;
  503. DWORD dwRecords = 0;
  504. DWORD dwOldestRecord = 0xFFFFFFFF;
  505. HRESULT Status = E_FAIL;
  506. if (NULL == szEventLog)
  507. {
  508. ExtErr("Internal error: null event log string\n");
  509. return E_INVALIDARG;
  510. }
  511. ExtOut("--------------------------------\n");
  512. // Open the event log.
  513. hEventLog = OpenEventLog(
  514. NULL, // uses local computer
  515. szEventLog); // source name
  516. if (NULL == hEventLog)
  517. {
  518. Status = HRESULT_FROM_WIN32(GetLastError());
  519. ExtErr("Unable to open '%s' event log, 0x%08X\n", szEventLog, Status);
  520. return Status;
  521. }
  522. // Get the number of records in the event log.
  523. if (!GetNumberOfEventLogRecords(
  524. hEventLog, // handle to event log
  525. &dwRecords)) // buffer for number of records
  526. {
  527. Status = HRESULT_FROM_WIN32(GetLastError());
  528. ExtErr("Unable to count '%s' event log records, 0x%08X\n",
  529. szEventLog, Status);
  530. goto Exit;
  531. }
  532. ExtOut("%s Event Log:\n # Records : %u\n", szEventLog, dwRecords);
  533. if (!GetOldestEventLogRecord(
  534. hEventLog, // handle to event log
  535. &dwOldestRecord)) // buffer for number of records
  536. {
  537. Status = HRESULT_FROM_WIN32(GetLastError());
  538. ExtErr("Unable to get oldest '%s' event log record, 0x%08X\n",
  539. szEventLog, Status);
  540. goto Exit;
  541. }
  542. ExtOut(" Oldest Record # : %u\n", dwOldestRecord);
  543. ExtOut(" Newest Record # : %u\n", dwOldestRecord + dwRecords - 1);
  544. DWORD dwBytesNeeded = 0;
  545. DWORD dwBufSize = 0;
  546. EVENTLOG_FULL_INFORMATION *pevfi;
  547. // Only try this once - we could retry if dwBufSize too small
  548. dwBufSize = sizeof(EVENTLOG_FULL_INFORMATION);
  549. pevfi = (EVENTLOG_FULL_INFORMATION *)calloc(1, dwBufSize);
  550. if (!pevfi)
  551. {
  552. Status = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
  553. ExtErr("Unable to allocate buffer, 0x%08X\n", Status);
  554. }
  555. else
  556. {
  557. if ((S_OK == (Status = InitDynamicCalls(&g_Advapi32CallsDesc))) &&
  558. g_Advapi32Calls.GetEventLogInformation)
  559. {
  560. if (!g_Advapi32Calls.GetEventLogInformation(
  561. hEventLog, // handle to event log
  562. EVENTLOG_FULL_INFO, // information to retrieve
  563. pevfi, // buffer for read data
  564. dwBufSize, // size of buffer in bytes
  565. &dwBytesNeeded)) // number of bytes needed
  566. {
  567. Status = HRESULT_FROM_WIN32(GetLastError());
  568. ExtErr("Unable to get full status from '%s', 0x%08X\n",
  569. szEventLog, Status);
  570. }
  571. else
  572. {
  573. ExtOut(" Event Log Full : %s\n",
  574. (pevfi->dwFull) ? "true"
  575. : "false");
  576. Status = S_OK;
  577. }
  578. }
  579. free(pevfi);
  580. }
  581. Exit:
  582. CloseEventLog(hEventLog);
  583. return Status;
  584. }
  585. void
  586. PrintEvLogOptionSettings ( void )
  587. /*++
  588. Routine Description:
  589. This function is used to display the option settings used by the !evlog
  590. extension for various defaults.
  591. Currently all cached option settings are used by the read command only.
  592. Example:
  593. ========
  594. Default EvLog Option Settings:
  595. --------------------------------
  596. Max Records Returned: 20
  597. Search Order: Backwards
  598. Data Display Width: 8
  599. --------------------------------
  600. Bounding Record Numbers:
  601. Application Event Log: 0
  602. System Event Log: 0
  603. Security Event Log: 0
  604. --------------------------------
  605. Arguments:
  606. None
  607. Return Value:
  608. None
  609. --*/
  610. {
  611. CHAR szSearchOrder[MAX_PATH];
  612. if (FORWARDS_READ == g_dwReadFlags)
  613. {
  614. CopyString(szSearchOrder, g_cszForwardsRead, sizeof(szSearchOrder));
  615. }
  616. else if (BACKWARDS_READ == g_dwReadFlags)
  617. {
  618. CopyString(szSearchOrder, g_cszBackwardsRead, sizeof(szSearchOrder));
  619. }
  620. else
  621. {
  622. PrintString(szSearchOrder,
  623. sizeof(szSearchOrder),
  624. "Unknown (%08X)",
  625. g_dwReadFlags);
  626. }
  627. ExtOut("Default EvLog Option Settings:\n");
  628. ExtOut("--------------------------------\n");
  629. ExtOut("Max Records Returned: %u\n", g_dwMaxRecords);
  630. ExtOut("Search Order: %s\n", szSearchOrder);
  631. ExtOut("Data Display Width: %u\n", g_wDataDisplayWidth);
  632. ExtOut("--------------------------------\n");
  633. ExtOut("Bounding Record Numbers:\n");
  634. ExtOut(" Application Event Log: %u\n", g_dwRecordOffsetAppEvt);
  635. ExtOut(" System Event Log: %u\n", g_dwRecordOffsetSysEvt);
  636. ExtOut(" Security Event Log: %u\n", g_dwRecordOffsetSecEvt);
  637. ExtOut("--------------------------------\n");
  638. }
  639. //----------------------------------------------------------------------------
  640. //
  641. // Debugger extension(s) options implementation
  642. //
  643. //----------------------------------------------------------------------------
  644. HRESULT
  645. EvLogAddSource ( PDEBUG_CLIENT Client, PCSTR args )
  646. /*++
  647. Routine Description:
  648. This function handles parsing and execution for the addsource command to
  649. the !evlog extension. It is used to add an event source to the registry
  650. so the events logged by that source display their description correctly
  651. instead of displaying the error message:
  652. Example: (of bad event source)
  653. ========
  654. Description:
  655. The description for Event ID ( 2 ) in Source ( DebuggerExtensions )
  656. cannot be found. The local computer may not have the necessary registry
  657. information or message DLL files to display messages from a remote
  658. computer. You may be able to use the /AUXSOURCE= flag to retrieve this
  659. description; see Help and Support for details. The following information
  660. is part of the event: Test Message with Event ID 2.
  661. Example: (of good event source registered correctly)
  662. ========
  663. Description:
  664. Test Message with Event ID 4000For more information, see Help and Support
  665. Center at http://go.microsoft.com/fwlink/events.asp.
  666. Arguments:
  667. Client - Pointer to IDebugClient passed to !evlog extension
  668. [not used by this command]
  669. args - Pointer to command line arguments passed to this command from
  670. !evlog extension
  671. Return Value:
  672. E_INVALIDARG if invalid argument syntax detected
  673. ERROR_BUFFER_OVERFLOW if argument length too long
  674. GetLastError() converted to HRESULT otherwise
  675. --*/
  676. {
  677. HKEY hk = NULL;
  678. HMODULE hModule = NULL;
  679. DWORD dwTypesSupported = 0;
  680. DWORD dwDisposition = 0;
  681. DWORD lResult = ERROR_SUCCESS;
  682. const DWORD cdwDefaultTypesSupported =
  683. EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
  684. EVENTLOG_INFORMATION_TYPE |
  685. EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
  686. CHAR szParamValue[MAX_PATH];
  687. CHAR szSource[MAX_PATH+1];
  688. CHAR szMessageFile[MAX_PATH+1];
  689. CHAR szRegPath[MAX_PATH+1];
  690. const CHAR cszUsage[] = "Usage:\n"
  691. " !evlog addsource [-d] [-s <source>] [-t <types>] [-f <msgfile>]"
  692. "\n\n"
  693. "Adds an event source to the registry. By default, only adds "
  694. "DebuggerExtensions\n"
  695. "event source to support !evlog report.\n\n"
  696. "Use !dreg to see the values added.\n\n"
  697. "Example:\n"
  698. " !dreg hklm\\system\\currentcontrolset\\services\\eventlog\\"
  699. "Application\\<source>!*\n\n"
  700. "Optional parameters:\n"
  701. "-d : Use defaults\n"
  702. "<source> : (default: DebuggerExtensions)\n"
  703. "<types> : All (default: 31), Success, Error (1), Warning (2),\n"
  704. " Information (4), Audit_Success (8), or Audit_Failure "
  705. "(16)\n"
  706. "<msgfile> : (default: local path to ext.dll)\n";
  707. const CHAR cszDefaultSource[] = "DebuggerExtensions";
  708. const CHAR cszDefaultExtensionDll[] = "uext.dll";
  709. INIT_API();
  710. ZeroMemory(szParamValue, sizeof(szParamValue));
  711. CopyString(szSource, cszDefaultSource, sizeof(szSource));
  712. dwTypesSupported = cdwDefaultTypesSupported;
  713. hModule = GetModuleHandle(cszDefaultExtensionDll);
  714. GetModuleFileName(hModule, szMessageFile, MAX_PATH);
  715. if (args)
  716. {
  717. SKIP_WSPACE(args);
  718. }
  719. if (!args || !args[0] ||
  720. !strncmp(args, "-h", 2) ||
  721. !strncmp(args, "-?", 2))
  722. {
  723. Status = E_INVALIDARG;
  724. ExtErr(cszUsage);
  725. goto Exit;
  726. }
  727. // Parse args
  728. while (*args)
  729. {
  730. SKIP_WSPACE(args);
  731. // Check for optional argument options to appear first
  732. if (('-' == *args) || ('/' == *args))
  733. {
  734. CHAR ch = *(++args); // Get next char + advance arg ptr
  735. ++args; // Skip one more char
  736. CHAR *szEndOfValue = NULL; // Ptr to last char in value
  737. size_t cchValue = 0; // Count of chars in value
  738. SKIP_WSPACE(args); // Advance to start of param value
  739. // Skip looking for value if this is start of another
  740. // parameter
  741. if (('-' != *args) && ('/' != *args))
  742. {
  743. // Parameter value is delimited by next space in string, or,
  744. // if quoted, by next quote
  745. if ('"' == *args)
  746. {
  747. ++args;
  748. szEndOfValue = strchr(args, '"');
  749. }
  750. else
  751. {
  752. szEndOfValue = strchr(args, ' ');
  753. }
  754. if (NULL == szEndOfValue)
  755. {
  756. // copy to end of line
  757. CopyString(szParamValue, args, sizeof(szParamValue));
  758. args += min(sizeof(szParamValue), strlen(args));
  759. }
  760. else
  761. {
  762. cchValue = szEndOfValue - args;
  763. if (cchValue < sizeof(szParamValue))
  764. {
  765. // copy next N chars
  766. CopyString(szParamValue, args, cchValue+1);
  767. args += cchValue;
  768. }
  769. else
  770. {
  771. Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  772. ExtErr("ERROR: Argument string too long. Aborting.\n");
  773. goto Exit;
  774. }
  775. // skip past (theoretically) paired quote
  776. if ('"' == *args)
  777. {
  778. ++args;
  779. }
  780. }
  781. }
  782. switch (ch)
  783. {
  784. case 'd': // Use defaults
  785. ExtVerb("Using defaults...\n");
  786. // do nothing
  787. break;
  788. case 's': // Source (string)
  789. ExtVerb("Setting Source...\n");
  790. CopyString(szSource, szParamValue, sizeof(szSource));
  791. break;
  792. case 't': // Event Type (number or string)
  793. ExtVerb("Setting Event Type...\n");
  794. if (!_strnicmp(szParamValue, "All", 3))
  795. {
  796. dwTypesSupported = EVENTLOG_ERROR_TYPE |
  797. EVENTLOG_WARNING_TYPE |
  798. EVENTLOG_INFORMATION_TYPE |
  799. EVENTLOG_AUDIT_SUCCESS |
  800. EVENTLOG_AUDIT_FAILURE;
  801. }
  802. else if (!_strnicmp(szParamValue, "Error", 5))
  803. {
  804. dwTypesSupported = EVENTLOG_ERROR_TYPE;
  805. }
  806. else if (!_strnicmp(szParamValue, "Warning", 7))
  807. {
  808. dwTypesSupported = EVENTLOG_WARNING_TYPE;
  809. }
  810. else if (!_strnicmp(szParamValue, "Information", 11))
  811. {
  812. dwTypesSupported = EVENTLOG_INFORMATION_TYPE;
  813. }
  814. else if (!_strnicmp(szParamValue, "Audit_Success", 13))
  815. {
  816. dwTypesSupported = EVENTLOG_AUDIT_SUCCESS;
  817. }
  818. else if (!_strnicmp(szParamValue, "Audit_Failure", 13))
  819. {
  820. dwTypesSupported = EVENTLOG_AUDIT_FAILURE;
  821. }
  822. else
  823. {
  824. dwTypesSupported = strtoul(szParamValue, NULL, 10);
  825. }
  826. break;
  827. case 'f': // Message File
  828. ExtVerb("Setting Message File...\n");
  829. CopyString(szMessageFile,
  830. szParamValue,
  831. sizeof(szMessageFile));
  832. break;
  833. default:
  834. Status = E_INVALIDARG;
  835. ExtErr("Invalid arg '-%c' specified\n", *args);
  836. ExtErr(cszUsage);
  837. goto Exit;
  838. break;
  839. }
  840. ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
  841. }
  842. else // Everything to end of line is message string
  843. {
  844. Status = E_INVALIDARG;
  845. ExtErr("Invalid arg '%s' specified\n", args);
  846. ExtErr(cszUsage);
  847. goto Exit;
  848. }
  849. }
  850. // Add source name as subkey under Application in EventLog registry key
  851. PrintString(szRegPath,
  852. sizeof(szRegPath),
  853. "SYSTEM\\CurrentControlSet\\Services\\EventLog\\"
  854. "Application\\%s",
  855. szSource);
  856. lResult = RegCreateKeyEx(
  857. HKEY_LOCAL_MACHINE, // key
  858. szRegPath, // subkey to open or create
  859. 0, // reserved, must be zero
  860. "", // object type class
  861. REG_OPTION_NON_VOLATILE, // special options (preserves data
  862. // after system restart)
  863. KEY_READ | KEY_WRITE, // access mask
  864. NULL, // security descriptor (NULL = not inherited
  865. // by child processes)
  866. &hk, // returned handle to opened or created key
  867. &dwDisposition); // returns REG_CREATED_NEW_KEY or
  868. // REG_OPENED_EXISTING_KEY
  869. if (ERROR_SUCCESS != lResult)
  870. {
  871. Status = HRESULT_FROM_WIN32(lResult);
  872. ExtErr("Could not open or create key, %u\n", lResult);
  873. goto Exit;
  874. }
  875. if (REG_CREATED_NEW_KEY == dwDisposition)
  876. {
  877. ExtOut("Created key:\nHKLM\\%s\n", szRegPath);
  878. }
  879. else if (REG_OPENED_EXISTING_KEY == dwDisposition)
  880. {
  881. ExtOut("Opened key:\nHKLM\\%s\n", szRegPath);
  882. }
  883. else
  884. {
  885. ExtWarn("Warning: Unexpected disposition action %u\n"
  886. "key: HKLM\\%s",
  887. szRegPath,
  888. dwDisposition);
  889. }
  890. // Set the value for EventMessageFile
  891. ExtVerb("Setting EventMessageFile to %s...\n", szMessageFile);
  892. lResult = RegSetValueEx(
  893. hk, // subkey handle
  894. "EventMessageFile", // value name
  895. 0, // must be zero
  896. REG_EXPAND_SZ, // value type
  897. (LPBYTE) szMessageFile, // pointer to value data
  898. strlen(szMessageFile) + 1); // length of value data
  899. if (ERROR_SUCCESS != lResult)
  900. {
  901. Status = HRESULT_FROM_WIN32(lResult);
  902. ExtErr("Could not set EventMessageFile, %u\n", lResult);
  903. goto Exit;
  904. }
  905. ExtOut(" EventMessageFile: %s\n", szMessageFile);
  906. // Set the supported event types value in the TypesSupported
  907. ExtVerb("Setting TypesSupported to %u...\n", dwTypesSupported);
  908. lResult = RegSetValueEx(
  909. hk, // subkey handle
  910. "TypesSupported", // value name
  911. 0, // must be zero
  912. REG_DWORD, // value type
  913. (LPBYTE) &dwTypesSupported, // pointer to value data
  914. sizeof(DWORD)); // length of value data
  915. if (ERROR_SUCCESS != lResult)
  916. {
  917. Status = HRESULT_FROM_WIN32(lResult);
  918. ExtErr("Could not set TypesSupported, %u\n", lResult);
  919. goto Exit;
  920. }
  921. ExtOut(" TypesSupported: %u\n", dwTypesSupported);
  922. Status = S_OK;
  923. Exit:
  924. if (NULL != hk)
  925. {
  926. RegCloseKey(hk);
  927. }
  928. EXIT_API();
  929. return Status;
  930. }
  931. HRESULT
  932. EvLogBackup ( PDEBUG_CLIENT Client, PCSTR args )
  933. /*++
  934. Routine Description:
  935. This function handles parsing and execution for the backup command to the
  936. !evlog extension. It is used to create a backup of an event log to a
  937. file.
  938. Arguments:
  939. Client - Pointer to IDebugClient passed to !evlog extension
  940. [not used by this command]
  941. args - Pointer to command line arguments passed to this command from
  942. d!evlog extension
  943. Return Value:
  944. E_INVALIDARG if invalid argument syntax detected
  945. ERROR_BUFFER_OVERFLOW if argument length too long
  946. GetLastError() converted to HRESULT otherwise
  947. --*/
  948. {
  949. HANDLE hEventLog = NULL;
  950. DWORD dwDirLen = 0;
  951. CHAR szParamValue[MAX_PATH];
  952. CHAR szEventLog[MAX_PATH+1];
  953. CHAR szBackupFileName[MAX_PATH+1];
  954. const CHAR cszUsage[] = "Usage:\n"
  955. " !evlog backup [-d] [-l <eventlog>] [-f <filename>]\n\n"
  956. "Makes backup of specified event log to a file.\n\n"
  957. "Optional parameters:\n"
  958. "-d : Use defaults\n"
  959. "<eventlog> : Application (default), System, Security\n"
  960. "<filename> : (default: %%cwd%%\\<eventlog>_backup.evt)\n";
  961. const CHAR cszDefaultEventLog[] = "Application";
  962. const CHAR cszDefaultFileNameAppend[] = "_backup.evt";
  963. INIT_API();
  964. // Initialize defaults
  965. ZeroMemory(szParamValue, sizeof(szParamValue));
  966. CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
  967. // Create default backup filename: %cwd%\Application_backup.evt
  968. dwDirLen = GetCurrentDirectory(sizeof(szEventLog)/sizeof(TCHAR),
  969. szEventLog); // temp use of szEventLog
  970. if (0 == dwDirLen)
  971. {
  972. ExtErr("ERROR: Current directory length too long. Using '.' for "
  973. "directory\n");
  974. CopyString(szEventLog, ".", sizeof(szEventLog));
  975. }
  976. PrintString(szBackupFileName,
  977. sizeof(szBackupFileName),
  978. "%s\\%s%s",
  979. szEventLog,
  980. cszDefaultEventLog,
  981. cszDefaultFileNameAppend);
  982. ZeroMemory(szEventLog, sizeof(szEventLog));
  983. CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
  984. if (args)
  985. {
  986. SKIP_WSPACE(args);
  987. }
  988. if (!args || !args[0] ||
  989. !strncmp(args, "-h", 2) ||
  990. !strncmp(args, "-?", 2))
  991. {
  992. Status = E_INVALIDARG;
  993. ExtErr(cszUsage);
  994. goto Exit;
  995. }
  996. // Parse args
  997. while (*args)
  998. {
  999. SKIP_WSPACE(args);
  1000. // Check for optional argument options to appear first
  1001. if (('-' == *args) || ('/' == *args))
  1002. {
  1003. CHAR ch = *(++args); // Get next char + advance arg ptr
  1004. ++args; // Skip one more char
  1005. CHAR *szEndOfValue = NULL; // Ptr to last char in value
  1006. size_t cchValue = 0; // Count of chars in value
  1007. SKIP_WSPACE(args); // Advance to start of param value
  1008. if (('-' != *args) && ('/' != *args))
  1009. {
  1010. // Parameter value is delimited by next space in string, or,
  1011. // if quoted, by next quote
  1012. if ('"' == *args)
  1013. {
  1014. ++args;
  1015. szEndOfValue = strchr(args, '"');
  1016. }
  1017. else
  1018. {
  1019. szEndOfValue = strchr(args, ' ');
  1020. }
  1021. if (NULL == szEndOfValue)
  1022. {
  1023. // copy to end of line
  1024. CopyString(szParamValue, args, sizeof(szParamValue));
  1025. args += min(sizeof(szParamValue), strlen(args));
  1026. }
  1027. else
  1028. {
  1029. cchValue = szEndOfValue - args;
  1030. if (cchValue < sizeof(szParamValue))
  1031. {
  1032. // copy next N chars
  1033. CopyString(szParamValue, args, cchValue+1);
  1034. args += cchValue;
  1035. }
  1036. else
  1037. {
  1038. Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  1039. ExtErr("ERROR: Argument string too long. Aborting.\n");
  1040. goto Exit;
  1041. }
  1042. // skip past (theoretically) paired quote
  1043. if ('"' == *args)
  1044. {
  1045. ++args;
  1046. }
  1047. }
  1048. }
  1049. switch (ch)
  1050. {
  1051. case 'd': // Use defaults
  1052. ExtVerb("Using defaults...\n");
  1053. // do nothing
  1054. break;
  1055. case 'l': // Source (string)
  1056. ExtVerb("Setting Event Log...\n");
  1057. CopyString(szEventLog, szParamValue, sizeof(szEventLog));
  1058. break;
  1059. case 'f': // Message File
  1060. ExtVerb("Setting Backup File Name...\n");
  1061. CopyString(szBackupFileName,
  1062. szParamValue,
  1063. sizeof(szBackupFileName));
  1064. break;
  1065. default:
  1066. Status = E_INVALIDARG;
  1067. ExtErr("Invalid arg '-%c' specified\n", *args);
  1068. ExtErr(cszUsage);
  1069. goto Exit;
  1070. break;
  1071. }
  1072. ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
  1073. }
  1074. else // Everything to end of line is message string
  1075. {
  1076. Status = E_INVALIDARG;
  1077. ExtErr("Invalid arg '%s' specified\n", args);
  1078. ExtErr(cszUsage);
  1079. goto Exit;
  1080. }
  1081. }
  1082. // Get a handle to the event log
  1083. ExtVerb("Opening event log '%s'...", szEventLog);
  1084. hEventLog = OpenEventLog(
  1085. NULL, // uses local computer
  1086. szEventLog); // source name
  1087. if (NULL == hEventLog)
  1088. {
  1089. Status = HRESULT_FROM_WIN32(GetLastError());
  1090. ExtErr("Unable to open '%s' event log, 0x%08X\n", szEventLog, Status);
  1091. goto Exit;
  1092. }
  1093. // Backup event log
  1094. ExtOut("Backing up '%s' event log...\n", szEventLog);
  1095. if (!BackupEventLog(
  1096. hEventLog, // handle to event log
  1097. szBackupFileName)) // name of backup file
  1098. {
  1099. Status = HRESULT_FROM_WIN32(GetLastError());
  1100. ExtErr("Unable to backup event log to '%s', 0x%08X\n",
  1101. szBackupFileName,
  1102. Status);
  1103. goto Exit;
  1104. }
  1105. ExtOut("Event log successfully backed up to '%s'\n", szBackupFileName);
  1106. Status = S_OK;
  1107. Exit:
  1108. if (hEventLog)
  1109. {
  1110. CloseEventLog(hEventLog);
  1111. }
  1112. EXIT_API();
  1113. return Status;
  1114. }
  1115. HRESULT
  1116. EvLogOption( PDEBUG_CLIENT Client, PCSTR args )
  1117. /*++
  1118. Routine Description:
  1119. This function handles parsing and execution for the option command to the
  1120. !evlog extension. It is used to modify and display the cached settings.
  1121. Currently all cached option settings are used by the read command only.
  1122. Arguments:
  1123. Client - Pointer to IDebugClient passed to !evlog extension
  1124. [not used by this command]
  1125. args - Pointer to command line arguments passed to this command from
  1126. !evlog extension
  1127. Return Value:
  1128. E_INVALIDARG if invalid argument syntax detected
  1129. ERROR_BUFFER_OVERFLOW if argument length too long
  1130. GetLastError() converted to HRESULT otherwise
  1131. --*/
  1132. {
  1133. WORD wDataDisplayWidth = g_wDataDisplayWidth;
  1134. DWORD dwRecordOffset = g_cdwDefaultRecordOffset;
  1135. DWORD dwReadFlags = g_cdwDefaultReadFlags;
  1136. enum
  1137. {
  1138. MASK_IGNORE_RECORD_OFFSET=0x0000,
  1139. MASK_RESET_RECORD_OFFSET_DEFAULT=0x0001,
  1140. MASK_SET_MAX_RECORD_OFFSET=0x0002,
  1141. MASK_SET_RECORD_OFFSET=0x0004
  1142. } fMaskRecordOffset = MASK_IGNORE_RECORD_OFFSET;
  1143. CHAR szEventLog[MAX_PATH+1];
  1144. CHAR szParamValue[MAX_PATH];
  1145. const CHAR cszUsage[] = "Usage:\n"
  1146. " !evlog option [-d] [-!] [-n <count>] [[-l <eventlog>] -+ | "
  1147. "-r <record>]\n"
  1148. " [-o <order>] [-w <width>]\n\n"
  1149. "Sets and resets default search option parameters for read command."
  1150. "\n\n"
  1151. "A backwards search order implies that by default all searches start "
  1152. "from the\n"
  1153. "most recent record logged to the event log and the search continues "
  1154. "in\n"
  1155. "reverse chronological order as matching records are found.\n\n"
  1156. "Bounding record numbers for each event log allow searches to "
  1157. "terminate after\n"
  1158. "a known record number is encountered. This can be useful when you "
  1159. "want to\n"
  1160. "view all records logged after a certain event only.\n\n"
  1161. "Optional parameters:\n"
  1162. "-d : Display defaults\n"
  1163. "-! : Reset all defaults\n"
  1164. "<count> : Count of max N records to retrieve for any query "
  1165. "(default: 20)\n"
  1166. "<eventlog> : All (default), Application, System, Security\n"
  1167. "-+ : Set bounding record # to current max record #\n"
  1168. "<record> : Use as bounding record # in read queries (default: 0 = "
  1169. "ignore)\n"
  1170. "<order> : Search order Forwards, Backwards (default: Backwards)\n"
  1171. "<width> : Set data display width (in bytes). This is the width "
  1172. "that \"Data:\"\n"
  1173. " sections display. (default: 8, same as event log)\n";
  1174. const CHAR cszDefaultEventLog[] = "All";
  1175. INIT_API();
  1176. // Initialize defaults
  1177. ZeroMemory(szParamValue, sizeof(szParamValue));
  1178. CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
  1179. if (args)
  1180. {
  1181. SKIP_WSPACE(args);
  1182. }
  1183. if (!args || !args[0] ||
  1184. !strncmp(args, "-h", 2) ||
  1185. !strncmp(args, "-?", 2))
  1186. {
  1187. Status = E_INVALIDARG;
  1188. ExtErr(cszUsage);
  1189. goto Exit;
  1190. }
  1191. // Parse args
  1192. while (*args)
  1193. {
  1194. SKIP_WSPACE(args);
  1195. // Check for optional argument options
  1196. if (('-' == *args) || ('/' == *args))
  1197. {
  1198. CHAR ch = *(++args); // Get next char + advance arg ptr
  1199. ++args; // Skip one more char
  1200. CHAR *szEndOfValue = NULL; // Ptr to last char in value
  1201. size_t cchValue = 0; // Count of chars in value
  1202. SKIP_WSPACE(args); // Advance to start of param value
  1203. // Parameter value is delimited by next space in string, or,
  1204. // if quoted, by next quote
  1205. if ('"' == *args)
  1206. {
  1207. ++args;
  1208. szEndOfValue = strchr(args, '"');
  1209. }
  1210. else
  1211. {
  1212. szEndOfValue = strchr(args, ' ');
  1213. }
  1214. if (NULL == szEndOfValue)
  1215. {
  1216. // copy to end of line
  1217. CopyString(szParamValue, args, sizeof(szParamValue));
  1218. args += strlen(args);
  1219. }
  1220. else
  1221. {
  1222. cchValue = szEndOfValue - args;
  1223. if (cchValue < sizeof(szParamValue))
  1224. {
  1225. // copy next N chars
  1226. CopyString(szParamValue, args, cchValue+1);
  1227. args += cchValue;
  1228. }
  1229. else
  1230. {
  1231. Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  1232. ExtErr("ERROR: Argument string too long. Aborting.\n");
  1233. goto Exit;
  1234. }
  1235. if ('"' == *args) // skip past (theoretically) paired quote
  1236. {
  1237. ++args;
  1238. }
  1239. }
  1240. switch (ch)
  1241. {
  1242. case 'd': // Use defaults
  1243. ExtVerb("Using defaults...\n");
  1244. // do nothing
  1245. break;
  1246. case 'l': // Source (string)
  1247. ExtVerb("Setting Event Log...\n");
  1248. CopyString(szEventLog, szParamValue, sizeof(szEventLog));
  1249. break;
  1250. case 'n': // number of records to retrieve
  1251. ExtVerb("Setting Max Record Count...\n");
  1252. g_dwMaxRecords = strtoul(szParamValue, NULL, 10);
  1253. break;
  1254. case '!': // Use defaults
  1255. ExtVerb("Resetting Defaults...\n");
  1256. fMaskRecordOffset = MASK_RESET_RECORD_OFFSET_DEFAULT;
  1257. g_dwMaxRecords = g_cdwDefaultMaxRecords;
  1258. g_dwReadFlags = g_cdwDefaultReadFlags;
  1259. g_wDataDisplayWidth = g_cwMaxDataDisplayWidth;
  1260. break;
  1261. case '+': // Set to max record number for event source
  1262. ExtVerb(
  1263. "Setting Record Number to Max Record Number...\n");
  1264. fMaskRecordOffset = MASK_SET_MAX_RECORD_OFFSET;
  1265. break;
  1266. case 'r': // record offset for bounds
  1267. ExtVerb("Setting Record Number...\n");
  1268. if (!_strnicmp(szParamValue, "0x", 2))
  1269. {
  1270. dwRecordOffset = strtoul(szParamValue, NULL, 16);
  1271. }
  1272. else
  1273. {
  1274. dwRecordOffset = strtoul(szParamValue, NULL, 10);
  1275. }
  1276. dwReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ;
  1277. fMaskRecordOffset = MASK_SET_RECORD_OFFSET;
  1278. break;
  1279. case 'o': // Source (string)
  1280. ExtVerb("Setting Search Order...\n");
  1281. if (!_stricmp(szParamValue, "Forwards"))
  1282. {
  1283. g_dwReadFlags ^= BACKWARDS_READ;
  1284. g_dwReadFlags |= FORWARDS_READ;
  1285. }
  1286. else if (!_stricmp(szParamValue, "Backwards"))
  1287. {
  1288. g_dwReadFlags ^= FORWARDS_READ;
  1289. g_dwReadFlags |= BACKWARDS_READ;
  1290. }
  1291. else
  1292. {
  1293. ExtErr("Ignoring invalid search order option '%s'\n",
  1294. szParamValue);
  1295. }
  1296. break;
  1297. case 'w': // record offset for bounds
  1298. ExtVerb("Setting Data Display Width...\n");
  1299. if (!_strnicmp(szParamValue, "0x", 2))
  1300. {
  1301. wDataDisplayWidth = (WORD)strtoul(szParamValue,
  1302. NULL,
  1303. 16);
  1304. }
  1305. else
  1306. {
  1307. wDataDisplayWidth = (WORD)strtoul(szParamValue,
  1308. NULL,
  1309. 10);
  1310. }
  1311. if ((0 == wDataDisplayWidth) ||
  1312. (wDataDisplayWidth > g_cwMaxDataDisplayWidth))
  1313. {
  1314. Status = E_INVALIDARG;
  1315. ExtErr("ERROR: Data display width %u exceeds bounds "
  1316. "(1...%u)\n",
  1317. wDataDisplayWidth, g_cwMaxDataDisplayWidth);
  1318. goto Exit;
  1319. }
  1320. g_wDataDisplayWidth = wDataDisplayWidth;
  1321. break;
  1322. default:
  1323. Status = E_INVALIDARG;
  1324. ExtErr("Invalid arg '-%c' specified\n", *args);
  1325. ExtErr(cszUsage);
  1326. goto Exit;
  1327. break;
  1328. }
  1329. ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
  1330. }
  1331. else // Everything to end of line is message string
  1332. {
  1333. Status = E_INVALIDARG;
  1334. ExtErr("Invalid arg '%s' specified\n", args);
  1335. ExtErr(cszUsage);
  1336. goto Exit;
  1337. }
  1338. }
  1339. // Set any variables not already set here
  1340. if (!_stricmp(szEventLog, "Application"))
  1341. {
  1342. if (MASK_SET_RECORD_OFFSET & fMaskRecordOffset)
  1343. {
  1344. g_dwRecordOffsetAppEvt = dwRecordOffset;
  1345. }
  1346. else if (MASK_RESET_RECORD_OFFSET_DEFAULT & fMaskRecordOffset)
  1347. {
  1348. g_dwRecordOffsetAppEvt = g_cdwDefaultRecordOffset;
  1349. }
  1350. else if (MASK_SET_MAX_RECORD_OFFSET & fMaskRecordOffset)
  1351. {
  1352. GetEvLogNewestRecord("Application", &g_dwRecordOffsetAppEvt);
  1353. }
  1354. else
  1355. {
  1356. ; // error
  1357. }
  1358. }
  1359. else if (!_stricmp(szEventLog, "System"))
  1360. {
  1361. if (MASK_SET_RECORD_OFFSET & fMaskRecordOffset)
  1362. {
  1363. g_dwRecordOffsetSysEvt = dwRecordOffset;
  1364. }
  1365. else if (MASK_RESET_RECORD_OFFSET_DEFAULT & fMaskRecordOffset)
  1366. {
  1367. g_dwRecordOffsetSysEvt = g_cdwDefaultRecordOffset;
  1368. }
  1369. else if (MASK_SET_MAX_RECORD_OFFSET & fMaskRecordOffset)
  1370. {
  1371. GetEvLogNewestRecord("System", &g_dwRecordOffsetSysEvt);
  1372. }
  1373. else
  1374. {
  1375. ; // error
  1376. }
  1377. }
  1378. else if (!_stricmp(szEventLog, "Security"))
  1379. {
  1380. if (MASK_SET_RECORD_OFFSET & fMaskRecordOffset)
  1381. {
  1382. g_dwRecordOffsetSecEvt = dwRecordOffset;
  1383. }
  1384. else if (MASK_RESET_RECORD_OFFSET_DEFAULT & fMaskRecordOffset)
  1385. {
  1386. g_dwRecordOffsetSecEvt = g_cdwDefaultRecordOffset;
  1387. }
  1388. else if (MASK_SET_MAX_RECORD_OFFSET& fMaskRecordOffset)
  1389. {
  1390. GetEvLogNewestRecord("Security", &g_dwRecordOffsetSecEvt);
  1391. }
  1392. else
  1393. {
  1394. ; // error
  1395. }
  1396. }
  1397. else
  1398. {
  1399. if (MASK_SET_RECORD_OFFSET & fMaskRecordOffset)
  1400. {
  1401. g_dwRecordOffsetAppEvt = dwRecordOffset;
  1402. g_dwRecordOffsetSysEvt = dwRecordOffset;
  1403. g_dwRecordOffsetSecEvt = dwRecordOffset;
  1404. }
  1405. else if (MASK_RESET_RECORD_OFFSET_DEFAULT & fMaskRecordOffset)
  1406. {
  1407. g_dwRecordOffsetAppEvt = g_cdwDefaultRecordOffset;
  1408. g_dwRecordOffsetSysEvt = g_cdwDefaultRecordOffset;
  1409. g_dwRecordOffsetSecEvt = g_cdwDefaultRecordOffset;
  1410. }
  1411. else if (MASK_SET_MAX_RECORD_OFFSET & fMaskRecordOffset)
  1412. {
  1413. GetEvLogNewestRecord("Application", &g_dwRecordOffsetAppEvt);
  1414. GetEvLogNewestRecord("System", &g_dwRecordOffsetSysEvt);
  1415. GetEvLogNewestRecord("Security", &g_dwRecordOffsetSecEvt);
  1416. }
  1417. else
  1418. {
  1419. if (fMaskRecordOffset)
  1420. {
  1421. Status = E_INVALIDARG;
  1422. ExtErr("ERROR: Must specify -!, -+, or -r option\n");
  1423. goto Exit;
  1424. }
  1425. }
  1426. }
  1427. // Display defaults here
  1428. PrintEvLogOptionSettings();
  1429. Status = S_OK;
  1430. Exit:
  1431. EXIT_API();
  1432. return Status;
  1433. }
  1434. HRESULT
  1435. EvLogClear ( PDEBUG_CLIENT Client, PCSTR args )
  1436. /*++
  1437. Routine Description:
  1438. This function handles parsing and execution for the clear command to the
  1439. !evlog extension. It is used to clear and, optionally, backup an event
  1440. log to a file.
  1441. Arguments:
  1442. Client - Pointer to IDebugClient passed to !evlog extension
  1443. [not used by this command]
  1444. args - Pointer to command line arguments passed to this command from
  1445. !evlog extension
  1446. Return Value:
  1447. E_INVALIDARG if invalid argument syntax detected
  1448. ERROR_BUFFER_OVERFLOW if argument length too long
  1449. GetLastError() converted to HRESULT otherwise
  1450. --*/
  1451. {
  1452. HANDLE hEventLog = NULL;
  1453. BOOL fIgnoreBackup = FALSE;
  1454. DWORD dwDirLen = 0;
  1455. CHAR szParamValue[MAX_PATH];
  1456. CHAR szEventLog[MAX_PATH+1];
  1457. CHAR szBackupFileName[MAX_PATH+1];
  1458. PCHAR pszBackupFileName = NULL;
  1459. const CHAR cszUsage[] = "Usage:\n"
  1460. " !evlog clear [-!] [-d] [-l <eventlog>] [-f <filename>]\n\n"
  1461. "Clears and creates backup of specified event log.\n\n"
  1462. "Optional parameters:\n"
  1463. "-! : Ignore backup\n"
  1464. "-d : Use defaults\n"
  1465. "<eventlog> : Application (default), System, Security\n"
  1466. "<filename> : (default: %%cwd%%\\<eventlog>_backup.evt)\n";
  1467. const CHAR cszDefaultEventLog[] = "Application";
  1468. const CHAR cszDefaultFileNameAppend[] = "_backup.evt";
  1469. INIT_API();
  1470. // Initialize default
  1471. ZeroMemory(szParamValue, sizeof(szParamValue));
  1472. CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
  1473. // Create default backup filename: %cwd%\Application_backup.evt
  1474. dwDirLen = GetCurrentDirectory(sizeof(szEventLog)/sizeof(TCHAR),
  1475. szEventLog); // temp use of szEventLog
  1476. if (0 == dwDirLen)
  1477. {
  1478. ExtErr("ERROR: Current directory length too long. Using '.' for "
  1479. "directory\n");
  1480. CopyString(szEventLog, ".", sizeof(szEventLog));
  1481. }
  1482. PrintString(szBackupFileName,
  1483. sizeof(szBackupFileName),
  1484. "%s\\%s%s",
  1485. szEventLog,
  1486. cszDefaultEventLog,
  1487. cszDefaultFileNameAppend);
  1488. ZeroMemory(szEventLog, sizeof(szEventLog));
  1489. CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
  1490. if (args)
  1491. {
  1492. SKIP_WSPACE(args);
  1493. }
  1494. if (!args || !args[0] ||
  1495. !strncmp(args, "-h", 2) ||
  1496. !strncmp(args, "-?", 2))
  1497. {
  1498. Status = E_INVALIDARG;
  1499. ExtErr(cszUsage);
  1500. goto Exit;
  1501. }
  1502. // Parse args
  1503. while (*args)
  1504. {
  1505. SKIP_WSPACE(args);
  1506. // Check for optional argument options to appear first
  1507. if (('-' == *args) || ('/' == *args))
  1508. {
  1509. CHAR ch = *(++args); // Get next char + advance arg ptr
  1510. ++args; // Skip one more char
  1511. CHAR *szEndOfValue = NULL; // Ptr to last char in value
  1512. size_t cchValue = 0; // Count of chars in value
  1513. SKIP_WSPACE(args); // Advance to start of param value
  1514. if (('-' != *args) && ('/' != *args))
  1515. {
  1516. // Parameter value is delimited by next space in string, or,
  1517. // if quoted, by next quote
  1518. if ('"' == *args)
  1519. {
  1520. ++args;
  1521. szEndOfValue = strchr(args, '"');
  1522. }
  1523. else
  1524. {
  1525. szEndOfValue = strchr(args, ' ');
  1526. }
  1527. if (NULL == szEndOfValue)
  1528. {
  1529. // copy to end of line
  1530. CopyString(szParamValue, args, sizeof(szParamValue));
  1531. args += strlen(args);
  1532. }
  1533. else
  1534. {
  1535. cchValue = szEndOfValue - args;
  1536. if (cchValue < sizeof(szParamValue))
  1537. {
  1538. // copy next N chars
  1539. CopyString(szParamValue, args, cchValue+1);
  1540. args += cchValue;
  1541. }
  1542. else
  1543. {
  1544. Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  1545. ExtErr("ERROR: Argument string too long. Aborting.\n");
  1546. goto Exit;
  1547. }
  1548. // skip past (theoretically) paired quote
  1549. if ('"' == *args)
  1550. {
  1551. ++args;
  1552. }
  1553. }
  1554. }
  1555. switch (ch)
  1556. {
  1557. case '!': // Use defaults
  1558. ExtVerb("Ignoring default backup procedure...\n");
  1559. fIgnoreBackup = TRUE;
  1560. break;
  1561. case 'd': // Use defaults
  1562. ExtVerb("Using defaults...\n");
  1563. // do nothing
  1564. break;
  1565. case 'l': // Source (string)
  1566. ExtVerb("Setting Event Log...\n");
  1567. CopyString(szEventLog, szParamValue, sizeof(szEventLog));
  1568. break;
  1569. case 'f': // Message File
  1570. ExtVerb("Setting Backup File Name...\n");
  1571. CopyString(szBackupFileName,
  1572. szParamValue,
  1573. sizeof(szBackupFileName));
  1574. break;
  1575. default:
  1576. Status = E_INVALIDARG;
  1577. ExtErr("Invalid arg '-%c' specified\n", *args);
  1578. ExtErr(cszUsage);
  1579. goto Exit;
  1580. break;
  1581. }
  1582. ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
  1583. }
  1584. else // Everything to end of line is message string
  1585. {
  1586. Status = E_INVALIDARG;
  1587. ExtErr("Invalid arg '%s' specified\n", args);
  1588. ExtErr(cszUsage);
  1589. goto Exit;
  1590. }
  1591. }
  1592. // Get a handle to the event log
  1593. ExtVerb("Opening event log '%s'...", szEventLog);
  1594. hEventLog = OpenEventLog(
  1595. NULL, // uses local computer
  1596. szEventLog); // source name
  1597. if (NULL == hEventLog)
  1598. {
  1599. Status = HRESULT_FROM_WIN32(GetLastError());
  1600. ExtErr("Unable to open '%s' event log, 0x%08X\n", szEventLog, Status);
  1601. goto Exit;
  1602. }
  1603. if (fIgnoreBackup)
  1604. {
  1605. pszBackupFileName = NULL;
  1606. }
  1607. else
  1608. {
  1609. pszBackupFileName = szBackupFileName;
  1610. }
  1611. // Clear event log
  1612. ExtOut("Clearing '%s' event log...\n", szEventLog);
  1613. if (!ClearEventLog(
  1614. hEventLog, // handle to event log
  1615. pszBackupFileName)) // name of backup file
  1616. {
  1617. Status = HRESULT_FROM_WIN32(GetLastError());
  1618. ExtErr("Unable to clear event log and backup to '%s', 0x%08X\n",
  1619. szBackupFileName,
  1620. Status);
  1621. goto Exit;
  1622. }
  1623. ExtOut("Event log successfully backed up to '%s'\n", szBackupFileName);
  1624. Status = S_OK;
  1625. Exit:
  1626. if (hEventLog)
  1627. {
  1628. CloseEventLog(hEventLog);
  1629. }
  1630. EXIT_API();
  1631. return Status;
  1632. }
  1633. HRESULT
  1634. EvLogInfo ( PDEBUG_CLIENT Client, PCSTR args )
  1635. /*++
  1636. Routine Description:
  1637. This function handles parsing and execution for the info command to the
  1638. !evlog extension. It is used to display summary information about all 3
  1639. standard event logs: Application, System, and Security.
  1640. A user without rights to see the System or Security event log will
  1641. probably get an error.
  1642. Arguments:
  1643. Client - Pointer to IDebugClient passed to !evlog extension
  1644. [not used by this command]
  1645. args - Pointer to command line arguments passed to this command from
  1646. !evlog extension
  1647. Return Value:
  1648. S_OK
  1649. --*/
  1650. {
  1651. INIT_API();
  1652. PrintEvLogSummary("Application");
  1653. PrintEvLogSummary("System");
  1654. PrintEvLogSummary("Security");
  1655. ExtOut("--------------------------------\n");
  1656. EXIT_API();
  1657. return S_OK;
  1658. }
  1659. HRESULT
  1660. EvLogRead ( PDEBUG_CLIENT Client, PCSTR args )
  1661. /*++
  1662. Routine Description:
  1663. This function handles parsing and execution for the read command to the
  1664. !evlog extension. It is used to display event records from any event
  1665. log.
  1666. Some search parameters can be set to filter the list of events displayed.
  1667. Also, the !evlog option command can be used to set some default
  1668. parameters.
  1669. A user without rights to see the System or Security event log will
  1670. probably get an error.
  1671. There are a few common scenarios where this extension command might be
  1672. used:
  1673. 1) To identify when the last event was logged (may have been a few
  1674. minutes ago or days...)
  1675. 2) To search for recent occurrences of specific known "interesting"
  1676. events
  1677. 3) To monitor events logged as a result of (or side effect of) stepping
  1678. over an instruction
  1679. Arguments:
  1680. Client - Pointer to IDebugClient passed to !evlog extension
  1681. [not used by this command]
  1682. args - Pointer to command line arguments passed to this command from
  1683. !evlog extension
  1684. Return Value:
  1685. E_INVALIDARG if invalid argument syntax detected
  1686. ERROR_BUFFER_OVERFLOW if argument length too long
  1687. GetLastError() converted to HRESULT otherwise
  1688. --*/
  1689. {
  1690. HANDLE hEventLog = NULL;
  1691. EVENTLOGRECORD *pevlr = NULL;
  1692. BYTE *pbBuffer = NULL;
  1693. LPCSTR cszMessage = NULL;
  1694. DWORD dwEventID = 0; // default (normally event id is non-zero)
  1695. WORD wEventCategory = 0; // default (normally categories start at 1)
  1696. WORD wEventType = 0; // default
  1697. const DWORD cdwDefaultBufSize = 4096; // default
  1698. DWORD dwBufSize = cdwDefaultBufSize;
  1699. DWORD dwBytesRead = 0;
  1700. DWORD dwBytesNeeded = 0;
  1701. DWORD dwReadFlags = g_dwReadFlags | EVENTLOG_SEQUENTIAL_READ;
  1702. DWORD dwRecordOffset = 0; // 0 = ignored for sequential reads
  1703. DWORD dwNumRecords = 0; // Count of records matched/found
  1704. DWORD dwTotalRecords = 0; // Count of records enumerated (for debugging)
  1705. DWORD dwMaxRecords = g_dwMaxRecords;
  1706. DWORD dwBoundingEventRecord = 0;
  1707. DWORD dwLastErr = 0;
  1708. BOOL fSuccess = FALSE;
  1709. BOOL fMatchSource = FALSE;
  1710. BOOL fMatchEventID = FALSE;
  1711. BOOL fMatchEventCategory = FALSE;
  1712. BOOL fMatchEventType = FALSE;
  1713. CHAR szEventLog[MAX_PATH+1];
  1714. CHAR szSource[MAX_PATH+1];
  1715. CHAR szParamValue[MAX_PATH];
  1716. // Note: this could get really fancy, searching on description, date
  1717. // ranges, event id ranges, data, etc. Keep it simple to just display
  1718. // last few events logged.
  1719. const CHAR cszUsage[] = "Usage:\n"
  1720. " !evlog read [-d] [-l <eventlog>] [-s <source>] "
  1721. "[-e <id>] [-c <category>]\n"
  1722. " [-t <type>] [-n <count>] [-r <record>]\n\n"
  1723. "Displays last N events logged to the specified event log, in "
  1724. "reverse\n"
  1725. "chronological order by default. If -n option is not specified, a "
  1726. "default max\n"
  1727. "of 20 records is enforced.\n\n"
  1728. "However, if -r is specified, only the specific event record will "
  1729. "be\n"
  1730. "displayed unless the -n option is also specified.\n\n"
  1731. "!evlog option can be used to override some defaults, including the "
  1732. "search\n"
  1733. "order of backwards. See !evlog option -d for default settings.\n\n"
  1734. "Optional parameters:\n"
  1735. "-d : Use defaults\n"
  1736. "<eventlog> : Application (default), System, Security\n"
  1737. "<source> : DebuggerExtensions (default: none)\n"
  1738. "<id> : 0, 1000, 2000, 3000, 4000, etc... (default: 0)\n"
  1739. "<category> : None (default: 0), Devices (1), Disk (2), Printers "
  1740. "(3),\n"
  1741. " Services (4), Shell (5), System_Event (6), Network "
  1742. "(7)\n"
  1743. "<type> : Success (default: 0), Error (1), Warning (2), "
  1744. "Information (4),\n"
  1745. " Audit_Success (8), or Audit_Failure (16)\n"
  1746. "<count> : Count of last N event records to retrieve (default: "
  1747. "1)\n"
  1748. "<record> : Specific record # to retrieve\n";
  1749. const CHAR cszDefaultEventLog[] = "Application";
  1750. const CHAR cszDefaultSource[] = "DebuggerExtensions";
  1751. INIT_API();
  1752. // Initialize defaults
  1753. ZeroMemory(szParamValue, sizeof(szParamValue));
  1754. CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
  1755. if (args)
  1756. {
  1757. SKIP_WSPACE(args);
  1758. }
  1759. if (!args || !args[0] ||
  1760. !strncmp(args, "-h", 2) ||
  1761. !strncmp(args, "-?", 2))
  1762. {
  1763. Status = E_INVALIDARG;
  1764. ExtErr(cszUsage);
  1765. goto Exit;
  1766. }
  1767. // Parse args
  1768. while (*args)
  1769. {
  1770. SKIP_WSPACE(args);
  1771. // Check for optional argument options
  1772. if (('-' == *args) || ('/' == *args))
  1773. {
  1774. CHAR ch = *(++args); // Get next char + advance arg ptr
  1775. ++args; // Skip one more char
  1776. CHAR *szEndOfValue = NULL; // Ptr to last char in value
  1777. size_t cchValue = 0; // Count of chars in value
  1778. SKIP_WSPACE(args); // Advance to start of param value
  1779. // Parameter value is delimited by next space in string, or,
  1780. // if quoted, by next quote
  1781. if ('"' == *args)
  1782. {
  1783. ++args;
  1784. szEndOfValue = strchr(args, '"');
  1785. }
  1786. else
  1787. {
  1788. szEndOfValue = strchr(args, ' ');
  1789. }
  1790. if (NULL == szEndOfValue)
  1791. {
  1792. // copy to end of line
  1793. CopyString(szParamValue, args, sizeof(szParamValue));
  1794. args += strlen(args);
  1795. }
  1796. else
  1797. {
  1798. cchValue = szEndOfValue - args;
  1799. if (cchValue < sizeof(szParamValue))
  1800. {
  1801. // copy next N chars
  1802. CopyString(szParamValue, args, cchValue+1);
  1803. args += cchValue;
  1804. }
  1805. else
  1806. {
  1807. Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  1808. ExtErr("ERROR: Argument string too long. Aborting.\n");
  1809. goto Exit;
  1810. }
  1811. // skip past (theoretically) paired quote
  1812. if ('"' == *args)
  1813. {
  1814. ++args;
  1815. }
  1816. }
  1817. switch (ch)
  1818. {
  1819. case 'd': // Use defaults
  1820. ExtVerb("Using defaults...\n");
  1821. // do nothing
  1822. break;
  1823. case 'l': // Source (string)
  1824. ExtVerb("Setting Event Log...\n");
  1825. CopyString(szEventLog, szParamValue, sizeof(szEventLog));
  1826. break;
  1827. case 's': // Source (string)
  1828. ExtVerb("Setting Source...\n");
  1829. CopyString(szSource, szParamValue, sizeof(szSource));
  1830. fMatchSource = TRUE;
  1831. break;
  1832. case 'e': // Event ID (number or string)
  1833. ExtVerb("Setting Event ID...\n");
  1834. //
  1835. // Some events only display the low WORD, but the high
  1836. // WORD actually contains a status code like 8000 or
  1837. // C000.
  1838. // So allow for hex input as well as decimal...
  1839. //
  1840. if (!_strnicmp(szParamValue, "0x", 2))
  1841. {
  1842. dwEventID = strtoul(szParamValue, NULL, 16);
  1843. }
  1844. else
  1845. {
  1846. dwEventID = strtoul(szParamValue, NULL, 10);
  1847. }
  1848. fMatchEventID = TRUE;
  1849. break;
  1850. case 'c': // Event Category (number or string)
  1851. ExtVerb("Setting Category...\n");
  1852. if (!_strnicmp(szParamValue, "None", 4))
  1853. {
  1854. wEventCategory = 0;
  1855. }
  1856. else if (!_strnicmp(szParamValue, "Devices", 7))
  1857. {
  1858. wEventCategory = 1;
  1859. }
  1860. else if (!_strnicmp(szParamValue, "Disk", 4))
  1861. {
  1862. wEventCategory = 2;
  1863. }
  1864. else if (!_strnicmp(szParamValue, "Printers", 8))
  1865. {
  1866. wEventCategory = 3;
  1867. }
  1868. else if (!_strnicmp(szParamValue, "Services", 8))
  1869. {
  1870. wEventCategory = 4;
  1871. }
  1872. else if (!_strnicmp(szParamValue, "Shell", 5))
  1873. {
  1874. wEventCategory = 5;
  1875. }
  1876. else if (!_strnicmp(szParamValue, "System_Event", 12))
  1877. {
  1878. wEventCategory = 6;
  1879. }
  1880. else if (!_strnicmp(szParamValue, "Network", 7))
  1881. {
  1882. wEventCategory = 7;
  1883. }
  1884. else
  1885. {
  1886. wEventCategory = (WORD)strtoul(szParamValue,
  1887. NULL, 10);
  1888. }
  1889. fMatchEventCategory = TRUE;
  1890. break;
  1891. case 't': // Event Type (number or string)
  1892. ExtVerb("Setting Event Type...\n");
  1893. if (!_strnicmp(szParamValue, "Success", 7))
  1894. {
  1895. wEventType = EVENTLOG_SUCCESS;
  1896. }
  1897. else if (!_strnicmp(szParamValue, "Error", 5))
  1898. {
  1899. wEventType = EVENTLOG_ERROR_TYPE;
  1900. }
  1901. else if (!_strnicmp(szParamValue, "Warning", 7))
  1902. {
  1903. wEventType = EVENTLOG_WARNING_TYPE;
  1904. }
  1905. else if (!_strnicmp(szParamValue, "Information", 11))
  1906. {
  1907. wEventType = EVENTLOG_INFORMATION_TYPE;
  1908. }
  1909. else if (!_strnicmp(szParamValue, "Audit_Success", 13))
  1910. {
  1911. wEventType = EVENTLOG_AUDIT_SUCCESS;
  1912. }
  1913. else if (!_strnicmp(szParamValue, "Audit_Failure", 13))
  1914. {
  1915. wEventType = EVENTLOG_AUDIT_FAILURE;
  1916. }
  1917. else
  1918. {
  1919. wEventType = (WORD)strtoul(szParamValue, NULL, 10);
  1920. }
  1921. fMatchEventType = TRUE;
  1922. break;
  1923. case 'n': // number of records to retrieve
  1924. ExtVerb("Setting Max Record Count...\n");
  1925. dwMaxRecords = strtoul(szParamValue, NULL, 10);
  1926. break;
  1927. case 'r': // record offset + switch flags to do seek
  1928. ExtVerb("Setting Record Number...\n");
  1929. dwRecordOffset = strtoul(szParamValue, NULL, 10);
  1930. dwReadFlags ^= EVENTLOG_SEQUENTIAL_READ; // disable
  1931. dwReadFlags |= EVENTLOG_SEEK_READ; // enable
  1932. dwMaxRecords = 1;
  1933. break;
  1934. default:
  1935. Status = E_INVALIDARG;
  1936. ExtErr("Invalid arg '-%c' specified\n", *args);
  1937. ExtErr(cszUsage);
  1938. goto Exit;
  1939. break;
  1940. }
  1941. ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
  1942. }
  1943. else // Everything to end of line is message string
  1944. {
  1945. SKIP_WSPACE(args);
  1946. cszMessage = args;
  1947. args += strlen(args);
  1948. }
  1949. }
  1950. // Get a handle to the event log
  1951. ExtVerb("Opening event log '%s'...", szEventLog);
  1952. hEventLog = OpenEventLog(NULL, szEventLog);
  1953. if (!hEventLog)
  1954. {
  1955. Status = HRESULT_FROM_WIN32(GetLastError());
  1956. ExtErr("Unable to open event log, 0x%08X\n", Status);
  1957. goto Exit;
  1958. }
  1959. // If this record number is hit during read, it will
  1960. // not be displayed and reads will halt immediately
  1961. if (!strcmp(szEventLog, "System"))
  1962. {
  1963. dwBoundingEventRecord = g_dwRecordOffsetSysEvt;
  1964. }
  1965. else if (!strcmp(szEventLog, "Security"))
  1966. {
  1967. dwBoundingEventRecord = g_dwRecordOffsetSecEvt;
  1968. }
  1969. else // szEventLog == "Application" or default
  1970. {
  1971. dwBoundingEventRecord = g_dwRecordOffsetAppEvt;
  1972. }
  1973. ExtVerb("Using Bounding Event Record %u...\n", dwBoundingEventRecord);
  1974. do
  1975. {
  1976. // Allocate buffer if unallocated
  1977. if (NULL == pbBuffer)
  1978. {
  1979. pbBuffer = (BYTE *)calloc(dwBufSize, sizeof(BYTE));
  1980. if (NULL == pbBuffer)
  1981. {
  1982. Status = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
  1983. ExtErr("Unable to allocate buffer, 0x%08X\n", Status);
  1984. goto Exit;
  1985. }
  1986. }
  1987. // Reposition to beginning of buffer for next read
  1988. pevlr = (EVENTLOGRECORD *) pbBuffer;
  1989. // Read next N event records that fit into dwBufSize
  1990. fSuccess = ReadEventLog(
  1991. hEventLog, // handle to event log
  1992. dwReadFlags, // how to read log
  1993. dwRecordOffset, // initial record offset
  1994. pevlr, // buffer for read data
  1995. dwBufSize, // bytes to read
  1996. &dwBytesRead, // number of bytes read
  1997. &dwBytesNeeded); // bytes required
  1998. if (!fSuccess)
  1999. {
  2000. dwLastErr = GetLastError();
  2001. if (ERROR_INSUFFICIENT_BUFFER == dwLastErr)
  2002. {
  2003. ExtVerb("Increasing buffer from %u to %u bytes\n",
  2004. dwBufSize, dwBufSize+cdwDefaultBufSize);
  2005. // Retry ReadEventLog with larger buffer next time
  2006. dwBufSize += cdwDefaultBufSize;
  2007. free(pbBuffer);
  2008. pbBuffer = NULL; // allow reallocation above
  2009. // TODO: Really should put upper limit on buffer size
  2010. continue; // retry ReadEventLog
  2011. }
  2012. else if (ERROR_HANDLE_EOF == dwLastErr)
  2013. {
  2014. Status = S_OK;
  2015. goto Exit;
  2016. }
  2017. else
  2018. {
  2019. ExtErr("Error reading event log, %u\n", dwLastErr);
  2020. Status = HRESULT_FROM_WIN32(dwLastErr);
  2021. goto Exit;
  2022. }
  2023. }
  2024. // Go through all records returned from last successful read
  2025. // Display only the ones that match the criteria
  2026. do
  2027. {
  2028. if (dwBoundingEventRecord == pevlr->RecordNumber)
  2029. {
  2030. ExtWarn("Bounding record #%u reached. Terminating search.\n",
  2031. dwBoundingEventRecord);
  2032. ExtWarn("Use !evlog option -d to view defaults.\n");
  2033. goto Exit;
  2034. }
  2035. BOOL fDisplayRecord = TRUE;
  2036. dwTotalRecords++;
  2037. // Ignore this record if it does not match criteria
  2038. if (fMatchSource &&
  2039. _stricmp(szSource,
  2040. (CHAR *)(BYTE *)pevlr + sizeof(EVENTLOGRECORD)))
  2041. {
  2042. fDisplayRecord = FALSE;
  2043. }
  2044. if (fMatchEventID && (dwEventID != pevlr->EventID))
  2045. {
  2046. fDisplayRecord = FALSE;
  2047. }
  2048. if (fMatchEventCategory &&
  2049. (wEventCategory != pevlr->EventCategory))
  2050. {
  2051. fDisplayRecord = FALSE;
  2052. }
  2053. if (fMatchEventType && (wEventType != pevlr->EventType))
  2054. {
  2055. fDisplayRecord = FALSE;
  2056. }
  2057. if (fDisplayRecord)
  2058. {
  2059. dwNumRecords++;
  2060. if (dwNumRecords > dwMaxRecords)
  2061. {
  2062. ExtWarn("WARNING: Max record count (%u) exceeded, "
  2063. "increase record count to view more\n",
  2064. dwMaxRecords);
  2065. goto Exit;
  2066. }
  2067. ExtOut("-------------- %02u --------------\n", dwNumRecords);
  2068. PrintEvLogEvent(szEventLog, pevlr);
  2069. }
  2070. if (dwTotalRecords % 20)
  2071. {
  2072. ExtVerb("dwTotalRecords = %u, "
  2073. "Current Record # = %u, "
  2074. "dwRecordOffset = %u\n",
  2075. dwTotalRecords,
  2076. pevlr->RecordNumber,
  2077. dwRecordOffset);
  2078. }
  2079. if (CheckControlC())
  2080. {
  2081. ExtOut("Terminated w/ctrl-C...\n");
  2082. goto Exit;
  2083. }
  2084. if (dwReadFlags & (EVENTLOG_SEEK_READ |
  2085. EVENTLOG_FORWARDS_READ))
  2086. {
  2087. // Start next read at new "forward" seek position
  2088. dwRecordOffset = pevlr->RecordNumber + 1;
  2089. }
  2090. else if (dwReadFlags &
  2091. (EVENTLOG_SEEK_READ | EVENTLOG_BACKWARDS_READ))
  2092. {
  2093. // Start next read at new "backwards" seek position
  2094. dwRecordOffset = pevlr->RecordNumber - 1;
  2095. }
  2096. dwBytesRead -= pevlr->Length;
  2097. pevlr = (EVENTLOGRECORD *) ((BYTE *)pevlr + pevlr->Length);
  2098. } while (dwBytesRead > 0);
  2099. } while (TRUE);
  2100. Status = S_OK;
  2101. Exit:
  2102. if ((0 == dwNumRecords) && (S_OK == Status))
  2103. {
  2104. ExtOut("No matching event records found.\n");
  2105. }
  2106. if (hEventLog)
  2107. {
  2108. CloseEventLog(hEventLog);
  2109. }
  2110. free(pbBuffer);
  2111. pbBuffer = NULL;
  2112. EXIT_API();
  2113. return Status;
  2114. }
  2115. HRESULT
  2116. EvLogReport ( PDEBUG_CLIENT Client, PCSTR args )
  2117. /*++
  2118. Routine Description:
  2119. This function handles parsing and execution for the report command to the
  2120. !evlog extension. It is used to log events to the Application event log
  2121. ONLY.
  2122. To make the events view nicely in the event viewer, there must be a
  2123. registered event source. The !evlog addsource command can be used to
  2124. register the uext.dll as a an event message file for any event source.
  2125. Since this feature is implemented under the guise of a debugger extension,
  2126. the default source name is "DebuggerExtensions".
  2127. There are a few reasons why this extension command might be handy:
  2128. 1) A developer can log a note to recall later
  2129. 2) A large lab with many machines running cdb/ntsd/windbg user mode
  2130. debuggers can set a command to log an event when the machine breaks
  2131. into the debugger. The event might then be monitored by a central
  2132. console which might, in turn, send an e-mail notification or page
  2133. someone immediately regarding the break.
  2134. Arguments:
  2135. Client - Pointer to IDebugClient passed to !evlog extension
  2136. [not used by this command]
  2137. args - Pointer to command line arguments passed to this command from
  2138. !evlog extension
  2139. Return Value:
  2140. E_INVALIDARG if invalid argument syntax detected
  2141. ERROR_BUFFER_OVERFLOW if argument length too long
  2142. GetLastError() converted to HRESULT otherwise
  2143. --*/
  2144. {
  2145. HANDLE hEventSource = NULL;
  2146. LPCSTR cszMessage = NULL;
  2147. WORD wEventCategory = 0; // default (normally categories start at 1)
  2148. WORD wEventType = 0; // default
  2149. DWORD dwEventID = 0; // default (normally event id is non-zero)
  2150. CHAR szParamValue[MAX_PATH];
  2151. CHAR szSource[MAX_PATH+1];
  2152. const CHAR cszUsage[] = "Usage:\n"
  2153. " !evlog report [-s <source>] "
  2154. "[-e <id>] [-c <category>] [-t <type>] <message>\n\n"
  2155. "Logs an event to the application event log.\n\n"
  2156. "Use !evlog addsource to configure an event source in the registry."
  2157. "Once\n"
  2158. "configured, the following Event IDs will be recognized by the event "
  2159. "viewer:\n\n"
  2160. " 0 Displays raw message in event description field\n"
  2161. " 1000 Prefixes description with \"Information:\"\n"
  2162. " 2000 Prefixes description with \"Success:\"\n"
  2163. " 3000 Prefixes description with \"Warning:\"\n"
  2164. " 4000 Prefixes description with \"Error:\"\n\n"
  2165. "Optional parameters:\n"
  2166. "<source> : (default: DebuggerExtensions)\n"
  2167. "<id> : 0, 1000, 2000, 3000, 4000, etc... (default: 0)\n"
  2168. "<category> : None (default: 0), Devices (1), Disk (2), Printers "
  2169. "(3),\n"
  2170. " Services (4), Shell (5), System_Event (6), Network "
  2171. "(7)\n"
  2172. "<type> : Success (default: 0), Error (1), Warning (2), "
  2173. "Information (4),\n"
  2174. " Audit_Success (8), or Audit_Failure (16)\n"
  2175. "<message> : Text message to add to description\n";
  2176. const CHAR cszDefaultSource[] = "DebuggerExtensions";
  2177. INIT_API();
  2178. // Initialize defaults
  2179. ZeroMemory(szParamValue, sizeof(szParamValue));
  2180. CopyString(szSource, cszDefaultSource, sizeof(szSource));
  2181. if (args)
  2182. {
  2183. SKIP_WSPACE(args);
  2184. }
  2185. if (!args || !args[0] ||
  2186. !strncmp(args, "-h", 2) ||
  2187. !strncmp(args, "-?", 2))
  2188. {
  2189. Status = E_INVALIDARG;
  2190. ExtErr(cszUsage);
  2191. goto Exit;
  2192. }
  2193. // Parse args
  2194. while (*args)
  2195. {
  2196. SKIP_WSPACE(args);
  2197. // Check for optional argument options to appear first
  2198. if (('-' == *args) || ('/' == *args))
  2199. {
  2200. CHAR ch = *(++args); // Get next char + advance arg ptr
  2201. ++args; // Skip one more char
  2202. CHAR *szEndOfValue = NULL; // Ptr to last char in value
  2203. size_t cchValue = 0; // Count of chars in value
  2204. SKIP_WSPACE(args); // Advance to start of param value
  2205. // Parameter value is delimited by next space in string
  2206. szEndOfValue = strchr(args, ' ');
  2207. if (NULL == szEndOfValue)
  2208. {
  2209. // copy to end of line
  2210. CopyString(szParamValue, args, sizeof(szParamValue));
  2211. args += strlen(args);
  2212. }
  2213. else
  2214. {
  2215. cchValue = szEndOfValue - args;
  2216. if (cchValue < sizeof(szParamValue))
  2217. {
  2218. // copy next N chars
  2219. CopyString(szParamValue, args, cchValue+1);
  2220. args += cchValue;
  2221. }
  2222. else
  2223. {
  2224. Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  2225. ExtErr("ERROR: Argument string too long. Aborting.\n");
  2226. goto Exit;
  2227. }
  2228. }
  2229. switch (ch)
  2230. {
  2231. case 's': // Source (string)
  2232. ExtVerb("Setting Source...\n");
  2233. CopyString(szSource, szParamValue, sizeof(szSource));
  2234. break;
  2235. case 'e': // Event ID (number or string)
  2236. ExtVerb("Setting Event ID...\n");
  2237. if (!_strnicmp(szParamValue, "0x", 2))
  2238. {
  2239. dwEventID = strtoul(szParamValue, NULL, 16);
  2240. }
  2241. else
  2242. {
  2243. dwEventID = strtoul(szParamValue, NULL, 10);
  2244. }
  2245. break;
  2246. case 'c': // Event Category (number or string)
  2247. ExtVerb("Setting Category...\n");
  2248. if (!_strnicmp(szParamValue, "None", 4))
  2249. {
  2250. wEventCategory = 0;
  2251. }
  2252. else if (!_strnicmp(szParamValue, "Devices", 7))
  2253. {
  2254. wEventCategory = 1;
  2255. }
  2256. else if (!_strnicmp(szParamValue, "Disk", 4))
  2257. {
  2258. wEventCategory = 2;
  2259. }
  2260. else if (!_strnicmp(szParamValue, "Printers", 8))
  2261. {
  2262. wEventCategory = 3;
  2263. }
  2264. else if (!_strnicmp(szParamValue, "Services", 8))
  2265. {
  2266. wEventCategory = 4;
  2267. }
  2268. else if (!_strnicmp(szParamValue, "Shell", 5))
  2269. {
  2270. wEventCategory = 5;
  2271. }
  2272. else if (!_strnicmp(szParamValue, "System_Event", 12))
  2273. {
  2274. wEventCategory = 6;
  2275. }
  2276. else if (!_strnicmp(szParamValue, "Network", 7))
  2277. {
  2278. wEventCategory = 7;
  2279. }
  2280. else
  2281. {
  2282. wEventCategory = (WORD)strtoul(szParamValue,
  2283. NULL, 10);
  2284. }
  2285. break;
  2286. case 't': // Event Type (number or string)
  2287. ExtVerb("Setting Event Type...\n");
  2288. if (!_strnicmp(szParamValue, "Success", 7))
  2289. {
  2290. wEventType = EVENTLOG_SUCCESS;
  2291. }
  2292. else if (!_strnicmp(szParamValue, "Error", 5))
  2293. {
  2294. wEventType = EVENTLOG_ERROR_TYPE;
  2295. }
  2296. else if (!_strnicmp(szParamValue, "Warning", 7))
  2297. {
  2298. wEventType = EVENTLOG_WARNING_TYPE;
  2299. }
  2300. else if (!_strnicmp(szParamValue, "Information", 11))
  2301. {
  2302. wEventType = EVENTLOG_INFORMATION_TYPE;
  2303. }
  2304. else if (!_strnicmp(szParamValue, "Audit_Success", 13))
  2305. {
  2306. wEventType = EVENTLOG_AUDIT_SUCCESS;
  2307. }
  2308. else if (!_strnicmp(szParamValue, "Audit_Failure", 13))
  2309. {
  2310. wEventType = EVENTLOG_AUDIT_FAILURE;
  2311. }
  2312. else
  2313. {
  2314. wEventType = (WORD)strtoul(szParamValue, NULL, 10);
  2315. }
  2316. break;
  2317. default:
  2318. Status = E_INVALIDARG;
  2319. ExtErr("Invalid arg '-%c' specified\n", *args);
  2320. ExtErr(cszUsage);
  2321. goto Exit;
  2322. break;
  2323. }
  2324. ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
  2325. }
  2326. else // Everything to end of line is message string
  2327. {
  2328. SKIP_WSPACE(args);
  2329. cszMessage = args;
  2330. args += strlen(args);
  2331. }
  2332. }
  2333. // Fix defaults for DebuggerExtensions events when wEventType not set
  2334. if (!strcmp(szSource, cszDefaultSource) && (0 == wEventType))
  2335. {
  2336. if ((EVENT_MSG_GENERIC == dwEventID) ||
  2337. (EVENT_MSG_INFORMATIONAL == dwEventID))
  2338. {
  2339. wEventType = EVENTLOG_INFORMATION_TYPE;
  2340. }
  2341. else if (EVENT_MSG_SUCCESS == dwEventID)
  2342. {
  2343. wEventType = EVENTLOG_SUCCESS;
  2344. }
  2345. else if (EVENT_MSG_WARNING == dwEventID)
  2346. {
  2347. wEventType = EVENTLOG_WARNING_TYPE;
  2348. }
  2349. else if (EVENT_MSG_ERROR == dwEventID)
  2350. {
  2351. wEventType = EVENTLOG_ERROR_TYPE;
  2352. }
  2353. }
  2354. // Get a handle to the NT application log
  2355. hEventSource = RegisterEventSource(NULL, szSource);
  2356. if (!hEventSource)
  2357. {
  2358. Status = HRESULT_FROM_WIN32(GetLastError());
  2359. ExtErr("Unable to open event log, 0x%08X\n", Status);
  2360. goto Exit;
  2361. }
  2362. if (!ReportEvent(hEventSource, // event log handle
  2363. wEventType, // event type
  2364. wEventCategory, // category
  2365. dwEventID, // event identifier
  2366. NULL, // no user security identifier
  2367. 1, // one substitution string
  2368. 0, // no data
  2369. &cszMessage, // pointer to string array
  2370. NULL)) // pointer to data
  2371. {
  2372. Status = HRESULT_FROM_WIN32(GetLastError());
  2373. ExtErr("Unable to report event, 0x%08X\n", Status);
  2374. goto Exit;
  2375. }
  2376. Status = S_OK;
  2377. // Output format similar to copy to clipboard format in event viewer
  2378. ExtOut("Event Type:\t%s (%u)\n",
  2379. (wEventType <= 16)
  2380. ? g_pcszEventType[wEventType]
  2381. : "None",
  2382. wEventType);
  2383. ExtOut("Event Source:\t%s\n", szSource);
  2384. ExtOut("Event Category:\t%s (%u)\n",
  2385. (!strcmp(szSource, cszDefaultSource) && wEventCategory <= 7)
  2386. ? g_pcszAppEventCategory[wEventCategory]
  2387. : "",
  2388. wEventCategory);
  2389. ExtOut("Event ID:\t%u\n", dwEventID);
  2390. ExtOut("Description:\n%s\n", cszMessage);
  2391. ExtVerb("Event successfully written.\n");
  2392. Exit:
  2393. if (hEventSource)
  2394. {
  2395. DeregisterEventSource(hEventSource);
  2396. }
  2397. EXIT_API();
  2398. return Status;
  2399. }
  2400. //----------------------------------------------------------------------------
  2401. //
  2402. // Debugger extension(s) implementation
  2403. //
  2404. //----------------------------------------------------------------------------
  2405. DECLARE_API( evlog )
  2406. /*++
  2407. Routine Description:
  2408. This is the function exported through the uext extension interface. It
  2409. is used to delegate the real work to the extension command specified as
  2410. an argument.
  2411. All event log related commands can be combined as sub-commands under this
  2412. one !evlog command.
  2413. Arguments:
  2414. Client - Pointer to IDebugClient passed to !evlog extension
  2415. [not used by this command]
  2416. args - Pointer to command line arguments passed to this command from
  2417. !evlog extension
  2418. Return Value:
  2419. E_INVALIDARG if invalid argument syntax detected
  2420. ERROR_BUFFER_OVERFLOW if argument length too long
  2421. GetLastError() converted to HRESULT otherwise
  2422. --*/
  2423. {
  2424. CHAR *szEndOfValue = NULL; // Ptr to last char in value
  2425. size_t cchValue = 0; // Count of chars in value
  2426. CHAR szParamValue[MAX_PATH];
  2427. const CHAR cszUsage[] = "Usage:\n"
  2428. "The following Event Log commands are available:\n\n"
  2429. "!evlog Display this help message\n"
  2430. "!evlog addsource Adds event source entry to registry\n"
  2431. "!evlog backup Makes backup of event log\n"
  2432. "!evlog clear Clears and creates backup of event log\n"
  2433. "!evlog info Displays summary info for event log\n"
  2434. "!evlog option Sets and clears cached option settings used "
  2435. "during read\n"
  2436. "!evlog read Reads event records from event log\n"
  2437. "!evlog report Writes event records to event log\n\n"
  2438. "Try command with -? or no parameters to see help.\n";
  2439. INIT_API();
  2440. if (args)
  2441. {
  2442. SKIP_WSPACE(args);
  2443. }
  2444. if (!args || !args[0] ||
  2445. !strncmp(args, "-h", 2) ||
  2446. !strncmp(args, "-?", 2))
  2447. {
  2448. Status = E_INVALIDARG;
  2449. ExtErr(cszUsage);
  2450. goto Exit;
  2451. }
  2452. // whitespace already skipped...
  2453. ZeroMemory(szParamValue, sizeof(szParamValue));
  2454. // Parameter value (command) is delimited by next space in string
  2455. szEndOfValue = strchr(args, ' ');
  2456. if (NULL == szEndOfValue)
  2457. {
  2458. // copy to end of line
  2459. CopyString(szParamValue, args, sizeof(szParamValue));
  2460. args += strlen(args);
  2461. }
  2462. else
  2463. {
  2464. cchValue = szEndOfValue - args;
  2465. if (cchValue < sizeof(szParamValue))
  2466. {
  2467. // copy next N chars
  2468. CopyString(szParamValue, args, cchValue+1);
  2469. args += cchValue;
  2470. }
  2471. else
  2472. {
  2473. Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  2474. ExtErr("ERROR: Argument string too long. Aborting.\n");
  2475. goto Exit;
  2476. }
  2477. ++args; // skip space
  2478. }
  2479. if (!_stricmp(szParamValue, "addsource"))
  2480. {
  2481. Status = EvLogAddSource(Client, args);
  2482. }
  2483. else if (!_stricmp(szParamValue, "backup"))
  2484. {
  2485. Status = EvLogBackup(Client, args);
  2486. }
  2487. else if (!_stricmp(szParamValue, "option"))
  2488. {
  2489. Status = EvLogOption(Client, args);
  2490. }
  2491. else if (!_stricmp(szParamValue, "clear"))
  2492. {
  2493. Status = EvLogClear(Client, args);
  2494. }
  2495. else if (!_stricmp(szParamValue, "info"))
  2496. {
  2497. Status = EvLogInfo(Client, args);
  2498. }
  2499. else if (!_stricmp(szParamValue, "read"))
  2500. {
  2501. Status = EvLogRead(Client, args);
  2502. }
  2503. else if (!_stricmp(szParamValue, "report"))
  2504. {
  2505. Status = EvLogReport(Client, args);
  2506. }
  2507. else
  2508. {
  2509. Status = E_INVALIDARG;
  2510. ExtErr("Invalid command '%s' specified\n", szParamValue);
  2511. ExtErr(cszUsage);
  2512. goto Exit;
  2513. }
  2514. // do not set Status to S_OK here, it is returned above
  2515. Exit:
  2516. EXIT_API();
  2517. return Status;
  2518. }