Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2857 lines
71 KiB

/*++
Copyright (c) 1992-1997 Microsoft Corporation
Module Name:
iis.c
Abstract:
Resource DLL for IIS. This DLL supports the following IIS services
WWW
FTP
Each instance of a resouce is an IIS instance ( a.k.a. virtual server )
Virtual root may have dependencies on IP Addresses, Physical Disks, or UNC names.
Known Limitations
Author:
Pete Benoit (v-pbenoi) 12-SEP-1996
Revision History:
Rich Demar (rdemar) 5-18-1999
--*/
#define INITGUID
#include "iisutil.h"
#include <clusapi.h>
#include <resapi.h>
//#include "resmonp.h"
//#include "clusres.h"
#include <pudebug.h>
DECLARE_DEBUG_BUFFER;
//
// Names used to start service
//
LPCWSTR ActualServiceName[] = {
L"W3SVC", // WWW
L"MSFTPSVC", // FTP
L"SMTPSVC", // SMTP
L"NNTPSVC" // NNTP
};
#define PARAM_NAME__SERVICENAME L"ServiceName"
#define PARAM_NAME__INSTANCEID L"InstanceId"
#define MAX_SCMFAILURE_RETRY 5
#define BACKOFF_MULTIPLIER 2
#define DELAY_BETWEEN_ISALIVE_CHECKS 5*1000
#define MAX_DIFFERENCE_BETWEEN_RESOURCE_CHECKS 5*60*1000 // five minutes in milliseconds
//
// IIS resource private read-write parameters.
//
RESUTIL_PROPERTY_ITEM
IISResourcePrivateProperties[] = {
{ PARAM_NAME__SERVICENAME, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(IIS_PARAMS,ServiceName) },
{ PARAM_NAME__INSTANCEID, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(IIS_PARAMS,InstanceId) },
{ 0 }
};
//
// Global data.
//
CRITICAL_SECTION IISTableLock;
LIST_ENTRY IISResourceTable;
BOOL g_fIISResourceTable_HadChanged = FALSE;
CLUS_WORKER g_cwAlivePollingThread;
DWORD g_dwTickOfLastResourceCheck = 0;
DWORD g_dwTlsCoInit = 0xffffffff;
#if defined(DBG_CANT_VERIFY)
BOOL g_fDbgCantVerify = FALSE;
#endif
LONG g_lOpenRefs = 0;
bool g_fWinsockInitialized = false;
PLOG_EVENT_ROUTINE g_IISLogEvent = NULL;
PSET_RESOURCE_STATUS_ROUTINE IISSetResourceStatus = NULL;
HANDLE g_hEventLog = NULL;
extern CLRES_FUNCTION_TABLE IISFunctionTable;
//
// Forward routines
//
PWSTR
IISGetParameter(
IN HKEY ClusterKey,
IN LPCWSTR ValueName
);
BOOL
WINAPI
IISIsAlive(
IN RESID Resource
);
DWORD
IISReadParameters(
IN OUT LPIIS_RESOURCE ResourceEntry
);
DWORD
IISBuildInternalParameters(
IN OUT IIS_PARAMS* ResourceEntry
);
VOID
IISInitializeParams(
IN OUT IIS_PARAMS* Params
);
VOID
IISFreeInternalParameters(
IN IIS_PARAMS* Params
);
LPIIS_RESOURCE
GetValidResource(
IN RESID Resource,
IN LPWSTR RoutineName
);
DWORD
IISSetPrivateResProperties(
IN OUT LPIIS_RESOURCE ResourceEntry,
IN PVOID InBuffer,
IN DWORD InBufferSize
);
DWORD
IISGetPrivateResProperties(
IN OUT LPIIS_RESOURCE ResourceEntry,
OUT PVOID OutBuffer,
IN DWORD OutBufferSize,
OUT LPDWORD BytesReturned
);
DWORD
IISValidatePrivateResProperties(
IN OUT LPIIS_RESOURCE ResourceEntry,
IN PVOID InBuffer,
IN DWORD InBufferSize,
OUT PIIS_PARAMS Params
);
void
IISReplicateProperties(
IN IIS_PARAMS* lpNewParams,
IN IIS_PARAMS* lpOldParams
);
void
IISSetRemoteNodeProperties(
IN LPWSTR wcsNodeName,
IN IIS_PARAMS* lpNewParams,
IN IIS_PARAMS* lpOldParams
);
DWORD
WINAPI
IISAlivePollingThread(
IN PCLUS_WORKER pWorker,
IN LPVOID lpVoid );
//
// Function definitions
//
BOOLEAN
IISInit(
VOID
)
{
WSADATA wsaData;
INT serr;
INIT_DEBUG;
INITIALIZE_CRITICAL_SECTION(&IISTableLock);
InitializeListHead(&IISResourceTable);
g_dwTlsCoInit = TlsAlloc();
SetCoInit( FALSE );
g_hEventLog = RegisterEventSource( NULL, L"CLUSIIS4" );
//
// Initialize winsock support
//
serr = WSAStartup( MAKEWORD( 2, 0), &wsaData);
if( serr == 0 )
{
g_fWinsockInitialized = true;
}
else
{
TR( (DEBUG_BUFFER,"[TcpSockConnectToLocalHost] WSAStartup failed with %08x\n",serr) );
}
return TRUE;
}
VOID
IISCleanup()
{
DeleteCriticalSection(&IISTableLock);
TlsFree( g_dwTlsCoInit );
if ( g_hEventLog != NULL )
{
DeregisterEventSource( g_hEventLog );
}
if (g_fWinsockInitialized)
{
WSACleanup();
}
TERMINATE_DEBUG;
}
extern "C" BOOL WINAPI
DllMain(
IN HINSTANCE DllHandle,
IN DWORD Reason,
IN LPVOID Reserved
)
{
switch( Reason )
{
case DLL_PROCESS_ATTACH:
if ( !IISInit() )
{
return(FALSE);
}
break;
case DLL_PROCESS_DETACH:
IISCleanup();
break;
case DLL_THREAD_ATTACH:
SetCoInit( FALSE );
break;
default:
break;
}
return(TRUE);
} // IISShareDllEntryPoint
BOOLEAN
StartIIS(
LPIIS_RESOURCE ResourceEntry
)
/*++
Routine Description:
Start the IIS service
Arguments:
ResourceEntry - resource entry structure, includes name of service
Return Value:
TRUE - Success
FALSE - Failure
--*/
{
SC_HANDLE scManagerHandle;
SC_HANDLE serviceHandle;
DWORD errorCode;
DWORD iPoll;
SERVICE_STATUS ss;
BOOL fSt = FALSE;
INT iMaxWaitTime = 1000; // One second
//
// (# 261897) IIS cluster resources are failing to come online because "OpenSCManager" is failing with error "1723"
//
srand( (unsigned int)GetCurrentThreadId() );
for(int iScmRetry=0; iScmRetry < MAX_SCMFAILURE_RETRY; iScmRetry++)
{
//
// Open the service control manager
//
scManagerHandle = OpenSCManager( NULL, // local machine
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS ); // all access
if ( scManagerHandle == NULL )
{
if( iScmRetry == (MAX_SCMFAILURE_RETRY-1) )
{
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Unable to open Service Control Manager '%1!ws!'. Error: %2!u!. Reached maximum retries (%3!u!)\n",
ResourceEntry->ResourceName,
GetLastError(),
iScmRetry );
return FALSE;
}
else
{
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Unable to open Service Control Manager '%1!ws!'. Error: %2!u!. Retry attempt (%3!u!)\n",
ResourceEntry->ResourceName,
GetLastError(),
iScmRetry );
}
Sleep( (DWORD)(rand() % iMaxWaitTime) );
iMaxWaitTime *= BACKOFF_MULTIPLIER;
}
else
{
break;
}
}
//
// Open the service
//
serviceHandle = OpenService( scManagerHandle,
ResourceEntry->Params.ServiceName, // Service Name
SERVICE_ALL_ACCESS );
TR( (DEBUG_BUFFER,"[StartIIS] starting %S\n", ResourceEntry->Params.ServiceName) );
if ( serviceHandle == NULL )
{
CloseServiceHandle( scManagerHandle );
return FALSE;
}
//
// Make sure the service is running
//
if ( !StartService( serviceHandle,
0,
NULL) )
{
errorCode = GetLastError();
if ( errorCode == ERROR_SERVICE_ALREADY_RUNNING )
{
TR( (DEBUG_BUFFER,"[StartIIS] allready running\n") );
fSt = TRUE;
}
}
else
{
for ( iPoll = 0 ; iPoll < SERVICE_START_MAX_POLL ; ++iPoll )
{
if ( !QueryServiceStatus( serviceHandle, &ss ) )
{
break;
}
if ( ss.dwCurrentState == SERVICE_RUNNING )
{
fSt = TRUE;
break;
}
//
// Give the IIS Server a second to start up
//
Sleep( SERVICE_START_POLL_DELAY );
}
}
//
// Close open handles
//
CloseServiceHandle( serviceHandle );
CloseServiceHandle( scManagerHandle);
return (BOOLEAN)fSt;
} // StartIIS
DWORD
WINAPI
Startup(
IN LPCWSTR ResourceType,
IN DWORD MinVersionSupported,
IN DWORD MaxVersionSupported,
IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus,
IN PLOG_EVENT_ROUTINE LogEvent,
OUT PCLRES_FUNCTION_TABLE *FunctionTable
)
/*++
Routine Description:
Startup a particular resource type. This means verifying the version
requested, and returning the function table for this resource type.
Arguments:
ResourceType - Supplies the type of resource.
MinVersionSupported - The minimum version number supported by the cluster
service on this system.
MaxVersionSupported - The maximum version number supported by the cluster
service on this system.
FunctionTable - Returns the Function Table for this resource type.
Return Value:
ERROR_SUCCESS if successful.
A Win32 error code on failure.
--*/
{
DWORD serviceType = MAX_SERVICE;
DWORD i;
HRESULT hRes;
#if 1
hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if ( FAILED(hRes) )
{
TR( (DEBUG_BUFFER,"[Startup] fail CoInitialize %08x\n",hRes) );
}
#endif
//
// Search for a valid service name supported by this DLL
//
for ( i = 0; i < MAX_RESOURCE_TYPE; i++ )
{
if ( lstrcmpiW( ResourceType, RESOURCE_TYPE[i] ) == 0 )
break;
}
if ( MAX_RESOURCE_TYPE == i )
{
TR( (DEBUG_BUFFER,"[Startup] bad resource type\n") );
return ERROR_UNKNOWN_REVISION;
}
g_IISLogEvent = LogEvent;
IISSetResourceStatus = SetResourceStatus;
if ( (MinVersionSupported <= CLRES_VERSION_V1_00) &&
(MaxVersionSupported >= CLRES_VERSION_V1_00) )
{
TR( (DEBUG_BUFFER,"[Startup] OK, leave\n") );
*FunctionTable = &IISFunctionTable;
return ERROR_SUCCESS;
}
TR( (DEBUG_BUFFER,"[Startup] revision mismatch\n") );
return ERROR_REVISION_MISMATCH;
} // Startup
RESID
WINAPI
IISOpen(
IN LPCWSTR ResourceName,
IN HKEY ResourceKey,
IN RESOURCE_HANDLE ResourceHandle
)
/*++
Routine Description:
Open routine for IIS resource.
Arguments:
ResourceName - supplies the resource name
ResourceKey - Supplies handle to resource's cluster registry key.
ResourceHandle - the resource handle to be supplied with SetResourceStatus
is called.
Return Value:
RESID of created resource
Zero on failure
--*/
{
DWORD status;
DWORD readStatus;
LPIIS_RESOURCE ResourceEntry;
DWORD count;
DWORD Index;
DWORD serviceType = MAX_SERVICE;
LPCWSTR ResourceType;
HKEY hKey;
TR( (DEBUG_BUFFER,"[IISOpen] Enter\n") );
EnterCriticalSection(&IISTableLock);
//
// Check if IIS is installed
//
if ( ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services\\IISADMIN",
0,
KEY_READ,
&hKey))
{
LeaveCriticalSection(&IISTableLock);
return (RESID)0;
}
else
{
RegCloseKey(hKey);
}
if ( g_lOpenRefs == 0 )
{
if ( !CMetaData::Init() )
{
LeaveCriticalSection(&IISTableLock);
return (RESID)0;
}
}
InterlockedIncrement( &g_lOpenRefs );
LeaveCriticalSection(&IISTableLock);
ResourceEntry = (LPIIS_RESOURCE)LocalAlloc( LMEM_FIXED, sizeof(IIS_RESOURCE) );
if ( ResourceEntry == NULL )
{
(g_IISLogEvent)(
ResourceHandle,
LOG_ERROR,
L"Unable to allocate IIS resource structure.\n");
TR( (DEBUG_BUFFER,"[IISOpen] can't alloc ResourceEntry\n") );
InterlockedDecrement( &g_lOpenRefs );
return (RESID)0;
}
ZeroMemory( ResourceEntry, sizeof(IIS_RESOURCE) );
ResourceEntry->Signature = IIS_RESOURCE_SIGNATURE;
//
// Set the resource handle for logging and init the virtual root entry
//
ResourceEntry->ResourceHandle = ResourceHandle;
//
// Read the Name of the resource, since the GUID is passed in.
//
ResourceEntry->ResourceName = IISGetParameter( ResourceKey, L"Name" );
if ( ResourceEntry->ResourceName == NULL )
{
(g_IISLogEvent)(
ResourceHandle,
LOG_ERROR,
L"Unable to read resource name.\n" );
status = ERROR_RESOURCE_NOT_FOUND;
FreeIISResource(ResourceEntry);
LocalFree( ResourceEntry );
TR( (DEBUG_BUFFER,"[IISOpen] Can't get name\n") );
InterlockedDecrement( &g_lOpenRefs );
return (RESID)0;
}
//
// Open the Parameters key for this resource.
//
status = ClusterRegOpenKey( ResourceKey,
L"Parameters",
KEY_READ,
&ResourceEntry->ParametersKey );
if ( status != ERROR_SUCCESS )
{
(g_IISLogEvent)(
ResourceHandle,
LOG_ERROR,
L"Unable to open parameters key for resource. Error: %1!u!.\n",
status );
FreeIISResource( ResourceEntry );
LocalFree( ResourceEntry );
TR( (DEBUG_BUFFER,"[IISOpen] Can't open parameters\n") );
InterlockedDecrement( &g_lOpenRefs );
return (RESID)0;
}
ResourceEntry->State = ClusterResourceOffline;
//
// If instance parameters exist, check instance stopped & marked cluster enabled
//
LPWSTR pwszServiceName = IISGetParameter( ResourceEntry->ParametersKey,
PARAM_NAME__SERVICENAME );
LPWSTR pwszInstanceId = IISGetParameter( ResourceEntry->ParametersKey,
PARAM_NAME__INSTANCEID );
if ( pwszServiceName && pwszInstanceId )
{
InstanceEnableCluster( pwszServiceName, pwszInstanceId );
}
if ( pwszServiceName )
{
LocalFree(pwszServiceName);
}
if ( pwszInstanceId )
{
LocalFree(pwszInstanceId);
}
//
// Initialize the metadata path for this instance
//
ResourceEntry->Params.MDPath = NULL;
//
// Set the resource's initial state to alive
//
ResourceEntry->bAlive = TRUE ;
//
// If this is first element being added to the list than start the polling thread
//
if ( IsListEmpty (&IISResourceTable) )
{
//
// Initialize the timestamp used in the isalive/looksalive to determine if the polling thread is still running
//
InterlockedExchange( (LPLONG) &g_dwTickOfLastResourceCheck, GetTickCount() );
TR( (DEBUG_BUFFER,"[IISOpen] initialized g_dwTickOfLastResourceCheck (%d)\n", g_dwTickOfLastResourceCheck) );
if( ERROR_SUCCESS != (status = ClusWorkerCreate( &g_cwAlivePollingThread,
(PWORKER_START_ROUTINE)IISAlivePollingThread,
&IISResourceTable)) )
{
TR( (DEBUG_BUFFER,"[IISOpen] Error creating IISAlivePollingThread %08x\n",status) );
return (RESID)0;
}
TR( (DEBUG_BUFFER,"[IISOpen] created IISAlivePollingThread\n") );
}
//
// Add to resource list
//
EnterCriticalSection(&IISTableLock);
InsertHeadList( &IISResourceTable, &ResourceEntry->ListEntry );
g_fIISResourceTable_HadChanged = TRUE;
LeaveCriticalSection(&IISTableLock);
(g_IISLogEvent)(
ResourceHandle,
LOG_INFORMATION,
L"Open request succeeded with id = %1!u!.\n",
ResourceEntry );
TR( (DEBUG_BUFFER,"[IISOpen] token=%x Leave\n", ResourceEntry) );
return (RESID)ResourceEntry;
} // IISOpen
DWORD
WINAPI
IISOnlineThread(
IN PCLUS_WORKER pWorker,
IN LPIIS_RESOURCE ResourceEntry
)
/*++
Routine Description:
Brings a share resource online.
Arguments:
ResourceEntry - A pointer to a IIS_RESOURCE block for this resource
Returns:
ERROR_SUCCESS if successful.
Win32 error code on failure.
--*/
{
DWORD status;
DWORD retry;
RESOURCE_STATUS resourceStatus;
TR( (DEBUG_BUFFER,"[IISOnlineThread] Enter\n") );
ResUtilInitializeResourceStatus( &resourceStatus );
resourceStatus.ResourceState = ClusterResourceOnlinePending;
resourceStatus.WaitHint = 0;
resourceStatus.CheckPoint = 1;
ResourceEntry->State = ClusterResourceOnlinePending;
InterlockedExchange( (LPLONG) &ResourceEntry->bAlive, TRUE );
if (IISReadParameters(ResourceEntry) != ERROR_SUCCESS)
{
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"ERROR [OnLineThread] Could not read resource parameters\n");
status = ERROR_RESOURCE_NOT_FOUND;
TR( (DEBUG_BUFFER,"[IISOnlineThread] can't read parameters\n") );
goto SendStatus;
}
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"INFO [OnLineThread] Service = %1!ws! InstanceId = %2!ws!\n",
ResourceEntry->Params.ServiceName,
ResourceEntry->Params.InstanceId );
//
// get the server bindings information, if this fails should be able to pick the information up in the polling thread
//
if ( ERROR_SUCCESS != (status = GetServerBindings( ResourceEntry->Params.MDPath, ResourceEntry->Params.ServiceType, &ResourceEntry->saServer, &ResourceEntry->dwPort )) )
{
TR( (DEBUG_BUFFER,"[IISOnLineThread] failed to get server bindings for %S (%d)\n", ResourceEntry->Params.MDPath, status) );
}
//
// Try to Online the resources
//
//
if ( !StartIIS( ResourceEntry ) )
{
status = ERROR_SERVICE_REQUEST_TIMEOUT;
resourceStatus.ResourceState = ClusterResourceFailed;
ResourceEntry->State = ClusterResourceFailed;
TR( (DEBUG_BUFFER,"[IISOnlineThread] can't start IIS\n") );
}
else
{
status = SetInstanceState( pWorker,
ResourceEntry,
&resourceStatus,
ClusterResourceOnline,
L"online",
MD_SERVER_COMMAND_START,
MD_SERVER_STATE_STARTED );
}
SendStatus:
//
// Set the state of the resource
//
(IISSetResourceStatus)( ResourceEntry->ResourceHandle,
&resourceStatus );
TR( (DEBUG_BUFFER,"[IISOnlineThread] status = %d, Leave\n",status) );
return status;
} // IISOnlineThread
DWORD
WINAPI
IISOfflineThread(
IN PCLUS_WORKER pWorker,
IN LPIIS_RESOURCE ResourceEntry
)
/*++
Routine Description:
Brings a share resource offline.
Arguments:
ResourceEntry - A pointer to a IIS_RESOURCE block for this resource
Returns:
ERROR_SUCCESS if successful.
Win32 error code on failure.
--*/
{
DWORD status;
DWORD retry;
RESOURCE_STATUS resourceStatus;
TR( (DEBUG_BUFFER,"[IISOfflineThread] Enter\n") );
#if defined(DBG_CANT_VERIFY)
if ( g_fDbgCantVerify )
{
TR( (DEBUG_BUFFER,"[IISOfflineThread] skip stop after failure to verify service\n") );
return ERROR_INVALID_PARAMETER;
}
#endif
ResUtilInitializeResourceStatus( &resourceStatus );
resourceStatus.ResourceState = ClusterResourceOfflinePending;
resourceStatus.WaitHint = 0;
resourceStatus.CheckPoint = 1;
ResourceEntry->State = ClusterResourceOfflinePending;
if (IISReadParameters(ResourceEntry) != ERROR_SUCCESS)
{
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"ERROR [OffLineThread] Could not read resource parameters\n");
status = ERROR_RESOURCE_NOT_FOUND;
goto SendStatus;
}
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"INFO [OffLineThread] Service = %1!ws! InstanceId = %2!ws!\n",
ResourceEntry->Params.ServiceName,
ResourceEntry->Params.InstanceId );
//
// Try to Offline the resources
//
//
status = SetInstanceState( pWorker, ResourceEntry, &resourceStatus, ClusterResourceOffline, L"offline", MD_SERVER_COMMAND_STOP, MD_SERVER_STATE_STOPPED );
SendStatus:
//
// Set the state of the resource
//
(IISSetResourceStatus)( ResourceEntry->ResourceHandle,
&resourceStatus );
TR( (DEBUG_BUFFER,"[IISOfflineThread] status = %d, Leave\n",status) );
return(status);
} // IISOfflineThread
DWORD
WINAPI
IISOnline(
IN RESID Resource,
IN OUT PHANDLE EventHandle
)
/*++
Routine Description:
Online routine for IIS resource.
Arguments:
Resource - supplies resource id to be brought online
EventHandle - supplies a pointer to a handle to signal on error.
Return Value:
ERROR_SUCCESS if successful.
ERROR_RESOURCE_NOT_FOUND if RESID is not valid.
ERROR_RESOURCE_NOT_AVAILABLE if resource was arbitrated but failed to
acquire 'ownership'.
Win32 error code if other failure.
--*/
{
LPIIS_RESOURCE ResourceEntry = NULL;
DWORD status;
TR( (DEBUG_BUFFER,"[IISOnline] Enter\n") );
//
// Get a valid resource
//
ResourceEntry = GetValidResource(Resource,L"OnLine");
if ( ResourceEntry == NULL )
{
return ERROR_RESOURCE_NOT_FOUND;
}
//
// Log the online request
//
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Online request for IIS Resource %1!u! (%2!ws!).\n",
Resource,
ResourceEntry->ResourceName );
// Terminate (or wait) for workers
ClusWorkerTerminate( &ResourceEntry->OnlineThread );
ClusWorkerTerminate( &ResourceEntry->OfflineThread );
status = ClusWorkerCreate( &ResourceEntry->OnlineThread,
(PWORKER_START_ROUTINE)IISOnlineThread,
ResourceEntry );
if ( status == ERROR_SUCCESS )
{
return ERROR_IO_PENDING;
}
//
// Failure
//
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Online request failed, error %1!u!.\n",
GetLastError() );
TR( (DEBUG_BUFFER,"[IISOnline] failed %d, Leave\n",GetLastError()) );
return status;
} // IISOnline
VOID
WINAPI
IISTerminate(
IN RESID Resource
)
/*++
Routine Description:
Terminate routine for IIS resource.
Arguments:
Resource - supplies resource id to be terminated
Return Value:
None.
--*/
{
DWORD status;
LPIIS_RESOURCE ResourceEntry;
DWORD dwS;
int retry;
TR( (DEBUG_BUFFER,"[IISTerminate] Enter\n") );
#if defined(DBG_CANT_VERIFY)
if ( g_fDbgCantVerify )
{
TR( (DEBUG_BUFFER,"[IISTerminate] skip stop after failure to verify service\n") );
return;
}
#endif
//
// Get a valid resource entry, return on error
//
ResourceEntry = GetValidResource(Resource,L"Terminate");
if (ResourceEntry == NULL)
{
return;
}
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Terminate or offline request for Resource%1!u! (%2!ws!).\n",
Resource,
ResourceEntry->ResourceName );
//
// Try to take the resources offline, dont return if an error since
// the resources may be offline when terminate called
//
//
// Terminate the Online & Offline Threads
//
ClusWorkerTerminate( &ResourceEntry->OnlineThread);
ClusWorkerTerminate( &ResourceEntry->OfflineThread);
status = ERROR_SERVICE_NOT_ACTIVE;
CMetaData MD;
if ( MD.Open( ResourceEntry->Params.MDPath,
FALSE,
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) )
{
if ( MD.GetDword( L"", MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwS, 0 ) )
{
TR( (DEBUG_BUFFER,"[IISTerminate] state prob is %d\n",dwS) );
}
else
{
TR( (DEBUG_BUFFER,"[IISTerminate] failed to probe server state\n") );
dwS = 0xffffffff;
}
if ( dwS != MD_SERVER_STATE_STOPPED )
{
if ( MD.SetDword( L"", MD_CLUSTER_SERVER_COMMAND, IIS_MD_UT_SERVER, MD_SERVER_COMMAND_STOP, 0 ) )
{
MD.Close();
TR( (DEBUG_BUFFER,"[IISTerminate] command set to %d\n",MD_SERVER_COMMAND_STOP) );
status = ERROR_SUCCESS;
for ( retry = 3 ; retry-- ; )
{
if ( MD.GetDword( ResourceEntry->Params.MDPath, MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwS, 0 ) )
{
if ( dwS == MD_SERVER_STATE_STOPPED )
{
break;
}
}
else
{
TR( (DEBUG_BUFFER,"[IISTerminate] failed to get server state\n") );
break;
}
Sleep(SERVER_START_DELAY);
}
}
else
{
MD.Close();
TR( (DEBUG_BUFFER,"[IISTerminate] failed to set command to %d\n",MD_SERVER_COMMAND_STOP) );
}
}
else
{
status = ERROR_SUCCESS;
MD.Close();
}
}
if ( status != ERROR_SUCCESS )
{
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Error removing Resource. Error %1!u!. Resource. %2!ws! Service. %3!ws!\n",
status,
ResourceEntry->ResourceName,
ResourceEntry->Params.ServiceName);
}
//
// Set status to offline
//
ResourceEntry->State = ClusterResourceOffline;
TR( (DEBUG_BUFFER,"[IISTerminate] Leave\n") );
} // IISTerminate
DWORD
WINAPI
IISOffline(
IN RESID Resource
)
/*++
Routine Description:
Offline routine for IIS resource.
Arguments:
Resource - supplies the resource it to be taken offline
Return Value:
ERROR_SUCCESS - always successful.
--*/
{
LPIIS_RESOURCE ResourceEntry = NULL;
DWORD status;
//
// Get a valid resource
//
ResourceEntry = GetValidResource(Resource,L"OffLine");
if ( ResourceEntry == NULL )
{
return ERROR_RESOURCE_NOT_FOUND;
}
//
// Log the online request
//
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Offline request for IIS Resource %1!u! (%2!ws!).\n",
Resource,
ResourceEntry->ResourceName );
// Terminate (or wait) for workers
ClusWorkerTerminate( &ResourceEntry->OnlineThread );
ClusWorkerTerminate( &ResourceEntry->OfflineThread );
status = ClusWorkerCreate( &ResourceEntry->OfflineThread,
(PWORKER_START_ROUTINE)IISOfflineThread,
ResourceEntry );
if ( status == ERROR_SUCCESS )
{
return ERROR_IO_PENDING;
}
//
// Failure
//
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"Offline request failed, error %1!u!.\n",
GetLastError() );
return status;
} // IISOffline
BOOL
WINAPI
IISIsAlive(
IN RESID Resource
)
/*++
Routine Description:
IsAlive routine for IIS service resource.
Arguments:
Resource - supplies the resource id to be polled.
Return Value:
TRUE - if service is running
FALSE - if service is in any other state
--*/
{
LPIIS_RESOURCE ResourceEntry;
DWORD dwTickDifference = 0;
DWORD dwCurrent;
DWORD dwTickOfLastResourceCheck = 0;
//
// Get a valid resource
//
ResourceEntry = GetValidResource(Resource,L"IsAlive");
if (ResourceEntry == NULL)
{
return(FALSE);
}
//
// Save the current global tick count so it can't change during the calculation
//
dwTickOfLastResourceCheck = g_dwTickOfLastResourceCheck;
//
// Save the current tick count
//
dwCurrent = GetTickCount();
if (dwCurrent >= g_dwTickOfLastResourceCheck)
{
dwTickDifference = dwCurrent - g_dwTickOfLastResourceCheck ;
}
else
{
dwTickDifference = (0xFFFFFFFF - g_dwTickOfLastResourceCheck) + dwCurrent ;
}
//
// if the polling thread is taking too long there must be a problem
//
if ( dwTickDifference > MAX_DIFFERENCE_BETWEEN_RESOURCE_CHECKS)
{
TR( (DEBUG_BUFFER,"[IsAlive] (dwTickDifference(%d) > MAX_DIFFERENCE_BETWEEN_RESOURCE_CHECKS) returning FALSE dwCurrent(%d) dwTickOfLastResourceCheck(%d)\n", dwTickDifference, dwCurrent, dwTickOfLastResourceCheck) );
return FALSE;
}
return ResourceEntry->bAlive ;
} // IISIsAlive
BOOL
WINAPI
IISLooksAlive(
IN RESID Resource
)
/*++
Routine Description:
LooksAlive routine for IIS resource.
Arguments:
Resource - supplies the resource id to be polled.
Return Value:
TRUE - Resource looks like it is alive and well
FALSE - Resource looks like it is toast.
--*/
{
LPIIS_RESOURCE ResourceEntry;
DWORD dwTickDifference = 0;
DWORD dwCurrent;
DWORD dwTickOfLastResourceCheck = 0;
//
// Get a valid resource
//
ResourceEntry = GetValidResource(Resource,L"LooksAlive");
if (ResourceEntry == NULL)
{
return(FALSE);
}
//
// Save the current global tick count so it can't change during the calculation
//
dwTickOfLastResourceCheck = g_dwTickOfLastResourceCheck;
//
// Save the current tick count
//
dwCurrent = GetTickCount();
if (dwCurrent >= g_dwTickOfLastResourceCheck)
{
dwTickDifference = dwCurrent - g_dwTickOfLastResourceCheck ;
}
else
{
dwTickDifference = (0xFFFFFFFF - g_dwTickOfLastResourceCheck) + dwCurrent ;
}
//
// if the polling thread is taking too long there must be a problem
//
if ( dwTickDifference > MAX_DIFFERENCE_BETWEEN_RESOURCE_CHECKS )
{
TR( (DEBUG_BUFFER,"[LooksAlive] (dwTickDifference(%d) > MAX_DIFFERENCE_BETWEEN_RESOURCE_CHECKS) returning FALSE dwCurrent(%d) dwTickOfLastResourceCheck(%d)\n", dwTickDifference, dwCurrent, dwTickOfLastResourceCheck) );
return FALSE;
}
return ResourceEntry->bAlive ;
} // IISLooksAlive
VOID
WINAPI
IISClose(
IN RESID Resource
)
/*++
Routine Description:
Close routine for IIS resource.
Arguments:
Resource - supplies resource id to be closed
Return Value:
None.
--*/
{
LPIIS_RESOURCE ResourceEntry = NULL;
//
// Get a valid resource
//
ResourceEntry = GetValidResource( Resource, L"Close");
if (ResourceEntry == NULL)
{
return; // this should not happen
}
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_INFORMATION,
L"Close request for resource '%1!ws!' Service '%2!ws!' \n",
ResourceEntry->ResourceName,
ResourceEntry->Params.ServiceName);
//
// Remove from list
//
EnterCriticalSection(&IISTableLock);
RemoveEntryList( &ResourceEntry->ListEntry );
g_fIISResourceTable_HadChanged = TRUE;
DestructIISResource(ResourceEntry);
if ( g_lOpenRefs > 0 )
{
if ( !InterlockedDecrement( &g_lOpenRefs ) )
{
CMetaData::Terminate();
}
}
LeaveCriticalSection(&IISTableLock);
//
// If this was the last resource in the list than stop the polling thread
//
if ( IsListEmpty (&IISResourceTable) )
{
ClusWorkerTerminate( &g_cwAlivePollingThread );
TR( (DEBUG_BUFFER,"[IISClose] Terminated IISAlivePollingThread\n") );
}
} // IISClose
LPIIS_RESOURCE
GetValidResource(
IN RESID Resource,
IN LPWSTR RoutineName
)
/*++
Routine Description:
Validate the resource ID, log any error, return valid resource
Arguments:
Resource - the resource to validate
RoutineName - the routine that is requesting the validation
Return Value:
Success - ResourceEntry
NULL - Error
--*/
{
DWORD Index;
LPIIS_RESOURCE ResourceEntry;
ResourceEntry = (LPIIS_RESOURCE)Resource;
//
// Check for a valid
//
if ( ResourceEntry == NULL )
{
(g_IISLogEvent)(
NULL,
LOG_ERROR,
L"[%1!ws!] Resource Entry is NULL for Resource Id = %2!u!\n",
RoutineName,
Resource);
return NULL;
}
//
// Sanity check the resource struct
//
if ( ResourceEntry->Signature != IIS_RESOURCE_SIGNATURE )
{
(g_IISLogEvent)(
ResourceEntry->ResourceHandle,
LOG_ERROR,
L"[%1!ws!] IIS Resource index sanity checked failed! Index = %2!u!.\n",
RoutineName,
Resource );
return NULL;
}
return ResourceEntry;
} // END GetValidResource
PWSTR
IISGetParameter(
IN HKEY ClusterKey,
IN LPCWSTR ValueName
)
/*++
Routine Description:
Queries a REG_SZ parameter out of the registry and allocates the
necessary storage for it.
Arguments:
ClusterKey - Supplies the cluster key where the parameter is stored
ValueName - Supplies the name of the value.
Return Value:
A pointer to a buffer containing the parameter if successful.
NULL if unsuccessful.
--*/
{
PWSTR Value;
PWSTR Value2;
DWORD ValueLength;
DWORD ValueType;
DWORD Status;
ValueLength = 0;
Status = ClusterRegQueryValue(ClusterKey,
ValueName,
&ValueType,
NULL,
&ValueLength);
if ( (Status != ERROR_SUCCESS) &&
(Status != ERROR_MORE_DATA) )
{
TR( (DEBUG_BUFFER,"[IISGetParameter] Failed to open %S, error %u\n",ValueName,Status) );
return(NULL);
}
//
// Add on the size of the null terminator.
//
ValueLength += sizeof(UNICODE_NULL);
Value = (WCHAR*)LocalAlloc(LMEM_FIXED, ValueLength);
if (Value == NULL)
{
return(NULL);
}
Status = ClusterRegQueryValue(ClusterKey,
ValueName,
&ValueType,
(LPBYTE)Value,
&ValueLength);
if (Status != ERROR_SUCCESS)
{
LocalFree(Value);
Value = NULL;
}
else
{
//
// Numeric value are prefixed with '!' to force them to be stored as string,
// so remove leading '!' if present
//
if ( Value[0] == L'!' )
{
Value2 = (WCHAR*)LocalAlloc(LMEM_FIXED, ValueLength);
if (Value2 == NULL)
{
LocalFree( Value );
return(NULL);
}
wcscpy( Value2, Value + 1 );
LocalFree( Value );
Value = Value2;
}
}
TR( (DEBUG_BUFFER,"[IISGetParameter] Read %S, length %d, value %S\n",ValueName,ValueLength,Value?Value:L"ERROR") );
return(Value);
} // IISGetParameter
LPWSTR
GetResourceParameter(
IN HRESOURCE hResource,
IN LPCWSTR ValueName
)
/*++
Routine Description:
Opens the parameter key for the resource. Then Queries a REG_SZ parameter
out of the registry and allocates the necessary storage for it.
Arguments:
hResource - the resource to query
ValueName - Supplies the name of the value.
Return Value:
A pointer to a buffer containing the parameter if successful.
NULL if unsuccessful.
--*/
{
HKEY hKey = NULL;
HKEY hParametersKey = NULL;
DWORD status;
LPWSTR paramValue = NULL;
//
// Get Resource key
//
hKey = GetClusterResourceKey(hResource,KEY_READ);
if (hKey == NULL)
{
return(NULL);
}
//
// Get parameters key
//
status = ClusterRegOpenKey(hKey,
L"Parameters",
KEY_READ,
&hParametersKey );
if (status != ERROR_SUCCESS)
{
goto error_exit;
}
paramValue = IISGetParameter(hParametersKey,ValueName);
if (paramValue == NULL)
{
goto error_exit;
}
error_exit:
if (hParametersKey != NULL)
{
ClusterRegCloseKey(hParametersKey);
}
if (hKey != NULL)
{
ClusterRegCloseKey(hKey);
}
return(paramValue);
} // GetResourceParameter
DWORD
IISReadParameters(
IN OUT LPIIS_RESOURCE ResourceEntry
)
/*++
Routine Description:
Reads all the parameters for a resource.
Arguments:
ResourceEntry - Entry in the resource table.
Return Value:
ERROR_SUCCESS - Success
ERROR_RESOURCE_NOT_FOUND - failure
--*/
{
DWORD status;
//
// Read the parameters for the resource.
//
status = ResUtilReadProperties( ResourceEntry->ParametersKey,
IISResourcePrivateProperties,
(LPBYTE) &ResourceEntry->Params,
ResourceEntry->ResourceHandle,
g_IISLogEvent );
if ( status != ERROR_SUCCESS )
{
return status;
}
return IISBuildInternalParameters( &ResourceEntry->Params );
}
DWORD
IISBuildInternalParameters(
IN OUT IIS_PARAMS* Params
)
/*++
Routine Description:
Build all the parameters for a resource from wolfpack parameters
Arguments:
ResourceEntry - Entry in the resource table.
Return Value:
ERROR_SUCCESS - Success
ERROR_RESOURCE_NOT_FOUND - failure
--*/
{
DWORD status;
INT iServiceType;
DWORD length;
//
// Make sure we got passed a valid service name
//
for ( iServiceType = 0 ; iServiceType < MAX_SERVICE ; iServiceType++ )
{
if ( lstrcmpiW( Params->ServiceName, ActualServiceName[iServiceType] ) == 0 )
{
break;
}
}
if ( iServiceType >= MAX_SERVICE )
{
TR( (DEBUG_BUFFER,"[IISBuildInternalParameters] Invalid service name %S", Params->ServiceName) );
return ERROR_RESOURCE_NOT_FOUND;
}
Params->ServiceType = iServiceType;
//
// Build MetaData path
//
TCHAR achMDPath[80];
DWORD dwL;
dwL = wsprintf( achMDPath, L"/LM/%s/%s", Params->ServiceName, Params->InstanceId );
Params->MDPath = (WCHAR*)LocalAlloc( LMEM_FIXED, (dwL+1)*sizeof(WCHAR) );
if ( Params->MDPath == NULL )
{
return ERROR_RESOURCE_NOT_FOUND;
}
memcpy( Params->MDPath, achMDPath,(dwL+1)*sizeof(TCHAR) );
TR( (DEBUG_BUFFER,"[IISBuildInternalParameters] Built path %S\n", Params->MDPath) );
return(ERROR_SUCCESS);
} // IISReadParameters
VOID
IISInitializeParams(
IN OUT IIS_PARAMS* Params
)
{
ZeroMemory( Params, sizeof(IIS_PARAMS) );
}
VOID
IISFreeInternalParameters(
IN IIS_PARAMS* Params
)
{
if ( Params->MDPath )
{
LocalFree( Params->MDPath );
Params->MDPath = NULL;
}
}
DWORD
IISGetRequiredDependencies(
OUT PVOID OutBuffer,
IN DWORD OutBufferSize,
OUT LPDWORD BytesReturned
)
/*++
Routine Description:
Processes the CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES control function
for resources of type IIS server instance.
Arguments:
OutBuffer - Supplies a pointer to the output buffer to be filled in.
OutBufferSize - Supplies the size, in bytes, of the available space
pointed to by OutBuffer.
BytesReturned - Returns the number of bytes of OutBuffer actually
filled in by the resource. If OutBuffer is too small, BytesReturned
contains the total number of bytes for the operation to succeed.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_MORE_DATA - The output buffer is too small to return the data.
BytesReturned contains the required size.
Win32 error code - The function failed.
--*/
{
typedef struct DEP_DATA
{
CLUSPROP_SZ_DECLARE( ipaddrEntry, sizeof(IP_ADDRESS_RESOURCE_NAME) / sizeof(WCHAR) );
CLUSPROP_SYNTAX endmark;
} DEP_DATA, *PDEP_DATA;
PDEP_DATA pdepdata = (PDEP_DATA)OutBuffer;
DWORD status;
*BytesReturned = sizeof(DEP_DATA);
if ( OutBufferSize < sizeof(DEP_DATA) )
{
if ( OutBuffer == NULL )
{
status = ERROR_SUCCESS;
}
else
{
TR( (DEBUG_BUFFER,"[IISGetRequiredDependencies] buffer too small: %d bytes\n",OutBufferSize) );
status = ERROR_MORE_DATA;
}
}
else
{
ZeroMemory( pdepdata, sizeof(DEP_DATA) );
pdepdata->ipaddrEntry.Syntax.dw = CLUSPROP_SYNTAX_NAME;
pdepdata->ipaddrEntry.cbLength = sizeof(IP_ADDRESS_RESOURCE_NAME);
lstrcpyW( pdepdata->ipaddrEntry.sz, IP_ADDRESS_RESOURCE_NAME );
pdepdata->endmark.dw = CLUSPROP_SYNTAX_ENDMARK;
status = ERROR_SUCCESS;
}
return status;
} // IISGetRequiredDependencies
DWORD
IISResourceControl(
IN RESID ResourceId,
IN DWORD ControlCode,
IN PVOID InBuffer,
IN DWORD InBufferSize,
OUT PVOID OutBuffer,
IN DWORD OutBufferSize,
OUT LPDWORD BytesReturned
)
/*++
Routine Description:
ResourceControl routine for IIS Virtual Root resources.
Perform the control request specified by ControlCode on the specified
resource.
Arguments:
ResourceId - Supplies the resource id for the specific resource.
ControlCode - Supplies the control code that defines the action
to be performed.
InBuffer - Supplies a pointer to a buffer containing input data.
InBufferSize - Supplies the size, in bytes, of the data pointed
to by InBuffer.
OutBuffer - Supplies a pointer to the output buffer to be filled in.
OutBufferSize - Supplies the size, in bytes, of the available space
pointed to by OutBuffer.
BytesReturned - Returns the number of bytes of OutBuffer actually
filled in by the resource. If OutBuffer is too small, BytesReturned
contains the total number of bytes for the operation to succeed.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_FUNCTION - The requested control code is not supported.
In some cases, this allows the cluster software to perform the work.
Win32 error code - The function failed.
--*/
{
DWORD status = ERROR_SUCCESS;
LPIIS_RESOURCE resourceEntry = NULL;
IIS_PARAMS params;
//
// Get a valid resource
//
resourceEntry = GetValidResource( ResourceId, L"ResourceControl");
if ( resourceEntry == NULL )
{
return ERROR_RESOURCE_NOT_FOUND; // this should not happen
}
switch ( ControlCode )
{
case CLUSCTL_RESOURCE_UNKNOWN:
*BytesReturned = 0;
status = ERROR_SUCCESS;
TR( (DEBUG_BUFFER,"[IISResourceControl] CLUSCTL_RESOURCE_UNKNOWN : status = %d\n",status) );
break;
case CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES:
status = IISGetRequiredDependencies(
OutBuffer,
OutBufferSize,
BytesReturned
);
TR( (DEBUG_BUFFER,"[IISResourceControl] CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES : status = %d\n",status) );
break;
case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES:
status = IISGetPrivateResProperties( resourceEntry,
OutBuffer,
OutBufferSize,
BytesReturned );
TR( (DEBUG_BUFFER,"[IISResourceControl] CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES : status = %d\n",status) );
break;
case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES:
status = IISValidatePrivateResProperties(
resourceEntry,
InBuffer,
InBufferSize,
&params );
TR( (DEBUG_BUFFER,"[IISResourceControl] CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES : status = %d\n",status) );
break;
case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
status = IISSetPrivateResProperties(
resourceEntry,
InBuffer,
InBufferSize );
TR( (DEBUG_BUFFER,"[IISResourceControl] CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES : status = %d\n",status) );
break;
case CLUSCTL_RESOURCE_DELETE:
InstanceDisableCluster( resourceEntry->Params.ServiceName, resourceEntry->Params.InstanceId);
IISReplicateProperties(NULL, &(resourceEntry->Params));
TR( (DEBUG_BUFFER,"[IISResourceControl] CLUSCTL_RESOURCE_DELETE : status = %d\n",status) );
break;
default:
status = ERROR_INVALID_FUNCTION;
TR( (DEBUG_BUFFER,"[IISResourceControl] default %d: status = %d\n",ControlCode,status) );
break;
}
return(status);
} // IISResourceControl
DWORD
IISResourceTypeControl(
IN LPCWSTR ResourceTypeName,
IN DWORD ControlCode,
IN PVOID InBuffer,
IN DWORD InBufferSize,
OUT PVOID OutBuffer,
IN DWORD OutBufferSize,
OUT LPDWORD BytesReturned
)
/*++
Routine Description:
ResourceTypeControl routine for IIS Virtual Root resources.
Perform the control request specified by ControlCode.
Arguments:
ResourceTypeName - Supplies the name of the resource type.
ControlCode - Supplies the control code that defines the action
to be performed.
InBuffer - Supplies a pointer to a buffer containing input data.
InBufferSize - Supplies the size, in bytes, of the data pointed
to by InBuffer.
OutBuffer - Supplies a pointer to the output buffer to be filled in.
OutBufferSize - Supplies the size, in bytes, of the available space
pointed to by OutBuffer.
BytesReturned - Returns the number of bytes of OutBuffer actually
filled in by the resource. If OutBuffer is too small, BytesReturned
contains the total number of bytes for the operation to succeed.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_FUNCTION - The requested control code is not supported.
In some cases, this allows the cluster software to perform the work.
Win32 error code - The function failed.
--*/
{
DWORD status;
switch ( ControlCode )
{
case CLUSCTL_RESOURCE_TYPE_UNKNOWN:
*BytesReturned = 0;
status = ERROR_SUCCESS;
break;
case CLUSCTL_RESOURCE_TYPE_GET_REQUIRED_DEPENDENCIES:
status = IISGetRequiredDependencies(
OutBuffer,
OutBufferSize,
BytesReturned
);
break;
default:
status = ERROR_INVALID_FUNCTION;
break;
}
return(status);
} // IISResourceTypeControl
DWORD
IISGetPrivateResProperties(
IN OUT LPIIS_RESOURCE ResourceEntry,
OUT PVOID OutBuffer,
IN DWORD OutBufferSize,
OUT LPDWORD BytesReturned
)
/*++
Routine Description:
Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function
for resources of type IIS.
Arguments:
ResourceEntry - Supplies the resource entry on which to operate.
OutBuffer - Returns the output data.
OutBufferSize - Supplies the size, in bytes, of the data pointed
to by OutBuffer.
BytesReturned - The number of bytes returned in OutBuffer.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
Win32 error code - The function failed.
--*/
{
DWORD status;
DWORD required;
status = ResUtilGetAllProperties( ResourceEntry->ParametersKey,
IISResourcePrivateProperties,
OutBuffer,
OutBufferSize,
BytesReturned,
&required );
if ( status == ERROR_MORE_DATA )
{
*BytesReturned = required;
}
return(status);
} // IISGetPrivateResProperties
DWORD
IISValidatePrivateResProperties(
IN OUT LPIIS_RESOURCE ResourceEntry,
IN PVOID InBuffer,
IN DWORD InBufferSize,
OUT PIIS_PARAMS Params
)
/*++
Routine Description:
Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control
function for resources of type IIS Virtual Root.
Arguments:
ResourceEntry - Supplies the resource entry on which to operate.
InBuffer - Supplies a pointer to a buffer containing input data.
InBufferSize - Supplies the size, in bytes, of the data pointed
to by InBuffer.
Params - Supplies the parameters structure to fill in. Pointers in this
structure will point to the data in InBuffer.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
Win32 error code - The function failed.
--*/
{
DWORD status;
IIS_PARAMS params;
PIIS_PARAMS pParams;
//
// Check if there is input data.
//
if ( (InBuffer == NULL) ||
(InBufferSize < sizeof(DWORD)) )
{
return(ERROR_INVALID_DATA);
}
//
// Duplicate the resource parameter block.
//
if ( Params == NULL )
{
pParams = &params;
}
else
{
pParams = Params;
}
ZeroMemory( pParams, sizeof(params) );
status = ResUtilDupParameterBlock( (LPBYTE) pParams,
(LPBYTE) &ResourceEntry->Params,
IISResourcePrivateProperties );
if ( status != ERROR_SUCCESS )
{
return(status);
}
//
// Parse and validate the properties.
//
status = ResUtilVerifyPropertyTable( IISResourcePrivateProperties,
NULL,
TRUE,
InBuffer,
InBufferSize,
(LPBYTE) pParams );
//
// Cleanup our parameter block.
//
if ( pParams == &params )
{
ResUtilFreeParameterBlock( (LPBYTE) &params,
(LPBYTE) &ResourceEntry->Params,
IISResourcePrivateProperties );
}
return(status);
} // IISValidatePrivateResProperties
DWORD
IISSetPrivateResProperties(
IN OUT LPIIS_RESOURCE ResourceEntry,
IN PVOID InBuffer,
IN DWORD InBufferSize
)
/*++
Routine Description:
Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function
for resources of type IIS Virtual Root.
Arguments:
ResourceEntry - Supplies the resource entry on which to operate.
InBuffer - Supplies a pointer to a buffer containing input data.
InBufferSize - Supplies the size, in bytes, of the data pointed
to by InBuffer.
Return Value:
ERROR_SUCCESS - The function completed successfully.
ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
Win32 error code - The function failed.
--*/
{
DWORD status = ERROR_SUCCESS;
DWORD dupstatus = ERROR_SUCCESS;
IIS_PARAMS params;
IISInitializeParams( &params );
//
// Parse the properties so they can be validated together.
// This routine does individual property validation.
//
status = IISValidatePrivateResProperties( ResourceEntry,
InBuffer,
InBufferSize,
&params );
if ( status != ERROR_SUCCESS )
{
ResUtilFreeParameterBlock( (LPBYTE) &params,
(LPBYTE) &ResourceEntry->Params,
IISResourcePrivateProperties );
return(status);
}
//
// Save the original paramters. Use them to unset properties.
//
IIS_PARAMS oldParams;
dupstatus = ResUtilDupParameterBlock( (LPBYTE) &oldParams,
(LPBYTE) &ResourceEntry->Params,
IISResourcePrivateProperties
);
//
// Save the parameter values.
//
status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey,
IISResourcePrivateProperties,
NULL,
(LPBYTE) &params,
InBuffer,
InBufferSize,
(LPBYTE) &ResourceEntry->Params );
//
// If the resource is online, return a non-success status.
//
if (status == ERROR_SUCCESS)
{
if ( params.ServiceName && params.InstanceId )
{
if ( IISBuildInternalParameters( &params ) == ERROR_SUCCESS )
{
//
// Reflect all properties we set to other nodes of the cluster.
//
if (ERROR_SUCCESS == dupstatus)
{
InstanceDisableCluster( oldParams.ServiceName, oldParams.InstanceId);
IISReplicateProperties(&params, &oldParams);
}
else
{
IISReplicateProperties(&params, NULL);
}
InstanceEnableCluster( params.ServiceName, params.InstanceId );
IISFreeInternalParameters( &params );
}
}
if ( (ResourceEntry->State == ClusterResourceOnline) ||
(ResourceEntry->State == ClusterResourceOnlinePending) )
{
status = ERROR_RESOURCE_PROPERTIES_STORED;
}
else
{
status = ERROR_SUCCESS;
}
}
ResUtilFreeParameterBlock( (LPBYTE) &params,
(LPBYTE) &ResourceEntry->Params,
IISResourcePrivateProperties );
if (ERROR_SUCCESS == dupstatus)
{
ResUtilFreeParameterBlock( (LPBYTE) &oldParams,
(LPBYTE) &ResourceEntry->Params,
IISResourcePrivateProperties );
}
return status;
} // IISSetPrivateResProperties
void
IISReplicateProperties(
IN IIS_PARAMS* lpNewParams,
IN IIS_PARAMS* lpOldParams
)
{
//
// Get Local Computer Name. Do not replicate to local computer
//
WCHAR wszLocalComputerName[MAX_COMPUTERNAME_LENGTH+1] = L"";
DWORD dwLength = MAX_COMPUTERNAME_LENGTH+1;
GetComputerName(wszLocalComputerName, &dwLength);
HCLUSTER hClus = OpenCluster(NULL);
if (hClus)
{
HCLUSENUM hClusEnumNode = ClusterOpenEnum( hClus, CLUSTER_ENUM_NODE);
if (hClusEnumNode)
{
DWORD dwType, dwIndex = 0;
WCHAR wszNodeName[MAX_COMPUTERNAME_LENGTH+1] = L"";
DWORD cbBufferLength = MAX_COMPUTERNAME_LENGTH+1;
while ( ERROR_SUCCESS == ClusterEnum( hClusEnumNode,
dwIndex,
&dwType,
wszNodeName,
&cbBufferLength))
{
dwIndex++;
//
// Set these properties on the node (if not local computer)
//
if (wcscmp(wszNodeName, wszLocalComputerName))
{
IISSetRemoteNodeProperties(wszNodeName, lpNewParams, lpOldParams);
}
cbBufferLength = MAX_COMPUTERNAME_LENGTH+1;
}
ClusterCloseEnum(hClusEnumNode);
}
CloseCluster(hClus);
}
} // IISReplicateProperties
void
IISSetRemoteNodeProperties(
IN LPWSTR wszNodeName,
IN IIS_PARAMS* lpNewParams,
IN IIS_PARAMS* lpOldParams
)
{
IMSAdminBaseW * pcAdmCom = NULL;
METADATA_HANDLE hmd;
HRESULT hRes = S_OK;
COSERVERINFO csiMachine;
MULTI_QI QI = {&IID_IMSAdminBase, NULL, 0};
//
// Open Metabase path to the remote Node
//
ZeroMemory( &csiMachine, sizeof(COSERVERINFO) );
csiMachine.pwszName = (LPWSTR)wszNodeName;
hRes = CoCreateInstanceEx( GETAdminBaseCLSID(TRUE),
NULL,
CLSCTX_SERVER,
&csiMachine,
1,
&QI
);
if ( SUCCEEDED(hRes) && SUCCEEDED(QI.hr))
{
WCHAR achMDPath[80];
METADATA_RECORD mdRecord;
DWORD dwVal;
pcAdmCom = (IMSAdminBaseW *)QI.pItf;
if ( lpOldParams && lpOldParams->ServiceName && lpOldParams->InstanceId)
{
wcscpy(achMDPath,L"/LM/");
wcscat(achMDPath,lpOldParams->ServiceName);
wcscat(achMDPath,L"/");
wcscat(achMDPath,lpOldParams->InstanceId);
dwVal = 0;
if( SUCCEEDED( pcAdmCom->OpenKey( METADATA_MASTER_ROOT_HANDLE,
achMDPath,
METADATA_PERMISSION_WRITE,
5000,
&hmd)) )
{
MD_SET_DATA_RECORD ( &mdRecord,
MD_CLUSTER_ENABLED,
METADATA_INHERIT,
IIS_MD_UT_SERVER,
DWORD_METADATA,
sizeof(DWORD),
&dwVal
);
pcAdmCom->SetData( hmd, L"", &mdRecord);
pcAdmCom->CloseKey( hmd );
}
}
if ( lpNewParams && lpNewParams->ServiceName && lpNewParams->InstanceId)
{
wcscpy(achMDPath,L"/LM/");
wcscat(achMDPath,lpNewParams->ServiceName);
wcscat(achMDPath,L"/");
wcscat(achMDPath,lpNewParams->InstanceId);
dwVal = 1;
if( SUCCEEDED( pcAdmCom->OpenKey( METADATA_MASTER_ROOT_HANDLE,
achMDPath,
METADATA_PERMISSION_WRITE,
5000,
&hmd)) )
{
MD_SET_DATA_RECORD ( &mdRecord,
MD_CLUSTER_ENABLED,
METADATA_INHERIT,
IIS_MD_UT_SERVER,
DWORD_METADATA,
sizeof(DWORD),
&dwVal
);
pcAdmCom->SetData( hmd, L"", &mdRecord);
pcAdmCom->CloseKey( hmd );
}
}
pcAdmCom->Release();
}
} // IISSetRemoteNodeProperties
DWORD
WINAPI
IISAlivePollingThread(
IN PCLUS_WORKER pWorker,
IN LPVOID lpVoid )
/*++
Routine Description:
Polls the state of the given resource
Arguments:
Returns:
Win32 error code.
--*/
{
BOOL bIsAlive = TRUE;
BOOL bFoundRes = FALSE;
DWORD dwStatus = ERROR_SUCCESS;
PLIST_ENTRY pRes = NULL; // temp list entry for looping
PLIST_ENTRY pListStart = NULL; // temp list head used for looping
PLIST_ENTRY pEntry = NULL; // list entry used to check the list of resources
LPIIS_RESOURCE pResourceEntry = NULL; // resource entry that corresponds to the list entry
TR( (DEBUG_BUFFER,"[IISAlivePollingThread] Thread Started \n") );
while ( !ClusWorkerCheckTerminate( &g_cwAlivePollingThread ) )
{
DWORD dwPort = 0;
SOCKADDR saServer;
DWORD dwServiceType = 0;
LPWSTR szMDPath = NULL;
BOOL bReadBindingsInfo = FALSE ;
//
// update the time stamp that is used by the isalive/looksalive check to determine this thread is still running
//
InterlockedExchange( (LPLONG) &g_dwTickOfLastResourceCheck, GetTickCount() );
TR( (DEBUG_BUFFER,"[IISAlivePollingThread] set g_dwTickOfLastResourceCheck (%d)\n", g_dwTickOfLastResourceCheck) );
EnterCriticalSection( &IISTableLock );
{
//
// check that the list is not empty
//
if ( IsListEmpty ( &IISResourceTable ) )
{
LeaveCriticalSection( &IISTableLock );
pEntry = NULL;
goto done_check;
}
//
// if we don't have a list element yet get the fisrt element in the list
//
if ( !pEntry || g_fIISResourceTable_HadChanged)
{
pEntry = IISResourceTable.Flink;
g_fIISResourceTable_HadChanged = FALSE;
}
//
// look for an element in the list that is OnLine
//
bFoundRes = FALSE ;
pListStart = pEntry;
do
{
if ( &IISResourceTable != pEntry )
{
//
// get the structure that contains this list element
//
pResourceEntry = CONTAINING_RECORD( pEntry,
IIS_RESOURCE,
ListEntry );
if ( pResourceEntry &&
(ClusterResourceOnline == pResourceEntry->State) )
{
//
// grab the info that we need from this structure so we can release the critical section
//
dwPort = pResourceEntry->dwPort;
dwServiceType = pResourceEntry->Params.ServiceType;
memcpy( (LPVOID) &saServer, (LPVOID) &pResourceEntry->saServer, sizeof(SOCKADDR) );
//
// allocate memory for the metabase path for this resource
//
if ( pResourceEntry->Params.MDPath )
{
szMDPath = (LPWSTR) LocalAlloc ( LPTR, (lstrlen(pResourceEntry->Params.MDPath)+1) * sizeof(WCHAR) ) ;
if ( szMDPath )
{
lstrcpy( szMDPath, pResourceEntry->Params.MDPath );
TR( (DEBUG_BUFFER,"[IISAlivePollingThread] checking resource (%S/%S), State:%d OnLineState:%d\n", pResourceEntry->Params.ServiceName, pResourceEntry->Params.InstanceId, pResourceEntry->State, ClusterResourceOnline) );
bFoundRes = TRUE;
break;
}
else
{
TR( (DEBUG_BUFFER,"[IISAlivePollingThread] Failed to allocate memory for metadata path\n") );
}
}
if ( szMDPath ) TR( (DEBUG_BUFFER,"[IISAlivePollingThread] Checking resource Metadata Path:%S\n", szMDPath) );
}
else
{
if ( pResourceEntry ) TR( (DEBUG_BUFFER,"[IISAlivePollingThread] Not checking resource (%S/%S) because it's not Online, State:%d OnLineState:%d\n", pResourceEntry->Params.ServiceName, pResourceEntry->Params.InstanceId, pResourceEntry->State, ClusterResourceOnline) );
}
}
//
// check the next element
//
pEntry = pEntry->Flink ;
}
while ( pListStart != pEntry );
//
// verify the loop ended because a valid resource was found
//
if ( !bFoundRes )
{
LeaveCriticalSection( &IISTableLock );
pEntry = NULL;
goto done_check;
}
}
LeaveCriticalSection( &IISTableLock );
//
// if the service status check (isalive/looksalive check) fails then get server bindings and try again
//
if ( ERROR_SUCCESS != (dwStatus = VerifyIISService( szMDPath, dwServiceType, dwPort, saServer, g_IISLogEvent )) )
{
TR( (DEBUG_BUFFER,"[IISAlivePollingThread] VerifyIISService failed , calling GetServerBindings() and retrying \n") );
if ( ERROR_SUCCESS == (dwStatus = GetServerBindings( szMDPath, dwServiceType, &saServer, &dwPort )) )
{
bReadBindingsInfo = TRUE ;
dwStatus = VerifyIISService( szMDPath, dwServiceType, dwPort, saServer, g_IISLogEvent );
}
}
bIsAlive = (dwStatus == ERROR_SUCCESS);
if ( szMDPath )
{
LocalFree ( szMDPath );
szMDPath = NULL;
}
EnterCriticalSection( &IISTableLock );
{
//
// check that the list is not empty
//
if ( IsListEmpty ( &IISResourceTable ) )
{
LeaveCriticalSection( &IISTableLock );
pEntry = NULL;
goto done_check;
}
//
// check that the element still exists in the list
//
bFoundRes = FALSE ;
for ( pRes = IISResourceTable.Flink;
pRes != &IISResourceTable;
pRes = pRes->Flink )
{
if ( pEntry == pRes )
{
bFoundRes = TRUE ;
break;
}
}
if( bFoundRes )
{
//
// get the elemement
//
pResourceEntry = CONTAINING_RECORD( pEntry,
IIS_RESOURCE,
ListEntry );
//
// update the element's state information (isalive/looksalive)
//
InterlockedExchange( (LPLONG)&pResourceEntry->bAlive , bIsAlive );
TR( (DEBUG_BUFFER,"[IISAlivePollingThread] Updating IsAlive/LooksAlive status for resource Metadata Path:%S bAlive:%d State:%d\n", pResourceEntry->Params.MDPath, bIsAlive, pResourceEntry->State) );
//
// update the bindings info if new information was found . these assignments do not need to be sync'ed
// since the only other thread they are used in is in the online thread . before the resource has been marked online
// so this code will never be executed at the same time .
//
if ( bReadBindingsInfo )
{
pResourceEntry->dwPort = dwPort ;
memcpy( (LPVOID) &pResourceEntry->saServer, (LPVOID) &saServer, sizeof(SOCKADDR) );
}
//
// print some error messages so users will know when a resource fails
//
if ( !pResourceEntry->bAlive )
{
if ( g_IISLogEvent )
{
//
// Some type of error
//
(g_IISLogEvent)(
pResourceEntry->ResourceHandle,
LOG_ERROR,
L"IsAlive/LooksAlive ERROR getting information for service %1!ws! resource %2!ws!\n",
pResourceEntry->Params.ServiceName,
pResourceEntry->ResourceName );
}
if ( g_hEventLog )
{
LPCTSTR aErrStr[3];
WCHAR aErrCode[32];
_ultow( dwStatus, aErrCode, 10 );
aErrStr[0] = pResourceEntry->Params.ServiceName;
aErrStr[1] = pResourceEntry->Params.InstanceId;
aErrStr[2] = aErrCode;
ReportEvent( g_hEventLog,
EVENTLOG_ERROR_TYPE,
0,
IISCL_EVENT_CANT_ACCESS_IIS,
NULL,
sizeof(aErrStr)/sizeof(LPCTSTR),
0,
aErrStr,
NULL );
}
}
//
// move to the next element
//
pEntry = pEntry->Flink;
}
else
{
//
// the resource was not found , probably deleted so pEntry is now invalid
//
pEntry = NULL;
TR( (DEBUG_BUFFER,"[IISAlivePollingThread] Failed to find a resource after checking its state\n") );
}
}
LeaveCriticalSection( &IISTableLock );
done_check:
Sleep( DELAY_BETWEEN_ISALIVE_CHECKS );
}
TR( (DEBUG_BUFFER,"[IISAlivePollingThread] Thread Exiting \n") );
return dwStatus;
} // IISAlivePollingThread
//***********************************************************
//
// Define Function Table
//
//***********************************************************
// Define entry points
CLRES_V1_FUNCTION_TABLE( IISFunctionTable,
CLRES_VERSION_V1_00,
IIS,
NULL,
NULL,
IISResourceControl,
IISResourceTypeControl );