/*++ Copyright (c) 1992-1997 Microsoft Corporation Module Name: iis.c Abstract: Resource DLL for IIS. This DLL supports the following IIS services WWW FTP GOPHER Each instance of a resouce is an IIS Virtual Roots. A Virtual root may have dependencies on IP Addresses, Names, Physical Disks, or UNC names. Known Limitations 1. The current version expects that the IIS "Virtual Root" information is created using the Inet manager tool (inetmgr). The "Cluster" resource contains . Root Name . Directory . IP Address [OPTIONAL] Currently, this dll does not support Virtual Roots containing access information (i.e. password) 2. The IIS management interfaces update the "Virtual Root" information in the registry. This means the OPEN call needs to remove any "Virtual Root" managed by the cluster. Otherwise, the it's possible for the resource to be incorrectly online after the Open (i.e., the cluster crashed without doing a offline). Author: Pete Benoit (v-pbenoi) 12-SEP-1996 Revision History: Pete Benoit (v-pbenoi) 10-MAR-1997 Updated to use clusres utility functions, add more error codes, get rid of routines replaced by clusres, added global mutext so iis resource dll's running in separate resource monitors have controled access to virtual root updates (NOTE: the IIS Management utility still breaks this exclusion) --*/ #include "iisutil.h" #include "resmonp.h" // // Names used to start service // LPCWSTR ActualServiceName[] = { L"W3SVC", // WWW L"MSFTPSVC", // FTP L"GOPHERSVC" // GOPHER }; #define IIS_SEMAPHORE_NAME L"$$$IIS$VIRTUAL$ROOT$MODIFY$SEMAPHORE$" #define PARAM_NAME__SERVICENAME L"ServiceName" #define PARAM_NAME__ALIAS L"Alias" #define PARAM_NAME__DIRECTORY L"Directory" #define PARAM_NAME__ACCESSMASK L"AccessMask" /* Remove for the first release #define PARAM_NAME__ACCOUNTNAME L"AccountName" #define PARAM_NAME__PASSWORD L"Password" */ #define PARAM_MIN__ACCESSMASK 0 #define PARAM_MAX__ACCESSMASK 0xFFFFFFFF #define PARAM_DEFAULT__ACCESSMASK 0 // // Global data. // // The current IIS management (2-3.0) interface does not have // an API to get/remove a single virtual root entry. Each update requires // a read (all virtual roots) modify (a virtual root) write (all virtual roots) sequence. // This mutex guards a read/modify/write sequence. // // This is a global mutex to guard for multiple iis resources (i.e., ones running in // separate resource monitors) NOTE: This does not solve potential // conflicts with the inetmgr application modifying a root at the same time // HANDLE g_hIISUpdateVirtualRootLock = NULL; LPIIS_RESOURCE g_IISTable[MAX_IIS_RESOURCES] = { 0 }; PLOG_EVENT_ROUTINE g_IISLogEvent = NULL; PSET_RESOURCE_STATUS_ROUTINE g_IISSetResourceStatus = NULL; extern CLRES_FUNCTION_TABLE IISFunctionTable; // // 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__ALIAS, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(IIS_PARAMS,Alias) }, { PARAM_NAME__DIRECTORY, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(IIS_PARAMS,Directory) }, { PARAM_NAME__ACCESSMASK, NULL, CLUSPROP_FORMAT_DWORD, 0, PARAM_MIN__ACCESSMASK, PARAM_MAX__ACCESSMASK, 0, FIELD_OFFSET(IIS_PARAMS,AccessMask) }, { 0 } }; // // Forward routines // DWORD OnlineVirtualRootExclusive( IN LPIIS_RESOURCE ResourceEntry ); DWORD OfflineVirtualRootExclusive( IN LPIIS_RESOURCE ResourceEntry ); BOOL WINAPI IISIsAlive( IN RESID Resource ); LPIIS_RESOURCE GetValidResource( IN RESID Resource, IN LPWSTR RoutineName ); DWORD IISReadParameters( IN OUT LPIIS_RESOURCE ResourceEntry ); 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 ); DWORD IISSetPrivateResProperties( IN OUT LPIIS_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize ); // // Function definitions // BOOLEAN IISInit( VOID ) { IISLoadMngtDll(); g_hIISUpdateVirtualRootLock = CreateSemaphoreW( NULL, 0, 1, IIS_SEMAPHORE_NAME); if (g_hIISUpdateVirtualRootLock == NULL) { return(FALSE); } if ( GetLastError() != ERROR_ALREADY_EXISTS ) { // set initial state to 1 if it didn't exist ReleaseSemaphore(g_hIISUpdateVirtualRootLock, 1, NULL); } return(TRUE); } VOID IISCleanup() { if (g_hIISUpdateVirtualRootLock) { CloseHandle(g_hIISUpdateVirtualRootLock); } IISUnloadMngtDll(); } BOOLEAN WINAPI IISDllEntryPoint( 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; default: break; } return(TRUE); } // IISShareDllEntryPoint 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; // // Search for a valid service name supported by this DLL // if ( lstrcmpiW( ResourceType, IIS_RESOURCE_NAME ) != 0 ) { return(ERROR_UNKNOWN_REVISION); } g_IISLogEvent = LogEvent; g_IISSetResourceStatus = SetResourceStatus; if ( (MinVersionSupported <= CLRES_VERSION_V1_00) && (MaxVersionSupported >= CLRES_VERSION_V1_00) ) { *FunctionTable = &IISFunctionTable; return(ERROR_SUCCESS); } return(ERROR_REVISION_MISMATCH); } // Startup DWORD IISOpenThread( IN PCLUS_WORKER pWorker, IN LPIIS_RESOURCE ResourceEntry ) /*++ Routine Description: Performs some additional initialization for the IIS service. 1. Makes sure the service is running 2. Make sure that any virtual roots contained in this resource are offline (i.e., removed from the IIS). Arguments: PWorker - Worker thread structure 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 = MAX_OPEN_RETRY; // //HACKHACK: // The IIS mngt API updates the registry every time the resource // is brought online. This means that if the cluster terminated abnormally // (i.e., did not do offline), then the resource potentially could be // incorrectly ONLINE after the OPEN. // // Until the IIS mngt changes, filter out the "Virtual Root" managed by // the Cluster // // // Make sure the IIS is installed // status = IsIISMngtDllLoaded(); if (status != ERROR_SUCCESS) { goto error_exit; } while (retry--) { // // Check to see if we were requested to terminate. // If so do so cleanly (i.e, no locks held) // if (ClusWorkerCheckTerminate(pWorker)){ status = ERROR_OPERATION_ABORTED; break; } // // Read the Parameter specific information from the registry // // CAVEAT: Normally this is done in the online routine. // We do it here to make sure that the resource comes up // in an OFFLINE state. The problem is that any iis change // updates the registry. So its possible that if the cluster // did not terminate normall that a resource could be incorrectly // brought up in an ONLINE state. // // status = IISReadParameters(ResourceEntry); if (status == ERROR_SUCCESS){ // // Try to offline the virtual roots. // status = OfflineVirtualRootExclusive(ResourceEntry); if (status == ERROR_SUCCESS) { break; } if ( ( status == ERROR_SERVICE_NOT_ACTIVE ) || ( status == RPC_S_SERVER_UNAVAILABLE ) ) { // // Try to start the service // ResUtilStartResourceService( ResourceEntry->Params.ServiceName, NULL ); // // Cleanup for next retry // DestructVR(ResourceEntry->VirtualRoot); ResourceEntry->VirtualRoot = NULL; } else { break; } } else { //END (status == ERROR_SUCCESS) // // ERROR_RESOURCE_NOT_FOUND indicates that the IP Address // dependency could not be found. We may have to wait a bit // for the other resources to come online before this will succeed // // All other errors are non-recoverable and we should exit // if (status != ERROR_RESOURCE_NOT_FOUND) { goto error_exit; } } // // Check to see if we were requested to terminate. // If so do so cleanly (i.e, no locks held) // if (ClusWorkerCheckTerminate(pWorker)){ status = ERROR_OPERATION_ABORTED; break; } status = ERROR_TIMEOUT; // // Give the service time to start // Sleep(SERVER_START_DELAY); } error_exit: if (status != ERROR_SUCCESS) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISOpenthread] NON-CRITICAL Unable to initialize or offline virtual root Error: %1!u!.\n", status ); } // // The online thread will block until the openthread completes. The online // thread re-reads the parameters key so free up the storage here. // DestructVR(ResourceEntry->VirtualRoot); ResourceEntry->VirtualRoot = NULL; return(status); } // END IISOpenThread 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; LPIIS_RESOURCE ResourceEntry; IIS_RESOURCE tmpResourceEntry; DWORD count = 0; DWORD Index = 0; DWORD serviceType = MAX_SERVICE; DWORD threadId; LPCWSTR ResourceType; HCLUSTER hCluster; ZeroMemory( &tmpResourceEntry, sizeof(tmpResourceEntry) ); // // Set the resource handle for logging and init the virtual root entry // tmpResourceEntry.ResourceHandle = ResourceHandle; tmpResourceEntry.VirtualRoot = NULL; tmpResourceEntry.State = ClusterResourceOffline; // // Open the cluster. // hCluster = OpenCluster(NULL); if ( hCluster == NULL ) { status = GetLastError(); (g_IISLogEvent)( ResourceHandle, LOG_ERROR, L"[IISOpen] Unable to open cluster. Error: %1!u!.\n", status ); goto error_exit; } tmpResourceEntry.hResource = OpenClusterResource( hCluster, ResourceName ); CloseCluster(hCluster); if ( tmpResourceEntry.hResource == NULL ) { status = GetLastError(); (g_IISLogEvent)( ResourceHandle, LOG_ERROR, L"[IISOpen] Unable to open cluster resource handle. Error: %1!u!.\n", status ); goto error_exit; } // // Open the Parameters key for this resource. // status = ClusterRegOpenKey( ResourceKey, L"Parameters", KEY_READ, &tmpResourceEntry.ParametersKey ); if ( status != ERROR_SUCCESS ) { (g_IISLogEvent)( ResourceHandle, LOG_ERROR, L"[IISOpen] Unable to open parameters key for resource. Error: %1!u!.\n", status ); goto error_exit; } // // Find a free slot in the resource table // for ( count = 1; count <= MAX_IIS_RESOURCES; count++ ) { if ( g_IISTable[count-1] == NULL ) { break; } } if ( count > MAX_IIS_RESOURCES ) { (g_IISLogEvent)( ResourceHandle, LOG_ERROR, L"[IISOpen] No more IIS Resources available.\n"); status = ERROR_RESOURCE_NOT_FOUND; goto error_exit; } // // Allocate a ResourceEntry // ResourceEntry = LocalAlloc( LPTR, sizeof(IIS_RESOURCE) ); if ( ResourceEntry == NULL ) { (g_IISLogEvent)( ResourceHandle, LOG_ERROR, L"[IISOpen] Unable to allocate IIS resource structure.\n"); status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } // // Save resource in the resource table // g_IISTable[count-1] = ResourceEntry; // // Copy the tmp resource entry // CopyMemory(ResourceEntry,&tmpResourceEntry,sizeof(IIS_RESOURCE)); ResourceEntry->Index = count; // // Make sure that the virtual roots contained in this resource // are offline. The IISOpenThread function performs this task. // status = ClusWorkerCreate( &ResourceEntry->OpenThread, IISOpenThread, ResourceEntry ); if ( status != ERROR_SUCCESS ) { ResourceEntry->State = ClusterResourceFailed; (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISOpen] Unable to create open worker thread, status %1!u!.\n", status ); goto error_exit; } // // Log success // (g_IISLogEvent)( ResourceHandle, LOG_INFORMATION, L"[IISOpen] Open request succeeded with id = %1!u!.\n", ResourceEntry->Index); return((RESID)ResourceEntry->Index); error_exit: if (count > 0){ g_IISTable[count -1] = NULL; } if (tmpResourceEntry.ParametersKey != NULL){ ClusterRegCloseKey( tmpResourceEntry.ParametersKey ); } if (tmpResourceEntry.hResource != NULL) { CloseClusterResource( tmpResourceEntry.hResource ); } if (ResourceEntry != 0){ DestructIISResource(ResourceEntry); } else { FreeIISResource(&tmpResourceEntry); } (g_IISLogEvent)( ResourceHandle, LOG_ERROR, L"[IISOpen] Error %1!u! opening iis resource %2!ws!.\n", status, ResourceName ); SetLastError(status); return((RESID)(0)); } // IISOpen DWORD IISOnlineThread( IN PCLUS_WORKER pWorker, IN LPIIS_RESOURCE ResourceEntry ) /*++ Routine Description: Brings a share resource online. Arguments: pWorker - Supplies the worker structure 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; ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceOnlinePending; //resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; ResourceEntry->State = ClusterResourceOnlinePending; // // See if the open worker thread has terminated // retry = MAX_OPEN_RETRY*2; while (retry--) { // // Check to see if we were requested to terminate. // If so do so cleanly (i.e, no locks held) // if (ClusWorkerCheckTerminate(pWorker)){ status = ERROR_OPERATION_ABORTED; goto error_exit; } // // See if the open thread terminated // if (ClusWorkerCheckTerminate(&ResourceEntry->OpenThread)){ status = ERROR_SUCCESS; break; } // // Wait for a bit // status = ERROR_TIMEOUT; Sleep(SERVER_START_DELAY); } // END retry #if 0 if (status != ERROR_SUCCESS) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"ERROR [OnLineThread] TIMEOUT waiting for open thread to terminate \n"); } #endif // // Try to Read the Parameter specific information from // the registry. This must defer this till after open thread terminates // i.e., because open also reads the parameters status = IISReadParameters( ResourceEntry ); if (status != ERROR_SUCCESS){ (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"ERROR [OnLineThread] Could not read resource parameters\n"); goto error_exit; } retry = MAX_ONLINE_RETRY; while (retry--) { // // Check to see if we were requested to terminate. // If so do so cleanly (i.e, no locks held) // if (ClusWorkerCheckTerminate(pWorker)){ status = ERROR_OPERATION_ABORTED; break; } #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"INFO [OnLineThread] Root = %1!ws! IP = %2!ws! Dir = %3!ws! Mask = %4!u!\n", ResourceEntry->VirtualRoot->pszRoot, ResourceEntry->VirtualRoot->pszAddress, ResourceEntry->VirtualRoot->pszDirectory, ResourceEntry->VirtualRoot->dwMask); #endif // // Try to Online the resources status = OnlineVirtualRootExclusive(ResourceEntry); #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"INFO [OnLineThread] OnLineVirtualRoot status = %1!u!\n", status); #endif if ( status == ERROR_SUCCESS) { break; } // // Check to see if we were requested to terminate. // If so do so cleanly (i.e, no locks held) // if (ClusWorkerCheckTerminate(pWorker)){ status = ERROR_OPERATION_ABORTED; break; } // // If we failed for any reason except // the service is not active then fail // if ( ( status == ERROR_SERVICE_NOT_ACTIVE ) || ( status == RPC_S_SERVER_UNAVAILABLE ) ) { // // Try to start the service // ResUtilStartResourceService( ResourceEntry->Params.ServiceName, NULL ); } else { goto error_exit; } // // We restarted the service. It's possible that the service terminated // abnormally. If the resource is online, leave it and return success // if (VerifyIISService(ResourceEntry,FALSE,g_IISLogEvent)) { status = ERROR_SUCCESS; goto error_exit; } // // Check to see if we were requested to terminate. // If so do so cleanly (i.e, no locks held) // if (ClusWorkerCheckTerminate(pWorker)){ status = ERROR_OPERATION_ABORTED; break; } status = ERROR_TIMEOUT; // // Give the service time to start // Sleep(SERVER_START_DELAY); } // END While retry-- error_exit: if ( status != ERROR_SUCCESS ) { // // Error // (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISOnlineThread] Error %1!u! cannot bring resource online.\n", status ); resourceStatus.ResourceState = ClusterResourceFailed; ResourceEntry->State = ClusterResourceFailed; } else { // // Success, update state, log message // resourceStatus.ResourceState = ClusterResourceOnline; ResourceEntry->State = ClusterResourceOnline; #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISOnlineThread] Success bringing resource online.\n" ); #endif } // // Set the state of the resource // (g_IISSetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); return(status); } // IISOnlineThread 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 ERROR_SERVICE_NOT_ACTIVE if it could not find the IIS management DLL acquire 'ownership'. Win32 error code if other failure. --*/ { LPIIS_RESOURCE ResourceEntry = NULL; DWORD threadId; DWORD status; // // Get a valid resource // ResourceEntry = GetValidResource(Resource,L"OnLine"); if (ResourceEntry == NULL) { return(ERROR_RESOURCE_NOT_FOUND); } #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISOnline] Online request for IIS Resource %1!u!.\n", Resource ); #endif // Initialize the state to offline ResourceEntry->State = ClusterResourceOffline; // Terminate (or wait) for workers ClusWorkerTerminate( &ResourceEntry->OnlineThread ); // Make sure the IIS is installed status = IsIISMngtDllLoaded(); if (status != ERROR_SUCCESS) { // Set the state to failed AND log event ResourceEntry->State = ClusterResourceFailed; (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISOnline] Error loading IIS mngt dll or one of its functions error status: %1!u!\n", status); return(ERROR_SERVICE_NOT_ACTIVE); } // Create new online thread status = ClusWorkerCreate( &ResourceEntry->OnlineThread, IISOnlineThread, ResourceEntry ); if (status != ERROR_SUCCESS){ // Set the state to failed AND log event ResourceEntry->State = ClusterResourceFailed; (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISOnline] Unable to create cluster worker thread, status %1!u!.\n", status ); } else { status = ERROR_IO_PENDING; } 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; // // Get a valid resource entry, return on error // ResourceEntry = GetValidResource(Resource,L"Terminate"); if (ResourceEntry == NULL) { return; } else { status = ERROR_SUCCESS; } #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISTerminate] Terminate or offline request for Resource%1!u!.\n", Resource ); #endif ClusWorkerTerminate( &ResourceEntry->OnlineThread ); ClusWorkerTerminate( &ResourceEntry->OpenThread ); // // Try to take the resources offline, dont return if an error since // the resources may be offline when terminate called // if ((ResourceEntry->State == ClusterResourceOnlinePending) || (ResourceEntry->State == ClusterResourceOnline)) { status = OfflineVirtualRootExclusive(ResourceEntry); } if ( status != ERROR_SUCCESS ) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISTerminate] Error terminating Resource. NT Status %1!u!. Service. %2!ws!\n", status, ResourceEntry->Params.ServiceName); } // // Set status to offline // ResourceEntry->State = ClusterResourceOffline; // // Reclaim storage for parameters, close handles // DestructVR(ResourceEntry->VirtualRoot); ResourceEntry->VirtualRoot = NULL; } // 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; // // Get a valid resource entry, return on error // ResourceEntry = GetValidResource(Resource,L"Offline"); if (ResourceEntry == NULL) { return(ERROR_RESOURCE_NOT_FOUND); } #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISOffline] Offline request for Resource%1!u!.\n", Resource ); #endif IISTerminate(Resource); return(ERROR_SUCCESS); } // 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; // // Get a valid resource // ResourceEntry = GetValidResource(Resource,L"IsAlive"); if (ResourceEntry == NULL) { return(FALSE); } // // Verify the resource // return( VerifyIISService( ResourceEntry, TRUE, g_IISLogEvent )); } // 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; HANDLE serviceHandle; HANDLE scManagerHandle; DWORD status; SERVICE_STATUS serviceStatus; // // Get a valid resource // ResourceEntry = GetValidResource(Resource,L"LooksAlive"); if (ResourceEntry == NULL) { return(FALSE); } // // Query the status of the server // scManagerHandle = OpenSCManager( NULL, // local machine NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS ); // all access if ( scManagerHandle == NULL ) { status = GetLastError(); #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISLooksAlive] Cannot access service controller '%1!ws!' status = %2!u!\n", ResourceEntry->Params.ServiceName, status); #endif return(FALSE); } serviceHandle = OpenService( scManagerHandle, ResourceEntry->Params.ServiceName, SERVICE_ALL_ACCESS ); // // CLOSE Service Manager Handle // CloseServiceHandle( scManagerHandle ); if ( serviceHandle == NULL ) { status = GetLastError(); #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISLooksAlive] Cannot open service '%1!ws!' status = %2!u!\n", ResourceEntry->Params.ServiceName, status); #endif return(FALSE); } if ( !QueryServiceStatus(serviceHandle, &serviceStatus) ) { status = GetLastError(); #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISLooksAlive] Error querying service '%1!ws!' status = %2!u!\n", ResourceEntry->Params.ServiceName, status); #endif CloseServiceHandle( serviceHandle ); return FALSE; } // // CLOSE Service Handle // CloseServiceHandle( serviceHandle ); if ( serviceStatus.dwCurrentState == SERVICE_RUNNING ) { return TRUE; } #if 1 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISLooksAlive] Service not running '%1!ws!' state = %2!u!\n", ResourceEntry->Params.ServiceName, serviceStatus.dwCurrentState); #endif return FALSE; } // 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 } #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISClose] Close request for Service '%1!ws!' \n", ResourceEntry->Params.ServiceName); #endif // // Clear the table entry // g_IISTable[ResourceEntry->Index -1] = NULL; DestructIISResource(ResourceEntry); } // IISClose DWORD OnlineVirtualRootExclusive( IN LPIIS_RESOURCE ResourceEntry ) /*++ Routine Description: This routine performs an atomic read modify write operation to ONline a virtual root Arguments: ResourceEntry - Supplies the resource to offline Return Value: ERROR_SUCCESS if successful. A Win32 error code on failure. --*/ { DWORD status; WaitForSingleObject(g_hIISUpdateVirtualRootLock, INFINITE); status = OnLineVirtualRoot(ResourceEntry, g_IISLogEvent); ReleaseSemaphore( g_hIISUpdateVirtualRootLock, 1, 0); return status; } DWORD OfflineVirtualRootExclusive( IN LPIIS_RESOURCE ResourceEntry ) /*++ Routine Description: This routine performs an atomic read modify write operation to offline a virtual root Arguments: ResourceEntry - Supplies the resource to offline Return Value: ERROR_SUCCESS if successful. A Win32 error code on failure. --*/ { DWORD status; WaitForSingleObject(g_hIISUpdateVirtualRootLock,INFINITE); status = OffLineVirtualRoot(ResourceEntry, g_IISLogEvent); ReleaseSemaphore( g_hIISUpdateVirtualRootLock, 1, 0); return status; } 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; // // Validate the resource id is in the correct range // Index = (DWORD)Resource -1; if ( Index > MAX_IIS_RESOURCES) { (g_IISLogEvent)( NULL, LOG_ERROR, L"[%1!ws!] Invalid resource id (out of range) Resource Id = %2!u!\n", RoutineName, Index); return(NULL); } ResourceEntry = g_IISTable[Index]; // // 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, Index); return(NULL); } // // Sanity check the resource id // if ( ResourceEntry->Index != (DWORD)Resource ) { (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 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 = ResUtilGetSzValue( hParametersKey, ValueName ); 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 RECOVERABLE ERROR ERROR_RESOURCE_NOT_FOUND - The IP Address dependency was not found try again later FATAL ERRORS ERROR_INVALID_PARAMETER - One of the required parameters was incorrect or NULL ERROR_NOT_ENOUGH_MEMORY - Could not satisfy the memory request ERROR_INVALID_SERVICENAME - Service name is invalid for this resource dll ERROR_DUP_NAME - Duplicate exclusive parameter name found --*/ { DWORD status; LPCTSTR password = NULL; LPINET_INFO_VIRTUAL_ROOT_ENTRY tmpVr; DWORD accessMask; HRESOURCE hResource = NULL; INT iServiceType; DWORD length; LPWSTR nameOfPropInError; // // Each offline (or at end of OpenThread) frees the VirtualRoot // Make sure the entry is NULL // if ( ResourceEntry->VirtualRoot != NULL ) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] NON-Empty virtual root entry.\n" ); } // // Allocate memory for the virtual root // tmpVr = LocalAlloc( LPTR, sizeof(INET_INFO_VIRTUAL_ROOT_ENTRY) ); if ( tmpVr == NULL ) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] Cannot allocate storage for virtual root for resource\n" ); return( ERROR_NOT_ENOUGH_MEMORY ); } // // Read the parameters for the resource. // status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey, IISResourcePrivateProperties, (LPBYTE) &ResourceEntry->Params, TRUE, // CheckForRequiredProperties &nameOfPropInError ); if ( status != ERROR_SUCCESS ) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the '%1' property. Error: %2!u!.\n", (nameOfPropInError == NULL ? L"" : nameOfPropInError), status ); goto error_exit; } // // Make sure we got passed a valid service name // for ( iServiceType = 0 ; iServiceType < MAX_SERVICE ; iServiceType++ ) { if ( lstrcmpiW( ResourceEntry->Params.ServiceName, ActualServiceName[iServiceType] ) == 0 ) { break; } } if ( iServiceType >= MAX_SERVICE ) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] Service name %1!ws! not supported or found\n", ResourceEntry->Params.ServiceName ); status = ERROR_INVALID_SERVICENAME; goto error_exit; } ResourceEntry->ServiceType = iServiceType; // // Copy the parameters to where they are used. // tmpVr->pszRoot = LocalAlloc( LMEM_FIXED, (lstrlenW( ResourceEntry->Params.Alias ) + 1) * sizeof(WCHAR) ); if ( tmpVr->pszRoot == NULL ) { status = GetLastError(); goto error_exit; } lstrcpyW( tmpVr->pszRoot, ResourceEntry->Params.Alias ); tmpVr->pszDirectory = LocalAlloc( LMEM_FIXED, (lstrlenW( ResourceEntry->Params.Directory ) + 1) * sizeof(WCHAR) ); if ( tmpVr->pszDirectory == NULL ) { status = GetLastError(); goto error_exit; } lstrcpyW( tmpVr->pszDirectory, ResourceEntry->Params.Directory ); // // Get the Access Mask // if ( ResourceEntry->ServiceType != GOPHER_SERVICE ) { status = ResUtilGetDwordValue( ResourceEntry->ParametersKey, PARAM_NAME__ACCESSMASK, &accessMask, 0 ); if (status != ERROR_SUCCESS ) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] Unable to read virtual root Access Mask\n" ); status = ERROR_INVALID_PARAMETER; goto error_exit; } tmpVr->dwMask = accessMask; } // END != GOPHER_SERVICE if ( ResourceEntry->ServiceType == WWW_SERVICE ) { // // Get the IP Address // hResource = ResUtilGetResourceDependency( ResourceEntry->hResource, IP_ADDRESS_RESOURCE_NAME ); if ( hResource == NULL ) { status = GetLastError(); (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] Unable to find IP Address dependency status =%1!u!\n", status ); // // A status of ERROR_NO_DATA indicates that // there were no dependencies for this resource // if (status == ERROR_NO_DATA) { status = ERROR_INVALID_PARAMETER; } else { status = ERROR_RESOURCE_NOT_FOUND; } goto error_exit; } tmpVr->pszAddress = GetResourceParameter( hResource, L"Address" ); if ( tmpVr->pszAddress == NULL ) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] Unable to get TPC/IP Address from Cluster Reg\n" ); status = ERROR_INVALID_PARAMETER; goto error_exit; } } else { // ELSE NOT WWW_SERVICE // // Allocate memory for NULL ip address field // tmpVr->pszAddress = LocalAlloc( LPTR, sizeof(WCHAR)*10 ); if (tmpVr->pszAddress == NULL) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] Cannot allocate storage for NULL (GOPHER, FTP) IP Address" ); status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } // end pszAddress == NULL // // Copy a NULL character // lstrcpyW( tmpVr->pszAddress, L"\0" ); } // END IF WWW_SERVICE //HACK HACK //make sure the password field is null terminated // lstrcpyW( tmpVr->AccountPassword, L"\0" ); // // Get the [optional] AccountName // /*BUGBUG //This version does not support protecting UNC physical directories //with an account name and password... /*BUGBUG tmpVr->pszAccountName = ResUtilGetSzValue( ResourceEntry->ParametersKey, PARAM_NAME__ACCOUNTNAME ); */ if ( tmpVr->pszAccountName == NULL ) { #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"[IISReadParameters] NO Account name entered set to NULL\n" ); #endif tmpVr->pszAccountName = LocalAlloc( LPTR, sizeof(WCHAR)*2 ); // // Make sure we can still allocate memory // if ( tmpVr->pszAccountName == NULL ) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] Cannot allocate memory for Account Name\n" ); status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } //END pszAccountName == NULL // // Copy a NULL character // lstrcpyW( tmpVr->pszAccountName, L"\0" ); } else { // // Get the [optional] Account password // // /* BUGBUG Add password after encryption in registry available password = ResUtilGetSzValue( ResourceEntry->ParametersKey, PARAM_NAME__PASSWORD ); */ if ( password == NULL ) { #if 0 (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISReadParameters] No password specified\n" ); #endif } else { lstrcpyW( tmpVr->AccountPassword, password ); } // END password == NULL } // END else pszAccountName == NULL status = ERROR_SUCCESS; ResourceEntry->VirtualRoot = tmpVr; error_exit: if (password != NULL) { LocalFree((PVOID)password); } if (status != ERROR_SUCCESS) { DestructVR(tmpVr); } if (hResource != NULL) { CloseClusterResource(hResource); } return(status); } // IISReadParameters 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 Virtual Root. 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 { 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; LPIIS_RESOURCE resourceEntry = NULL; DWORD required; // // Get a valid resource // resourceEntry = GetValidResource( ResourceId, L"Close"); if ( resourceEntry == NULL ) { return(ERROR_RESOURCE_NOT_FOUND); // this should not happen } switch ( ControlCode ) { case CLUSCTL_RESOURCE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES: status = IISGetRequiredDependencies( OutBuffer, OutBufferSize, BytesReturned ); break; case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( IISResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES: status = IISGetPrivateResProperties( resourceEntry, OutBuffer, OutBufferSize, BytesReturned ); break; case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES: status = IISValidatePrivateResProperties( resourceEntry, InBuffer, InBufferSize, NULL ); break; case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES: status = IISSetPrivateResProperties( resourceEntry, InBuffer, InBufferSize ); break; default: status = ERROR_INVALID_FUNCTION; 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; DWORD required; switch ( ControlCode ) { case CLUSCTL_RESOURCE_TYPE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( IISResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } 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 IISValidateUniqueProperties( IN HRESOURCE hSelf, IN HRESOURCE hResource, PIIS_PARAMS pParams ) /*++ Routine Description: Callback function to validate that a resources properties are unique. For the IIS resource the ALIAS property must be unique for a virtual server (i.e., same IP Address dependency) Arguments: hSelf - A handle to the original resource. hResource - A handle to a resource of the same Type. Check against this to make sure the new properties do not conflict. pParams - Contains the properties for this resource Return Value: ERROR_SUCCESS - The function completed successfully, the name is unique ERROR_DUP_NAME - The name is not unique (i.e., already claimed by another resource) Win32 error code - The function failed. --*/ { DWORD dwError; LPWSTR lpszAlias = NULL; LPWSTR lpszService = NULL; BYTE szDependsName[MAX_DEFAULT_WSTRING_SIZE]; HKEY hKey = NULL; HKEY hParamKey = NULL; DWORD dwSize; DWORD dwRetSize; HRESOURCE hSelfDepends = NULL; HRESOURCE hResDepends = NULL; hKey = GetClusterResourceKey( hResource, KEY_READ ); if( !hKey ){ return(GetLastError()); } dwSize = MAX_DEFAULT_WSTRING_SIZE; dwError = ClusterRegOpenKey( hKey, L"Parameters", KEY_READ, &hParamKey); if (dwError != ERROR_SUCCESS) { goto error_exit; } if ( !pParams->Alias || !pParams->ServiceName ) { dwError = ERROR_INVALID_PARAMETER; goto error_exit; } lpszAlias = ResUtilGetSzValue(hParamKey, PARAM_NAME__ALIAS); if (!lpszAlias) { dwError = GetLastError(); goto error_exit; } lpszService = ResUtilGetSzValue(hParamKey, PARAM_NAME__SERVICENAME); if (!lpszService) { dwError = GetLastError(); goto error_exit; } // Assume success dwError = ERROR_SUCCESS; // // An Alias property must be unique in a group (i.e., ip address), // and service (i.e., WWW, FTP, GOPHER) // if ( !(_wcsicmp( lpszAlias, pParams->Alias) ) && !(_wcsicmp( lpszService, pParams->ServiceName ) ) ){ // // Get the dependent IP_ADDRESS resource for the callee // hSelfDepends = ResUtilGetResourceDependency(hSelf, IP_ADDRESS_RESOURCE_NAME); if (!hSelfDepends) { dwError = GetLastError(); goto error_exit; } // // Get the dependent IP_ADDRESS resource for hResource // dwError = ClusterResourceControl( hResource, //Handle to the resource NULL, //Don't care about node CLUSCTL_RESOURCE_GET_NAME, //Get the name 0, // &InBuffer 0, // nInBufferSize, &szDependsName, // &OutBuffer MAX_DEFAULT_WSTRING_SIZE, // OutBufferSize, &dwRetSize ); // returned size if (dwError != ERROR_SUCCESS) { goto error_exit; } hResDepends = ResUtilGetResourceNameDependency((LPWSTR)&szDependsName, IP_ADDRESS_RESOURCE_NAME); if (!hResDepends) { dwError = GetLastError(); CloseClusterResource( hSelfDepends); goto error_exit; } // // See if the name of the IP_ADDRESS dependencies match. If so // then we have a duplicate IIS VR // if ( ResUtilResourcesEqual( hSelfDepends, hResDepends) ) { dwError = ERROR_DUP_NAME; } CloseClusterResource( hResDepends ); CloseClusterResource( hSelfDepends ); } error_exit: if (hKey) ClusterRegCloseKey(hKey); if (hParamKey) ClusterRegCloseKey(hParamKey); if (lpszService) LocalFree(lpszService); if (lpszAlias) LocalFree(lpszAlias); return( dwError ); }//IISValidateUniqueProperties 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 parameter block to fill in. Return Value: ERROR_SUCCESS - The function completed successfully. ERROR_INVALID_PARAMETER - The data is formatted incorrectly. ERROR_INVALID_DATA - Parameter data was invalid. 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 = ¶ms; } 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, // Allow unknowns InBuffer, InBufferSize, (LPBYTE) pParams ); if ( status == ERROR_SUCCESS ) { // // BUGBUG: Validate the parameter values. // if ( (pParams->Alias == NULL) || (pParams->Alias[0] != L'/') ) { status = ERROR_INVALID_DATA; } // // Check for Unique Alias name within virtual server // status = ResUtilEnumResources(ResourceEntry->hResource, IIS_RESOURCE_NAME, IISValidateUniqueProperties, pParams); if (status != ERROR_SUCCESS) { (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[IISValidatePrivateResourceProperty] status = %1!d!\n", status); status = ERROR_INVALID_DATA; } } // // Cleanup our parameter block. // if ( pParams == ¶ms ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (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; IIS_PARAMS params; ZeroMemory( ¶ms, sizeof(IIS_PARAMS) ); // // Parse and validate the properties. // status = IISValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, ¶ms ); if ( status != ERROR_SUCCESS ) { ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, IISResourcePrivateProperties ); return(status); } // // Save the parameter values. // status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey, IISResourcePrivateProperties, NULL, (LPBYTE) ¶ms, InBuffer, InBufferSize, (LPBYTE) &ResourceEntry->Params ); ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, IISResourcePrivateProperties ); // // If the resource is online, return a non-success status. // if (status == ERROR_SUCCESS) { if ( (ResourceEntry->State == ClusterResourceOnline) || (ResourceEntry->State == ClusterResourceOnlinePending) ) { status = ERROR_RESOURCE_PROPERTIES_STORED; } else { status = ERROR_SUCCESS; } } return status; } // IISSetPrivateResProperties //*********************************************************** // // Define Function Table // //*********************************************************** // Define entry points CLRES_V1_FUNCTION_TABLE( IISFunctionTable, CLRES_VERSION_V1_00, IIS, NULL, NULL, IISResourceControl, IISResourceTypeControl );