// DBGLauncher.cpp : Implementation of WinMain // Note: Proxy/Stub Information // To build a separate proxy/stub DLL, // run nmake -f DBGLauncherps.mk in the project directory. #include "stdafx.h" #include "resource.h" #include "Messages.h" #include #include #include #include "DBGLauncher_i.c" #include "ocamon.h" #define MAX_QUEUE_OPEN_RETRY 5 static WCHAR strGuidMQTestType[] = L"{c30e0960-a2c0-11cf-9785-00608cb3e80c}"; // Some useful macros #define RELEASE(punk) if (punk) { (punk)->Release(); (punk) = NULL; } #define ADDREF(punk) ((punk) ? (punk)->AddRef() : 0) CServiceModule _Module; HANDLE g_hStopEvent = NULL; BOOL g_ReadQueueFlag = TRUE; BEGIN_OBJECT_MAP(ObjectMap) END_OBJECT_MAP() LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2) { while (p1 != NULL && *p1 != NULL) { LPCTSTR p = p2; while (p != NULL && *p != NULL) { if (*p1 == *p) return CharNext(p1); p = CharNext(p); } p1 = CharNext(p1); } return NULL; } // Although some of these functions are big they are declared inline since they are only used once inline HRESULT CServiceModule::RegisterServer(BOOL bRegTypeLib, BOOL bService) { HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) return hr; // Remove any previous service since it may point to // the incorrect file Uninstall(); SetupEventLog(FALSE); // Add service entries UpdateRegistryFromResource(IDR_DBGLauncher, TRUE); // Adjust the AppID for Local Server or Service CRegKey keyAppID; LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE); if (lRes != ERROR_SUCCESS) return lRes; CRegKey key; lRes = key.Open(keyAppID, _T("{5D3C7CA6-DF04-4864-897D-83BF996692B3}"), KEY_WRITE); if (lRes != ERROR_SUCCESS) return lRes; key.DeleteValue(_T("LocalService")); if (bService) { key.SetValue(_T("DBGLauncher"), _T("LocalService") ); key.SetValue(_T("-Service"), _T("ServiceParameters")); // Create service Install(); } // Add object entries hr = CComModule::RegisterServer(bRegTypeLib); CoUninitialize(); return hr; } inline HRESULT CServiceModule::UnregisterServer() { HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) return hr; // Remove service entries UpdateRegistryFromResource(IDR_DBGLauncher, FALSE); // Remove service Uninstall(); // Remove object entries CComModule::UnregisterServer(TRUE); CoUninitialize(); return S_OK; } inline void CServiceModule::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, UINT nServiceNameID, const GUID* plibid) { CComModule::Init(p, h, plibid); m_bService = TRUE; LoadString(h, nServiceNameID, m_szServiceName, sizeof(m_szServiceName) / sizeof(TCHAR)); // set up the initial service status m_hServiceStatus = NULL; m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; m_status.dwCurrentState = SERVICE_STOPPED; m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; m_status.dwWin32ExitCode = 0; m_status.dwServiceSpecificExitCode = 0; m_status.dwCheckPoint = 0; m_status.dwWaitHint = 0; // // Create Named Events // g_hStopEvent = CreateEvent( NULL, FALSE, FALSE, s_cszStopEvent ); if(NULL == g_hStopEvent) { LogEvent( _T("Failed to create stop event: %s; hr=%ld"), s_cszStopEvent, GetLastError()); } m_hMonNotifyPipe = NULL; } LONG CServiceModule::Unlock() { LONG l = CComModule::Unlock(); if (l == 0 && !m_bService) PostThreadMessage(dwThreadID, WM_QUIT, 0, 0); return l; } BOOL CServiceModule::IsInstalled() { BOOL bResult = FALSE; SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (hSCM != NULL) { SC_HANDLE hService = ::OpenService(hSCM, m_szServiceName, SERVICE_QUERY_CONFIG); if (hService != NULL) { bResult = TRUE; ::CloseServiceHandle(hService); } ::CloseServiceHandle(hSCM); } return bResult; } inline BOOL CServiceModule::Install() { if (IsInstalled()) return TRUE; SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (hSCM == NULL) { MessageBox(NULL, _T("Couldn't open service manager"), m_szServiceName, MB_OK); return FALSE; } SetupEventLog(TRUE); // Get the executable file path TCHAR szFilePath[_MAX_PATH]; ::GetModuleFileName(NULL, szFilePath, _MAX_PATH); SC_HANDLE hService = ::CreateService( hSCM, m_szServiceName, m_szServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL, _T("RPCSS\0"), NULL, NULL); if (hService == NULL) { ::CloseServiceHandle(hSCM); MessageBox(NULL, _T("Couldn't create service"), m_szServiceName, MB_OK); return FALSE; } SERVICE_FAILURE_ACTIONS Failure; SC_ACTION Actions[3]; Failure.cActions = 3; Failure.dwResetPeriod = 1200; Failure.lpCommand = _T(""); Failure.lpRebootMsg = _T(""); Failure.lpsaActions = Actions; Actions[0].Delay = 2000; Actions[0].Type = SC_ACTION_RESTART; Actions[1].Delay = 2000; Actions[1].Type = SC_ACTION_RESTART; Actions[2].Delay = 2000; Actions[2].Type = SC_ACTION_RESTART; ::CloseServiceHandle(hService); ::CloseServiceHandle(hSCM); return TRUE; } inline BOOL CServiceModule::Uninstall() { if (!IsInstalled()) return TRUE; SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (hSCM == NULL) { MessageBox(NULL, _T("Couldn't open service manager"), m_szServiceName, MB_OK); return FALSE; } SC_HANDLE hService = ::OpenService(hSCM, m_szServiceName, SERVICE_STOP | DELETE); if (hService == NULL) { ::CloseServiceHandle(hSCM); MessageBox(NULL, _T("Couldn't open service"), m_szServiceName, MB_OK); return FALSE; } SERVICE_STATUS status; ::ControlService(hService, SERVICE_CONTROL_STOP, &status); BOOL bDelete = ::DeleteService(hService); ::CloseServiceHandle(hService); ::CloseServiceHandle(hSCM); if (bDelete) return TRUE; MessageBox(NULL, _T("Service could not be deleted"), m_szServiceName, MB_OK); return FALSE; } /////////////////////////////////////////////////////////////////////////////////////// // Logging functions void CServiceModule::LogEvent(LPCTSTR pFormat, ...) { TCHAR chMsg[256]; HANDLE hEventSource; LPTSTR lpszStrings[1]; va_list pArg; va_start(pArg, pFormat); // _vstprintf(chMsg, pFormat, pArg); if (StringCbVPrintf(chMsg,sizeof chMsg, pFormat, pArg) != S_OK) { return; } va_end(pArg); lpszStrings[0] = chMsg; if (m_bService) { /* Get a handle to use with ReportEvent(). */ hEventSource = RegisterEventSource(NULL, m_szServiceName); if (hEventSource != NULL) { /* Write to event log. */ ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, EVENT_MESSAGE, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL); DeregisterEventSource(hEventSource); } } else { // As we are not running as a service, just write the error to the console. _putts(chMsg); } } ////////////////////////////////////////////////////////////////////////////////////////////// // Service startup and registration inline void CServiceModule::Start() { SERVICE_TABLE_ENTRY st[] = { { m_szServiceName, _ServiceMain }, { NULL, NULL } }; if (m_bService && !::StartServiceCtrlDispatcher(st)) { m_bService = FALSE; } if (m_bService == FALSE) Run(); } inline void CServiceModule::ServiceMain(DWORD /* dwArgc */, LPTSTR* /* lpszArgv */) { // Register the control request handler m_status.dwCurrentState = SERVICE_START_PENDING; m_hServiceStatus = RegisterServiceCtrlHandler(m_szServiceName, _Handler); if (m_hServiceStatus == NULL) { LogEvent(_T("Handler not installed")); return; } SetServiceStatus(SERVICE_START_PENDING); m_status.dwWin32ExitCode = S_OK; m_status.dwCheckPoint = 0; m_status.dwWaitHint = 0; // When the Run function returns, the service has stopped. Run(); SetupEventLog(TRUE); SetServiceStatus(SERVICE_STOPPED); LogEvent(_T("Service stopped")); } inline void CServiceModule::Handler(DWORD dwOpcode) { switch (dwOpcode) { case SERVICE_CONTROL_STOP: SetServiceStatus(SERVICE_STOP_PENDING); // SetServiceStatus(SERVICE_STOP_PENDING); if(NULL != g_hStopEvent) { if(FALSE == SetEvent( g_hStopEvent )) { LogFatalEvent( _T("Unable to signal Stop Event; Error: %ld"), GetLastError()); } CloseHandle( g_hStopEvent ); } /// PostThreadMessage(dwThreadID, WM_QUIT, 0, 0); // break; PostThreadMessage(dwThreadID, WM_QUIT, 0, 0); break; case SERVICE_CONTROL_PAUSE: break; case SERVICE_CONTROL_CONTINUE: break; case SERVICE_CONTROL_INTERROGATE: break; case SERVICE_CONTROL_SHUTDOWN: break; default: LogEvent(_T("Bad service request")); } } void WINAPI CServiceModule::_ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv) { _Module.ServiceMain(dwArgc, lpszArgv); } void WINAPI CServiceModule::_Handler(DWORD dwOpcode) { _Module.Handler(dwOpcode); } void CServiceModule::SetServiceStatus(DWORD dwState) { m_status.dwCurrentState = dwState; ::SetServiceStatus(m_hServiceStatus, &m_status); } void CServiceModule::Run() { BYTE *byteVersionBuff = NULL; VS_FIXEDFILEINFO *pVersionInfo = NULL; UINT uLength = 0; _Module.dwThreadID = GetCurrentThreadId(); TCHAR szFilePath[_MAX_PATH]; DWORD dwPlaceHolder = 0; // HRESULT hr = CoInitialize(NULL); // If you are running on NT 4.0 or higher you can use the following call // instead to make the EXE free threaded. // This means that calls come in on a random RPC thread HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); _ASSERTE(SUCCEEDED(hr)); // This provides a NULL DACL which will allow access to everyone. CSecurityDescriptor sd; sd.InitializeFromThreadToken(); hr = CoInitializeSecurity(sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); _ASSERTE(SUCCEEDED(hr)); hr = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, REGCLS_MULTIPLEUSE); _ASSERTE(SUCCEEDED(hr)); ZeroMemory(szFilePath, sizeof szFilePath); ::GetModuleFileName(NULL, szFilePath, _MAX_PATH); if (szFilePath[0] != _T('\0')) { DWORD dwBufferSize = GetFileVersionInfoSize(szFilePath,&dwPlaceHolder); if (dwBufferSize > 0) byteVersionBuff = (BYTE*) malloc (dwBufferSize); if (byteVersionBuff) { GetFileVersionInfo(szFilePath,NULL,dwBufferSize,byteVersionBuff); VerQueryValue(byteVersionBuff,_T("\\"),(VOID **) &pVersionInfo, &uLength); LogEvent(_T("DbgLauncher service version: %d.%d.%d.%d Started."), HIWORD (pVersionInfo->dwFileVersionMS),LOWORD(pVersionInfo->dwFileVersionMS) ,HIWORD(pVersionInfo->dwFileVersionLS),LOWORD(pVersionInfo->dwFileVersionLS)) ; } } // DWORD NameLength = sizeof(TCHAR) * (MAX_COMPUTERNAME_LENGTH + 1); if (m_bService) SetServiceStatus(SERVICE_RUNNING); // // Execute Archive Service // try { PrepareForDebuggerLaunch(); } catch(...) { LogEvent( _T("dbgLauncher Service CRASHED !!! ")); } if (m_hMonNotifyPipe != NULL && m_hMonNotifyPipe != INVALID_HANDLE_VALUE) { CloseHandle(m_hMonNotifyPipe); m_hMonNotifyPipe = NULL; } MSG msg; while (GetMessage(&msg, 0, 0, 0)) DispatchMessage(&msg); _Module.RevokeClassObjects(); CoUninitialize(); } ///////////////////////////////////////////////////////////////////////////// // extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nShowCmd*/) { lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT _Module.Init(ObjectMap, hInstance, IDS_SERVICENAME, &LIBID_DBGLAUNCHERLib); _Module.m_bService = TRUE; TCHAR szTokens[] = _T("-/"); LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens); while (lpszToken != NULL) { if (CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, lpszToken, -1, _T("UnregServer"), -1 ) == CSTR_EQUAL) // if (lstrcmpi(lpszToken, _T("UnregServer"))==0) return _Module.UnregisterServer(); // Register as Local Server if (CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, lpszToken, -1, _T("RegServer"), -1 ) == CSTR_EQUAL) //if (lstrcmpi(lpszToken, _T("RegServer"))==0) return _Module.RegisterServer(TRUE, FALSE); // Register as Service if (CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), NORM_IGNORECASE, lpszToken, -1, _T("Service"), -1 ) == CSTR_EQUAL) // if (lstrcmpi(lpszToken, _T("Service"))==0) return _Module.RegisterServer(TRUE, TRUE); lpszToken = FindOneOf(lpszToken, szTokens); } // Are we Service or Local Server CRegKey keyAppID; LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_READ); if (lRes != ERROR_SUCCESS) return lRes; CRegKey key; lRes = key.Open(keyAppID, _T("{5D3C7CA6-DF04-4864-897D-83BF996692B3}"), KEY_READ); if (lRes != ERROR_SUCCESS) return lRes; TCHAR szValue[_MAX_PATH]; DWORD dwLen = _MAX_PATH; lRes = key.QueryValue(szValue,_T("LocalService"), &dwLen); _Module.m_bService = FALSE; if (lRes == ERROR_SUCCESS) _Module.m_bService = TRUE; _Module.Start(); // When we get here, the service has been stopped return _Module.m_status.dwWin32ExitCode; } BOOL CServiceModule::GetServiceParams(SVCPARAMS *ServiceParams) { HKEY hHKLM; HKEY hDbgLauncherKey; BYTE Buffer[MAX_PATH + 1]; DWORD Type; DWORD BufferSize = MAX_PATH +1; // Set for largest value BOOL Status = TRUE; if(!RegConnectRegistry(NULL, HKEY_LOCAL_MACHINE, &hHKLM)) { if(!RegOpenKeyEx(hHKLM,_T("Software\\Microsoft\\DbgLauncherSvc"), 0, KEY_ALL_ACCESS, &hDbgLauncherKey)) { // Get the input queue directory path BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); if (RegQueryValueEx(hDbgLauncherKey,_T("DebuggerName"), 0, &Type, Buffer, &BufferSize) != ERROR_SUCCESS) { LogEvent(_T("Failed to get InputQueue value from registry.")); Status = FALSE; } else { if (StringCbCopy (ServiceParams->DebuggerName, sizeof ServiceParams->DebuggerName,(TCHAR *) Buffer)!= S_OK) { LogEvent (_T("Failed to copy debuggername reg value to ServiceParams->DebuggerName")); Status = FALSE; } } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the Primary Queue connection string if (RegQueryValueEx(hDbgLauncherKey,_T("PrimaryQueue"), 0, &Type, Buffer, &BufferSize) != ERROR_SUCCESS) { LogEvent(_T("Failed to get PrimaryQueue value from registry.")); Status = FALSE; } else { if (StringCbCopy(ServiceParams->PrimaryQueue,sizeof ServiceParams->PrimaryQueue, (TCHAR *) Buffer)!= S_OK) { LogEvent (_T("Failed to copy PrimaryQueue reg value to ServiceParams->PrimaryQueue")); Status = FALSE; } } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the Primary Queue connection string if (RegQueryValueEx(hDbgLauncherKey,_T("SecondaryQueue"), 0, &Type, Buffer, &BufferSize) != ERROR_SUCCESS) { LogEvent(_T("Failed to get SecondaryQueue value from registry.")); Status = FALSE; } else { if (StringCbCopy(ServiceParams->SecondaryQueue,sizeof ServiceParams->SecondaryQueue, (TCHAR *) Buffer)!= S_OK) { LogEvent (_T("Failed to copy SecondaryQueue reg value to ServiceParams->SecondaryQueue")); Status = FALSE; } } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // now get the primary response queue if (RegQueryValueEx(hDbgLauncherKey,_T("PrimaryResponseQueue"), 0, &Type, Buffer, &BufferSize) != ERROR_SUCCESS) { LogEvent(_T("Failed to get Primary ResponseQueue value from registry.")); Status = FALSE; } else { if (StringCbCopy(ServiceParams->PrimaryResponseQueue,sizeof ServiceParams->PrimaryResponseQueue, (TCHAR *) Buffer) != S_OK) { LogEvent (_T("Failed to copy PrimaryResponseQueue reg value to ServiceParams->PrimaryResponseQueue")); Status = FALSE; } } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // now get the secondary response queue if (RegQueryValueEx(hDbgLauncherKey,_T("SecondaryResponseQueue"), 0, &Type, Buffer, &BufferSize) != ERROR_SUCCESS) { LogEvent(_T("Failed to get Secondary ResponseQueue value from registry.")); Status = FALSE; } else { if (StringCbCopy(ServiceParams->SecondaryResponseQueue,sizeof ServiceParams->SecondaryResponseQueue, (TCHAR *) Buffer)!= S_OK) { LogEvent (_T("Failed to copy SecondaryResponseQueue reg value to ServiceParams->SecondaryResponseQueue")); Status = FALSE; } } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the ini release point if (RegQueryValueEx(hDbgLauncherKey,_T("IniInstallLocation"), 0, &Type, Buffer, &BufferSize) != ERROR_SUCCESS) { LogEvent(_T("Failed to get IniInstallLocation Queue value from registry.")); Status = FALSE; } else { if (StringCbCopy(ServiceParams->IniInstallLocation,sizeof ServiceParams->IniInstallLocation, (TCHAR *) Buffer)!= S_OK) { LogEvent (_T("Failed to copy IniInstallLocation reg value to ServiceParams->IniInstallLocation")); Status = FALSE; } } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the Symbols server connection string if (RegQueryValueEx(hDbgLauncherKey,_T("SymSrv"), 0, &Type, Buffer, &BufferSize) != ERROR_SUCCESS) { LogFatalEvent(_T("Failed to get the symbol server value from the registry.")); Status = FALSE; } else { if (StringCbCopy(ServiceParams->Symsrv,sizeof ServiceParams->Symsrv, (TCHAR *) Buffer) != S_OK) { LogEvent (_T("Failed to copy Symsrv reg value to ServiceParams->Symsrv")); Status = FALSE; } } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the Delay -- Min time between kd launches if (RegQueryValueEx(hDbgLauncherKey,_T("Delay"), 0, &Type, Buffer, &BufferSize)) { LogEvent(_T("Failed to get the min delay between dbg launches.")); } else { ServiceParams->dwDelay = *((DWORD*)Buffer); } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the Primary Queue delay if (RegQueryValueEx(hDbgLauncherKey,_T("PrimaryInterval"), 0, &Type, Buffer, &BufferSize)) { LogFatalEvent(_T("Failed to get the Primary queue wait interval.")); Status = FALSE; } else { ServiceParams->dwPrimaryWait = *((DWORD*)Buffer); } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the Primary Queue delay if (RegQueryValueEx(hDbgLauncherKey,_T("IniWaitTime"), 0, &Type, Buffer, &BufferSize)) { LogFatalEvent(_T("Failed to get the triage.Ini wait interval.")); Status = FALSE; } else { ServiceParams->IniCheckWaitTime = *((DWORD*)Buffer); } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the Memory usage threshold if (RegQueryValueEx(hDbgLauncherKey,_T("MaxKdProcesses"), 0, &Type, Buffer, &BufferSize)) { LogFatalEvent(_T("Failed to get the Max Kd Processes setting.")); Status = FALSE; } else { ServiceParams->dwMaxKdProcesses = *((DWORD*)Buffer); } BufferSize = MAX_PATH +1; ZeroMemory(Buffer, BufferSize); // Now get the Memory usage threshold if (RegQueryValueEx(hDbgLauncherKey,_T("MaxDumpSize"), 0, &Type, Buffer, &BufferSize)) { LogEvent(_T("Failed to get the Max Dump Size setting.")); ServiceParams->dwMaxDumpSize = -1; } else { ServiceParams->dwMaxDumpSize = *((DWORD*)Buffer); } RegCloseKey(hHKLM); RegCloseKey(hDbgLauncherKey); return TRUE; } else { RegCloseKey(hHKLM); return FALSE; } } else return FALSE; } DWORD CServiceModule::CheckForIni (SVCPARAMS *ServiceParams) { FILETIME CreationTimeCurrent; FILETIME CreationTimeNew; TCHAR IniFileName[MAX_PATH]; TCHAR DebuggerPath[MAX_PATH]; TCHAR *Temp = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hFile2 = INVALID_HANDLE_VALUE; ZeroMemory (&CreationTimeCurrent, sizeof FILETIME); ZeroMemory (&CreationTimeNew, sizeof FILETIME); if (_tcslen(ServiceParams->DebuggerName) + _tcslen(_T("\\winxp\\triage.ini")) < MAX_PATH) { if (StringCbCopy(DebuggerPath, sizeof DebuggerPath, ServiceParams->DebuggerName)!= S_OK) { LogEvent(_T("CheckForIni: Failed to build Debugger Path")); } Temp = DebuggerPath; Temp += _tcslen(DebuggerPath); if (Temp != DebuggerPath) { while(*Temp != _T('\\')) { -- Temp; } *(Temp+1) = _T('\0'); } // Get the file path of the debugger remove the ocakd.exe string then add triage\\triage.ini if (StringCbCat (DebuggerPath,sizeof DebuggerPath, _T("winxp\\triage.ini")) != S_OK) { LogEvent(_T("CheckForIni: Failed to build debugger path")); } // Check to see if we need a new triage.ini file. // open a shared read handle to the existing ini file. hFile = CreateFile(DebuggerPath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { // Get the time date stamp of the existing triage ini file. if ( GetFileTime(hFile,NULL,NULL,&CreationTimeCurrent) ) { // Now get the filetime for the triage.ini replacement. hFile2 = CreateFile(ServiceParams->IniInstallLocation, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile2 != INVALID_HANDLE_VALUE) { if (GetFileTime(hFile2, NULL,NULL,&CreationTimeNew)) { // LogEvent (_T("Comparing the creation times")); if ( CompareFileTime( &CreationTimeNew , &CreationTimeCurrent) == 1) { // copy the new file here. if (hFile != INVALID_HANDLE_VALUE) CloseHandle (hFile); if (hFile2 != INVALID_HANDLE_VALUE) CloseHandle (hFile2); if (CopyFile(ServiceParams->IniInstallLocation, DebuggerPath,FALSE) ) { // LogEvent(_T("Successfully copied triage.ini from: %s to %s"), // ServiceParams->IniInstallLocation, DebuggerPath); ; } else { // LogEvent(_T("Copy of file:%s to %s failed\n error code: %d"),ServiceParams->IniInstallLocation, DebuggerPath, GetLastError()); ; } } } if (hFile2 != INVALID_HANDLE_VALUE) CloseHandle (hFile2); } } if (hFile != INVALID_HANDLE_VALUE) CloseHandle (hFile); } } return 0; } HRESULT CServiceModule::ConnectToMSMQ(QUEUEHANDLE *hQueue, wchar_t *QueueConnectStr) /*++ Routine Description: This function is called when the WWW service is shutdown. Arguments: dwFlags - HSE_TERM_ADVISORY_UNLOAD or HSE_TERM_MUST_UNLOAD Return Value: TRUE when extension is ready to be unloaded, --*/ { HRESULT Hr = S_OK; DWORD i; i = 0; Hr = MQOpenQueue(QueueConnectStr, MQ_RECEIVE_ACCESS, MQ_DENY_NONE, hQueue); return Hr; } BOOL CServiceModule::Initialize(PSVCPARAMS pParams) { HRESULT hr; if (!GetServiceParams(pParams) ) { LogFatalEvent(_T("Failed to read initialization data from the registry.")); return FALSE; } //ConnectToMSMQ hr = ConnectToMSMQ(&(pParams->hPrimaryQueue),pParams->PrimaryQueue); if (SUCCEEDED(hr)) { LogEvent(_T("Connected to primary Queue")); //pParams->PrimaryConnected = TRUE; } else { //pParams->PrimaryConnected = FALSE; LogEvent(_T("Cannot connected to primary Queue %s, error %lx"), pParams->PrimaryQueue, hr); } hr = ConnectToMSMQ(&(pParams->hSecondaryQueue),pParams->SecondaryQueue); if (SUCCEEDED(hr)) { //pParams->PrimaryConnected = TRUE; LogEvent(_T("Connected to secondary Queue")); } else { //pParams->PrimaryConnected = FALSE; LogEvent(_T("Cannot connected to secondary Queue %s, error %lx"), pParams->SecondaryQueue, hr); } return TRUE; } BOOL CServiceModule::ReceiveQueueMessage(PSVCPARAMS pParams, wchar_t *RecMessageBody, wchar_t *szMessageGuid, BOOL *bUsePrimary, int *Type, wchar_t *szSR) { MSGPROPID PropIds[5]; MQPROPVARIANT PropVariants[5]; HRESULT hrProps[5]; MQMSGPROPS MessageProps; DWORD i = 0; wchar_t RecLabel[100]; wchar_t LocalRecBody[MAX_PATH]; DWORD RecMessageBodySize = sizeof LocalRecBody; DWORD RecLabelLength = sizeof RecLabel; HRESULT hResult = 0; BOOL Status = FALSE; TCHAR *Temp = NULL; ZeroMemory(LocalRecBody,sizeof LocalRecBody); ZeroMemory(RecLabel,sizeof RecLabel); i = 0; PropIds[i] = PROPID_M_LABEL_LEN; PropVariants[i].vt = VT_UI4; PropVariants[i].ulVal = RecLabelLength; i++; PropIds[i] = PROPID_M_LABEL; PropVariants[i].vt = VT_LPWSTR; PropVariants[i].pwszVal = RecLabel; i++; MessageProps.aPropID = PropIds; MessageProps.aPropVar = PropVariants; MessageProps.aStatus = hrProps; MessageProps.cProp = i; // retrieve the current message i = 0; PropIds[i] = PROPID_M_LABEL_LEN; PropVariants[i].vt = VT_UI4; PropVariants[i].ulVal = RecLabelLength; i++; PropIds[i] = PROPID_M_LABEL; PropVariants[i].vt = VT_LPWSTR; PropVariants[i].pwszVal = RecLabel; i++; PropIds[i] = PROPID_M_BODY_SIZE; PropVariants[i].vt = VT_UI4; i++; PropIds[i] = PROPID_M_BODY_TYPE; PropVariants[i].vt = VT_UI4; i++; PropIds[i] = PROPID_M_BODY; PropVariants[i].vt = VT_VECTOR|VT_UI1; PropVariants[i].caub.pElems = (LPBYTE) LocalRecBody; PropVariants[i].caub.cElems = RecMessageBodySize; i++; MessageProps.aPropID = PropIds; MessageProps.aPropVar = PropVariants; MessageProps.aStatus = hrProps; MessageProps.cProp = i; hResult = MQReceiveMessage( pParams->hPrimaryQueue, 0, MQ_ACTION_RECEIVE, &MessageProps, NULL, NULL, NULL, MQ_NO_TRANSACTION); if (FAILED (hResult) ) { if (hResult != (HRESULT) MQ_ERROR_IO_TIMEOUT) { if (hResult == (HRESULT) MQ_ERROR_QUEUE_NOT_AVAILABLE) { // Non Fatal Event LogFatalEvent(_T("The %s MSMQ is unavailable."), pParams->PrimaryQueue ); } else { if (hResult == (HRESULT) MQ_ERROR_INVALID_HANDLE) { // Close the current handle and attempt to reconnect to the MSMQ MQCloseQueue( pParams->hPrimaryQueue ); ConnectToMSMQ(&(pParams->hPrimaryQueue), pParams->PrimaryQueue ); } } } //g_ReadQueueFlag = !g_ReadQueueFlag; hResult = MQReceiveMessage( pParams->hSecondaryQueue, 0, MQ_ACTION_RECEIVE, &MessageProps, NULL, NULL, NULL, MQ_NO_TRANSACTION); if (FAILED(hResult) ) { Status = FALSE; if (hResult != (HRESULT)MQ_ERROR_IO_TIMEOUT) { if (hResult == (HRESULT)MQ_ERROR_QUEUE_NOT_AVAILABLE) { // Non Fatal Event LogFatalEvent(_T("The %s MSMQ is unavailable."), pParams->SecondaryQueue); } else { if (hResult == (HRESULT)MQ_ERROR_INVALID_HANDLE) { // Close the current handle and attempt to reconnect to the MSMQ MQCloseQueue(pParams->hSecondaryQueue); ConnectToMSMQ(&(pParams->hSecondaryQueue), pParams->SecondaryQueue); } } } return Status; } // // We have valid message from SecondaryQueue // } else { // // We have valid message from Primary Queue // } // // Copy the messge parameters // if (StringCbCopy(RecMessageBody,RecMessageBodySize, LocalRecBody) != S_OK) { LogEvent(_T("Failed to copy the recieved message body to RecMessageBody")); } if (StringCbCopy(szMessageGuid,RecLabelLength, RecLabel) != S_OK) { LogEvent(_T("Failed to copy the RecLabel into szMessageGuid.")); } Status = TRUE; *bUsePrimary = TRUE; Temp = RecMessageBody; while ( (*Temp != _T(';')) && (*Temp != _T('\0'))) { ++Temp; } if (*Temp != _T('\0')) { *Type = _wtoi (Temp+1); // terminate the string here. *Temp = _T('\0'); } else { *Type = 1; } Temp = _tcsstr(LocalRecBody, _T(";SR=")); if (Temp != NULL) { // we got a SR number in message Temp += 4; StringCbCopy(szSR, 50, Temp); } else { *szSR = _T('\0'); } // Flip the read from queue flag for the next call //g_ReadQueueFlag = !g_ReadQueueFlag; return Status; } ULONG64 CServiceModule::GetFileSize( LPWSTR wszFile ) { HANDLE hFile; DWORD dwFileSize, dwFileSizeHi; hFile = CreateFileW(wszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != NULL && hFile != INVALID_HANDLE_VALUE) { dwFileSizeHi = 0; dwFileSize=::GetFileSize(hFile,&dwFileSizeHi); return (ULONG64) dwFileSize + ((ULONG64)dwFileSizeHi << 32); } return -1; } VOID CServiceModule::NotifyDebuggerLaunch( PDBADDCRASH_PARAMS pDbParams ) { HANDLE hPipe; DWORD dwMode, cbWritten; OCAKD_MONITOR_MESSAGE Msg; OVERLAPPED WriteOverlapped; Msg.MessageId = OKD_MESSAGE_DBGLAUNCH_NOTIFY; Msg.u.DbglNotice.SizeOfStruct = sizeof(Msg.u.DbglNotice); Msg.u.DbglNotice.Source = pDbParams->Source; Msg.u.DbglNotice.nKdsRunning = m_DebuggerCount; if (StringCbPrintfA(Msg.u.DbglNotice.CrashGuid, sizeof(Msg.u.DbglNotice.CrashGuid), "%ws", pDbParams->Guid) != S_OK || StringCbPrintfA(Msg.u.DbglNotice.OriginalPath,sizeof(Msg.u.DbglNotice.OriginalPath), "%ws", pDbParams->DumpPath) != S_OK) { return; } for (hPipe = m_hMonNotifyPipe;m_hMonNotifyPipe == NULL;) { hPipe = CreateFile(c_tszCollectPipeName, FILE_WRITE_DATA, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (hPipe != INVALID_HANDLE_VALUE) { m_hMonNotifyPipe = hPipe; break; } if (GetLastError() != ERROR_PIPE_BUSY) { return; } // Do not wait long, this might block other processes if (!WaitNamedPipe(c_tszCollectPipeName, 5*1000)) { return; } } // We are now connected to pipe WriteOverlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); if (WriteOverlapped.hEvent != NULL) { // Send crash information to monitor pipe if (!WriteFile(hPipe, (LPVOID) &Msg, sizeof(Msg), &cbWritten, &WriteOverlapped)) { if (GetLastError() == ERROR_NO_DATA || GetLastError() == ERROR_BAD_PIPE) { // Open a different pipe next time CloseHandle(hPipe); m_hMonNotifyPipe = NULL; } else if (GetLastError() == ERROR_IO_PENDING || !GetOverlappedResult(hPipe, &WriteOverlapped, &cbWritten, TRUE)) { // failed to write, exit silently // Its up to monitor if it is keeping track of kds launched } else { // Open a different pipe next time CloseHandle(hPipe); m_hMonNotifyPipe = NULL; } } CloseHandle(WriteOverlapped.hEvent); } FlushFileBuffers(hPipe); return; } BOOL CServiceModule::LaunchDebugger( PDBADDCRASH_PARAMS pDbParams, PPROCESS_INFORMATION pDbgProcess ) { STARTUPINFO StartupInfo; wchar_t CommandLine [1024]; wchar_t SrParam[100]; HRESULT hr; ZeroMemory(&StartupInfo,sizeof(STARTUPINFO)); StartupInfo.cb = sizeof(STARTUPINFO); if ((pDbParams->dwMaxDumpSize != 0) && (pDbParams->dwMaxDumpSize != -1) && (GetFileSize(pDbParams->DumpPath) > pDbParams->dwMaxDumpSize)) { LogEvent(_T("Dump %s is too large, deleting file\n"), pDbParams->DumpPath); DeleteFileW(pDbParams->DumpPath); return FALSE; } if (pDbParams->SrNumber[0]) { hr = StringCbPrintf(SrParam, sizeof(SrParam), _T(" -sr %s"), pDbParams->SrNumber); if (hr != S_OK) { LogEvent(_T("Bad SR %s: %lx"), pDbParams->SrNumber, hr); return FALSE; } } else { SrParam[0] = _T('\0'); } if ((hr = StringCbPrintf(CommandLine,sizeof CommandLine, _T("%s -i %s -y %s -z \"%s\" -c \"!dbaddcrash -source %d -g %s -s %s -p %s%s;q\""), pDbParams->Debugger, pDbParams->SymPath, pDbParams->SymPath, pDbParams->DumpPath, pDbParams->Source, pDbParams->Guid, pDbParams->ResponseMQ, pDbParams->DumpPath, SrParam)) != S_OK) { LogEvent(_T("Failed to build command line: %lx"), hr); } //LogEvent(_T("CommandLine Guid=%s queue=%d"),szMessageLabel,bUsePrimary); //LogEvent(_T("CommandLine Path=%s"),szFilePath); //LogEvent(_T("CommandLine: %s"),CommandLine); if (!CreateProcess(NULL, CommandLine, NULL, NULL, FALSE, //CREATE_NEW_CONSOLE, CREATE_NO_WINDOW, NULL, NULL, &StartupInfo, pDbgProcess)) { hr = GetLastError(); LogEvent(_T("Failed to launch debugger. Commandline %s, error %lx\n"), CommandLine, hr); return FALSE; } // // Notify Site monitor of debugger launch // NotifyDebuggerLaunch(pDbParams); return TRUE; } BOOL CServiceModule::PrepareForDebuggerLaunch() { HANDLE ProcessHandles[50]; PROCESS_INFORMATION ProcessInfo; wchar_t szFilePath[MAX_PATH]; wchar_t szSR[100]; HRESULT hr = 0; HANDLE hStopEvent = INVALID_HANDLE_VALUE; SVCPARAMS ServiceParams; DWORD dwIndex = 0; DWORD FreeIndex = 0; DWORD dwWaitResult = 0; BOOL Done = FALSE; __time64_t CurrentTime = 0; __time64_t NextCheckTime = 0; wchar_t szMessageLabel[255]; BOOL bUsePrimary = FALSE; int Type = 1; DBADDCRASH_PARAMS DbParams; ZeroMemory (ProcessHandles,sizeof ProcessHandles); ZeroMemory(&ServiceParams,sizeof SVCPARAMS); ZeroMemory (szMessageLabel, sizeof szMessageLabel); ZeroMemory (szFilePath, sizeof szFilePath); ZeroMemory(&DbParams, sizeof(DbParams)); m_DebuggerCount = 0; hStopEvent = OpenEvent( EVENT_ALL_ACCESS, FALSE, s_cszStopEvent ); if (hStopEvent == NULL) { LogFatalEvent(_T("Failed to open stop event. Terminating")); goto Done; } // get reg data // Insert the stop event into the ocakd queue ++ m_DebuggerCount; ProcessHandles[m_DebuggerCount -1] = hStopEvent; if (!Initialize(&ServiceParams)) { goto Done; } // fill the ocakd queue Done = FALSE; while ( (m_DebuggerCount <= ServiceParams.dwMaxKdProcesses+1 ) && (!Done)) { if (!ReceiveQueueMessage(&ServiceParams, szFilePath, szMessageLabel, &bUsePrimary, &Type, szSR)) { Done = TRUE; } else { DbParams.Debugger = ServiceParams.DebuggerName; DbParams.DumpPath = szFilePath; DbParams.Guid = szMessageLabel; DbParams.SrNumber = szSR; DbParams.ResponseMQ = (bUsePrimary) ? ServiceParams.PrimaryResponseQueue : ServiceParams.SecondaryResponseQueue; DbParams.SymPath = ServiceParams.Symsrv; DbParams.Source = Type; DbParams.dwMaxDumpSize = ServiceParams.dwMaxDumpSize; if (LaunchDebugger(&DbParams, &ProcessInfo)) { // LogEvent(_T("Launched: %s for file: %s with guid:%s and source: %d with return queue =%s"), // ServiceParams.DebuggerName,szFilePath, szMessageLabel, Type, (bUsePrimary) ? ServiceParams.PrimaryResponseQueue : ServiceParams.SecondaryResponseQueue); ++ m_DebuggerCount; ProcessHandles[m_DebuggerCount -1] = ProcessInfo.hProcess; CloseHandle( ProcessInfo.hThread ); } // end else }// end else } // end while if (m_DebuggerCount > ServiceParams.dwMaxKdProcesses+1) m_DebuggerCount = ServiceParams.dwMaxKdProcesses+1; while (1) { _time64(&CurrentTime); if (CurrentTime > NextCheckTime) { // LogEvent (_T("Checking for new ini file")); CheckForIni (&ServiceParams); NextCheckTime= CurrentTime + (ServiceParams.IniCheckWaitTime * 60); // LogEvent (_T("Back from ini file check")); } // now wait for one of the processes to complete or hStopEvent to be signaled. dwWaitResult = WaitForMultipleObjects(m_DebuggerCount, (HANDLE *)ProcessHandles, FALSE, ServiceParams.dwDelay); if (dwWaitResult == WAIT_FAILED) { LogFatalEvent(_T("Failed while waiting for prcesses to complete error code: %d"), GetLastError()); goto Cleanup; } else { if (dwWaitResult-WAIT_OBJECT_0 == 0) { LogEvent(_T("Stop request received terminating")); goto Cleanup; } else // we have an event we can take action on. { if ( dwWaitResult == WAIT_TIMEOUT) { // no process completed so let's fill any empty slots in the // Process array. if (m_DebuggerCount <= ServiceParams.dwMaxKdProcesses) { // fill the queue until m_DebuggerCount == ServiceParams.dwMaxKdProcesses Done = FALSE; dwIndex = 1; while ( (!Done) && (m_DebuggerCount <= ServiceParams.dwMaxKdProcesses) ) { if (ProcessHandles[dwIndex] == NULL) { if (ReceiveQueueMessage(&ServiceParams, szFilePath, szMessageLabel,&bUsePrimary,&Type,szSR)) { DbParams.Debugger = ServiceParams.DebuggerName; DbParams.DumpPath = szFilePath; DbParams.Guid = szMessageLabel; DbParams.SrNumber = szSR; DbParams.ResponseMQ = (bUsePrimary) ? ServiceParams.PrimaryResponseQueue : ServiceParams.SecondaryResponseQueue; DbParams.SymPath = ServiceParams.Symsrv; DbParams.Source = Type; DbParams.dwMaxDumpSize = ServiceParams.dwMaxDumpSize; if (LaunchDebugger(&DbParams, &ProcessInfo)) { ProcessHandles[dwIndex] = ProcessInfo.hProcess; CloseHandle( ProcessInfo.hThread ); ++m_DebuggerCount; } // end else } else { Done = TRUE; } } // end if ++dwIndex; } // end while } } // end if else // A kd process completed now lets clean it up and launch a new one. { FreeIndex = (dwWaitResult - WAIT_OBJECT_0); // check to make sure we are within our array range. if ( (FreeIndex > ServiceParams.dwMaxKdProcesses)) { LogFatalEvent( _T("Invalid array index returned by WaitForMultipleObjects. \n Index = %d, Count = %d"), FreeIndex,m_DebuggerCount); } else { CloseHandle(ProcessHandles[FreeIndex]); ProcessHandles[FreeIndex] = NULL; // if the queue is not now empty take the last Process handle and replace the current one. if (m_DebuggerCount > 2) { // We have to do this because NULLs are not allowed in the HANDLE Array. ProcessHandles[FreeIndex] = ProcessHandles[m_DebuggerCount-1]; ProcessHandles[m_DebuggerCount-1] = NULL; -- m_DebuggerCount; } else { --m_DebuggerCount; } if (ReceiveQueueMessage(&ServiceParams, szFilePath, szMessageLabel,&bUsePrimary, &Type, szSR)) { DbParams.Debugger = ServiceParams.DebuggerName; DbParams.DumpPath = szFilePath; DbParams.Guid = szMessageLabel; DbParams.SrNumber = szSR; DbParams.ResponseMQ = (bUsePrimary) ? ServiceParams.PrimaryResponseQueue : ServiceParams.SecondaryResponseQueue; DbParams.SymPath = ServiceParams.Symsrv; DbParams.Source = Type; DbParams.dwMaxDumpSize = ServiceParams.dwMaxDumpSize; if (LaunchDebugger(&DbParams, &ProcessInfo)) { ++m_DebuggerCount; ProcessHandles[m_DebuggerCount-1] = ProcessInfo.hProcess; CloseHandle( ProcessInfo.hThread ); } // end else } // end if } // end else } // end else } // end else } // end else }// end while(1) Cleanup: // // Cleanup - Close handle to the queue // Done: // set the service status to stopped and return. if (hStopEvent != INVALID_HANDLE_VALUE) CloseHandle(hStopEvent); return TRUE; } /////////////////////////////////////////////////////////////////////////////////////// // Routine to Log Fatal Errors to NT Event Log void CServiceModule::LogFatalEvent(LPCTSTR pFormat, ...) { TCHAR chMsg[256]; HANDLE hEventSource; LPTSTR lpszStrings[1]; va_list pArg; va_start(pArg, pFormat); // _vstprintf(chMsg, pFormat, pArg); if (StringCbVPrintf(chMsg,sizeof chMsg, pFormat, pArg)!= S_OK) { return; } va_end(pArg); lpszStrings[0] = chMsg; /* Get a handle to use with ReportEvent(). */ hEventSource = RegisterEventSource(NULL, _T("DBGLauncher")); if (hEventSource != NULL) { /* Write to event log. */ ReportEvent(hEventSource, EVENTLOG_ERROR_TYPE, 0, EVENT_ERROR, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL); DeregisterEventSource(hEventSource); } } /////////////////////////////////////////////////////////////////////////////////////// // Routine to setup NT Event logging HRESULT CServiceModule::SetupEventLog ( BOOL fSetup ) { CRegKey key; TCHAR szEventKey[MAX_PATH]; LONG lRes = 0; if (StringCbCopy(szEventKey, sizeof szEventKey,s_cszEventLogKey)!= S_OK) { return E_FAIL; } if (StringCbCat(szEventKey,sizeof szEventKey, _T("\\")) != S_OK) return E_FAIL; if (StringCbCat(szEventKey, sizeof szEventKey,(LPTSTR)m_szServiceName) != S_OK) return E_FAIL; lRes = key.Create(HKEY_LOCAL_MACHINE, szEventKey); if (lRes != ERROR_SUCCESS) { goto done; } if( TRUE == fSetup ) { TCHAR szMyName[MAX_PATH]; GetModuleFileName(NULL, szMyName, (sizeof szMyName)/(sizeof szMyName[0]) ); key.SetValue(szMyName, _T("EventMessageFile")); key.SetValue(EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE, _T("TypesSupported")); } else { RegDeleteKey(HKEY_LOCAL_MACHINE, szEventKey); } done: key.Close(); return HRESULT_FROM_WIN32(GetLastError()); }