1029 lines
29 KiB
C++
1029 lines
29 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name :
|
|
WpConfig.cxx
|
|
|
|
Abstract:
|
|
Module implementing the Worker Process Configuration Data structure.
|
|
WP_CONFIG object encapsulates configuration supplied from the commandline
|
|
as well as remotely supplied from the admin process.
|
|
|
|
Author:
|
|
|
|
Murali R. Krishnan ( MuraliK ) 21-Oct-1998
|
|
|
|
Environment:
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
IIS Worker Process
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
|
|
# include "precomp.hxx"
|
|
|
|
#include "CatMeta.h"
|
|
|
|
|
|
/*
|
|
Usage: \bin\iisrearc\inetsrv\iiswp [options] APN
|
|
APN -- AppPool Name
|
|
-d <URL List> -- Indicates that the process should register the given
|
|
namespace itself. This mode is for running worker process in
|
|
stand alone mode (for debugging)
|
|
-l -- Log errors that stop the worker process into the eventlog
|
|
-ld -- Disables logging the errors of worker process to eventlog
|
|
(default is not write to eventlog)
|
|
-a -- Look for web admin service and register with the same
|
|
(default is look for web admin service)
|
|
-ad -- Do not look for web admin service and do registration
|
|
-r <n> -- Restart the worker process after n requests.
|
|
-t <n> -- Shutdown the worker process if idle for n milliseconds.
|
|
-h <path> -- Assume the default site is rooted at this path.
|
|
<URL List> uses the syntax: {http[s]://IP:port/URL | http[s]://hostname:port/URL }+
|
|
with space as separator
|
|
eg: -d http://localhost:80/ => listen for all HTTP requests on port 80
|
|
eg: -d http://localhost:80/ http://localhost:81/ => listen on port 80 & 81
|
|
|
|
*/
|
|
|
|
const CHAR g_rgchUsage[] =
|
|
"Usage: %ws [options] APN\n"
|
|
"\tAPN -- AppPool Name\n"
|
|
"\t-d <URL List> -- Indicates that the process should register the given \n"
|
|
"\t\tnamespace itself. This mode is for running worker process in\n"
|
|
"\t\tstand alone mode (for debugging)\n"
|
|
"\t-l -- Log errors that stop the worker process into the eventlog\n"
|
|
"\t-ld -- Disables logging the errors of worker process to eventlog\n"
|
|
"\t\t(default is not write to eventlog)\n"
|
|
"\t-a -- Look for web admin service and register with the same\n"
|
|
"\t\t(default is look for web admin service)\n"
|
|
"\t-ad -- Do not look for web admin service and do registration\n"
|
|
"\t-r <n> -- Restart the worker process after n requests\n"
|
|
"\t-t <n> -- Shutdown the worker process if idle for n milliseconds\n"
|
|
"\t-h <path> -- Assume the default site is rooted at this path\n"
|
|
"\t-p -- Tell COR to add IceCAP instrumentation\n"
|
|
"\t<URL List> uses the syntax: {http[s]://IP:port/URL | http[s]://hostname:port/URL }+\n"
|
|
"\t\twith space as separator\n"
|
|
"\t\t eg: -d http://*:80/ => listen for all HTTP requests on port 80\n"
|
|
"\t\t eg: -d http://localhost:80/ http://localhost:81/ => listen on port 80 & 81\n"
|
|
;
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
* Convert a DWORD to a hex string. The incoming buffer must
|
|
* be big enough to hold "12345678" (including the null
|
|
* teriminator).
|
|
************************************************************/
|
|
|
|
static const WCHAR hexDigits[] = L"0123456789ABCDEF";
|
|
|
|
// Convert a 32-bit DWORD to a hex string. The incoming buffer must
|
|
// be big enough to hold "12345678" (including the null terminator).
|
|
void DwordToHexString(DWORD dword, WCHAR wszHexDword[])
|
|
{
|
|
const int DWORDHEXDIGITS = sizeof(DWORD) * 2;
|
|
|
|
WCHAR* p = wszHexDword;
|
|
|
|
unsigned shift = (sizeof(DWORD) * 8) - 4;
|
|
DWORD mask = 0xFu << shift;
|
|
int i;
|
|
for (i = 0; i < DWORDHEXDIGITS; ++i, mask >>= 4, shift -= 4)
|
|
{
|
|
unsigned digit = (dword & mask) >> shift;
|
|
p[i] = hexDigits[digit];
|
|
}
|
|
p[i] = L'\0';
|
|
}
|
|
|
|
|
|
HMODULE LoadXSP()
|
|
{
|
|
// Load xspisapi.dll. We find this DLL by inspecting
|
|
// HKLM/Software/Microsoft/XSP and it's InstallDir value.
|
|
HKEY hkeyXSP = NULL;
|
|
HMODULE hMod = NULL;
|
|
|
|
__try
|
|
{
|
|
long rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\XSP",
|
|
0, // reserved
|
|
KEY_READ,
|
|
&hkeyXSP);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "XSP is not installed\n"));
|
|
}
|
|
|
|
__leave;
|
|
}
|
|
|
|
WCHAR path[MAX_PATH + 1];
|
|
DWORD valueType = 0;
|
|
DWORD len = MAX_PATH;
|
|
rc = RegQueryValueEx(hkeyXSP,
|
|
L"InstallDir",
|
|
NULL, // reserved
|
|
&valueType,
|
|
reinterpret_cast<BYTE*>(path),
|
|
&len);
|
|
if (rc != ERROR_SUCCESS || (valueType != REG_SZ && valueType != REG_EXPAND_SZ))
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "XSP is not installed\n"));
|
|
}
|
|
|
|
__leave;
|
|
}
|
|
|
|
len /= sizeof path[0]; // Convert to characters
|
|
|
|
// I now have a path something like "d:\winnt\xspdt", so just
|
|
// append the DLL name.
|
|
wcscat(path + len - 1, L"\\xspisapi.dll");
|
|
|
|
hMod = LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
}
|
|
__finally
|
|
{
|
|
if (hkeyXSP != NULL)
|
|
RegCloseKey(hkeyXSP);
|
|
}
|
|
|
|
return hMod;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************
|
|
* Stupid pointer class.
|
|
************************************************************/
|
|
|
|
template <class T>
|
|
class StupidComPointer
|
|
{
|
|
public:
|
|
StupidComPointer()
|
|
: _ptr(NULL)
|
|
{
|
|
}
|
|
|
|
~StupidComPointer()
|
|
{
|
|
if (_ptr != NULL)
|
|
_ptr->Release();
|
|
}
|
|
|
|
operator T*()
|
|
{
|
|
return (T*)_ptr;
|
|
}
|
|
|
|
T* operator->()
|
|
{
|
|
return (T*)_ptr;
|
|
}
|
|
|
|
T** operator&()
|
|
{
|
|
return (T**)&_ptr;
|
|
}
|
|
|
|
T& operator*()
|
|
{
|
|
return *(T*)_ptr;
|
|
}
|
|
|
|
private:
|
|
IUnknown* _ptr;
|
|
};
|
|
|
|
|
|
/************************************************************
|
|
* Member functions of WP_CONFIG
|
|
************************************************************/
|
|
|
|
WP_CONFIG::WP_CONFIG(void)
|
|
: m_pwszAppPoolName (AP_NAME),
|
|
m_fSetupControlChannel(false),
|
|
m_fLogErrorsToEventLog(false),
|
|
m_fRegisterWithWAS (true),
|
|
m_RestartCount (0),
|
|
m_NamedPipeId (0),
|
|
m_IdleTime (0),
|
|
m_homeDirectory (NULL),
|
|
m_istDispenser (NULL),
|
|
m_catFile (NULL),
|
|
m_xspisapiDLL (NULL),
|
|
_AppDomainGetObject(NULL),
|
|
_AppDomainUninitFactory(NULL)
|
|
{
|
|
lstrcpy( m_pwszProgram, L"WP");
|
|
InitializeCriticalSection(&m_workerTableGuard);
|
|
}
|
|
|
|
WP_CONFIG::~WP_CONFIG()
|
|
{
|
|
DeleteCriticalSection(&m_workerTableGuard);
|
|
|
|
m_ulcc.Cleanup();
|
|
|
|
free(m_homeDirectory);
|
|
free(m_catFile);
|
|
|
|
if (m_istDispenser != NULL)
|
|
m_istDispenser->Release();
|
|
}
|
|
|
|
|
|
void
|
|
WP_CONFIG::PrintUsage() const
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, g_rgchUsage, m_pwszProgram));
|
|
}
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
Parses the command line to read in all configuration supplied.
|
|
This function updates the state variables inside WP_CONFIG for use
|
|
in starting up the Worker process.
|
|
|
|
See g_rgchUsage[] for details on the arguments that can be supplied
|
|
|
|
Arguments:
|
|
argc - count of arguments supplied
|
|
argv - pointer to strings containing the arguments.
|
|
|
|
Returns:
|
|
Boolean
|
|
|
|
--********************************************************************/
|
|
bool
|
|
WP_CONFIG::ParseCommandLine(int argc, PWSTR argv[])
|
|
{
|
|
bool fRet = true;
|
|
int iArg;
|
|
|
|
lstrcpyn( m_pwszProgram, argv[0], sizeof m_pwszProgram / sizeof m_pwszProgram[0]);
|
|
|
|
if ( argc < 2)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "Invalid number of parameters (%d)\n", argc));
|
|
PrintUsage();
|
|
return (false);
|
|
}
|
|
|
|
for( iArg = 1; iArg < argc; iArg++)
|
|
{
|
|
if ( (argv[iArg][0] == L'-') || (argv[iArg][0] == L'/'))
|
|
{
|
|
switch (argv[iArg][1])
|
|
{
|
|
|
|
case L'c': case L'C':
|
|
DBGPRINTF((DBG_CONTEXT, "-C: obsolete\n"));
|
|
break;
|
|
|
|
case L'd': case L'D':
|
|
|
|
m_fSetupControlChannel = true;
|
|
iArg++;
|
|
|
|
while ( (iArg < argc-1) &&
|
|
(argv[iArg][0] != L'-') && (argv[iArg][0] != L'/'))
|
|
{
|
|
if ( !InsertURLIntoList(argv[iArg]) )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "Invalid URL: %ws\n", argv[iArg]));
|
|
}
|
|
|
|
iArg++;
|
|
}
|
|
|
|
iArg--;
|
|
break;
|
|
|
|
case L'a': case L'A':
|
|
if ( (L'd' == argv[iArg][2]) || (L'D' == argv[iArg][2]))
|
|
{
|
|
m_fRegisterWithWAS = false;
|
|
}
|
|
else
|
|
{
|
|
// -a NamedPipeId
|
|
if (L'\0' == argv[iArg][2])
|
|
{
|
|
iArg++;
|
|
}
|
|
|
|
m_NamedPipeId = wcstoul(argv[iArg], NULL, 0);
|
|
DBGPRINTF((DBG_CONTEXT, "NamedPipe Id, %lu\n", m_NamedPipeId));
|
|
|
|
if (0 == m_NamedPipeId)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "Invalid NamedPipe Id, %ws\n",
|
|
argv[iArg]));
|
|
fRet = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case L'l': case L'L':
|
|
DBGPRINTF((DBG_CONTEXT, "Warning: This option is not supported presently\n"));
|
|
if (L' ' == argv[iArg][0])
|
|
{
|
|
m_fLogErrorsToEventLog = true;
|
|
}
|
|
break;
|
|
|
|
case L'r': case L'R':
|
|
m_RestartCount = wcstoul(argv[++iArg], NULL, 0);
|
|
|
|
if (m_RestartCount == 0)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "Invalid maximum requests %ws\n", argv[iArg]));
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "Maximum requests is %lu\n", m_RestartCount));
|
|
}
|
|
break;
|
|
|
|
case L't': case L'T':
|
|
m_IdleTime = wcstoul(argv[++iArg], NULL, 0);
|
|
|
|
if (m_IdleTime == 0)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "Invalid idle time %ws\n", argv[iArg]));
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "The idle time value is %lu\n", m_IdleTime));
|
|
}
|
|
break;
|
|
|
|
case L'h': case L'H':
|
|
m_homeDirectory = _wcsdup(argv[++iArg]);
|
|
if (m_homeDirectory == NULL)
|
|
return false;
|
|
break;
|
|
|
|
case L'p': case L'P':
|
|
SetEnvironmentVariable(L"CORDBG_ENABLE", L"0x20");
|
|
SetEnvironmentVariable(L"COR_PROFILER", L"\"ComPlusIcecapProfile.CorIcecapProfiler\"");
|
|
SetEnvironmentVariable(L"PROF_CONFIG", L"/callcap");
|
|
|
|
break;
|
|
|
|
default:
|
|
case L'?':
|
|
fRet = false;
|
|
break;
|
|
} // switch
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Take the next item as the NSG name and bail out here
|
|
//
|
|
m_pwszAppPoolName = argv[iArg];
|
|
|
|
if ( iArg != (argc - 1))
|
|
{
|
|
//
|
|
// this is not the last argument => unwanted parameters exist
|
|
// give warning and ignore
|
|
//
|
|
DBGPRINTF((DBG_CONTEXT, "Warning: Too many arguments supplied\n"));
|
|
}
|
|
|
|
break; // get out of here.
|
|
}
|
|
}
|
|
|
|
if (!m_fRegisterWithWAS)
|
|
{
|
|
m_RestartCount = 0;
|
|
m_IdleTime = 0;
|
|
}
|
|
|
|
if (!fRet)
|
|
{
|
|
PrintUsage();
|
|
}
|
|
|
|
return ( fRet);
|
|
|
|
} // WP_CONFIG::ParseCommandLine()
|
|
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
Sets up the control channel for processing requests. It uses
|
|
the configuration parameters supplied for initializing the
|
|
UL_CONTROL_CHANNEL.
|
|
|
|
See g_rgchUsage[] for details on the arguments that can be supplied
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
Win32 error
|
|
|
|
--********************************************************************/
|
|
|
|
ULONG
|
|
WP_CONFIG::SetupControlChannel(void)
|
|
{
|
|
|
|
//
|
|
// Setup a control channel for our local use now. Used mainly for
|
|
// the purpose of debugging.
|
|
// In general control channel work is done by the AdminProces.
|
|
//
|
|
|
|
return m_ulcc.Initialize( m_mszURLList, m_pwszAppPoolName);
|
|
|
|
} // WP_CONFIG::SetupControlChannel()
|
|
|
|
/********************************************************************++
|
|
--********************************************************************/
|
|
|
|
bool
|
|
WP_CONFIG::InsertURLIntoList( LPCWSTR pwszURL )
|
|
{
|
|
LPCWSTR pwszOriginalURL = pwszURL;
|
|
|
|
//
|
|
// Minimum length: 11 (http://*:1/). Begins with http
|
|
//
|
|
|
|
if ( ( wcslen(pwszURL) < 11 ) || ( 0 != _wcsnicmp(pwszURL, L"http", 4)) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pwszURL += 4;
|
|
|
|
//
|
|
// https
|
|
//
|
|
|
|
if ((L's' == *pwszURL) || (L'S' == *pwszURL))
|
|
{
|
|
pwszURL++;
|
|
}
|
|
|
|
//
|
|
// ://
|
|
//
|
|
|
|
if ( (L':' != *pwszURL) || (L'/' != *(pwszURL+1)) || (L'/' != *(pwszURL+2)) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pwszURL += 3;
|
|
|
|
//
|
|
// Skip host name or Ip Address
|
|
//
|
|
|
|
while ( (0 != *pwszURL) && ( L':' != *pwszURL))
|
|
{
|
|
pwszURL++;
|
|
}
|
|
|
|
//
|
|
// Check port # exists
|
|
//
|
|
|
|
if (0 == *pwszURL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Check port number is numeric
|
|
//
|
|
|
|
pwszURL++;
|
|
|
|
while ( (0 != *pwszURL) && ( L'/' != *pwszURL) )
|
|
{
|
|
if (( L'0' > *pwszURL) || ( L'9' < *pwszURL))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pwszURL++;
|
|
}
|
|
|
|
//
|
|
// Check / after port number exists
|
|
//
|
|
|
|
if (0 == *pwszURL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// URL is good.
|
|
//
|
|
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Inserting URL '%ws' into Config Group List\n",
|
|
pwszOriginalURL
|
|
));
|
|
}
|
|
return ( TRUE == m_mszURLList.Append( pwszOriginalURL));
|
|
|
|
} // WP_CONFIG::InsertURLIntoList()
|
|
|
|
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
Determine the path of the duct-tape root directory. This is where
|
|
the global config file and the System.IIS.dll must reside. We find
|
|
this by inspecting the registry key:
|
|
HKLM/Software/Microsoft/Catalog42/URT
|
|
under the value "Dll".
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
S_OK if successful, failure code if not.
|
|
|
|
--********************************************************************/
|
|
|
|
HRESULT
|
|
WP_CONFIG::GetDuctTapeRoot(WCHAR path[MAX_PATH+1], size_t* pathLength)
|
|
{
|
|
long rc;
|
|
HKEY hkeyCat42 = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
__try
|
|
{
|
|
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Microsoft\\Catalog42\\URT",
|
|
0, // reserved
|
|
KEY_READ,
|
|
&hkeyCat42);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Catalog42 is not installed\n"
|
|
));
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32(rc);
|
|
__leave;
|
|
}
|
|
|
|
DWORD valueType = 0;
|
|
DWORD len = MAX_PATH;
|
|
rc = RegQueryValueEx(hkeyCat42,
|
|
L"Dll",
|
|
NULL, // reserved
|
|
&valueType,
|
|
reinterpret_cast<BYTE*>(path),
|
|
&len);
|
|
if (rc != ERROR_SUCCESS || (valueType != REG_SZ && valueType != REG_EXPAND_SZ))
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Catalog42 is not installed\n"
|
|
));
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32(rc);
|
|
__leave;
|
|
}
|
|
|
|
*pathLength = len / sizeof path[0]; // Convert to characters
|
|
|
|
// I now have a path something like "d:\winnt\xspdt\catalog42.dll".
|
|
// The cooked-down catalog is in the same directory
|
|
// ("d:\winnt\xspdt") in a file called "WASMB.CLB".
|
|
WCHAR* lastWhack = wcsrchr(path, L'\\');
|
|
if (lastWhack == NULL)
|
|
{
|
|
// This should never happen, but if it does, just let the
|
|
// string be empty and let the world fall to pieces if
|
|
// it's not found in the current directory.
|
|
*pathLength = 0;
|
|
path[0] = L'\0';
|
|
}
|
|
else
|
|
{
|
|
// Chop off the dll name, but leave the trailing slash.
|
|
*pathLength -= *pathLength - (lastWhack - path) - 1;
|
|
path[*pathLength] = L'\0';
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
if (hkeyCat42 != NULL)
|
|
RegCloseKey(hkeyCat42);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
Prepares the catalog, and also prepares the app domain factory.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
S_OK if successful, failure code if not.
|
|
|
|
--********************************************************************/
|
|
|
|
HRESULT
|
|
WP_CONFIG::InitConfiguration()
|
|
{
|
|
static const WCHAR cookedCatalogName[] = L"WASMB.CLB";
|
|
HRESULT hr = S_OK;
|
|
|
|
// Get the duct-tape root directory.
|
|
WCHAR path[MAX_PATH + 1];
|
|
size_t pathLength = MAX_PATH;
|
|
hr = GetDuctTapeRoot(path, &pathLength);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Construct the catalog name by appending to the duct-tape root.
|
|
wcscat(path + pathLength, cookedCatalogName);
|
|
|
|
m_catFile = _wcsdup(path);
|
|
if (m_catFile == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Construct the query cells used in subsequent queries.
|
|
m_queryCell[0].pData = (void*)m_catFile;
|
|
m_queryCell[0].eOperator = eST_OP_EQUAL;
|
|
m_queryCell[0].iCell = iST_CELL_FILE;
|
|
m_queryCell[0].dbType = DBTYPE_WSTR;
|
|
m_queryCell[0].cbSize = (pathLength * sizeof m_catFile[0]) + sizeof cookedCatalogName;
|
|
|
|
// Get the SimpleTable dispenser
|
|
hr = GetSimpleTableDispenser(WSZ_PRODUCT_URT, 0, &m_istDispenser);
|
|
if (m_istDispenser == NULL)
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Could not get simple table dispenser for %S, hr=0x%x\n",
|
|
WSZ_PRODUCT_URT, hr
|
|
));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Initialize our xspisapi imports.
|
|
m_xspisapiDLL = LoadXSP();
|
|
if (m_xspisapiDLL == NULL)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
APP_DOMAIN_INIT_FACTORY* AppDomainInitFactory;
|
|
|
|
AppDomainInitFactory = (APP_DOMAIN_INIT_FACTORY*)GetProcAddress(m_xspisapiDLL,
|
|
"AppDomainInitFactory");
|
|
_AppDomainUninitFactory = (APP_DOMAIN_UNINIT_FACTORY*)GetProcAddress(m_xspisapiDLL,
|
|
"AppDomainUninitFactory");
|
|
_AppDomainGetObject = (APP_DOMAIN_GET_OBJECT*)GetProcAddress(m_xspisapiDLL,
|
|
"AppDomainGetObject");
|
|
|
|
// Initialize XSP's app domain factory.
|
|
path[pathLength] = L'\0';
|
|
wcscat(path + pathLength, L"System.IIS.dll");
|
|
|
|
hr = AppDomainInitFactory(path, L"System.IIS.Hosting.ULManagedWorker");
|
|
if (FAILED(hr))
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Could not initialize the app domain factory, hr=0x%x\n",
|
|
hr
|
|
));
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
} // WP_CONFIG::InitConfiguration()
|
|
|
|
|
|
HRESULT
|
|
WP_CONFIG::UnInitConfiguration()
|
|
{
|
|
HRESULT hr = _AppDomainUninitFactory();
|
|
|
|
FreeLibrary(m_xspisapiDLL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
xspmrt::_ULManagedWorker*
|
|
WP_CONFIG::ChooseWorker(void* pvRequest)
|
|
{
|
|
UL_HTTP_REQUEST* request = (UL_HTTP_REQUEST*)pvRequest;
|
|
|
|
// Get the UrlContext, which is actually the AppID.
|
|
UL_URL_CONTEXT urlContext = request->UrlContext;
|
|
|
|
xspmrt::_ULManagedWorker* worker = NULL;
|
|
WCHAR* appPath = NULL;
|
|
WCHAR* rootDirectory = NULL;
|
|
DWORD siteID = 0;
|
|
HRESULT hr = S_OK;
|
|
FILETIME ftCreateTime;
|
|
LARGE_INTEGER liCreateTime;
|
|
|
|
WCHAR appID[sizeof "12345678"];
|
|
DwordToHexString((DWORD)urlContext, appID);
|
|
|
|
__try
|
|
{
|
|
EnterCriticalSection(&m_workerTableGuard);
|
|
|
|
// Look for an existing worker in an existing app domain.
|
|
hr = _AppDomainGetObject(appID, NULL, (IUnknown**)&worker);
|
|
if (FAILED(hr))
|
|
__leave;
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
// The worker is new, so we need to initialize it.
|
|
|
|
if (m_mszURLList.First() != NULL)
|
|
{
|
|
// We're launching this from the command line. The
|
|
// root directory is given in m_homeDirectory, and
|
|
// we're going to hard-code the application as "/".
|
|
|
|
if (m_homeDirectory == NULL)
|
|
{
|
|
// Kludge
|
|
WCHAR defaultHome[] = L"%SystemDrive%\\InetPub\\DtRoot";
|
|
WCHAR buf[MAX_PATH + 1];
|
|
size_t len = ExpandEnvironmentStrings(defaultHome, buf, MAX_PATH);
|
|
if (len == 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
m_homeDirectory = _wcsdup(buf);
|
|
if (m_homeDirectory == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
rootDirectory = _wcsdup(m_homeDirectory);
|
|
appPath = _wcsdup(L"/");
|
|
if (rootDirectory == NULL || appPath == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
__leave;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Find the Application info we need: the corresponding site ID,
|
|
// and the application path.
|
|
hr = GetAppInfo(urlContext, &appPath, &siteID);
|
|
if (FAILED(hr))
|
|
__leave;
|
|
|
|
// Get the useful information about the corresponding site: it's
|
|
// home directory.
|
|
hr = GetSiteInfo(siteID, &rootDirectory);
|
|
if (FAILED(hr))
|
|
__leave;
|
|
}
|
|
|
|
// BUGBUG! For now, form the physical app path by concatenating
|
|
// the
|
|
WCHAR appPhysPath[MAX_PATH+1];
|
|
wcscpy(appPhysPath, rootDirectory);
|
|
wcscat(appPhysPath, appPath);
|
|
// Find any "/" and turn them to "\".
|
|
for (WCHAR* p = appPhysPath; *p != L'\0'; ++p)
|
|
{
|
|
if (*p == L'/')
|
|
*p = L'\\';
|
|
}
|
|
|
|
hr = _AppDomainGetObject(appID, appPhysPath, (IUnknown**)&worker);
|
|
if (FAILED(hr))
|
|
__leave;
|
|
|
|
//
|
|
// Get the current filetime. This timestamp for appdomain
|
|
// creation will be used in generating lame ETags for now
|
|
//
|
|
|
|
GetSystemTimeAsFileTime( &ftCreateTime );
|
|
liCreateTime.LowPart = ftCreateTime.dwLowDateTime;
|
|
liCreateTime.HighPart = ftCreateTime.dwHighDateTime;
|
|
|
|
hr = worker->Initialize(m_pwszAppPoolName,
|
|
rootDirectory,
|
|
appPath,
|
|
liCreateTime.QuadPart);
|
|
if (FAILED(hr))
|
|
{
|
|
worker->Release();
|
|
worker = NULL;
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
LeaveCriticalSection(&m_workerTableGuard);
|
|
|
|
free(appPath);
|
|
free(rootDirectory);
|
|
}
|
|
|
|
return worker;
|
|
}
|
|
|
|
HRESULT
|
|
WP_CONFIG::GetTable(const WCHAR* tableID, ISimpleTableRead2** pIst)
|
|
{
|
|
*pIst = NULL;
|
|
|
|
unsigned long numCells = sizeof m_queryCell / sizeof m_queryCell[0];
|
|
|
|
HRESULT hr = m_istDispenser->GetTable(wszDATABASE_URTGLOBAL,
|
|
tableID,
|
|
&m_queryCell,
|
|
&numCells,
|
|
eST_QUERYFORMAT_CELLS,
|
|
0, // fServiceRequests
|
|
reinterpret_cast<void**>(pIst));
|
|
if (FAILED(hr) || pIst == NULL)
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Could not get %S table from dispenser\n",
|
|
tableID));
|
|
}
|
|
|
|
*pIst = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
WP_CONFIG::GetAppInfo(UL_URL_CONTEXT urlContext, WCHAR** pAppPath, DWORD* pSiteID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
StupidComPointer<ISimpleTableRead2> istApplications;
|
|
|
|
*pAppPath = NULL;
|
|
*pSiteID = 0;
|
|
|
|
DWORD appID = urlContext;
|
|
|
|
m_queryCell[1].eOperator = eST_OP_EQUAL;
|
|
m_queryCell[1].iCell = iAPPS_AppID;
|
|
m_queryCell[1].dbType = DBTYPE_I4;
|
|
m_queryCell[1].cbSize = sizeof appID;
|
|
m_queryCell[1].pData = &appID;
|
|
|
|
hr = GetTable(wszTABLE_APPS, &istApplications);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
static ULONG appColumns[] = {
|
|
iAPPS_AppURL,
|
|
iAPPS_SiteID
|
|
};
|
|
static const size_t numAppColumns = sizeof appColumns / sizeof appColumns[0];
|
|
|
|
void* appValues[cAPPS_NumberOfColumns];
|
|
hr = istApplications->GetColumnValues(0,
|
|
numAppColumns,
|
|
appColumns,
|
|
NULL,
|
|
appValues);
|
|
if (FAILED(hr))
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Could not get Application row (AppID=%UI64) from dispenser\n",
|
|
urlContext));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
*pSiteID = *(DWORD*)appValues[iAPPS_SiteID];
|
|
*pAppPath = _wcsdup((WCHAR*)appValues[iAPPS_AppURL]);
|
|
if (*pAppPath == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
WP_CONFIG::GetSiteInfo(DWORD siteID, WCHAR** pHomeDirectory)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
StupidComPointer<ISimpleTableRead2> istSites;
|
|
|
|
*pHomeDirectory = NULL;
|
|
|
|
m_queryCell[1].eOperator = eST_OP_EQUAL;
|
|
m_queryCell[1].iCell = iSITES_SiteID;
|
|
m_queryCell[1].dbType = DBTYPE_I4;
|
|
m_queryCell[1].cbSize = sizeof siteID;
|
|
m_queryCell[1].pData = &siteID;
|
|
|
|
hr = GetTable(wszTABLE_SITES, &istSites);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
static ULONG siteColumns[] = {
|
|
iSITES_HomeDirectory,
|
|
iSITES_Bindings
|
|
};
|
|
static const size_t numSiteColumns = sizeof siteColumns / sizeof siteColumns[0];
|
|
|
|
void* siteValues[cSITES_NumberOfColumns];
|
|
hr = istSites->GetColumnValues(0,
|
|
numSiteColumns,
|
|
siteColumns,
|
|
NULL,
|
|
siteValues);
|
|
if (FAILED(hr))
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Error reading Sites row.\n"));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Expand any environment variables in the root directory.
|
|
WCHAR rootDirectory[MAX_PATH+1];
|
|
size_t rootDirLen = ExpandEnvironmentStrings((WCHAR*)siteValues[iSITES_HomeDirectory],
|
|
rootDirectory,
|
|
MAX_PATH);
|
|
if (rootDirLen == 0)
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Error while expanding environment strings.\n"));
|
|
}
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
*pHomeDirectory = _wcsdup(rootDirectory);
|
|
|
|
return S_OK;
|
|
}
|