/*++ Copyright (c) 1999 Microsoft Corp. Module Name: clusres.c Abstract: Resource DLL for Remote Storage Server Author: Ravisankar Pudipeddi (ravisp) 1 Sept 1999 Revision History: --*/ #pragma comment(lib, "clusapi.lib") #pragma comment(lib, "resutils.lib") #define UNICODE 1 #include #include #include #include "userenv.h" #include "winsvc.h" #include "aclapi.h" #define LOG_CURRENT_MODULE LOG_MODULE_RSCLUSTER #define SERVICES_ROOT L"SYSTEM\\CurrentControlSet\\Services\\" #define DBG_PRINT printf #pragma warning( disable : 4115 ) // named type definition in parentheses #pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union #pragma warning( disable : 4214 ) // nonstandard extension used : bit field types other than int #pragma warning( default : 4214 ) // nonstandard extension used : bit field types other than int #pragma warning( default : 4201 ) // nonstandard extension used : nameless struct/union #pragma warning( default : 4115 ) // named type definition in parentheses // // Type and constant definitions. // #define RSCLUSTER_RESNAME L"Remote Storage Server" #define RSCLUSTER_SVCNAME TEXT("Remote_Storage_Server") // Handle to service controller, set by the first create resource call. SC_HANDLE g_ScHandle = NULL; typedef struct _RSCLUSTER_RESOURCE { RESID ResId; // for validation HRESOURCE hResource; SC_HANDLE ServiceHandle; RESOURCE_HANDLE ResourceHandle; HKEY ResourceKey; HKEY ParametersKey; LPWSTR ResourceName; CLUS_WORKER PendingThread; BOOL Online; CLUS_WORKER OnlineThread; CLUSTER_RESOURCE_STATE State; DWORD dwServicePid; HANDLE hSem; } RSCLUSTER_RESOURCE, *PRSCLUSTER_RESOURCE; // // Global data. // // Event Logging routine. PLOG_EVENT_ROUTINE g_LogEvent = NULL; // Resource Status routine for pending Online and Offline calls. PSET_RESOURCE_STATUS_ROUTINE g_SetResourceStatus = NULL; // Forward reference to our RESAPI function table. extern CLRES_FUNCTION_TABLE g_RSClusterFunctionTable; // // RSCluster resource read-write private properties. // RESUTIL_PROPERTY_ITEM RSClusterResourcePrivateProperties[] = { { 0 } }; // // Function prototypes. // 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 ); RESID WINAPI RSClusterOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ); VOID WINAPI RSClusterClose( IN RESID ResourceId ); DWORD WINAPI RSClusterOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ); DWORD WINAPI RSClusterOnlineThread( PCLUS_WORKER WorkerPtr, IN PRSCLUSTER_RESOURCE ResourceEntry ); DWORD WINAPI RSClusterOffline( IN RESID ResourceId ); DWORD RSClusterOfflineThread( PCLUS_WORKER pWorker, IN PRSCLUSTER_RESOURCE ResourceEntry ); VOID WINAPI RSClusterTerminate( IN RESID ResourceId ); DWORD RSClusterDoTerminate( IN PRSCLUSTER_RESOURCE ResourceEntry ); BOOL WINAPI RSClusterLooksAlive( IN RESID ResourceId ); BOOL WINAPI RSClusterIsAlive( IN RESID ResourceId ); BOOL RSClusterCheckIsAlive( IN PRSCLUSTER_RESOURCE ResourceEntry ); DWORD WINAPI RSClusterResourceControl( IN RESID ResourceId, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); BOOLEAN WINAPI DllMain( IN HINSTANCE DllHandle, IN DWORD Reason, IN LPVOID Reserved ) /*++ Routine Description: Main DLL entry point. Arguments: DllHandle - DLL instance handle. Reason - Reason for being called. Reserved - Reserved argument. Return Value: TRUE - Success. FALSE - Failure. --*/ { switch( Reason ) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls( DllHandle ); break; case DLL_PROCESS_DETACH: break; } return(TRUE); } // DllMain 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 the resource DLL. This routine verifies that at least one currently supported version of the resource DLL is between MinVersionSupported and MaxVersionSupported. If not, then the resource DLL should return ERROR_REVISION_MISMATCH. If more than one version of the resource DLL interface is supported by the resource DLL, then the highest version (up to MaxVersionSupported) should be returned as the resource DLL's interface. If the returned version is not within range, then startup fails. The ResourceType is passed in so that if the resource DLL supports more than one ResourceType, it can pass back the correct function table associated with the ResourceType. Arguments: ResourceType - The type of resource requesting a function table. MinVersionSupported - The minimum resource DLL interface version supported by the cluster software. MaxVersionSupported - The maximum resource DLL interface version supported by the cluster software. SetResourceStatus - Pointer to a routine that the resource DLL should call to update the state of a resource after the Online or Offline routine returns a status of ERROR_IO_PENDING. LogEvent - Pointer to a routine that handles the reporting of events from the resource DLL. FunctionTable - Returns a pointer to the function table defined for the version of the resource DLL interface returned by the resource DLL. Return Value: ERROR_SUCCESS - The operation was successful. ERROR_MOD_NOT_FOUND - The resource type is unknown by this DLL. ERROR_REVISION_MISMATCH - The version of the cluster service doesn't match the versrion of the DLL. Win32 error code - The operation failed. --*/ { if ( (MinVersionSupported > CLRES_VERSION_V1_00) || (MaxVersionSupported < CLRES_VERSION_V1_00) ) { return(ERROR_REVISION_MISMATCH); } if ( lstrcmpiW( ResourceType, RSCLUSTER_RESNAME ) != 0 ) { return(ERROR_MOD_NOT_FOUND); } if ( !g_LogEvent ) { g_LogEvent = LogEvent; g_SetResourceStatus = SetResourceStatus; } *FunctionTable = &g_RSClusterFunctionTable; return(ERROR_SUCCESS); } // Startup RESID WINAPI RSClusterOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ) /*++ Routine Description: Open routine for RSCluster resources. Open the specified resource (create an instance of the resource). Allocate all structures necessary to bring the specified resource online. Arguments: ResourceName - Supplies the name of the resource to open. ResourceKey - Supplies handle to the resource's cluster configuration database key. ResourceHandle - A handle that is passed back to the resource monitor when the SetResourceStatus or LogEvent method is called. See the description of the SetResourceStatus and LogEvent methods on the RSClusterStatup routine. This handle should never be closed or used for any purpose other than passing it as an argument back to the Resource Monitor in the SetResourceStatus or LogEvent callback. Return Value: RESID of created resource. NULL on failure. --*/ { DWORD status; DWORD disposition; RESID resid = 0; HKEY parametersKey = NULL; // how many do I need actually?? HKEY resKey = NULL; PRSCLUSTER_RESOURCE resourceEntry = NULL; HCLUSTER hCluster; // Semaphore security PSID pSystemSID = NULL; PSID pAdminSID = NULL; PACL pACL = NULL; PSECURITY_DESCRIPTOR pSD = NULL; #define CLUSTER_SEMAPHORE_NUM_ACE 2 EXPLICIT_ACCESS ea[CLUSTER_SEMAPHORE_NUM_ACE]; SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY; SECURITY_ATTRIBUTES sa; // // Open the Parameters registry key for this resource. // status = ClusterRegCreateKey( ResourceKey, L"Parameters", 0, KEY_ALL_ACCESS, NULL, ¶metersKey, &disposition ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to open Parameters key. Error: %1!u!.\n", status ); goto exit; } status = ClusterRegOpenKey( ResourceKey, L"", KEY_READ, &resKey); if (status != ERROR_SUCCESS) { (g_LogEvent)(ResourceHandle, LOG_ERROR, L"Unable to open resource key. Error: %1!u!.\n", status ); goto error_exit; } if ( g_ScHandle == NULL ) { g_ScHandle = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS); if ( g_ScHandle == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to open service control manager, error %1!u!.\n", status); goto error_exit; } } resourceEntry = LocalAlloc( LMEM_FIXED, sizeof(RSCLUSTER_RESOURCE) ); if ( resourceEntry == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to allocate resource entry structure. Error: %1!u!.\n", status ); goto exit; } ZeroMemory( resourceEntry, sizeof(RSCLUSTER_RESOURCE) ); resourceEntry->ResId = (RESID)resourceEntry; // for validation resourceEntry->ResourceHandle = ResourceHandle; resourceEntry->ParametersKey = parametersKey; resourceEntry->State = ClusterResourceOffline; resourceEntry->ResourceKey = resKey; resourceEntry->hSem= NULL; resourceEntry->ResourceName = LocalAlloc( LMEM_FIXED, (lstrlenW( ResourceName ) + 1) * sizeof(WCHAR) ); if ( resourceEntry->ResourceName == NULL ) { goto exit; } lstrcpyW( resourceEntry->ResourceName, ResourceName ); // // Create SD that allows access only to local-system and admin // memset(ea, 0, sizeof(EXPLICIT_ACCESS) * CLUSTER_SEMAPHORE_NUM_ACE); if (! AllocateAndInitializeSid( &SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID) ) { status = GetLastError(); goto error_exit; } ea[0].grfAccessPermissions = FILE_ALL_ACCESS; ea[0].grfAccessMode = SET_ACCESS; ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; ea[0].Trustee.pMultipleTrustee = NULL; ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; ea[0].Trustee.ptstrName = (LPTSTR) pAdminSID; if (! AllocateAndInitializeSid( &SIDAuthNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSystemSID) ) { status = GetLastError(); goto error_exit; } ea[1].grfAccessPermissions = FILE_ALL_ACCESS; ea[1].grfAccessMode = SET_ACCESS; ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; ea[1].Trustee.pMultipleTrustee = NULL; ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[1].Trustee.TrusteeType = TRUSTEE_IS_USER; ea[1].Trustee.ptstrName = (LPTSTR) pSystemSID; if ((status = SetEntriesInAcl(CLUSTER_SEMAPHORE_NUM_ACE, ea, NULL, &pACL)) != ERROR_SUCCESS) { goto error_exit; } pSD = (PSECURITY_DESCRIPTOR)LocalAlloc( LMEM_FIXED, SECURITY_DESCRIPTOR_MIN_LENGTH); if (! pSD) { status = ERROR_OUTOFMEMORY; goto error_exit; } if (! InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { status = GetLastError(); goto error_exit; } if (! SetSecurityDescriptorDacl( pSD, TRUE, // fDaclPresent flag pACL, FALSE // not a default DACL ) ) { status = GetLastError(); goto error_exit; } sa.nLength = sizeof (SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = pSD; sa.bInheritHandle = FALSE; resourceEntry->hSem=CreateSemaphore(&sa,0,1,L"RemoteStorageServer"); status=GetLastError(); if(resourceEntry->hSem) { if(status==ERROR_ALREADY_EXISTS) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Remote Storage is controlled by another resource. Error: %2!u!.\n",status ); CloseHandle(resourceEntry->hSem); resourceEntry->hSem = NULL; goto error_exit; } } else { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to create semaphore for Remote Storage Server Service . Error: %2!u!.\n",status ); goto error_exit; } status = ERROR_SUCCESS; hCluster = OpenCluster(NULL); if (hCluster == NULL) { status = GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to open cluster, error %1!u!.", status ); goto error_exit; } resourceEntry->hResource = OpenClusterResource(hCluster, ResourceName); status = GetLastError(); CloseCluster(hCluster); if ( resourceEntry->hResource == NULL ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to open resource, error %1!u!.", status ); goto error_exit; } resid = (RESID)resourceEntry; error_exit: exit: if ( resid == 0 ) { if ( parametersKey != NULL ) { ClusterRegCloseKey( parametersKey ); } if ( resourceEntry != NULL ) { LocalFree( resourceEntry->ResourceName ); LocalFree( resourceEntry ); } } if (pAdminSID) { FreeSid(pAdminSID); } if (pSystemSID) { FreeSid(pSystemSID); } if (pACL) { LocalFree(pACL); } if (pSD) { LocalFree(pSD); } if ( status != ERROR_SUCCESS ) { SetLastError( status ); } return(resid); } // RSClusterOpen VOID WINAPI RSClusterClose( IN RESID ResourceId ) /*++ Routine Description: Close routine for RSCluster resources. Close the specified resource and deallocate all structures, etc., allocated in the Open call. If the resource is not in the offline state, then the resource should be taken offline (by calling Terminate) before the close operation is performed. Arguments: ResourceId - Supplies the RESID of the resource to close. Return Value: None. --*/ { PRSCLUSTER_RESOURCE resourceEntry; resourceEntry = (PRSCLUSTER_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "RSCluster: Close request for a nonexistent resource id %p\n", ResourceId ); return; } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Close resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return; } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Close request.\n" ); #endif if ( resourceEntry->ParametersKey ) { ClusterRegCloseKey( resourceEntry->ParametersKey ); } RSClusterTerminate( ResourceId ); ClusterRegCloseKey( resourceEntry->ParametersKey ); ClusterRegCloseKey( resourceEntry->ResourceKey ); CloseClusterResource( resourceEntry->hResource ); CloseHandle(resourceEntry->hSem); LocalFree( resourceEntry->ResourceName ); LocalFree( resourceEntry ); } // RSClusterClose DWORD WINAPI RSClusterOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ) /*++ Routine Description: Online routine for RSCluster resources. Bring the specified resource online (available for use). The resource DLL should attempt to arbitrate for the resource if it is present on a shared medium, like a shared SCSI bus. Arguments: ResourceId - Supplies the resource id for the resource to be brought online (available for use). EventHandle - Returns a signalable handle that is signaled when the resource DLL detects a failure on the resource. This argument is NULL on input, and the resource DLL returns NULL if asynchronous notification of failures is not supported, otherwise this must be the address of a handle that is signaled on resource failures. Return Value: ERROR_SUCCESS - The operation was successful, and the resource is now online. ERROR_RESOURCE_NOT_FOUND - RESID is not valid. ERROR_RESOURCE_NOT_AVAILABLE - If the resource was arbitrated with some other systems and one of the other systems won the arbitration. ERROR_IO_PENDING - The request is pending, a thread has been activated to process the online request. The thread that is processing the online request will periodically report status by calling the SetResourceStatus callback method, until the resource is placed into the ClusterResourceOnline state (or the resource monitor decides to timeout the online request and Terminate the resource. This pending timeout value is settable and has a default value of 3 minutes.). Win32 error code - The operation failed. --*/ { DWORD status; PRSCLUSTER_RESOURCE resourceEntry; resourceEntry = (PRSCLUSTER_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "RSCluster: Online request for a nonexistent resource id %p.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Online service sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Online request.\n" ); #endif resourceEntry->State = ClusterResourceOffline; ClusWorkerTerminate( &resourceEntry->OnlineThread ); status = ClusWorkerCreate( &resourceEntry->OnlineThread, (PWORKER_START_ROUTINE)RSClusterOnlineThread, resourceEntry ); if ( status != ERROR_SUCCESS ) { resourceEntry->State = ClusterResourceFailed; (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Online: Unable to start thread, status %1!u!.\n", status ); } else { status = ERROR_IO_PENDING; } return(status); } // RSClusterOnline DWORD WINAPI RSClusterOnlineThread( PCLUS_WORKER WorkerPtr, IN PRSCLUSTER_RESOURCE ResourceEntry ) /*++ Routine Description: Worker function which brings a resource from the resource table online. This function is executed in a separate thread. Arguments: WorkerPtr - Supplies the worker structure ResourceEntry - A pointer to the RSCLUSTER_RESOURCE block for this resource. Returns: ERROR_SUCCESS - The operation completed successfully. Win32 error code - The operation failed. --*/ { SERVICE_STATUS_PROCESS ServiceStatus; LPWSTR * serviceArgArray = NULL; DWORD serviceArgCount = 0; DWORD valueSize; LPVOID Environment = NULL; WCHAR * p; LPSERVICE_FAILURE_ACTIONS pSvcFailureActions = NULL; DWORD cbBytesNeeded, i; LPQUERY_SERVICE_CONFIG lpquerysvcconfig=NULL; HANDLE processToken = NULL; RESOURCE_STATUS resourceStatus; DWORD status = ERROR_SUCCESS; ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; // resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; ResourceEntry->dwServicePid = 0; ResourceEntry->ServiceHandle = OpenService( g_ScHandle, RSCLUSTER_SVCNAME, SERVICE_ALL_ACCESS ); if ( ResourceEntry->ServiceHandle == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to open service, error = %1!u!.\n", status ); goto error_exit; } valueSize = sizeof(QUERY_SERVICE_CONFIG); AllocSvcConfig: lpquerysvcconfig=(LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, valueSize); if(lpquerysvcconfig==NULL){ status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Remote Storage Server: Failed to allocate memory for query_service_config, error = %1!u!.\n", status ); goto error_exit; } if (!QueryServiceConfig(ResourceEntry->ServiceHandle, lpquerysvcconfig, valueSize, &cbBytesNeeded)) { status=GetLastError(); if (status != ERROR_INSUFFICIENT_BUFFER){ (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Remote Storage Server: Failed to query service configuration, error= %1!u!.\n", status ); goto error_exit; } status=ERROR_SUCCESS; LocalFree(lpquerysvcconfig); lpquerysvcconfig=NULL; valueSize = cbBytesNeeded; goto AllocSvcConfig; } if (lpquerysvcconfig->dwStartType == SERVICE_DISABLED) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Remote Storage Server: the service is DISABLED\n"); status=ERROR_SERVICE_DISABLED; goto error_exit; } ChangeServiceConfig( ResourceEntry->ServiceHandle, SERVICE_NO_CHANGE, SERVICE_DEMAND_START, // Manual start SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); if (!(QueryServiceConfig2(ResourceEntry->ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)&valueSize, sizeof(DWORD), &cbBytesNeeded))) { status = GetLastError(); if (status != ERROR_INSUFFICIENT_BUFFER) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Remote Storage Server: Failed to query service configuration for size, error= %1!u!.\n", status ); goto error_exit; } else status = ERROR_SUCCESS; } pSvcFailureActions = (LPSERVICE_FAILURE_ACTIONS)LocalAlloc(LMEM_FIXED, cbBytesNeeded); if ( pSvcFailureActions == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Remote Storage Server: Failed to allocate memory, error = %1!u!.\n", status ); goto error_exit; } if (!(QueryServiceConfig2(ResourceEntry->ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)pSvcFailureActions, cbBytesNeeded, &cbBytesNeeded))) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Remote Storage Server: Failed to query service configuration, error = %1!u!.\n", status ); goto error_exit; } for (i=0; icActions;i++) { if (pSvcFailureActions->lpsaActions[i].Type == SC_ACTION_RESTART) pSvcFailureActions->lpsaActions[i].Type = SC_ACTION_NONE; } ChangeServiceConfig2(ResourceEntry->ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS, pSvcFailureActions); if ( 0 ) { Environment = ResUtilGetEnvironmentWithNetName( ResourceEntry->hResource ); } else { BOOL success; OpenProcessToken( GetCurrentProcess(), MAXIMUM_ALLOWED, &processToken ); success = CreateEnvironmentBlock(&Environment, processToken, FALSE ); if ( processToken != NULL ) { CloseHandle( processToken ); } if ( !success ) { status = GetLastError(); goto error_exit; } } if (Environment != NULL) { HKEY ServicesKey; HKEY hKey; p = (WCHAR *)Environment; while (*p) { while (*p++) { } } valueSize = (DWORD)((PUCHAR)p - (PUCHAR)Environment) + sizeof(WCHAR); status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, SERVICES_ROOT, 0, KEY_READ, &ServicesKey ); if (status != ERROR_SUCCESS) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to open services key, error = %1!u!.\n", status ); goto error_exit; } status = RegOpenKeyExW( ServicesKey, RSCLUSTER_SVCNAME, 0, KEY_READ | KEY_WRITE, &hKey ); RegCloseKey(ServicesKey); if (status != ERROR_SUCCESS) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to open service key, error = %1!u!.\n", status ); goto error_exit; } status = RegSetValueExW( hKey, L"Environment", 0, REG_MULTI_SZ, Environment, valueSize ); RegCloseKey(hKey); if (status != ERROR_SUCCESS) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to set service environment value, error = %1!u!.\n", status ); goto error_exit; } } if ( !StartServiceW( ResourceEntry->ServiceHandle, serviceArgCount, serviceArgArray ) ) { status = GetLastError(); if (status != ERROR_SERVICE_ALREADY_RUNNING) { (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to start service. Error: %1!u!.\n", status ); status = ERROR_SERVICE_NEVER_STARTED; goto error_exit; } // add code to stop the service that is running // and start the service again.. } while (!ClusWorkerCheckTerminate(WorkerPtr)) { if (!QueryServiceStatusEx( ResourceEntry->ServiceHandle, SC_STATUS_PROCESS_INFO, (LPBYTE)&ServiceStatus, sizeof(SERVICE_STATUS_PROCESS), &cbBytesNeeded ) ) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Query Service Status failed %1!u!.\n", status ); goto error_exit; } if ( ServiceStatus.dwCurrentState != SERVICE_START_PENDING ) { break; } Sleep(250); } if (ClusWorkerCheckTerminate(WorkerPtr)) { goto error_exit; } if ( ServiceStatus.dwCurrentState != SERVICE_RUNNING ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to start service. Error: %1!u!.\n", ERROR_SERVICE_NEVER_STARTED ); status = ERROR_SERVICE_NEVER_STARTED; goto error_exit; } resourceStatus.ResourceState = ClusterResourceOnline; if (!(ServiceStatus.dwServiceFlags & SERVICE_RUNS_IN_SYSTEM_PROCESS)) { ResourceEntry->dwServicePid = ServiceStatus.dwProcessId; } (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Service is now on line.\n" ); error_exit: (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); if ( resourceStatus.ResourceState == ClusterResourceOnline ) { ResourceEntry->Online = TRUE; } else { ResourceEntry->Online = FALSE; } // more setting here to ensure no problems ResourceEntry->State = resourceStatus.ResourceState; //cleanup if (pSvcFailureActions) LocalFree(pSvcFailureActions); if (lpquerysvcconfig) LocalFree(lpquerysvcconfig); LocalFree( serviceArgArray ); if (Environment != NULL) { DestroyEnvironmentBlock(Environment); } return(status); } // RSClusterOnlineThread DWORD WINAPI RSClusterOffline( IN RESID ResourceId ) /*++ Routine Description: Offline routine for RSCluster resources. Take the specified resource offline gracefully (unavailable for use). Wait for any cleanup operations to complete before returning. Arguments: ResourceId - Supplies the resource id for the resource to be shutdown gracefully. Return Value: ERROR_SUCCESS - The request completed successfully and the resource is offline. ERROR_RESOURCE_NOT_FOUND - RESID is not valid. ERROR_IO_PENDING - The request is still pending, a thread has been activated to process the offline request. The thread that is processing the offline will periodically report status by calling the SetResourceStatus callback method, until the resource is placed into the ClusterResourceOffline state (or the resource monitor decides to timeout the offline request and Terminate the resource). Win32 error code - Will cause the resource monitor to log an event and call the Terminate routine. --*/ { // extra code here DWORD status; PRSCLUSTER_RESOURCE resourceEntry; resourceEntry = (PRSCLUSTER_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "RSCluster: Offline request for a nonexistent resource id %p\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Offline resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline request.\n" ); #endif ClusWorkerTerminate( &resourceEntry->PendingThread ); status = ClusWorkerCreate( &resourceEntry->PendingThread, (PWORKER_START_ROUTINE)RSClusterOfflineThread, resourceEntry ); if ( status == ERROR_SUCCESS ) { status = ERROR_IO_PENDING; } return(status); } // RSClusterOffline DWORD RSClusterOfflineThread( PCLUS_WORKER pWorker, IN PRSCLUSTER_RESOURCE ResourceEntry ) /*++ Routine Description: Brings remote storage service resource offline Arguments: Worker - Supplies the worker structure Context - A pointer to the DiskInfo block for this resource. Returns: ERROR_SUCCESS if successful. Win32 error code on failure. --*/ { RESOURCE_STATUS resourceStatus; DWORD retryTick = 300; // 300 msec at a time DWORD status = ERROR_SUCCESS; BOOL didStop = FALSE; SERVICE_STATUS ServiceStatus; ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; //resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; if ( ResourceEntry->ServiceHandle == NULL ) { resourceStatus.ResourceState = ClusterResourceOffline; goto FnExit; } while (!ClusWorkerCheckTerminate(pWorker)) { status = (ControlService( ResourceEntry->ServiceHandle, (didStop ? SERVICE_CONTROL_INTERROGATE : SERVICE_CONTROL_STOP), &ServiceStatus ) ? NO_ERROR : GetLastError()); if (status == NO_ERROR) { didStop = TRUE; if (ServiceStatus.dwCurrentState == SERVICE_STOPPED) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Service stopped.\n" ); //set the status ResourceEntry->Online = FALSE; resourceStatus.ResourceState = ClusterResourceOffline; CloseServiceHandle( ResourceEntry->ServiceHandle ); ResourceEntry->ServiceHandle = NULL; ResourceEntry->dwServicePid = 0; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Service is now offline.\n" ); break; } } if (status == ERROR_EXCEPTION_IN_SERVICE || status == ERROR_PROCESS_ABORTED || status == ERROR_SERVICE_NOT_ACTIVE) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Service died or not active any more; status = %1!u!.\n", status); ResourceEntry->Online = FALSE; resourceStatus.ResourceState = ClusterResourceOffline; CloseServiceHandle( ResourceEntry->ServiceHandle ); ResourceEntry->ServiceHandle = NULL; ResourceEntry->dwServicePid = 0; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Service is now offline.\n" ); break; } (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline: retrying...\n" ); Sleep(retryTick); } FnExit: (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); return(status); } // RSClusterOfflineThread VOID WINAPI RSClusterTerminate( IN RESID ResourceId ) /*++ Routine Description: Terminate routine for RSCluster resources. Take the specified resource offline immediately (the resource is unavailable for use). Arguments: ResourceId - Supplies the resource id for the resource to be brought offline. Return Value: None. --*/ { // extra code here PRSCLUSTER_RESOURCE resourceEntry; resourceEntry = (PRSCLUSTER_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "RSCluster: Terminate request for a nonexistent resource id %p\n", ResourceId ); return; } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Terminate resource sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return; } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Terminate request.\n" ); #endif RSClusterDoTerminate( resourceEntry ); resourceEntry->State = ClusterResourceOffline; } // RSClusterTerminate DWORD RSClusterDoTerminate( IN PRSCLUSTER_RESOURCE resourceEntry ) /*++ Routine Description: Do the actual Terminate work for RSCluster resources. Arguments: ResourceEntry - Supplies resource entry for resource to be terminated Return Value: ERROR_SUCCESS - The request completed successfully and the resource is offline. Win32 error code - Will cause the resource monitor to log an event and call the Terminate routine. --*/ { DWORD status = ERROR_SUCCESS; SERVICE_STATUS ServiceStatus; ClusWorkerTerminate( &resourceEntry->OnlineThread ); ClusWorkerTerminate( &resourceEntry->PendingThread ); if ( resourceEntry->ServiceHandle != NULL ) { DWORD dwRetryCount= 100; BOOL didStop = FALSE; DWORD dwRetryTick = 300; // 300 msec at a time DWORD dwStatus; while (dwRetryCount--) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"RSClusterTerminate : calling SCM\n" ); dwStatus = (ControlService( resourceEntry->ServiceHandle, (didStop ? SERVICE_CONTROL_INTERROGATE : SERVICE_CONTROL_STOP), &ServiceStatus ) ? NO_ERROR : GetLastError()); if (dwStatus == NO_ERROR) { didStop = TRUE; if (ServiceStatus.dwCurrentState == SERVICE_STOPPED) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Service stopped.\n" ); //set the status resourceEntry->Online = FALSE; resourceEntry->dwServicePid = 0; break; } } if (dwStatus == ERROR_EXCEPTION_IN_SERVICE || dwStatus == ERROR_PROCESS_ABORTED || dwStatus == ERROR_SERVICE_NOT_ACTIVE) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Service died; status = %1!u!.\n", dwStatus); //set the status resourceEntry->Online = FALSE; resourceEntry->dwServicePid = 0; break; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"RSClusterTerminate: retrying...\n" ); Sleep(dwRetryTick); } if (resourceEntry->dwServicePid) { HANDLE hSvcProcess = NULL; hSvcProcess = OpenProcess(PROCESS_TERMINATE, FALSE, resourceEntry->dwServicePid); if (hSvcProcess) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"RSClusterTerminate: terminating processid=%1!u!\n", resourceEntry->dwServicePid); TerminateProcess(hSvcProcess, 0); CloseHandle(hSvcProcess); } } CloseServiceHandle( resourceEntry->ServiceHandle ); resourceEntry->ServiceHandle = NULL; resourceEntry->dwServicePid = 0; } resourceEntry->Online = FALSE; // // resourceEntry->State = ClusterResourceOffline; /* if ( status == ERROR_SUCCESS ) { ResourceEntry->State = ClusterResourceOffline; } return(status); */ return(ERROR_SUCCESS); } // RSClusterDoTerminate BOOL WINAPI RSClusterLooksAlive( IN RESID ResourceId ) /*++ Routine Description: LooksAlive routine for RSCluster resources. Perform a quick check to determine if the specified resource is probably online (available for use). This call should not block for more than 300 ms, preferably less than 50 ms. Arguments: ResourceId - Supplies the resource id for the resource to polled. Return Value: TRUE - The specified resource is probably online and available for use. FALSE - The specified resource is not functioning normally. --*/ { PRSCLUSTER_RESOURCE resourceEntry; resourceEntry = (PRSCLUSTER_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT("RSCluster: LooksAlive request for a nonexistent resource id %p\n", ResourceId ); return(FALSE); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"LooksAlive sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(FALSE); } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"LooksAlive request.\n" ); #endif // return(RSClusterCheckIsAlive( resourceEntry )); } // RSClusterLooksAlive BOOL WINAPI RSClusterIsAlive( IN RESID ResourceId ) /*++ Routine Description: IsAlive routine for RSCluster resources. Perform a thorough check to determine if the specified resource is online (available for use). This call should not block for more than 400 ms, preferably less than 100 ms. Arguments: ResourceId - Supplies the resource id for the resource to polled. Return Value: TRUE - The specified resource is online and functioning normally. FALSE - The specified resource is not functioning normally. --*/ { PRSCLUSTER_RESOURCE resourceEntry; resourceEntry = (PRSCLUSTER_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT("RSCluster: IsAlive request for a nonexistent resource id %p\n", ResourceId ); return(FALSE); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"IsAlive sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(FALSE); } #ifdef LOG_VERBOSE (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"IsAlive request.\n" ); #endif return(RSClusterCheckIsAlive( resourceEntry )); } // RSClusterIsAlive BOOL RSClusterCheckIsAlive( IN PRSCLUSTER_RESOURCE resourceEntry ) /*++ Routine Description: Check to see if the resource is alive for RSCluster resources. Arguments: ResourceEntry - Supplies the resource entry for the resource to polled. Return Value: TRUE - The specified resource is online and functioning normally. FALSE - The specified resource is not functioning normally. --*/ { SERVICE_STATUS ServiceStatus; DWORD status = TRUE; if ( !QueryServiceStatus( resourceEntry->ServiceHandle, &ServiceStatus ) ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Query Service Status failed %1!u!.\n", GetLastError() ); return(FALSE); } if ( (ServiceStatus.dwCurrentState != SERVICE_RUNNING) && (ServiceStatus.dwCurrentState != SERVICE_START_PENDING) ) { status = FALSE; } if (!status) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Failed the IsAlive test. Current State is %1!u!.\n", ServiceStatus.dwCurrentState ); } return(status); } // RSClusterCheckIsAlive DWORD WINAPI RSClusterResourceControl( 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 RSCluster 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_RESOURCE_NOT_FOUND - RESID is not valid. 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; PRSCLUSTER_RESOURCE resourceEntry; resourceEntry = (PRSCLUSTER_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT("RSCluster: ResourceControl request for a nonexistent resource id %p\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } if ( resourceEntry->ResId != ResourceId ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"ResourceControl sanity check failed! ResourceId = %1!u!.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } switch ( ControlCode ) { case CLUSCTL_RESOURCE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // RSClusterResourceControl //*********************************************************** // // Define Function Table // //*********************************************************** CLRES_V1_FUNCTION_TABLE( g_RSClusterFunctionTable, // Name CLRES_VERSION_V1_00, // Version RSCluster, // Prefix NULL, // Arbitrate NULL, // Release RSClusterResourceControl, // ResControl NULL); // ResTypeControl