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

/*++
Copyright (c) 1991-2002 Microsoft Corporation
Module Name:
savedump.c
Abstract:
This module contains the code to recover a dump from the system paging
file.
Environment:
Kernel mode
Revision History:
--*/
#include <savedump.h>
// Flag for testing behavior.
BOOL g_Test = FALSE;
// MachineCrash key information.
ULONG g_McTempDestination;
WCHAR g_McDumpFile[MAX_PATH];
// CrashControl key information.
ULONG g_CcLogEvent;
ULONG g_CcSendAlert;
ULONG g_CcOverwrite;
ULONG g_CcReportMachineDump;
WCHAR g_CcMiniDumpDir[MAX_PATH];
WCHAR g_CcDumpFile[MAX_PATH];
// Dump information.
DUMP_HEADER g_DumpHeader;
WCHAR g_DumpBugCheckString[256];
WCHAR g_MiniDumpFile[MAX_PATH];
PWSTR g_FinalDumpFile;
HRESULT
FrrvToStatus(EFaultRepRetVal Frrv)
{
switch(Frrv)
{
case frrvOk:
case frrvOkManifest:
case frrvOkQueued:
case frrvOkHeadless:
return S_OK;
default:
return LAST_HR();
}
}
HRESULT
GetRegStr(HKEY Key,
PWSTR Value,
PWSTR Buffer,
ULONG BufferChars,
PWSTR Default)
{
ULONG Length;
ULONG Error;
ULONG Type;
HRESULT Status;
//
// We want to only return valid, terminated strings
// that fit in the given buffer. If the registry value
// is not a string, has a bad size or fills the buffer
// without termination it can't be returned.
//
Length = BufferChars * sizeof(WCHAR);
Error = RegQueryValueEx(Key, Value, NULL, &Type, (LPBYTE)Buffer, &Length);
if (Error != ERROR_SUCCESS)
{
Status = HRESULT_FROM_WIN32(Error);
}
else if ((Type != REG_SZ && Type != REG_EXPAND_SZ) ||
(Length & (sizeof(WCHAR) - 1)) ||
(Length == BufferChars * sizeof(WCHAR) &&
Buffer[Length / sizeof(WCHAR) - 1] != 0))
{
Status = E_INVALIDARG;
}
else
{
if (Length < BufferChars * sizeof(WCHAR))
{
// Ensure that the string is terminated.
Buffer[Length / sizeof(WCHAR)] = 0;
}
Status = S_OK;
}
if (Status != S_OK)
{
// Set to default.
if (!Default || wcslen(Default) >= BufferChars)
{
return E_NOINTERFACE;
}
StringCchCopy(Buffer, BufferChars, Default);
Status = S_OK;
}
return Status;
}
HRESULT
ExpandRegStr(HKEY Key,
PWSTR Value,
PWSTR Buffer,
ULONG BufferChars,
PWSTR Default,
PWSTR ExpandBuffer,
ULONG ExpandChars)
{
HRESULT Status;
ULONG Length;
if ((Status = GetRegStr(Key, Value, Buffer, BufferChars, Default)) != S_OK)
{
return Status;
}
Length = ExpandEnvironmentStrings(Buffer, ExpandBuffer, ExpandChars);
return Length > 0 && Length <= ExpandChars ? S_OK : E_INVALIDARG;
}
HRESULT
GetRegWord32(HKEY Key,
PWSTR Value,
PULONG Word,
ULONG Default,
BOOL CanDefault)
{
ULONG Length;
ULONG Error;
ULONG Type;
HRESULT Status;
Length = sizeof(*Word);
Error = RegQueryValueEx(Key, Value, NULL, &Type, (LPBYTE)Word, &Length);
if (Error != ERROR_SUCCESS)
{
Status = HRESULT_FROM_WIN32(Error);
}
else if (Type != REG_DWORD ||
Length != sizeof(*Word))
{
Status = E_INVALIDARG;
}
else
{
Status = S_OK;
}
if (Status != S_OK)
{
// Set to default.
if (!CanDefault)
{
return E_NOINTERFACE;
}
*Word = Default;
Status = S_OK;
}
return Status;
}
HRESULT
GetRegMachineCrash(void)
{
ULONG Error;
HKEY Key;
Error = RegOpenKey(HKEY_LOCAL_MACHINE,
SUBKEY_CRASH_CONTROL L"\\MachineCrash",
&Key);
if (Error != ERROR_SUCCESS)
{
// If the key doesn't exist we just go with the defaults.
return S_OK;
}
GetRegWord32(Key, L"TempDestination", &g_McTempDestination, 0, TRUE);
GetRegStr(Key, L"DumpFile",
g_McDumpFile, RTL_NUMBER_OF(g_McDumpFile),
L"");
RegCloseKey(Key);
return S_OK;
}
HRESULT
GetRegCrashControl(void)
{
HRESULT Status;
ULONG Error;
HKEY Key;
WCHAR TmpPath[MAX_PATH];
PWSTR Scan;
Error = RegOpenKey(HKEY_LOCAL_MACHINE,
SUBKEY_CRASH_CONTROL,
&Key);
if (Error != ERROR_SUCCESS)
{
// If the key doesn't exist we just go with the defaults.
return S_OK;
}
GetRegWord32(Key, L"LogEvent", &g_CcLogEvent, 0, TRUE);
GetRegWord32(Key, L"SendAlert", &g_CcSendAlert, 0, TRUE);
GetRegWord32(Key, L"Overwrite", &g_CcOverwrite, 0, TRUE);
GetRegWord32(Key, L"ReportMachineDump", &g_CcReportMachineDump, 0, TRUE);
if ((Status = ExpandRegStr(Key, L"MiniDumpDir",
TmpPath, RTL_NUMBER_OF(TmpPath),
L"%SystemRoot%\\Minidump",
g_CcMiniDumpDir,
RTL_NUMBER_OF(g_CcMiniDumpDir))) != S_OK)
{
g_CcMiniDumpDir[0] = 0;
goto Exit;
}
// Remove any trailing slash on the directory name.
Scan = g_CcMiniDumpDir + wcslen(g_CcMiniDumpDir);
if (Scan > g_CcMiniDumpDir && *(Scan - 1) == L'\\')
{
*--Scan = 0;
}
if ((Status = ExpandRegStr(Key, L"DumpFile",
TmpPath, RTL_NUMBER_OF(TmpPath),
L"%SystemRoot%\\MEMORY.DMP",
g_CcDumpFile,
RTL_NUMBER_OF(g_CcDumpFile))) != S_OK)
{
g_CcDumpFile[0] = 0;
goto Exit;
}
Status = S_OK;
Exit:
RegCloseKey(Key);
return Status;
}
HRESULT
GetDumpInfo(void)
{
HANDLE File;
ULONG Bytes;
BOOL Succ;
HRESULT Status;
if (!g_McDumpFile[0])
{
return E_NOINTERFACE;
}
File = CreateFile(g_McDumpFile,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);
if (File == INVALID_HANDLE_VALUE)
{
return E_NOINTERFACE;
}
Succ = ReadFile(File,
&g_DumpHeader,
sizeof(g_DumpHeader),
&Bytes,
NULL);
CloseHandle(File);
if (Succ &&
Bytes == sizeof(g_DumpHeader) &&
g_DumpHeader.Signature == DUMP_SIGNATURE &&
g_DumpHeader.ValidDump == DUMP_VALID_DUMP)
{
#ifdef _WIN64
Status =
StringCchPrintf(g_DumpBugCheckString,
RTL_NUMBER_OF(g_DumpBugCheckString),
L"0x%08x (0x%016I64x, 0x%016I64x, 0x%016I64x, 0x%016I64x)",
g_DumpHeader.BugCheckCode,
g_DumpHeader.BugCheckParameter1,
g_DumpHeader.BugCheckParameter2,
g_DumpHeader.BugCheckParameter3,
g_DumpHeader.BugCheckParameter4);
#else
Status =
StringCchPrintf(g_DumpBugCheckString,
RTL_NUMBER_OF(g_DumpBugCheckString),
L"0x%08x (0x%08x, 0x%08x, 0x%08x, 0x%08x)",
g_DumpHeader.BugCheckCode,
g_DumpHeader.BugCheckParameter1,
g_DumpHeader.BugCheckParameter2,
g_DumpHeader.BugCheckParameter3,
g_DumpHeader.BugCheckParameter4);
#endif
// This check and message are here just to make
// it easy to catch cases where the message outgrows
// the buffer. It is highly unlikely that this will happen.
if (Status != S_OK)
{
KdPrint(("SAVEDUMP: g_DumpBugCheckString too small\n"));
Status = S_OK;
}
}
else
{
ZeroMemory(&g_DumpHeader, sizeof(g_DumpHeader));
Status = E_NOINTERFACE;
}
return Status;
}
HRESULT
SetSecurity(HANDLE FileHandle)
{
PSID LocalSystemSid = NULL;
PSID AdminSid = NULL;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
PSECURITY_DESCRIPTOR SecurityDescriptor;
BYTE SdBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
PACL Acl;
BYTE AclBuffer[1024];
HANDLE Token = NULL;
PTOKEN_OWNER TokOwner;
ULONG TryLen = 256;
ULONG RetLen;
NTSTATUS NtStatus;
NtStatus = RtlAllocateAndInitializeSid(&NtAuthority, 1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&LocalSystemSid);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
NtStatus = RtlAllocateAndInitializeSid(&NtAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &AdminSid);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
SecurityDescriptor = (PSECURITY_DESCRIPTOR)SdBuffer;
//
// You can be fancy and compute the exact size, but since the
// security descriptor capture code has to do that anyway, why
// do it twice?
//
Acl = (PACL)AclBuffer;
NtStatus = RtlCreateSecurityDescriptor(SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
NtStatus = RtlCreateAcl(Acl, sizeof(AclBuffer), ACL_REVISION);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
//
// Current user, Administrator and System have full control
//
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &Token) &&
!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token))
{
NtStatus = STATUS_ACCESS_DENIED;
goto Exit;
}
for (;;)
{
TokOwner = (PTOKEN_OWNER)malloc(TryLen);
if (!TokOwner)
{
NtStatus = STATUS_NO_MEMORY;
goto Exit;
}
if (GetTokenInformation(Token, TokenOwner, TokOwner, TryLen, &RetLen))
{
NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION,
GENERIC_ALL | DELETE |
WRITE_DAC | WRITE_OWNER,
TokOwner->Owner);
break;
}
else if (RetLen <= TryLen)
{
NtStatus = STATUS_ACCESS_DENIED;
break;
}
free(TokOwner);
TryLen = RetLen;
}
free(TokOwner);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION,
GENERIC_ALL | DELETE |
WRITE_DAC | WRITE_OWNER,
AdminSid);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
NtStatus = RtlAddAccessAllowedAce(Acl, ACL_REVISION,
GENERIC_ALL | DELETE |
WRITE_DAC | WRITE_OWNER,
LocalSystemSid);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
NtStatus = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Acl,
FALSE);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
NtStatus = RtlSetOwnerSecurityDescriptor(SecurityDescriptor, AdminSid,
FALSE);
if (!NT_SUCCESS(NtStatus))
{
goto Exit;
}
NtStatus = NtSetSecurityObject(FileHandle,
DACL_SECURITY_INFORMATION,
SecurityDescriptor);
Exit:
if (AdminSid)
{
RtlFreeSid(AdminSid);
}
if (LocalSystemSid)
{
RtlFreeSid(LocalSystemSid);
}
if (Token)
{
CloseHandle(Token);
}
return NT_SUCCESS(NtStatus) ? S_OK : HRESULT_FROM_NT(NtStatus);
}
HRESULT
CreateMiniDumpFile(PHANDLE MiniFileHandle)
{
INT i;
SYSTEMTIME Time;
HRESULT Status;
HANDLE FileHandle;
if (!g_CcMiniDumpDir[0])
{
// Bad minidump directory.
return E_INVALIDARG;
}
//
// If directory does not exist, create it. Ignore errors here because
// they will be picked up later when we try to create the file.
//
CreateDirectory(g_CcMiniDumpDir, NULL);
//
// Format is: Mini-MM_DD_YY_HH_MM.dmp
//
GetLocalTime(&Time);
for (i = 1; i < 100; i++)
{
if ((Status = StringCchPrintf(g_MiniDumpFile,
RTL_NUMBER_OF(g_MiniDumpFile),
L"%s\\Mini%2.2d%2.2d%2.2d-%2.2d.dmp",
g_CcMiniDumpDir,
(int)Time.wMonth,
(int)Time.wDay,
(int)Time.wYear % 100,
(int)i)) != S_OK)
{
g_MiniDumpFile[0] = 0;
return Status;
}
FileHandle = CreateFile(g_MiniDumpFile,
GENERIC_WRITE | WRITE_DAC,
0,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (FileHandle != INVALID_HANDLE_VALUE)
{
break;
}
}
if (FileHandle == INVALID_HANDLE_VALUE)
{
// We failed to create a suitable file name.
g_MiniDumpFile[0] = 0;
return E_FAIL;
}
if ((Status = SetSecurity(FileHandle)) != S_OK)
{
CloseHandle(FileHandle);
DeleteFile(g_MiniDumpFile);
g_MiniDumpFile[0] = 0;
return Status;
}
*MiniFileHandle = FileHandle;
return S_OK;
}
#define COPY_CHUNK (1024 * 1024)
HRESULT
CopyAndSecureFile(PWSTR Source,
PWSTR Dest,
HANDLE DestHandle,
BOOL Overwrite,
BOOL DeleteDest)
{
HRESULT Status;
HANDLE SourceHandle = INVALID_HANDLE_VALUE;
PUCHAR Buffer = NULL;
Buffer = (PUCHAR)malloc(COPY_CHUNK);
if (!Buffer)
{
Status = E_OUTOFMEMORY;
goto Exit;
}
SourceHandle = CreateFile(Source,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (SourceHandle == INVALID_HANDLE_VALUE)
{
Status = LAST_HR();
goto Exit;
}
if (DestHandle == INVALID_HANDLE_VALUE)
{
DestHandle = CreateFile(Dest,
GENERIC_WRITE | WRITE_DAC,
0,
NULL,
Overwrite ? CREATE_ALWAYS : CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (DestHandle == INVALID_HANDLE_VALUE)
{
Status = LAST_HR();
goto Exit;
}
DeleteDest = TRUE;
if ((Status = SetSecurity(DestHandle)) != S_OK)
{
goto Exit;
}
}
for (;;)
{
ULONG Req, Done;
if (!ReadFile(SourceHandle, Buffer, COPY_CHUNK, &Done, NULL))
{
Status = LAST_HR();
break;
}
else if (Done == 0)
{
// End-of-file.
Status = S_OK;
break;
}
Req = Done;
if (!WriteFile(DestHandle, Buffer, Req, &Done, NULL))
{
Status = LAST_HR();
break;
}
else if (Done < Req)
{
Status = HRESULT_FROM_WIN32(ERROR_HANDLE_DISK_FULL);
break;
}
}
Exit:
if (DeleteDest)
{
CloseHandle(DestHandle);
if (Status != S_OK)
{
DeleteFile(Dest);
}
}
if (SourceHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(SourceHandle);
}
free(Buffer);
return Status;
}
HRESULT
MoveDumpFile(void)
{
HRESULT Status;
if (g_DumpHeader.Signature != DUMP_SIGNATURE)
{
// Dump file is not present or invalid, so there's nothing to do.
return S_OK;
}
//
// If the dump file needs to be copied, copy it now.
//
if (!g_McTempDestination)
{
g_FinalDumpFile = g_McDumpFile;
}
else
{
if (!g_Test)
{
//
// Set the priority class of this application down to the Lowest
// priority class to ensure that copying the file does not overload
// everything else that is going on during system initialization.
//
// We do not lower the priority in test mode because it just
// wastes time.
//
SetPriorityClass (GetCurrentProcess(), IDLE_PRIORITY_CLASS);
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_LOWEST);
}
if (g_DumpHeader.DumpType == DUMP_TYPE_FULL ||
g_DumpHeader.DumpType == DUMP_TYPE_SUMMARY)
{
if (!g_CcDumpFile[0])
{
// Invalid dump file registry entry.
return E_INVALIDARG;
}
if ((Status = CopyAndSecureFile(g_McDumpFile,
g_CcDumpFile,
INVALID_HANDLE_VALUE,
g_CcOverwrite ? TRUE : FALSE,
TRUE)) != S_OK)
{
return Status;
}
g_FinalDumpFile = g_CcDumpFile;
}
else if (g_DumpHeader.DumpType == DUMP_TYPE_TRIAGE)
{
HANDLE MiniFile;
if ((Status = CreateMiniDumpFile(&MiniFile)) != S_OK)
{
return Status;
}
if ((Status = CopyAndSecureFile(g_McDumpFile,
NULL,
MiniFile,
FALSE,
TRUE)) != S_OK)
{
g_MiniDumpFile[0] = 0;
return Status;
}
g_FinalDumpFile = g_MiniDumpFile;
}
DeleteFile(g_McDumpFile);
g_McDumpFile[0] = 0;
}
return S_OK;
}
HRESULT
ConvertDumpFile(void)
{
HRESULT Status;
IDebugClient4 *DebugClient;
IDebugControl *DebugControl;
HANDLE MiniFile;
//
// Produce a minidump by conversion if necessary.
//
if (!g_FinalDumpFile ||
(g_DumpHeader.DumpType != DUMP_TYPE_FULL &&
g_DumpHeader.DumpType != DUMP_TYPE_SUMMARY))
{
// No dump or not a convertable dump.
return S_OK;
}
if ((Status = CreateMiniDumpFile(&MiniFile)) != S_OK)
{
return Status;
}
if ((Status = DebugCreate(__uuidof(IDebugClient4),
(void **)&DebugClient)) != S_OK)
{
goto EH_File;
}
if ((Status = DebugClient->QueryInterface(__uuidof(IDebugControl),
(void **)&DebugControl)) != S_OK)
{
goto EH_Client;
}
if ((Status = DebugClient->OpenDumpFileWide(g_FinalDumpFile, 0)) != S_OK)
{
goto EH_Control;
}
if ((Status = DebugControl->
WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) != S_OK)
{
goto EH_Control;
}
Status = DebugClient->
WriteDumpFileWide(NULL, (ULONG_PTR)MiniFile, DEBUG_DUMP_SMALL,
DEBUG_FORMAT_DEFAULT, NULL);
EH_Control:
DebugControl->Release();
EH_Client:
DebugClient->EndSession(DEBUG_END_PASSIVE);
DebugClient->Release();
EH_File:
CloseHandle(MiniFile);
if (Status != S_OK)
{
DeleteFile(g_MiniDumpFile);
g_MiniDumpFile[0] = 0;
}
return Status;
}
VOID
LogEvent(ULONG Id,
WORD StringCount,
PWSTR* Strings)
{
HANDLE LogHandle;
BOOL Retry;
DWORD Retries;
//
// Attempt to register the event source.
// Savedump runs early in the startup process so
// it's possible that the event service hasn't started
// yet. If it appears that the service hasn't started,
// wait a bit until it comes around. If it hasn't
// come around after a reasonable amount of time bail out.
//
Retries = 0;
do
{
LogHandle = RegisterEventSource(NULL, L"Save Dump");
//
// Retry on specific failures that indicate the event
// service hasn't started yet.
//
if (LogHandle == NULL &&
Retries < 20 &&
(GetLastError () == RPC_S_SERVER_UNAVAILABLE ||
GetLastError () == RPC_S_UNKNOWN_IF))
{
Sleep(1500);
Retry = TRUE;
}
else
{
Retry = FALSE;
}
Retries++;
}
while (LogHandle == NULL && Retry);
if (!LogHandle)
{
KdPrint(("SAVEDUMP: Unable to register event source, %d\n",
GetLastError()));
return;
}
if (!ReportEvent(LogHandle,
EVENTLOG_INFORMATION_TYPE,
0,
Id,
NULL,
StringCount,
0,
(LPCWSTR *)Strings,
NULL))
{
KdPrint(("SAVEDUMP: Unable to report event, %d\n", GetLastError()));
}
DeregisterEventSource(LogHandle);
}
void
LogCrashDumpEvent(void)
{
LPWSTR StringArray[2];
WORD StringCount;
DWORD EventId;
//
// Set up the parameters based on how much information
// is available.
//
StringCount = 0;
if (g_DumpBugCheckString[0])
{
StringArray[StringCount++] = g_DumpBugCheckString;
}
if (g_FinalDumpFile)
{
StringArray[StringCount++] = g_FinalDumpFile;
}
//
// Report the appropriate event.
//
if (g_FinalDumpFile)
{
EventId = EVENT_BUGCHECK_SAVED;
}
else if (g_DumpBugCheckString[0])
{
EventId = EVENT_BUGCHECK;
}
else
{
EventId = EVENT_UNKNOWN_BUGCHECK;
}
LogEvent(EventId, StringCount, StringArray);
}
void
SendCrashDumpAlert(void)
{
PADMIN_OTHER_INFO AdminInfo;
DWORD AdminInfoSize;
DWORD Length;
DWORD i;
ULONG Error;
UCHAR VariableInfo[4096];
//
// Set up the administrator information variables for processing the
// buffer.
//
AdminInfo = (PADMIN_OTHER_INFO)VariableInfo;
AdminInfo->alrtad_numstrings = 0;
AdminInfoSize = sizeof(*AdminInfo);
//
// Format the bugcheck information into the appropriate message format.
//
if (g_DumpBugCheckString[0])
{
Length = (wcslen(g_DumpBugCheckString) + 1) * sizeof(WCHAR);
if (AdminInfoSize + Length > sizeof(VariableInfo))
{
goto Error;
}
RtlCopyMemory((PCHAR)AdminInfo + AdminInfoSize,
g_DumpBugCheckString, Length);
AdminInfo->alrtad_numstrings++;
AdminInfoSize += Length;
}
//
// Set up the administrator alert information according to the type of
// dump that was taken.
//
if (g_FinalDumpFile)
{
Length = (wcslen(g_FinalDumpFile) + 1) * sizeof(WCHAR);
if (AdminInfoSize + Length > sizeof(VariableInfo))
{
goto Error;
}
AdminInfo->alrtad_errcode = ALERT_BugCheckSaved;
RtlCopyMemory((PCHAR)AdminInfo + AdminInfoSize,
g_FinalDumpFile, Length);
AdminInfo->alrtad_numstrings++;
AdminInfoSize += Length;
}
else
{
AdminInfo->alrtad_errcode = ALERT_BugCheck;
}
//
// Get the name of the computer and insert it into the buffer.
//
Length = (sizeof(VariableInfo) - AdminInfoSize) / sizeof(WCHAR);
if (!GetComputerName((LPWSTR)((PCHAR)AdminInfo + AdminInfoSize),
&Length))
{
goto Error;
}
Length = (Length + 1) * sizeof(WCHAR);
AdminInfo->alrtad_numstrings++;
AdminInfoSize += Length;
//
// Raise the alert.
//
i = 0;
do
{
Error = NetAlertRaiseEx(ALERT_ADMIN_EVENT,
AdminInfo,
AdminInfoSize,
L"SAVEDUMP");
if (Error)
{
if (Error == ERROR_FILE_NOT_FOUND)
{
if (i++ > 20)
{
break;
}
if ((i & 3) == 0)
{
KdPrint(("SAVEDUMP: Waiting for alerter...\n"));
}
Sleep(15000);
}
}
} while (Error == ERROR_FILE_NOT_FOUND);
if (Error != ERROR_SUCCESS)
{
goto Error;
}
return;
Error:
KdPrint(("SAVEDUMP: Unable to raise alert\n"));
}
VOID
__cdecl
wmain(int Argc,
PWSTR Argv[])
{
int Arg;
BOOL Report = TRUE;
for (Arg = 1; Arg < Argc; Arg++)
{
if (Argv[Arg][0] == L'-' || Argv[Arg][0] == L'/')
{
switch (Argv[Arg][1])
{
case L't':
case L'T':
#if DBG
g_Test = TRUE;
#endif
break;
default:
break;
}
}
}
if (GetRegMachineCrash() != S_OK ||
GetRegCrashControl() != S_OK)
{
LogEvent(EVENT_UNABLE_TO_READ_REGISTRY, 0, NULL);
}
GetDumpInfo();
if (MoveDumpFile() != S_OK)
{
LogEvent(EVENT_UNABLE_TO_MOVE_DUMP_FILE, 0, NULL);
}
if (ConvertDumpFile() != S_OK)
{
LogEvent(EVENT_UNABLE_TO_CONVERT_DUMP_FILE, 0, NULL);
}
//if (WatchdogEventHandler(TRUE) == S_OK)
//{
// // Note: Bugcheck EA will be reported in the watchdog code since
// // we need to send minidump and wdl files together and we want to have
// // a single pop-up only.
// Report = FALSE;
//}
if (Report)
{
// The default behavior is to report a minidump
// even if the machine dump was not a minidump in
// order to minimize the amount of data sent.
// If the system is configured to report the
// machine dump go ahead and send it regardless
// of what kind of dump it is.
PWSTR ReportDumpFile = g_CcReportMachineDump ?
g_FinalDumpFile : g_MiniDumpFile;
if (ReportDumpFile && ReportDumpFile[0])
{
// Report bugcheck to Microsoft Error Reporting.
if (FrrvToStatus(ReportEREvent(eetKernelFault,
ReportDumpFile,
NULL)) != S_OK)
{
LogEvent(EVENT_UNABLE_TO_REPORT_BUGCHECK, 0, NULL);
}
}
}
//
// Knock down reliability ShutdownEventPending flag. We must always try
// to do this since somebody can set this flag and recover later on
// (e.g. watchdog's EventFlag cleared). With this flag set savedump
// will always run and we don't want that.
//
// Note: This flag is shared between multiple components.
// Only savedump is allowed to clear this flag, all other
// components are only allowed to set it to trigger
// savedump run at next logon.
//
HKEY Key;
if (RegOpenKey(HKEY_LOCAL_MACHINE,
SUBKEY_RELIABILITY,
&Key) == ERROR_SUCCESS)
{
RegDeleteValue(Key, L"ShutdownEventPending");
RegCloseKey(Key);
}
//
// If there was a dump produced we may need to log an event
// and send an alert.
// We delay these time consuming opertaions till the end. We had the
// case where SendCrashDumpAlert delayed PC Health pop-ups few minutes.
//
BOOL HaveCrashData =
g_McDumpFile[0] ||
g_DumpBugCheckString[0] ||
g_FinalDumpFile;
if (HaveCrashData && g_CcLogEvent)
{
LogCrashDumpEvent();
}
//
// This function will fill the BugCheckString for DirtyShutdown UI based
// on the flag set by EventLog service during startup time, in some case
// Eventlog service might start after savedump, so we will need to run this
// function after the first event was logged by savedump.
// if g_CcLogEvent == FALSE, it is OK not set the string since the user
// are not interested about the BugCheck info at all.
//
if (DirtyShutdownEventHandler(TRUE) != S_OK)
{
LogEvent(EVENT_UNABLE_TO_REPORT_DIRTY_SHUTDOWN, 0, NULL);
}
if (HaveCrashData && g_CcSendAlert)
{
SendCrashDumpAlert();
}
}