|
|
///////////////////////////////////////////////////////////////////////////
// File: WinHttpStressScheduler.cpp
//
// Copyright (c) 2001 Microsoft Corporation. All Rights Reserved.
//
// Purpose:
// ServerCommands.cpp: implementation of the ServerCommands class.
// This class is used to retrieve and act on command from the server.
//
// History:
// 02/08/01 DennisCh Created
//
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Includes
//////////////////////////////////////////////////////////////////////
//
// WIN32 headers
//
//
// Project headers
//
#include "ServerCommands.h"
#include "NetworkTools.h"
//////////////////////////////////////////////////////////////////////
// Globals and statics
//////////////////////////////////////////////////////////////////////
HANDLE g_hQueryServerForCommands; // Handle for the thread that queries the server for commands
CRITICAL_SECTION g_csServerCommandsVars; // For protecting CommandServer private member vars. Used in the QueryServerForCommands_ThreadProc.
extern ServerCommands g_objServerCommands; // Declared in WinHttpStressScheduler.cpp
////////////////////////////////////////////////////////////
// Function: QueryServerForCommands_ThreadProc(LPVOID)
//
// Purpose:
// This method sends a request to the command server for instructions
// and saves them in the public vars of ServerCommands.
//
////////////////////////////////////////////////////////////
DWORD WINAPI QueryServerForCommands_ThreadProc( LPVOID lpParam // [IN] thread proc param
) { BOOL bResult = TRUE; HINTERNET hRoot = NULL; HINTERNET hSession = NULL; HINTERNET hRequest = NULL; URL_COMPONENTSW urlComponents;
// Allocate space for URL components
ZeroMemory(&urlComponents, sizeof(urlComponents));
urlComponents.dwSchemeLength = MAX_PATH; urlComponents.lpszScheme = new TCHAR[MAX_PATH];
urlComponents.dwHostNameLength = MAX_PATH; urlComponents.lpszHostName = new TCHAR[MAX_PATH];
urlComponents.dwUrlPathLength = MAX_PATH; urlComponents.lpszUrlPath = new TCHAR[MAX_PATH];
urlComponents.dwExtraInfoLength = MAX_PATH; urlComponents.lpszExtraInfo = new TCHAR[MAX_PATH]; urlComponents.dwUserNameLength = MAX_PATH; urlComponents.lpszUserName = new TCHAR[MAX_PATH];
urlComponents.dwPasswordLength = MAX_PATH; urlComponents.lpszPassword = new TCHAR[MAX_PATH]; urlComponents.nPort = 0;
urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
// crack the Command Server URL to be used later
if (!WinHttpCrackUrl( g_objServerCommands.Get_CommandServerURL(), _tcslen(g_objServerCommands.Get_CommandServerURL()), ICU_ESCAPE, &urlComponents) ) { bResult = FALSE; goto Exit; }
hRoot = WinHttpOpen( STRESS_SCHEDULER_USER_AGENT, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
if (!hRoot) { bResult = FALSE; goto Exit; }
hSession = WinHttpConnect( hRoot, urlComponents.lpszHostName, // If the URL in urlComponents uses standard HTTP or HTTPS ports then use INTERNET_DEFAULT_PORT. Otherwise use the non-standard port gleaned from the URL.
((urlComponents.nPort == 80) || (urlComponents.nPort == 443)) ? INTERNET_DEFAULT_PORT : urlComponents.nPort, 0); if (!hSession) { bResult = FALSE; goto Exit; }
// Build a full URL with path and querystring
TCHAR szFullPath[MAX_PATH*2];
ZeroMemory(szFullPath, sizeof(szFullPath)); _tcsncpy(szFullPath, urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength); //szFullPath[urlComponents.dwUrlPathLength] = _T('\0');
_tcsncat(szFullPath, urlComponents.lpszExtraInfo, urlComponents.dwExtraInfoLength);
hRequest = WinHttpOpenRequest( hSession, _T("POST"), szFullPath, NULL, NULL, NULL, // if the URL in urlComponents uses HTTPS then pass in WINHTTP_FLAG_SECURE to this param. Otherwise, 0.
(0 == _tcsnicmp(urlComponents.lpszScheme, _T("https"), 5)) ? WINHTTP_FLAG_SECURE : 0);
if (!hRequest) { bResult = FALSE; goto Exit; }
// Set reasonable timeouts just in case
if (!WinHttpSetTimeouts(hRequest, 5000, 5000, 5000, 5000)) { bResult = FALSE; goto Exit; }
// Get the computer name and send it in a POST request
LPSTR szPost, szMachineName; szPost = new CHAR[MAX_PATH]; szMachineName = new CHAR[MAX_PATH];
GetEnvironmentVariableA("COMPUTERNAME", szMachineName, MAX_PATH); strcpy(szPost, FIELDNAME__USERINFO_MACHINENAME); strcat(szPost, szMachineName);
bResult = WinHttpSendRequest( hRequest, _T("Content-Type: application/x-www-form-urlencoded"), -1L, szPost, strlen(szPost), strlen(szPost), 0);
delete [] szPost; delete [] szMachineName; szPost = NULL; szMachineName = NULL;
if (!WinHttpReceiveResponse(hRequest, NULL)) { bResult = FALSE; goto Exit; }
TCHAR szBuffer[MAX_URL]; DWORD dwBufferSize, dwIndex;
// get all command headers that we're interested in.
// make sure there are no pending operations on member vars (pServerCommands->Set_* functions)
EnterCriticalSection(&g_csServerCommandsVars);
// *********************************
// **** COMMANDHEADER__EXIT: Exit if header is present, else continue.
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__EXIT, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_ExitStress(TRUE); else g_objServerCommands.Set_ExitStress(FALSE);
// *********************************
// **** COMMANDHEADER__WINHTTP_DLL_URL: valid values: Valid URL
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__WINHTTP_DLL_URL, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_WinHttpDllURL(szBuffer, dwBufferSize); else bResult = FALSE;
// *********************************
// **** COMMANDHEADER__WINHTTP_PDB_URL: valid values: Valid URL
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__WINHTTP_PDB_URL, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_WinHttpPDBURL(szBuffer, dwBufferSize); else bResult = FALSE;
// *********************************
// **** COMMANDHEADER__WINHTTP_SYM_URL: valid values: Valid URL
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__WINHTTP_SYM_URL, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_WinHttpSYMURL(szBuffer, dwBufferSize); else bResult = FALSE;
// *********************************
// **** COMMANDHEADER__COMMANDSERVER_URL: valid values: Valid URL
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__COMMANDSERVER_URL, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_CommandServerURL(szBuffer, dwBufferSize); else bResult = FALSE;
// *********************************
// **** COMMANDHEADER__BEGIN_TIME_HOUR: valid values: Valid string from 0-23
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__BEGIN_TIME_HOUR, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_TimeStressBegins(szBuffer, NULL); else bResult = FALSE;
// *********************************
// **** COMMANDHEADER__BEGIN_TIME_MINUTE: valid values: Valid string from 0-59
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__BEGIN_TIME_MINUTE, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_TimeStressBegins(NULL, szBuffer); else bResult = FALSE;
// *********************************
// **** COMMANDHEADER__END_TIME_HOUR: valid values: Valid string from 0-23
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__END_TIME_HOUR, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_TimeStressEnds(szBuffer, NULL); else bResult = FALSE;
// *********************************
// **** COMMANDHEADER__END_TIME_MINUTE: valid values: Valid string from 0-59
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__END_TIME_MINUTE, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_TimeStressEnds(NULL, szBuffer); else bResult = FALSE;
// *********************************
// **** COMMANDHEADER__RUN_FOREVER: valid values: doesn't matter. As long as header is present it gets sent
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__RUN_FOREVER, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_RunForever(TRUE); else g_objServerCommands.Set_RunForever(FALSE);
// *********************************
// **** COMMANDHEADER__UPDATE_INTERVAL: valid values: Valid string in INTERNET_RFC1123 format
DWORD dwTimeOut; dwTimeOut = 0; dwIndex = 0; dwBufferSize = sizeof(DWORD); ZeroMemory(szBuffer, sizeof(szBuffer)); if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM | WINHTTP_QUERY_FLAG_NUMBER, COMMANDHEADER__UPDATE_INTERVAL, &dwTimeOut, &dwBufferSize, &dwIndex)) g_objServerCommands.Set_CommandServerUpdateInterval(dwTimeOut); else bResult = FALSE;
// *********************************
// *********************************
// **** Query commands for building stress Instance objects
// ****
// **** COMMANDHEADER__STRESS_EXE_URL: valid values: Valid URL
DWORD dwStressExeID; DWORD dwPageHeapCommandIndex, dwUMDHCommandIndex, dwStressPDBIndex, dwStressSYMIndex, dwStressMemDmpPathIndex, dwStressExeIDIndex; LPTSTR szPageheapCommand, szUMDHCommand, szStressPDB_URL, szStressSYM_URL, szStressMemDmpPath, szStressExeID;
szPageheapCommand = new TCHAR[MAX_PATH]; szUMDHCommand = new TCHAR[MAX_PATH]; szStressPDB_URL = new TCHAR[MAX_STRESS_URL]; szStressSYM_URL = new TCHAR[MAX_STRESS_URL]; szStressMemDmpPath = new TCHAR[MAX_PATH]; szStressExeID = new TCHAR[MAX_PATH];
if (!g_objServerCommands.IsStressRunning()) { // free all old StressExeURLs first - we're replacing it with new URLs anyway
g_objServerCommands.Clear_StressExeURLs();
dwIndex = 0; dwStressExeIDIndex = 0; dwPageHeapCommandIndex = 0; dwUMDHCommandIndex = 0; dwStressPDBIndex = 0; dwStressSYMIndex = 0; dwStressMemDmpPathIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer));
while (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__STRESS_EXE_URL, szBuffer, &dwBufferSize, &dwIndex)) { // *************************************
// *************************************
// ** COMMANDHEADER__MEMORY_DUMP_PATH: A valid path
ZeroMemory(szStressMemDmpPath, MAX_PATH); dwBufferSize = MAX_PATH; WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__MEMORY_DUMP_PATH, szStressMemDmpPath, &dwBufferSize, &dwStressMemDmpPathIndex);
// *************************************
// *************************************
// ** Get COMMANDHEADER__STRESS_PDB_URL if there is one
// **
ZeroMemory(szStressPDB_URL, MAX_STRESS_URL); dwBufferSize = MAX_STRESS_URL; WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__STRESS_PDB_URL, szStressPDB_URL, &dwBufferSize, &dwStressPDBIndex);
// *************************************
// *************************************
// ** Get COMMANDHEADER__STRESS_SYM_URL if there is one
// **
ZeroMemory(szStressSYM_URL, MAX_STRESS_URL); dwBufferSize = MAX_STRESS_URL; WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__STRESS_SYM_URL, szStressSYM_URL, &dwBufferSize, &dwStressSYMIndex);
// *************************************
// *************************************
// ** Get COMMANDHEADER__STRESS_EXE_PAGEHEAP if there is one
// **
ZeroMemory(szPageheapCommand, MAX_PATH); dwBufferSize = MAX_PATH; WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__STRESS_EXE_PAGEHEAP, szPageheapCommand, &dwBufferSize, &dwPageHeapCommandIndex);
// *************************************
// *************************************
// ** Get COMMANDHEADER__STRESS_EXE_UMDH if there is one
// **
ZeroMemory(szUMDHCommand, MAX_PATH); dwBufferSize = MAX_PATH; WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__STRESS_EXE_UMDH, szUMDHCommand, &dwBufferSize, &dwUMDHCommandIndex);
// *************************************
// *************************************
// ** Get COMMANDHEADER__STRESS_EXE_INSTANCEID
// For each COMMANDHEADER__STRESS_EXE_URL, there must be an index for the stress instance from the StressADMIN DB table.
// This identifies the stressinstance run. The test case (stressinstance) WILL NOT be added and run without an ID number.
ZeroMemory(szStressExeID, MAX_PATH); dwBufferSize = MAX_PATH;
if (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__STRESS_EXE_INSTANCEID, szStressExeID, &dwBufferSize, &dwStressExeIDIndex)) { // convert header ID string to a DWORD
dwStressExeID = _ttol(szStressExeID);
// only add valid stressInstances with valid ID's
if (0 < dwStressExeID) { g_objServerCommands.Create_StressInstance( dwStressExeID, szPageheapCommand, szUMDHCommand, szStressPDB_URL, szStressSYM_URL, szStressMemDmpPath, szBuffer);
dwBufferSize = MAX_URL; ZeroMemory(szBuffer, MAX_URL); } } } }
delete [] szPageheapCommand; delete [] szUMDHCommand; delete [] szStressMemDmpPath; delete [] szStressPDB_URL; delete [] szStressSYM_URL; delete [] szStressExeID;
// *********************************
// **** COMMANDHEADER__ABORT: Abort the stress instance running specified by this header.
dwIndex = 0; dwBufferSize = MAX_URL; ZeroMemory(szBuffer, sizeof(szBuffer)); while (WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CUSTOM, COMMANDHEADER__ABORT, szBuffer, &dwBufferSize, &dwIndex)) g_objServerCommands.AbortStressInstance(_ttol(szBuffer));
LeaveCriticalSection(&g_csServerCommandsVars);
Exit: if (hRequest) WinHttpCloseHandle(hRequest);
if (hSession) WinHttpCloseHandle(hSession);
if (hRoot) WinHttpCloseHandle(hRoot);
delete [] urlComponents.lpszScheme; delete [] urlComponents.lpszHostName; delete [] urlComponents.lpszUrlPath; delete [] urlComponents.lpszExtraInfo; delete [] urlComponents.lpszPassword; delete [] urlComponents.lpszUserName;
ExitThread(bResult); }
// *******************************************************************
// *******************************************************************
// ****
// **** ServerCommands class member functions
// ****
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
ServerCommands::ServerCommands() { m_dwCommandServerUpdateInternval = STRESS_COMMAND_SERVER_UPDATE_INTERVAL; m_bExit = FALSE; m_dwStressInstanceIterator = NULL; g_hQueryServerForCommands = NULL;
m_szCommandServerURL = new TCHAR[MAX_STRESS_URL]; m_szCommandServerResultsURL = new TCHAR[MAX_STRESS_URL]; m_szWinHttpDLL_DownloadURL = new TCHAR[MAX_STRESS_URL]; m_szWinHttpPDB_DownloadURL = new TCHAR[MAX_STRESS_URL]; m_szWinHttpSYM_DownloadURL = new TCHAR[MAX_STRESS_URL]; m_szWinHttpDLL_FileName = new TCHAR[MAX_PATH]; m_szStressSchedulerCurrentDirectory = new TCHAR[MAX_PATH]; m_szClientMachineName = new CHAR[MAX_PATH];
ZeroMemory(m_szCommandServerURL, MAX_STRESS_URL); ZeroMemory(m_szCommandServerResultsURL, MAX_STRESS_URL); ZeroMemory(m_szWinHttpDLL_DownloadURL, MAX_STRESS_URL); ZeroMemory(m_szWinHttpPDB_DownloadURL, MAX_STRESS_URL); ZeroMemory(m_szWinHttpSYM_DownloadURL, MAX_STRESS_URL); ZeroMemory(m_szWinHttpDLL_FileName, MAX_PATH); ZeroMemory(m_szStressSchedulerCurrentDirectory, MAX_PATH); ZeroMemory(m_szClientMachineName, MAX_PATH);
// initilize start/end times to -1 so we know that
// there are not valid time and we'll skip the Begin/End stress time check
// until we get real values from the command server
m_iTimeStressBeginsHour = -1; m_iTimeStressBeginsMinute = -1; m_iTimeStressEndsHour = -1; m_iTimeStressEndsMinute = -1; m_bRunForever = 0;
// Set default URLs
wcsncpy(m_szCommandServerURL, STRESS_COMMAND_SERVER_URL, sizeof(STRESS_COMMAND_SERVER_URL)); wcsncpy(m_szCommandServerResultsURL, STRESS_COMMAND_SERVER_RESULTS_URL, sizeof(STRESS_COMMAND_SERVER_RESULTS_URL));
// Get the current working directory
GetCurrentDirectory(MAX_PATH, m_szStressSchedulerCurrentDirectory);
// Get the client's machine name
GetEnvironmentVariableA("COMPUTERNAME", m_szClientMachineName, MAX_PATH);
InitializeCriticalSection(&g_csServerCommandsVars);
// Tell the client that we are alive and also send system info
RegisterClient();
}
ServerCommands::~ServerCommands() { DWORD dwThreadExitCode = 0;
// LOGLOG: stressScheduler has exited
NetworkTools__SendLog(FIELDNAME__LOGTYPE_EXIT, "WinHttpStressScheduler has exited.", NULL, NULL);
// Shut down QueryServer thread
GetExitCodeThread(g_hQueryServerForCommands, &dwThreadExitCode);
if (STILL_ACTIVE == dwThreadExitCode) WaitForSingleObject(g_hQueryServerForCommands, INFINITE);
// free allocated memory for URLs
Clear_StressExeURLs();
// Free our handles
CloseHandle(g_hQueryServerForCommands); DeleteCriticalSection(&g_csServerCommandsVars);
delete [] m_szCommandServerURL; delete [] m_szCommandServerResultsURL; delete [] m_szWinHttpDLL_DownloadURL; delete [] m_szWinHttpPDB_DownloadURL; delete [] m_szWinHttpSYM_DownloadURL; delete [] m_szWinHttpDLL_FileName; delete [] m_szStressSchedulerCurrentDirectory; delete [] m_szClientMachineName; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::GetCurrentWorkingDirectory()
//
// Purpose:
// Returns string containing the current working directory for
// this application.
//
////////////////////////////////////////////////////////////
LPTSTR ServerCommands::Get_CurrentWorkingDirectory() { return m_szStressSchedulerCurrentDirectory; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Get_ClientMachineName()
//
// Purpose:
// Returns string containing the machine's NETBIOS name
//
////////////////////////////////////////////////////////////
LPSTR ServerCommands::Get_ClientMachineName() { return m_szClientMachineName; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::QueryServerForCommands()
//
// Purpose:
// This method sends a request to the command server for instructions
// and saves them in our private vars.
//
////////////////////////////////////////////////////////////
ServerCommands::QueryServerForCommands() { BOOL bResult = TRUE; DWORD dwThreadID = 0; DWORD dwTimeOut = 0; DWORD dwThreadExitCode = 0;
// See if thread is still active before spinning off a new one
GetExitCodeThread(g_hQueryServerForCommands, &dwThreadExitCode);
if (STILL_ACTIVE == dwThreadExitCode) { // wait for existing thread to finish
dwTimeOut = 0; dwTimeOut = WaitForSingleObject(g_hQueryServerForCommands, 500);
if (WAIT_TIMEOUT == dwTimeOut) bResult = FALSE; } else { // free handle for previous thread
CloseHandle(g_hQueryServerForCommands);
// spin off thread to query server
g_hQueryServerForCommands = NULL; g_hQueryServerForCommands = CreateThread(NULL, 0, QueryServerForCommands_ThreadProc, (LPVOID) this, 0, &dwThreadID);
if (!g_hQueryServerForCommands) bResult = FALSE; }
return bResult; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Download_WinHttpDLL()
//
// Purpose:
// Downloads the test DLL and symbols.
//
////////////////////////////////////////////////////////////
BOOL ServerCommands::Download_WinHttpDLL() { BOOL bResult = TRUE; LPTSTR szSymbolFileName = new TCHAR[MAX_PATH]; LPTSTR szBuffer = new TCHAR[MAX_PATH];
// ************************
// ************************
// ** download the file to the system32 directory
// **
if (!GetSystemDirectory(szBuffer, MAX_PATH)) { bResult = FALSE; goto Exit; }
// download DLL if needed
if (_tcsclen(m_szWinHttpDLL_DownloadURL) > 0) bResult = NetworkTools__URLDownloadToFile( m_szWinHttpDLL_DownloadURL, szBuffer, m_szWinHttpDLL_FileName);
if (bResult) { // download PDB file if needed
if (_tcsclen(m_szWinHttpPDB_DownloadURL) > 0) { NetworkTools__GetFileNameFromURL(m_szWinHttpPDB_DownloadURL, szSymbolFileName, MAX_PATH);
if (NetworkTools__URLDownloadToFile(m_szWinHttpPDB_DownloadURL, szBuffer, szSymbolFileName)) NetworkTools__SendLog(FIELDNAME__LOGTYPE_SUCCESS, "PDB symbol file downloaded successfully.", NULL, 0); else NetworkTools__SendLog(FIELDNAME__LOGTYPE_ERROR, "PDB symbol file failed to downloaded.", NULL, 0); }
// download sym file if needed
if (_tcsclen(m_szWinHttpSYM_DownloadURL) > 0) { NetworkTools__GetFileNameFromURL(m_szWinHttpSYM_DownloadURL, szSymbolFileName, MAX_PATH);
if (NetworkTools__URLDownloadToFile(m_szWinHttpSYM_DownloadURL, szBuffer, szSymbolFileName)) NetworkTools__SendLog(FIELDNAME__LOGTYPE_SUCCESS, "SYM symbol file downloaded successfully.", NULL, 0); else NetworkTools__SendLog(FIELDNAME__LOGTYPE_ERROR, "SYM symbol file failed to downloaded.", NULL, 0); } }
// if failed to download DLL, it's probably in use. We'll try to regsvr32 it anyways if it's there.
// ************************
// ************************
// ** regsvr32'ed the dll just downloaded
// **
HINSTANCE hLib; hLib = LoadLibrary(m_szWinHttpDLL_FileName);
if (hLib < (HINSTANCE)HINSTANCE_ERROR) { // unable to load the DLL;
bResult = FALSE; goto Exit; }
// **********************
// **********************
// ** Register the DLL
typedef VOID (CALLBACK* LPFNDLLFUNC1)(); LPFNDLLFUNC1 lpDllEntryPoint;
// Find the entry point.
(FARPROC&)lpDllEntryPoint = GetProcAddress(hLib, "DllRegisterServer");
if (lpDllEntryPoint != NULL) (*lpDllEntryPoint)(); else { //unable to locate entry point - regsvr failed
bResult = FALSE; }
Exit: if (bResult) NetworkTools__SendLog(FIELDNAME__LOGTYPE_INFORMATION, "winhttp5.dll downloaded and registered successfully.", NULL, 0);
delete [] szSymbolFileName; delete [] szBuffer;
return bResult; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_RunForever(BOOL)
//
// Purpose:
// Pass in TRUE to run forever ignoring begin/end time, FALSE not to.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_RunForever( BOOL bRunForever // [IN] TRUE to run forever ignoring begin/end time, FALSE not to.
) { m_bRunForever = bRunForever; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_ExitStress(BOOL)
//
// Purpose:
// Pass in TRUE to exit stress as soon as possible and FALSE
// not to.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_ExitStress( BOOL bExitStress // [IN] TRUE to exit stress, FALSE not to.
) { m_bExit = bExitStress; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_WinHttpDllURL(LPTSTR, DWORD)
//
// Purpose:
// Pass in an URL and its size and to get the WinHttp DLL from.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_WinHttpDllURL( LPTSTR szBuffer, // [IN] string buffer containing URL to the WINHTTP DLL
DWORD dwBufferSize // [IN] size of the szBuffer in TCHARs
) { _tcscpy(m_szWinHttpDLL_DownloadURL, szBuffer);
// Get the full DLL filename from the URL
NetworkTools__GetFileNameFromURL(m_szWinHttpDLL_DownloadURL, m_szWinHttpDLL_FileName, MAX_PATH); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_WinHttpPDBURL(LPTSTR, DWORD)
//
// Purpose:
// Pass in an URL and its size and to get the WinHttp PDB file from.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_WinHttpPDBURL( LPTSTR szBuffer, // [IN] string buffer containing URL to the WINHTTP DLL
DWORD dwBufferSize // [IN] size of the szBuffer in TCHARs
) { _tcscpy(m_szWinHttpPDB_DownloadURL, szBuffer); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_WinHttpSYMURL(LPTSTR, DWORD)
//
// Purpose:
// Pass in an URL and its size and to get the WinHttp SYM file from.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_WinHttpSYMURL( LPTSTR szBuffer, // [IN] string buffer containing URL to the WINHTTP DLL
DWORD dwBufferSize // [IN] size of the szBuffer in TCHARs
) { _tcscpy(m_szWinHttpSYM_DownloadURL, szBuffer); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_CommandServerURL(LPTSTR)
//
// Purpose:
// Pass in a timeout value in milliseconds to query the
// Command Server for commands.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_CommandServerURL( LPTSTR szBuffer, // [IN] string buffer containing URL to the WINHTTP DLL
DWORD dwBufferSize // [IN] size of the szBuffer in TCHARs
) { _tcscpy(m_szCommandServerURL, szBuffer); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_CommandServerUpdateInterval(DWORD)
//
// Purpose:
// Pass in a timeout value in milliseconds to query the
// Command Server for commands.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_CommandServerUpdateInterval( DWORD dwUpdateInterval // [IN] time to wait before pinging the Command Server in milliseconds
) { // server update interval must be at least greater than the minimum timeout
if (STRESS_COMMAND_SERVER_MINIMUM_UPDATE_INTERVAL < dwUpdateInterval && STRESS_COMMAND_SERVER_MAXIMUM_UPDATE_INTERVAL > dwUpdateInterval) m_dwCommandServerUpdateInternval = dwUpdateInterval; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_TimeStressBegins(DWORD, DWORD)
//
// Purpose:
// Pass in a time string to begin stress in 24 hour time.
// Takes two parameters to set the hour and minute. A parameter
// will be ignored if NULL.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_TimeStressBegins( LPTSTR szHour, // [IN] 0-23 parameter ignored if < 0
LPTSTR szMinute // [IN] 0-59 parameter ignored if < 0
) { if (szHour) m_iTimeStressBeginsHour = _ttoi(szHour);
if (szMinute) m_iTimeStressBeginsMinute = _ttoi(szMinute); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Set_TimeStressEnds(DWORD, DWORD)
//
// Purpose:
// Pass in a time string to end stress in 24 hour time.
// Takes two parameters to set the hour and minute. A parameter
// will be ignored if NULL.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Set_TimeStressEnds( LPTSTR szHour, // [IN] 0-23 parameter ignored if < 0
LPTSTR szMinute // [IN] 0-59 parameter ignored if < 0
) { if (szHour) m_iTimeStressEndsHour = _ttoi(szHour);
if (szMinute) m_iTimeStressEndsMinute = _ttoi(szMinute); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Create_StressInstance(LPTSTR)
//
// Purpose:
// Pass in an URL and its size and it will be added to the
// m_arStressInstanceList list. There is no limit on the number of
// URLs that can be added.
//
// Called by: QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Create_StressInstance( DWORD dwStressInstanceID, // [IN] ID from the stressAdmin DB identifying this stressInstance
LPTSTR szPageHeapCommand, // [IN] string buffer containing the pageheap command line
LPTSTR szUMDHCommand, // [IN] string buffer containing the UMDH command line
LPTSTR szPDB_URL, // [IN] string buffer containing URL to the stress EXE's PDB file
LPTSTR szSYM_URL, // [IN] string buffer containing URL to the stress EXE's SYM file
LPTSTR szMemDumpPath, // [IN] string buffer containing path to create memory dump files
LPTSTR szEXE_URL // [IN] string buffer containing URL to the stress EXE
) { PSTRESSINSTANCE pStressInstance = NULL;
// verify params just in case
if (!szEXE_URL) return;
// allocate memory for the object and put it in the list
pStressInstance = new StressInstance;
pStressInstance->Set_UMDHCommands(szUMDHCommand); pStressInstance->Set_PageHeapCommands(szPageHeapCommand); pStressInstance->Set_StressExeID(dwStressInstanceID); pStressInstance->Set_StressExeURL(szEXE_URL); pStressInstance->Set_StressExeMemoryDumpPath(szMemDumpPath); pStressInstance->Set_StressExePdbURL(szPDB_URL); pStressInstance->Set_StressExeSymURL(szSYM_URL);
m_arStressInstanceList.push_back(pStressInstance); m_dwStressInstanceIterator = m_arStressInstanceList.begin(); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Clear_StressExeURLs()
//
// Purpose:
// Frees memory from the m_arStressExeList vector.
//
// Called by: QueryServerForCommands_ThreadProc and ~ServerCommands
//
////////////////////////////////////////////////////////////
VOID ServerCommands::Clear_StressExeURLs() { // don't want to delete StressInstances if it's still running
if (IsStressRunning()) return;
/*
// walk the list and delete from the front to back
while (!m_arStressInstanceList.empty()) m_arStressInstanceList.erase(m_arStressInstanceList.begin()); */
StressInstance *pStressInstance = NULL;
for (int iIndex=0; iIndex < m_arStressInstanceList.size(); iIndex++) { pStressInstance = m_arStressInstanceList[iIndex]; m_arStressInstanceList.erase(&m_arStressInstanceList[iIndex]); delete [] pStressInstance; }
m_dwStressInstanceIterator = NULL; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Get_CommandServerUpdateInterval()
//
// Purpose:
// Returns the current setting for the Command Server Update
// interval.
//
////////////////////////////////////////////////////////////
DWORD ServerCommands::Get_CommandServerUpdateInterval() { if (STRESS_COMMAND_SERVER_MINIMUM_UPDATE_INTERVAL < m_dwCommandServerUpdateInternval && STRESS_COMMAND_SERVER_MAXIMUM_UPDATE_INTERVAL > m_dwCommandServerUpdateInternval) return m_dwCommandServerUpdateInternval; else return STRESS_COMMAND_SERVER_UPDATE_INTERVAL; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::BeginStress()
//
// Purpose:
// Queries for commands then starts the StressInstance objects.
//
////////////////////////////////////////////////////////////
void ServerCommands::BeginStress() { EnterCriticalSection(&g_csServerCommandsVars);
if (!m_arStressInstanceList.empty() && !IsStressRunning()) { // LOGLOG: Stress is beginning
NetworkTools__SendLog(FIELDNAME__LOGTYPE_BEGIN_STRESS, "Stress is beginning.", NULL, NULL);
// first download and regsvr32 the winhttp dll and symbols
Download_WinHttpDLL();
for(int iIndex = 0; iIndex < m_arStressInstanceList.size(); iIndex++) m_arStressInstanceList[iIndex]->Begin(); } else { // ping Command Server for list of stress EXE URLs.
QueryServerForCommands(); }
LeaveCriticalSection(&g_csServerCommandsVars); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::EndStress()
//
// Purpose:
// Ends stress and posts the results to the Command Server.
//
////////////////////////////////////////////////////////////
void ServerCommands::EndStress() { EnterCriticalSection(&g_csServerCommandsVars);
if (!m_arStressInstanceList.empty()) { if (IsStressRunning()) { // LOGLOG: Stress is ending
NetworkTools__SendLog(FIELDNAME__LOGTYPE_END_STRESS, "Stress is ending.", NULL, NULL); }
for (int iIndex = 0; iIndex < m_arStressInstanceList.size(); iIndex++) m_arStressInstanceList[iIndex]->End(); }
// Remove the stress objects that already finished
Clear_StressExeURLs();
LeaveCriticalSection(&g_csServerCommandsVars); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::AbortStressInstance(DWORD)
//
// Purpose:
// Aborts a all stress instances that recieved a server abort message.
//
// Called in:
// QueryServerForCommands_ThreadProc
//
////////////////////////////////////////////////////////////
VOID ServerCommands::AbortStressInstance(DWORD dwAbortID) { // EnterCriticalSection(&g_csServerCommandsVars);
if (!m_arStressInstanceList.empty()) { for (int iIndex = 0; iIndex < m_arStressInstanceList.size(); iIndex++) { if (m_arStressInstanceList[iIndex]->Get_ID() == dwAbortID) m_arStressInstanceList[iIndex]->End(); } }
//LeaveCriticalSection(&g_csServerCommandsVars);
}
////////////////////////////////////////////////////////////
// Function: ServerCommands::IsStressRunning()
//
// Purpose:
// Returns TRUE if any of the stressinstances is running. FALSE if not.
//
////////////////////////////////////////////////////////////
BOOL ServerCommands::IsStressRunning() { BOOL bIsRunning = FALSE;
EnterCriticalSection(&g_csServerCommandsVars);
if (!m_arStressInstanceList.empty()) { for (int iIndex = 0; iIndex < m_arStressInstanceList.size(); iIndex++) { if (m_arStressInstanceList[iIndex]->IsRunning(STRESSINSTANCE_STRESS_EXE_CLOSE_TIMEOUT)) bIsRunning = TRUE; } }
LeaveCriticalSection(&g_csServerCommandsVars);
return bIsRunning; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::IsTimeToBeginStress()
//
// Purpose:
// TRUE if it's time to begin stress based on the time returned
// from the Command Server. Will return TRUE if m_sysTimeStressBegins
// is current or in the future. FALSE if not.
//
////////////////////////////////////////////////////////////
BOOL ServerCommands::IsTimeToBeginStress() { SYSTEMTIME stCurrent, stBeginStress, stEndStress; FILETIME ftCurrent, ftBeginStress, ftEndStress; BOOL bResult = FALSE;
EnterCriticalSection(&g_csServerCommandsVars);
// always run stress now if server tells us to
if (m_bRunForever) { bResult = TRUE; goto Exit; }
// check to see if valid time values have been received. If not, then we always fail.
if ( m_iTimeStressBeginsHour < 0 || m_iTimeStressBeginsMinute < 0 || m_iTimeStressEndsHour < 0 || m_iTimeStressEndsMinute < 0 ) { bResult = FALSE; goto Exit; }
GetLocalTime(&stCurrent); GetLocalTime(&stBeginStress); GetLocalTime(&stEndStress);
// use the hour and minute time that we got from the command server
stBeginStress.wHour = m_iTimeStressBeginsHour; stBeginStress.wMinute = m_iTimeStressBeginsMinute;
stEndStress.wHour = m_iTimeStressEndsHour; stEndStress.wMinute = m_iTimeStressEndsMinute;
// convert to file time so we can compare
SystemTimeToFileTime(&stCurrent, &ftCurrent); SystemTimeToFileTime(&stBeginStress, &ftBeginStress); SystemTimeToFileTime(&stEndStress, &ftEndStress);
// If EndTime < BeginTime, then it means stress is running for
// over a day so we have to add 24 hours to the end time.
ULARGE_INTEGER ulEndStress; ULONGLONG ullNanoSecondsInAFreakingDay;
ulEndStress.LowPart = ftEndStress.dwLowDateTime; ulEndStress.HighPart = ftEndStress.dwHighDateTime;
// stress runs across two days so we wrap around one day
ullNanoSecondsInAFreakingDay = 24 * 60; // minutes in a day
ullNanoSecondsInAFreakingDay *= 60; // seconds in a day
ullNanoSecondsInAFreakingDay *= 1000000000; // number of nanoseconds in a day. 10^9 NS in a second
ullNanoSecondsInAFreakingDay /= 100; // The FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601.
if (m_iTimeStressEndsHour < m_iTimeStressBeginsHour) { // ********************
// ********************
// ** increase by 24 hours
// **
ulEndStress.QuadPart += ullNanoSecondsInAFreakingDay;
// copy back to the original EndStress Date/Time
ftEndStress.dwHighDateTime = ulEndStress.HighPart; ftEndStress.dwLowDateTime = ulEndStress.LowPart;
FileTimeToSystemTime(&ftEndStress, &stEndStress); } else { // stress runs in the same day
if ((m_iTimeStressEndsHour == m_iTimeStressBeginsHour) && (m_iTimeStressEndsMinute <= m_iTimeStressBeginsMinute)) { // if 7:30 to 7:20 - we wrap around one day.
ulEndStress.QuadPart += ullNanoSecondsInAFreakingDay;
// copy back to the original EndStress Date/Time
ftEndStress.dwHighDateTime = ulEndStress.HighPart; ftEndStress.dwLowDateTime = ulEndStress.LowPart;
FileTimeToSystemTime(&ftEndStress, &stEndStress); } }
// Begin stress if:
// (BeginTime <= CurrentTime <= EndTime)
if ((0 <= CompareFileTime(&ftCurrent, &ftBeginStress)) && (0 <= CompareFileTime(&ftEndStress, &ftCurrent))) bResult = TRUE; else bResult = FALSE;
Exit: LeaveCriticalSection(&g_csServerCommandsVars);
return bResult; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::IsTimeToExitStress()
//
// Purpose:
// TRUE if it's time to end stress based on the COMMANDHEADER__EXIT headers
// from the Command Server exists. FALSE if not.
//
////////////////////////////////////////////////////////////
BOOL ServerCommands::IsTimeToExitStress() { return m_bExit; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Get_CommandServerURL()
//
// Purpose:
// Returns the Command Server URL.
//
////////////////////////////////////////////////////////////
LPTSTR ServerCommands::Get_CommandServerURL() { return m_szCommandServerURL; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Get_CommandServerResultsURL()
//
// Purpose:
// Returns the Command Server Results URL.
//
////////////////////////////////////////////////////////////
LPTSTR ServerCommands::Get_CommandServerResultsURL() { return m_szCommandServerResultsURL; }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Get_NumberOfStressInstances()
//
// Purpose:
// Returns the number of stressInstances running or pending.
//
////////////////////////////////////////////////////////////
DWORD ServerCommands::Get_NumberOfStressInstances() { return m_arStressInstanceList.size(); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::Get_TestDllFileName()
//
// Purpose:
// Returns the name of the test DLL. ex. "winhttp5.dll"
//
////////////////////////////////////////////////////////////
LPTSTR ServerCommands::Get_TestDllFileName() { // m_szWinHttpDLL_FileName can be NULL in the case that a test DLL is not downloaded
if (0 < _tcslen(m_szWinHttpDLL_FileName)) return m_szWinHttpDLL_FileName; else return _T("winhttp5.dll"); }
////////////////////////////////////////////////////////////
// Function: ServerCommands::RegisterClient()
//
// Purpose:
// Sends the command server the system info on this client.
// This lets the command server know that this client is alive.
//
// NOTE: This only works in NT because we query
// environment vars not present in Win9x
//
////////////////////////////////////////////////////////////
BOOL ServerCommands::RegisterClient() { OSVERSIONINFOA osInfo; BOOL bResult = FALSE; DWORD dwPostSize = 5000; DWORD dwTempSize = MAX_PATH; DWORD dwSizeSoFar = 0; // size of string written to szTemp so far.
LPSTR szPost = new CHAR[dwPostSize]; LPSTR szTemp = new CHAR[dwTempSize];
ZeroMemory(szTemp, dwTempSize); ZeroMemory(szPost, dwPostSize);
// *********************
// *********************
// ** Get windows version info
osInfo.dwOSVersionInfoSize = sizeof(osInfo); if (!GetVersionExA(&osInfo)) goto Exit;
dwSizeSoFar += GetEnvironmentVariableA("OS", szTemp, dwTempSize); dwSizeSoFar += sizeof(FIELDNAME__OS_PLATFORM); strcat(szPost, FIELDNAME__OS_PLATFORM); strcat(szPost, szTemp);
strcat(szPost, "&" FIELDNAME__OS_BUILD); strcat(szPost, _itoa(osInfo.dwBuildNumber, szTemp, 10));
strcat(szPost, "&" FIELDNAME__OS_MAJORVERSION); strcat(szPost, _itoa(osInfo.dwMajorVersion, szTemp, 10));
strcat(szPost, "&" FIELDNAME__OS_MINORVERSION); strcat(szPost, _itoa(osInfo.dwMinorVersion, szTemp, 10));
strcat(szPost, "&" FIELDNAME__OS_EXTRAINFO); strcat(szPost, osInfo.szCSDVersion);
// *********************
// *********************
// ** Get processor info
GetEnvironmentVariableA("PROCESSOR_ARCHITECTURE", szTemp, dwTempSize); strcat(szPost, "&" FIELDNAME__SYSTEMINFO_PROCSSSOR_ARCHITECTURE); strcat(szPost, szTemp);
GetEnvironmentVariableA("PROCESSOR_IDENTIFIER", szTemp, dwTempSize); strcat(szPost, "&" FIELDNAME__SYSTEMINFO_PROCSSSOR_ID); strcat(szPost, szTemp);
GetEnvironmentVariableA("PROCESSOR_LEVEL", szTemp, dwTempSize); strcat(szPost, "&" FIELDNAME__SYSTEMINFO_PROCSSSOR_LEVEL); strcat(szPost, szTemp);
GetEnvironmentVariableA("PROCESSOR_REVISION", szTemp, dwTempSize); strcat(szPost, "&" FIELDNAME__SYSTEMINFO_PROCSSSOR_REVISION); strcat(szPost, szTemp);
GetEnvironmentVariableA("NUMBER_OF_PROCESSORS", szTemp, dwTempSize); strcat(szPost, "&" FIELDNAME__SYSTEMINFO_PROCSSSOR_NUMBER_OF); strcat(szPost, szTemp);
// *********************
// *********************
// ** Get user info
GetEnvironmentVariableA("USERNAME", szTemp, dwTempSize); strcat(szPost, "&" FIELDNAME__USERINFO_USERALIAS); strcat(szPost, szTemp);
GetEnvironmentVariableA("USERDOMAIN", szTemp, dwTempSize); strcat(szPost, "&" FIELDNAME__USERINFO_USERDOMAIN); strcat(szPost, szTemp);
// BUGBUG: someone needs to resolve the user alias to the real full name of the user
// FIELDNAME__USERINFO_FULLNAME
// get the client's machine name
strcat(szPost, "&" FIELDNAME__USERINFO_MACHINENAME); strcat(szPost, m_szClientMachineName);
// Let the Command Server know that this client is alive
bResult = NetworkTools__POSTResponse(STRESS_COMMAND_SERVER_REGISTERCLIENT_URL, szPost, NULL);
// LOGLOG: stressScheduler has started
bResult = NetworkTools__SendLog(FIELDNAME__LOGTYPE_START_UP, "WinHttpStressScheduler has started.", NULL, NULL);
Exit: delete [] szPost; delete [] szTemp;
return bResult; }
|