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.

1189 lines
30 KiB

  1. /*++
  2. Copyright (c) 1991-2002 Microsoft Corporation
  3. Module Name:
  4. savedump.c
  5. Abstract:
  6. This module contains the code to recover a dump from the system paging
  7. file.
  8. Environment:
  9. Kernel mode
  10. Revision History:
  11. --*/
  12. #include <savedump.h>
  13. // Flag for testing behavior.
  14. BOOL g_Test = FALSE;
  15. // MachineCrash key information.
  16. ULONG g_McTempDestination;
  17. WCHAR g_McDumpFile[MAX_PATH];
  18. // CrashControl key information.
  19. ULONG g_CcLogEvent;
  20. ULONG g_CcSendAlert;
  21. ULONG g_CcOverwrite;
  22. ULONG g_CcReportMachineDump;
  23. WCHAR g_CcMiniDumpDir[MAX_PATH];
  24. WCHAR g_CcDumpFile[MAX_PATH];
  25. // Dump information.
  26. DUMP_HEADER g_DumpHeader;
  27. WCHAR g_DumpBugCheckString[256];
  28. WCHAR g_MiniDumpFile[MAX_PATH];
  29. PWSTR g_FinalDumpFile;
  30. HRESULT
  31. FrrvToStatus(EFaultRepRetVal Frrv)
  32. {
  33. switch(Frrv)
  34. {
  35. case frrvOk:
  36. case frrvOkManifest:
  37. case frrvOkQueued:
  38. case frrvOkHeadless:
  39. return S_OK;
  40. default:
  41. return LAST_HR();
  42. }
  43. }
  44. HRESULT
  45. GetRegStr(HKEY Key,
  46. PWSTR Value,
  47. PWSTR Buffer,
  48. ULONG BufferChars,
  49. PWSTR Default)
  50. {
  51. ULONG Length;
  52. ULONG Error;
  53. ULONG Type;
  54. HRESULT Status;
  55. //
  56. // We want to only return valid, terminated strings
  57. // that fit in the given buffer. If the registry value
  58. // is not a string, has a bad size or fills the buffer
  59. // without termination it can't be returned.
  60. //
  61. Length = BufferChars * sizeof(WCHAR);
  62. Error = RegQueryValueEx(Key, Value, NULL, &Type, (LPBYTE)Buffer, &Length);
  63. if (Error != ERROR_SUCCESS)
  64. {
  65. Status = HRESULT_FROM_WIN32(Error);
  66. }
  67. else if ((Type != REG_SZ && Type != REG_EXPAND_SZ) ||
  68. (Length & (sizeof(WCHAR) - 1)) ||
  69. (Length == BufferChars * sizeof(WCHAR) &&
  70. Buffer[Length / sizeof(WCHAR) - 1] != 0))
  71. {
  72. Status = E_INVALIDARG;
  73. }
  74. else
  75. {
  76. if (Length < BufferChars * sizeof(WCHAR))
  77. {
  78. // Ensure that the string is terminated.
  79. Buffer[Length / sizeof(WCHAR)] = 0;
  80. }
  81. Status = S_OK;
  82. }
  83. if (Status != S_OK)
  84. {
  85. // Set to default.
  86. if (!Default || wcslen(Default) >= BufferChars)
  87. {
  88. return E_NOINTERFACE;
  89. }
  90. StringCchCopy(Buffer, BufferChars, Default);
  91. Status = S_OK;
  92. }
  93. return Status;
  94. }
  95. HRESULT
  96. ExpandRegStr(HKEY Key,
  97. PWSTR Value,
  98. PWSTR Buffer,
  99. ULONG BufferChars,
  100. PWSTR Default,
  101. PWSTR ExpandBuffer,
  102. ULONG ExpandChars)
  103. {
  104. HRESULT Status;
  105. ULONG Length;
  106. if ((Status = GetRegStr(Key, Value, Buffer, BufferChars, Default)) != S_OK)
  107. {
  108. return Status;
  109. }
  110. Length = ExpandEnvironmentStrings(Buffer, ExpandBuffer, ExpandChars);
  111. return Length > 0 && Length <= ExpandChars ? S_OK : E_INVALIDARG;
  112. }
  113. HRESULT
  114. GetRegWord32(HKEY Key,
  115. PWSTR Value,
  116. PULONG Word,
  117. ULONG Default,
  118. BOOL CanDefault)
  119. {
  120. ULONG Length;
  121. ULONG Error;
  122. ULONG Type;
  123. HRESULT Status;
  124. Length = sizeof(*Word);
  125. Error = RegQueryValueEx(Key, Value, NULL, &Type, (LPBYTE)Word, &Length);
  126. if (Error != ERROR_SUCCESS)
  127. {
  128. Status = HRESULT_FROM_WIN32(Error);
  129. }
  130. else if (Type != REG_DWORD ||
  131. Length != sizeof(*Word))
  132. {
  133. Status = E_INVALIDARG;
  134. }
  135. else
  136. {
  137. Status = S_OK;
  138. }
  139. if (Status != S_OK)
  140. {
  141. // Set to default.
  142. if (!CanDefault)
  143. {
  144. return E_NOINTERFACE;
  145. }
  146. *Word = Default;
  147. Status = S_OK;
  148. }
  149. return Status;
  150. }
  151. HRESULT
  152. GetRegMachineCrash(void)
  153. {
  154. ULONG Error;
  155. HKEY Key;
  156. Error = RegOpenKey(HKEY_LOCAL_MACHINE,
  157. SUBKEY_CRASH_CONTROL L"\\MachineCrash",
  158. &Key);
  159. if (Error != ERROR_SUCCESS)
  160. {
  161. // If the key doesn't exist we just go with the defaults.
  162. return S_OK;
  163. }
  164. GetRegWord32(Key, L"TempDestination", &g_McTempDestination, 0, TRUE);
  165. GetRegStr(Key, L"DumpFile",
  166. g_McDumpFile, RTL_NUMBER_OF(g_McDumpFile),
  167. L"");
  168. RegCloseKey(Key);
  169. return S_OK;
  170. }
  171. HRESULT
  172. GetRegCrashControl(void)
  173. {
  174. HRESULT Status;
  175. ULONG Error;
  176. HKEY Key;
  177. WCHAR TmpPath[MAX_PATH];
  178. PWSTR Scan;
  179. Error = RegOpenKey(HKEY_LOCAL_MACHINE,
  180. SUBKEY_CRASH_CONTROL,
  181. &Key);
  182. if (Error != ERROR_SUCCESS)
  183. {
  184. // If the key doesn't exist we just go with the defaults.
  185. return S_OK;
  186. }
  187. GetRegWord32(Key, L"LogEvent", &g_CcLogEvent, 0, TRUE);
  188. GetRegWord32(Key, L"SendAlert", &g_CcSendAlert, 0, TRUE);
  189. GetRegWord32(Key, L"Overwrite", &g_CcOverwrite, 0, TRUE);
  190. GetRegWord32(Key, L"ReportMachineDump", &g_CcReportMachineDump, 0, TRUE);
  191. if ((Status = ExpandRegStr(Key, L"MiniDumpDir",
  192. TmpPath, RTL_NUMBER_OF(TmpPath),
  193. L"%SystemRoot%\\Minidump",
  194. g_CcMiniDumpDir,
  195. RTL_NUMBER_OF(g_CcMiniDumpDir))) != S_OK)
  196. {
  197. g_CcMiniDumpDir[0] = 0;
  198. goto Exit;
  199. }
  200. // Remove any trailing slash on the directory name.
  201. Scan = g_CcMiniDumpDir + wcslen(g_CcMiniDumpDir);
  202. if (Scan > g_CcMiniDumpDir && *(Scan - 1) == L'\\')
  203. {
  204. *--Scan = 0;
  205. }
  206. if ((Status = ExpandRegStr(Key, L"DumpFile",
  207. TmpPath, RTL_NUMBER_OF(TmpPath),
  208. L"%SystemRoot%\\MEMORY.DMP",
  209. g_CcDumpFile,
  210. RTL_NUMBER_OF(g_CcDumpFile))) != S_OK)
  211. {
  212. g_CcDumpFile[0] = 0;
  213. goto Exit;
  214. }
  215. Status = S_OK;
  216. Exit:
  217. RegCloseKey(Key);
  218. return Status;
  219. }
  220. HRESULT
  221. GetDumpInfo(void)
  222. {
  223. HANDLE File;
  224. ULONG Bytes;
  225. BOOL Succ;
  226. HRESULT Status;
  227. if (!g_McDumpFile[0])
  228. {
  229. return E_NOINTERFACE;
  230. }
  231. File = CreateFile(g_McDumpFile,
  232. GENERIC_READ,
  233. FILE_SHARE_READ,
  234. NULL,
  235. OPEN_EXISTING,
  236. 0,
  237. NULL);
  238. if (File == INVALID_HANDLE_VALUE)
  239. {
  240. return E_NOINTERFACE;
  241. }
  242. Succ = ReadFile(File,
  243. &g_DumpHeader,
  244. sizeof(g_DumpHeader),
  245. &Bytes,
  246. NULL);
  247. CloseHandle(File);
  248. if (Succ &&
  249. Bytes == sizeof(g_DumpHeader) &&
  250. g_DumpHeader.Signature == DUMP_SIGNATURE &&
  251. g_DumpHeader.ValidDump == DUMP_VALID_DUMP)
  252. {
  253. #ifdef _WIN64
  254. Status =
  255. StringCchPrintf(g_DumpBugCheckString,
  256. RTL_NUMBER_OF(g_DumpBugCheckString),
  257. L"0x%08x (0x%016I64x, 0x%016I64x, 0x%016I64x, 0x%016I64x)",
  258. g_DumpHeader.BugCheckCode,
  259. g_DumpHeader.BugCheckParameter1,
  260. g_DumpHeader.BugCheckParameter2,
  261. g_DumpHeader.BugCheckParameter3,
  262. g_DumpHeader.BugCheckParameter4);
  263. #else
  264. Status =
  265. StringCchPrintf(g_DumpBugCheckString,
  266. RTL_NUMBER_OF(g_DumpBugCheckString),
  267. L"0x%08x (0x%08x, 0x%08x, 0x%08x, 0x%08x)",
  268. g_DumpHeader.BugCheckCode,
  269. g_DumpHeader.BugCheckParameter1,
  270. g_DumpHeader.BugCheckParameter2,
  271. g_DumpHeader.BugCheckParameter3,
  272. g_DumpHeader.BugCheckParameter4);
  273. #endif
  274. // This check and message are here just to make
  275. // it easy to catch cases where the message outgrows
  276. // the buffer. It is highly unlikely that this will happen.
  277. if (Status != S_OK)
  278. {
  279. KdPrint(("SAVEDUMP: g_DumpBugCheckString too small\n"));
  280. Status = S_OK;
  281. }
  282. }
  283. else
  284. {
  285. ZeroMemory(&g_DumpHeader, sizeof(g_DumpHeader));
  286. Status = E_NOINTERFACE;
  287. }
  288. return Status;
  289. }
  290. HRESULT
  291. SetSecurity(HANDLE FileHandle)
  292. {
  293. PSID LocalSystemSid = NULL;
  294. PSID AdminSid = NULL;
  295. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  296. SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
  297. PSECURITY_DESCRIPTOR SecurityDescriptor;
  298. BYTE SdBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
  299. PACL Acl;
  300. BYTE AclBuffer[1024];
  301. HANDLE Token = NULL;
  302. PTOKEN_OWNER TokOwner;
  303. ULONG TryLen = 256;
  304. ULONG RetLen;
  305. NTSTATUS NtStatus;
  306. NtStatus = RtlAllocateAndInitializeSid(&NtAuthority, 1,
  307. SECURITY_LOCAL_SYSTEM_RID,
  308. 0, 0, 0, 0, 0, 0, 0,
  309. &LocalSystemSid);
  310. if (!NT_SUCCESS(NtStatus))
  311. {
  312. goto Exit;
  313. }
  314. NtStatus = RtlAllocateAndInitializeSid(&NtAuthority, 2,
  315. SECURITY_BUILTIN_DOMAIN_RID,
  316. DOMAIN_ALIAS_RID_ADMINS,
  317. 0, 0, 0, 0, 0, 0, &AdminSid);
  318. if (!NT_SUCCESS(NtStatus))
  319. {
  320. goto Exit;
  321. }
  322. SecurityDescriptor = (PSECURITY_DESCRIPTOR)SdBuffer;
  323. //
  324. // You can be fancy and compute the exact size, but since the
  325. // security descriptor capture code has to do that anyway, why
  326. // do it twice?
  327. //
  328. Acl = (PACL)AclBuffer;
  329. NtStatus = RtlCreateSecurityDescriptor(SecurityDescriptor,
  330. SECURITY_DESCRIPTOR_REVISION);
  331. if (!NT_SUCCESS(NtStatus))
  332. {
  333. goto Exit;
  334. }
  335. NtStatus = RtlCreateAcl(Acl, sizeof(AclBuffer), ACL_REVISION);
  336. if (!NT_SUCCESS(NtStatus))
  337. {
  338. goto Exit;
  339. }
  340. //
  341. // Current user, Administrator and System have full control
  342. //
  343. if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &Token) &&
  344. !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token))
  345. {
  346. NtStatus = STATUS_ACCESS_DENIED;
  347. goto Exit;
  348. }
  349. for (;;)
  350. {
  351. TokOwner = (PTOKEN_OWNER)malloc(TryLen);
  352. if (!TokOwner)
  353. {
  354. NtStatus = STATUS_NO_MEMORY;
  355. goto Exit;
  356. }
  357. if (GetTokenInformation(Token, TokenOwner, TokOwner, TryLen, &RetLen))
  358. {
  359. NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION,
  360. GENERIC_ALL | DELETE |
  361. WRITE_DAC | WRITE_OWNER,
  362. TokOwner->Owner);
  363. break;
  364. }
  365. else if (RetLen <= TryLen)
  366. {
  367. NtStatus = STATUS_ACCESS_DENIED;
  368. break;
  369. }
  370. free(TokOwner);
  371. TryLen = RetLen;
  372. }
  373. free(TokOwner);
  374. if (!NT_SUCCESS(NtStatus))
  375. {
  376. goto Exit;
  377. }
  378. NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION,
  379. GENERIC_ALL | DELETE |
  380. WRITE_DAC | WRITE_OWNER,
  381. AdminSid);
  382. if (!NT_SUCCESS(NtStatus))
  383. {
  384. goto Exit;
  385. }
  386. NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION,
  387. GENERIC_ALL | DELETE |
  388. WRITE_DAC | WRITE_OWNER,
  389. LocalSystemSid);
  390. if (!NT_SUCCESS(NtStatus))
  391. {
  392. goto Exit;
  393. }
  394. NtStatus = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Acl,
  395. FALSE);
  396. if (!NT_SUCCESS(NtStatus))
  397. {
  398. goto Exit;
  399. }
  400. NtStatus = RtlSetOwnerSecurityDescriptor(SecurityDescriptor, AdminSid,
  401. FALSE);
  402. if (!NT_SUCCESS(NtStatus))
  403. {
  404. goto Exit;
  405. }
  406. NtStatus = NtSetSecurityObject(FileHandle,
  407. DACL_SECURITY_INFORMATION,
  408. SecurityDescriptor);
  409. Exit:
  410. if (AdminSid)
  411. {
  412. RtlFreeSid(AdminSid);
  413. }
  414. if (LocalSystemSid)
  415. {
  416. RtlFreeSid(LocalSystemSid);
  417. }
  418. if (Token)
  419. {
  420. CloseHandle(Token);
  421. }
  422. return NT_SUCCESS(NtStatus) ? S_OK : HRESULT_FROM_NT(NtStatus);
  423. }
  424. HRESULT
  425. CreateMiniDumpFile(PHANDLE MiniFileHandle)
  426. {
  427. INT i;
  428. SYSTEMTIME Time;
  429. HRESULT Status;
  430. HANDLE FileHandle;
  431. if (!g_CcMiniDumpDir[0])
  432. {
  433. // Bad minidump directory.
  434. return E_INVALIDARG;
  435. }
  436. //
  437. // If directory does not exist, create it. Ignore errors here because
  438. // they will be picked up later when we try to create the file.
  439. //
  440. CreateDirectory(g_CcMiniDumpDir, NULL);
  441. //
  442. // Format is: Mini-MM_DD_YY_HH_MM.dmp
  443. //
  444. GetLocalTime(&Time);
  445. for (i = 1; i < 100; i++)
  446. {
  447. if ((Status = StringCchPrintf(g_MiniDumpFile,
  448. RTL_NUMBER_OF(g_MiniDumpFile),
  449. L"%s\\Mini%2.2d%2.2d%2.2d-%2.2d.dmp",
  450. g_CcMiniDumpDir,
  451. (int)Time.wMonth,
  452. (int)Time.wDay,
  453. (int)Time.wYear % 100,
  454. (int)i)) != S_OK)
  455. {
  456. g_MiniDumpFile[0] = 0;
  457. return Status;
  458. }
  459. FileHandle = CreateFile(g_MiniDumpFile,
  460. GENERIC_WRITE | WRITE_DAC,
  461. 0,
  462. NULL,
  463. CREATE_NEW,
  464. FILE_ATTRIBUTE_NORMAL,
  465. NULL);
  466. if (FileHandle != INVALID_HANDLE_VALUE)
  467. {
  468. break;
  469. }
  470. }
  471. if (FileHandle == INVALID_HANDLE_VALUE)
  472. {
  473. // We failed to create a suitable file name.
  474. g_MiniDumpFile[0] = 0;
  475. return E_FAIL;
  476. }
  477. if ((Status = SetSecurity(FileHandle)) != S_OK)
  478. {
  479. CloseHandle(FileHandle);
  480. DeleteFile(g_MiniDumpFile);
  481. g_MiniDumpFile[0] = 0;
  482. return Status;
  483. }
  484. *MiniFileHandle = FileHandle;
  485. return S_OK;
  486. }
  487. #define COPY_CHUNK (1024 * 1024)
  488. HRESULT
  489. CopyAndSecureFile(PWSTR Source,
  490. PWSTR Dest,
  491. HANDLE DestHandle,
  492. BOOL Overwrite,
  493. BOOL DeleteDest)
  494. {
  495. HRESULT Status;
  496. HANDLE SourceHandle = INVALID_HANDLE_VALUE;
  497. PUCHAR Buffer = NULL;
  498. Buffer = (PUCHAR)malloc(COPY_CHUNK);
  499. if (!Buffer)
  500. {
  501. Status = E_OUTOFMEMORY;
  502. goto Exit;
  503. }
  504. SourceHandle = CreateFile(Source,
  505. GENERIC_READ,
  506. 0,
  507. NULL,
  508. OPEN_EXISTING,
  509. FILE_ATTRIBUTE_NORMAL,
  510. NULL);
  511. if (SourceHandle == INVALID_HANDLE_VALUE)
  512. {
  513. Status = LAST_HR();
  514. goto Exit;
  515. }
  516. if (DestHandle == INVALID_HANDLE_VALUE)
  517. {
  518. DestHandle = CreateFile(Dest,
  519. GENERIC_WRITE | WRITE_DAC,
  520. 0,
  521. NULL,
  522. Overwrite ? CREATE_ALWAYS : CREATE_NEW,
  523. FILE_ATTRIBUTE_NORMAL,
  524. NULL);
  525. if (DestHandle == INVALID_HANDLE_VALUE)
  526. {
  527. Status = LAST_HR();
  528. goto Exit;
  529. }
  530. DeleteDest = TRUE;
  531. if ((Status = SetSecurity(DestHandle)) != S_OK)
  532. {
  533. goto Exit;
  534. }
  535. }
  536. for (;;)
  537. {
  538. ULONG Req, Done;
  539. if (!ReadFile(SourceHandle, Buffer, COPY_CHUNK, &Done, NULL))
  540. {
  541. Status = LAST_HR();
  542. break;
  543. }
  544. else if (Done == 0)
  545. {
  546. // End-of-file.
  547. Status = S_OK;
  548. break;
  549. }
  550. Req = Done;
  551. if (!WriteFile(DestHandle, Buffer, Req, &Done, NULL))
  552. {
  553. Status = LAST_HR();
  554. break;
  555. }
  556. else if (Done < Req)
  557. {
  558. Status = HRESULT_FROM_WIN32(ERROR_HANDLE_DISK_FULL);
  559. break;
  560. }
  561. }
  562. Exit:
  563. if (DeleteDest)
  564. {
  565. CloseHandle(DestHandle);
  566. if (Status != S_OK)
  567. {
  568. DeleteFile(Dest);
  569. }
  570. }
  571. if (SourceHandle != INVALID_HANDLE_VALUE)
  572. {
  573. CloseHandle(SourceHandle);
  574. }
  575. free(Buffer);
  576. return Status;
  577. }
  578. HRESULT
  579. MoveDumpFile(void)
  580. {
  581. HRESULT Status;
  582. if (g_DumpHeader.Signature != DUMP_SIGNATURE)
  583. {
  584. // Dump file is not present or invalid, so there's nothing to do.
  585. return S_OK;
  586. }
  587. //
  588. // If the dump file needs to be copied, copy it now.
  589. //
  590. if (!g_McTempDestination)
  591. {
  592. g_FinalDumpFile = g_McDumpFile;
  593. }
  594. else
  595. {
  596. if (!g_Test)
  597. {
  598. //
  599. // Set the priority class of this application down to the Lowest
  600. // priority class to ensure that copying the file does not overload
  601. // everything else that is going on during system initialization.
  602. //
  603. // We do not lower the priority in test mode because it just
  604. // wastes time.
  605. //
  606. SetPriorityClass (GetCurrentProcess(), IDLE_PRIORITY_CLASS);
  607. SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_LOWEST);
  608. }
  609. if (g_DumpHeader.DumpType == DUMP_TYPE_FULL ||
  610. g_DumpHeader.DumpType == DUMP_TYPE_SUMMARY)
  611. {
  612. if (!g_CcDumpFile[0])
  613. {
  614. // Invalid dump file registry entry.
  615. return E_INVALIDARG;
  616. }
  617. if ((Status = CopyAndSecureFile(g_McDumpFile,
  618. g_CcDumpFile,
  619. INVALID_HANDLE_VALUE,
  620. g_CcOverwrite ? TRUE : FALSE,
  621. TRUE)) != S_OK)
  622. {
  623. return Status;
  624. }
  625. g_FinalDumpFile = g_CcDumpFile;
  626. }
  627. else if (g_DumpHeader.DumpType == DUMP_TYPE_TRIAGE)
  628. {
  629. HANDLE MiniFile;
  630. if ((Status = CreateMiniDumpFile(&MiniFile)) != S_OK)
  631. {
  632. return Status;
  633. }
  634. if ((Status = CopyAndSecureFile(g_McDumpFile,
  635. NULL,
  636. MiniFile,
  637. FALSE,
  638. TRUE)) != S_OK)
  639. {
  640. g_MiniDumpFile[0] = 0;
  641. return Status;
  642. }
  643. g_FinalDumpFile = g_MiniDumpFile;
  644. }
  645. DeleteFile(g_McDumpFile);
  646. g_McDumpFile[0] = 0;
  647. }
  648. return S_OK;
  649. }
  650. HRESULT
  651. ConvertDumpFile(void)
  652. {
  653. HRESULT Status;
  654. IDebugClient4 *DebugClient;
  655. IDebugControl *DebugControl;
  656. HANDLE MiniFile;
  657. //
  658. // Produce a minidump by conversion if necessary.
  659. //
  660. if (!g_FinalDumpFile ||
  661. (g_DumpHeader.DumpType != DUMP_TYPE_FULL &&
  662. g_DumpHeader.DumpType != DUMP_TYPE_SUMMARY))
  663. {
  664. // No dump or not a convertable dump.
  665. return S_OK;
  666. }
  667. if ((Status = CreateMiniDumpFile(&MiniFile)) != S_OK)
  668. {
  669. return Status;
  670. }
  671. if ((Status = DebugCreate(__uuidof(IDebugClient4),
  672. (void **)&DebugClient)) != S_OK)
  673. {
  674. goto EH_File;
  675. }
  676. if ((Status = DebugClient->QueryInterface(__uuidof(IDebugControl),
  677. (void **)&DebugControl)) != S_OK)
  678. {
  679. goto EH_Client;
  680. }
  681. if ((Status = DebugClient->OpenDumpFileWide(g_FinalDumpFile, 0)) != S_OK)
  682. {
  683. goto EH_Control;
  684. }
  685. if ((Status = DebugControl->
  686. WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) != S_OK)
  687. {
  688. goto EH_Control;
  689. }
  690. Status = DebugClient->
  691. WriteDumpFileWide(NULL, (ULONG_PTR)MiniFile, DEBUG_DUMP_SMALL,
  692. DEBUG_FORMAT_DEFAULT, NULL);
  693. EH_Control:
  694. DebugControl->Release();
  695. EH_Client:
  696. DebugClient->EndSession(DEBUG_END_PASSIVE);
  697. DebugClient->Release();
  698. EH_File:
  699. CloseHandle(MiniFile);
  700. if (Status != S_OK)
  701. {
  702. DeleteFile(g_MiniDumpFile);
  703. g_MiniDumpFile[0] = 0;
  704. }
  705. return Status;
  706. }
  707. VOID
  708. LogEvent(ULONG Id,
  709. WORD StringCount,
  710. PWSTR* Strings)
  711. {
  712. HANDLE LogHandle;
  713. BOOL Retry;
  714. DWORD Retries;
  715. //
  716. // Attempt to register the event source.
  717. // Savedump runs early in the startup process so
  718. // it's possible that the event service hasn't started
  719. // yet. If it appears that the service hasn't started,
  720. // wait a bit until it comes around. If it hasn't
  721. // come around after a reasonable amount of time bail out.
  722. //
  723. Retries = 0;
  724. do
  725. {
  726. LogHandle = RegisterEventSource(NULL, L"Save Dump");
  727. //
  728. // Retry on specific failures that indicate the event
  729. // service hasn't started yet.
  730. //
  731. if (LogHandle == NULL &&
  732. Retries < 20 &&
  733. (GetLastError () == RPC_S_SERVER_UNAVAILABLE ||
  734. GetLastError () == RPC_S_UNKNOWN_IF))
  735. {
  736. Sleep(1500);
  737. Retry = TRUE;
  738. }
  739. else
  740. {
  741. Retry = FALSE;
  742. }
  743. Retries++;
  744. }
  745. while (LogHandle == NULL && Retry);
  746. if (!LogHandle)
  747. {
  748. KdPrint(("SAVEDUMP: Unable to register event source, %d\n",
  749. GetLastError()));
  750. return;
  751. }
  752. if (!ReportEvent(LogHandle,
  753. EVENTLOG_INFORMATION_TYPE,
  754. 0,
  755. Id,
  756. NULL,
  757. StringCount,
  758. 0,
  759. (LPCWSTR *)Strings,
  760. NULL))
  761. {
  762. KdPrint(("SAVEDUMP: Unable to report event, %d\n", GetLastError()));
  763. }
  764. DeregisterEventSource(LogHandle);
  765. }
  766. void
  767. LogCrashDumpEvent(void)
  768. {
  769. LPWSTR StringArray[2];
  770. WORD StringCount;
  771. DWORD EventId;
  772. //
  773. // Set up the parameters based on how much information
  774. // is available.
  775. //
  776. StringCount = 0;
  777. if (g_DumpBugCheckString[0])
  778. {
  779. StringArray[StringCount++] = g_DumpBugCheckString;
  780. }
  781. if (g_FinalDumpFile)
  782. {
  783. StringArray[StringCount++] = g_FinalDumpFile;
  784. }
  785. //
  786. // Report the appropriate event.
  787. //
  788. if (g_FinalDumpFile)
  789. {
  790. EventId = EVENT_BUGCHECK_SAVED;
  791. }
  792. else if (g_DumpBugCheckString[0])
  793. {
  794. EventId = EVENT_BUGCHECK;
  795. }
  796. else
  797. {
  798. EventId = EVENT_UNKNOWN_BUGCHECK;
  799. }
  800. LogEvent(EventId, StringCount, StringArray);
  801. }
  802. void
  803. SendCrashDumpAlert(void)
  804. {
  805. PADMIN_OTHER_INFO AdminInfo;
  806. DWORD AdminInfoSize;
  807. DWORD Length;
  808. DWORD i;
  809. ULONG Error;
  810. UCHAR VariableInfo[4096];
  811. //
  812. // Set up the administrator information variables for processing the
  813. // buffer.
  814. //
  815. AdminInfo = (PADMIN_OTHER_INFO)VariableInfo;
  816. AdminInfo->alrtad_numstrings = 0;
  817. AdminInfoSize = sizeof(*AdminInfo);
  818. //
  819. // Format the bugcheck information into the appropriate message format.
  820. //
  821. if (g_DumpBugCheckString[0])
  822. {
  823. Length = (wcslen(g_DumpBugCheckString) + 1) * sizeof(WCHAR);
  824. if (AdminInfoSize + Length > sizeof(VariableInfo))
  825. {
  826. goto Error;
  827. }
  828. RtlCopyMemory((PCHAR)AdminInfo + AdminInfoSize,
  829. g_DumpBugCheckString, Length);
  830. AdminInfo->alrtad_numstrings++;
  831. AdminInfoSize += Length;
  832. }
  833. //
  834. // Set up the administrator alert information according to the type of
  835. // dump that was taken.
  836. //
  837. if (g_FinalDumpFile)
  838. {
  839. Length = (wcslen(g_FinalDumpFile) + 1) * sizeof(WCHAR);
  840. if (AdminInfoSize + Length > sizeof(VariableInfo))
  841. {
  842. goto Error;
  843. }
  844. AdminInfo->alrtad_errcode = ALERT_BugCheckSaved;
  845. RtlCopyMemory((PCHAR)AdminInfo + AdminInfoSize,
  846. g_FinalDumpFile, Length);
  847. AdminInfo->alrtad_numstrings++;
  848. AdminInfoSize += Length;
  849. }
  850. else
  851. {
  852. AdminInfo->alrtad_errcode = ALERT_BugCheck;
  853. }
  854. //
  855. // Get the name of the computer and insert it into the buffer.
  856. //
  857. Length = (sizeof(VariableInfo) - AdminInfoSize) / sizeof(WCHAR);
  858. if (!GetComputerName((LPWSTR)((PCHAR)AdminInfo + AdminInfoSize),
  859. &Length))
  860. {
  861. goto Error;
  862. }
  863. Length = (Length + 1) * sizeof(WCHAR);
  864. AdminInfo->alrtad_numstrings++;
  865. AdminInfoSize += Length;
  866. //
  867. // Raise the alert.
  868. //
  869. i = 0;
  870. do
  871. {
  872. Error = NetAlertRaiseEx(ALERT_ADMIN_EVENT,
  873. AdminInfo,
  874. AdminInfoSize,
  875. L"SAVEDUMP");
  876. if (Error)
  877. {
  878. if (Error == ERROR_FILE_NOT_FOUND)
  879. {
  880. if (i++ > 20)
  881. {
  882. break;
  883. }
  884. if ((i & 3) == 0)
  885. {
  886. KdPrint(("SAVEDUMP: Waiting for alerter...\n"));
  887. }
  888. Sleep(15000);
  889. }
  890. }
  891. } while (Error == ERROR_FILE_NOT_FOUND);
  892. if (Error != ERROR_SUCCESS)
  893. {
  894. goto Error;
  895. }
  896. return;
  897. Error:
  898. KdPrint(("SAVEDUMP: Unable to raise alert\n"));
  899. }
  900. VOID
  901. __cdecl
  902. wmain(int Argc,
  903. PWSTR Argv[])
  904. {
  905. int Arg;
  906. BOOL Report = TRUE;
  907. for (Arg = 1; Arg < Argc; Arg++)
  908. {
  909. if (Argv[Arg][0] == L'-' || Argv[Arg][0] == L'/')
  910. {
  911. switch (Argv[Arg][1])
  912. {
  913. case L't':
  914. case L'T':
  915. #if DBG
  916. g_Test = TRUE;
  917. #endif
  918. break;
  919. default:
  920. break;
  921. }
  922. }
  923. }
  924. if (GetRegMachineCrash() != S_OK ||
  925. GetRegCrashControl() != S_OK)
  926. {
  927. LogEvent(EVENT_UNABLE_TO_READ_REGISTRY, 0, NULL);
  928. }
  929. GetDumpInfo();
  930. if (MoveDumpFile() != S_OK)
  931. {
  932. LogEvent(EVENT_UNABLE_TO_MOVE_DUMP_FILE, 0, NULL);
  933. }
  934. if (ConvertDumpFile() != S_OK)
  935. {
  936. LogEvent(EVENT_UNABLE_TO_CONVERT_DUMP_FILE, 0, NULL);
  937. }
  938. //if (WatchdogEventHandler(TRUE) == S_OK)
  939. //{
  940. // // Note: Bugcheck EA will be reported in the watchdog code since
  941. // // we need to send minidump and wdl files together and we want to have
  942. // // a single pop-up only.
  943. // Report = FALSE;
  944. //}
  945. if (Report)
  946. {
  947. // The default behavior is to report a minidump
  948. // even if the machine dump was not a minidump in
  949. // order to minimize the amount of data sent.
  950. // If the system is configured to report the
  951. // machine dump go ahead and send it regardless
  952. // of what kind of dump it is.
  953. PWSTR ReportDumpFile = g_CcReportMachineDump ?
  954. g_FinalDumpFile : g_MiniDumpFile;
  955. if (ReportDumpFile && ReportDumpFile[0])
  956. {
  957. // Report bugcheck to Microsoft Error Reporting.
  958. if (FrrvToStatus(ReportEREvent(eetKernelFault,
  959. ReportDumpFile,
  960. NULL)) != S_OK)
  961. {
  962. LogEvent(EVENT_UNABLE_TO_REPORT_BUGCHECK, 0, NULL);
  963. }
  964. }
  965. }
  966. //
  967. // Knock down reliability ShutdownEventPending flag. We must always try
  968. // to do this since somebody can set this flag and recover later on
  969. // (e.g. watchdog's EventFlag cleared). With this flag set savedump
  970. // will always run and we don't want that.
  971. //
  972. // Note: This flag is shared between multiple components.
  973. // Only savedump is allowed to clear this flag, all other
  974. // components are only allowed to set it to trigger
  975. // savedump run at next logon.
  976. //
  977. HKEY Key;
  978. if (RegOpenKey(HKEY_LOCAL_MACHINE,
  979. SUBKEY_RELIABILITY,
  980. &Key) == ERROR_SUCCESS)
  981. {
  982. RegDeleteValue(Key, L"ShutdownEventPending");
  983. RegCloseKey(Key);
  984. }
  985. //
  986. // If there was a dump produced we may need to log an event
  987. // and send an alert.
  988. // We delay these time consuming opertaions till the end. We had the
  989. // case where SendCrashDumpAlert delayed PC Health pop-ups few minutes.
  990. //
  991. BOOL HaveCrashData =
  992. g_McDumpFile[0] ||
  993. g_DumpBugCheckString[0] ||
  994. g_FinalDumpFile;
  995. if (HaveCrashData && g_CcLogEvent)
  996. {
  997. LogCrashDumpEvent();
  998. }
  999. //
  1000. // This function will fill the BugCheckString for DirtyShutdown UI based
  1001. // on the flag set by EventLog service during startup time, in some case
  1002. // Eventlog service might start after savedump, so we will need to run this
  1003. // function after the first event was logged by savedump.
  1004. // if g_CcLogEvent == FALSE, it is OK not set the string since the user
  1005. // are not interested about the BugCheck info at all.
  1006. //
  1007. if (DirtyShutdownEventHandler(TRUE) != S_OK)
  1008. {
  1009. LogEvent(EVENT_UNABLE_TO_REPORT_DIRTY_SHUTDOWN, 0, NULL);
  1010. }
  1011. if (HaveCrashData && g_CcSendAlert)
  1012. {
  1013. SendCrashDumpAlert();
  1014. }
  1015. }