/*++ Copyright (c) 1992-2000 Microsoft Corporation Module Name: gensvc.c Abstract: Resource DLL to control and monitor NT services. Author: Robs 3/28/96, based on RodGa's generic resource dll Revision History: --*/ #define UNICODE 1 #include "clusres.h" #include "clusrtl.h" #include "userenv.h" // Uncomment the next line to test the Terminate() function when // a shutdown is in progress. //#define TEST_TERMINATE_ON_SHUTDOWN #define LOG_CURRENT_MODULE LOG_MODULE_GENSVC #define SERVICES_ROOT L"SYSTEM\\CurrentControlSet\\Services\\" #define DBG_PRINT printf #define PARAM_NAME__SERVICENAME CLUSREG_NAME_GENSVC_SERVICE_NAME #define PARAM_NAME__STARTUPPARAMETERS CLUSREG_NAME_GENSVC_STARTUP_PARAMS #define PARAM_NAME__USENETWORKNAME CLUSREG_NAME_GENSVC_USE_NETWORK_NAME #define PARAM_MIN__USENETWORKNAME 0 #define PARAM_MAX__USENETWORKNAME 1 #define PARAM_DEFAULT__USENETWORKNAME 0 typedef struct _GENSVC_PARAMS { PWSTR ServiceName; PWSTR StartupParameters; DWORD UseNetworkName; } GENSVC_PARAMS, *PGENSVC_PARAMS; typedef struct _GENSVC_RESOURCE { GENSVC_PARAMS Params; HRESOURCE hResource; HANDLE ServiceHandle; RESOURCE_HANDLE ResourceHandle; HKEY ResourceKey; HKEY ParametersKey; CLUS_WORKER PendingThread; BOOL Online; DWORD dwServicePid; HANDLE hSem; } GENSVC_RESOURCE, *PGENSVC_RESOURCE; // // Global Data // // Handle to service controller, set by the first create resource call. SC_HANDLE g_ScHandle = NULL; // Log Event Routine #define g_LogEvent ClusResLogEvent #define g_SetResourceStatus ClusResSetResourceStatus // Forward reference to our RESAPI function table. extern CLRES_FUNCTION_TABLE GenSvcFunctionTable; // // Generic Service resource read-write private properties. // RESUTIL_PROPERTY_ITEM GenSvcResourcePrivateProperties[] = { { PARAM_NAME__SERVICENAME, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(GENSVC_PARAMS,ServiceName) }, { PARAM_NAME__STARTUPPARAMETERS, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(GENSVC_PARAMS,StartupParameters) }, { PARAM_NAME__USENETWORKNAME, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__USENETWORKNAME, PARAM_MIN__USENETWORKNAME, PARAM_MAX__USENETWORKNAME, 0, FIELD_OFFSET(GENSVC_PARAMS,UseNetworkName) }, { 0 } }; // // Forward routines // BOOL VerifyService( IN RESID ResourceId, IN BOOL IsAliveFlag ); void wparse_cmdline ( WCHAR *cmdstart, WCHAR **argv, WCHAR *args, int *numargs, int *numchars ); DWORD GenSvcGetPrivateResProperties( IN const PGENSVC_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); DWORD GenSvcValidatePrivateResProperties( IN const PGENSVC_RESOURCE ResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize, OUT PGENSVC_PARAMS Params ); DWORD GenSvcSetPrivateResProperties( IN OUT PGENSVC_RESOURCE ResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize ); DWORD GenSvcInvalidGenericServiceCheck( IN OUT PGENSVC_RESOURCE ResourceEntry, IN LPCWSTR ServiceName, IN HKEY ServiceKey ); DWORD GenSvcIsValidService( IN OUT PGENSVC_RESOURCE ResourceEntry, IN LPCWSTR ServiceName ); DWORD GenSvcOfflineThread( PCLUS_WORKER pWorker, IN PGENSVC_RESOURCE ResourceEntry ); BOOLEAN GenSvcInit( VOID ) { return(TRUE); } BOOLEAN WINAPI GenSvcDllEntryPoint( IN HINSTANCE DllHandle, IN DWORD Reason, IN LPVOID Reserved ) { switch( Reason ) { case DLL_PROCESS_ATTACH: if ( !GenSvcInit() ) { return(FALSE); } break; case DLL_PROCESS_DETACH: break; default: break; } return(TRUE); } // GenSvc DllEntryPoint DWORD GenSvcOnlineThread( PCLUS_WORKER pWorker, IN PGENSVC_RESOURCE ResourceEntry ) /*++ Routine Description: Brings a disk resource online. 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. --*/ { SERVICE_STATUS_PROCESS ServiceStatus; DWORD status = ERROR_SUCCESS; DWORD numchars; LPWSTR * serviceArgArray = NULL; DWORD serviceArgCount; SC_HANDLE serviceHandle; RESOURCE_STATUS resourceStatus; DWORD valueSize; LPVOID Environment = NULL; WCHAR * p; LPWSTR nameOfPropInError; LPSERVICE_FAILURE_ACTIONS pSvcFailureActions = NULL; DWORD cbBytesNeeded, i; LPQUERY_SERVICE_CONFIG lpquerysvcconfig=NULL; HANDLE processToken = NULL; DWORD dwRetryCount = 2400; // Try 10 min max. DWORD dwRetryTick = 250; // msec ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; resourceStatus.CheckPoint = 1; //set it to NULL, when it is brought online and if the //service is not running in the system or lsa process //then store the process id for forceful termination ResourceEntry->dwServicePid = 0; // // Read our parameters. // status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey, GenSvcResourcePrivateProperties, (LPBYTE) &ResourceEntry->Params, TRUE, // CheckForRequiredProperties &nameOfPropInError ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the '%1!ls!' property. Error: %2!u!.\n", (nameOfPropInError == NULL ? L"" : nameOfPropInError), status ); goto error_exit; } // // Parse the startup parameters // if ( ResourceEntry->Params.StartupParameters != NULL ) { // // Crack the startup parameters into its component arguments because // the service controller is not good enough to do this for us. // First, find out how many args we have. // wparse_cmdline( ResourceEntry->Params.StartupParameters, NULL, NULL, &serviceArgCount, &numchars ); // // Allocate space for vector and strings // serviceArgArray = LocalAlloc( LMEM_FIXED, serviceArgCount * sizeof(WCHAR *) + numchars * sizeof(WCHAR) ); if ( serviceArgArray == NULL ) { status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } wparse_cmdline( ResourceEntry->Params.StartupParameters, serviceArgArray, (WCHAR *)(((char *)serviceArgArray) + serviceArgCount * sizeof(WCHAR *)), &serviceArgCount, &numchars ); } else { serviceArgCount = 0; serviceArgArray = NULL; } // // Now open the requested service // ResourceEntry->ServiceHandle = OpenService( g_ScHandle, ResourceEntry->Params.ServiceName, SERVICE_ALL_ACCESS ); if ( ResourceEntry->ServiceHandle == NULL ) { status = GetLastError(); ClusResLogSystemEventByKeyData(ResourceEntry->ResourceKey, LOG_CRITICAL, RES_GENSVC_OPEN_FAILED, sizeof(status), &status); (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: // Query the service to make sure it is not disabled lpquerysvcconfig=(LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, valueSize); if(lpquerysvcconfig==NULL){ status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[Gensvc] 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"svc: 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"svc:The service is DISABLED\n"); status=ERROR_SERVICE_DISABLED; goto error_exit; } // // Make sure service is set to manual start. // ChangeServiceConfig( ResourceEntry->ServiceHandle, SERVICE_NO_CHANGE, SERVICE_DEMAND_START, // Manual start SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); //if any of the service action is set to service restart, set it to // none 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"svc: 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"[Gensvc] 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"[Gensvc] 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 ( ResourceEntry->Params.UseNetworkName ) { // // Create the new environment with the simulated net name. // Environment = ResUtilGetEnvironmentWithNetName( ResourceEntry->hResource ); } else { BOOL success; // // get the current process token. If it fails, we revert to using just the // system environment area // OpenProcessToken( GetCurrentProcess(), MAXIMUM_ALLOWED, &processToken ); // // Clone the current environment, picking up any changes that might have // been made after resmon started // success = CreateEnvironmentBlock(&Environment, processToken, FALSE ); if ( processToken != NULL ) { CloseHandle( processToken ); } if ( !success ) { status = GetLastError(); goto error_exit; } } if (Environment != NULL) { HKEY ServicesKey; HKEY hKey; // // Compute the size of the environment. We are looking for // the double NULL terminator that ends the environment block. // p = (WCHAR *)Environment; while (*p) { while (*p++) { } } valueSize = (DWORD)((PUCHAR)p - (PUCHAR)Environment) + sizeof(WCHAR); // // Set the environment value in the service's registry key. // status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, LOCAL_SERVICES, 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, ResourceEntry->Params.ServiceName, 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) { ClusResLogSystemEventByKeyData(ResourceEntry->ResourceKey, LOG_CRITICAL, RES_GENSVC_START_FAILED, sizeof(status), &status); (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to start service. Error: %1!u!.\n", status ); goto error_exit; } else { status = ERROR_SUCCESS; } } //wait for the service to comeonline unless we are asked to terminate while (!ClusWorkerCheckTerminate(pWorker) && dwRetryCount--) { // // Tell the Resource Monitor that we are still working. // resourceStatus.ResourceState = ClusterResourceOnlinePending; resourceStatus.CheckPoint++; (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; 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(dwRetryTick); } // // If we terminated the loop above before setting ServiceStatus, // then return now. // if (ClusWorkerCheckTerminate(pWorker) || (dwRetryCount == (DWORD)-1)) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"GensvcOnlineThread: Asked to terminate or retry period expired...\n"); goto error_exit; } if ( ServiceStatus.dwCurrentState != SERVICE_RUNNING ) { if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) { status = ServiceStatus.dwServiceSpecificExitCode; } else { status = ServiceStatus.dwWin32ExitCode; } ClusResLogSystemEventByKeyData(ResourceEntry->ResourceKey, LOG_CRITICAL, RES_GENSVC_FAILED_AFTER_START, sizeof(status), &status); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Service failed during initialization. Error: %1!u!.\n", status ); 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; } //cleanup if (pSvcFailureActions) LocalFree(pSvcFailureActions); if (lpquerysvcconfig) LocalFree(lpquerysvcconfig); LocalFree( serviceArgArray ); if (Environment != NULL) { RtlDestroyEnvironment(Environment); } return(status); } // GenSvcOnlineThread RESID WINAPI GenSvcOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ) /*++ Routine Description: Open routine for generic service resource. This routine gets a handle to the service controller, if we don't already have one, and then gets a handle to the specified service. The service handle is saved in the GENSVC_RESOURCE structure. Arguments: ResourceName - supplies the resource name ResourceKey - supplies a handle to the 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 --*/ { RESID svcResid = 0; DWORD status; HKEY parametersKey = NULL; HKEY resKey = NULL; PGENSVC_RESOURCE resourceEntry = NULL; DWORD paramNameSize = 0; DWORD paramNameMaxSize = 0; HCLUSTER hCluster; LPWSTR nameOfPropInError; LPWSTR lpwTemp=NULL; // // Open registry parameters key for this resource. // status = ClusterRegOpenKey( ResourceKey, CLUSREG_KEYNAME_PARAMETERS, KEY_READ, ¶metersKey ); if ( status != NO_ERROR ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to open parameters key. Error: %1!u!.\n", status); goto error_exit; } // // Get a handle to our resource key so that we can get our name later // if we need to log an event. // 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; } // // First get a handle to the service controller. // 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(GENSVC_RESOURCE) ); if ( resourceEntry == NULL ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to allocate a service info structure.\n"); status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } ZeroMemory( resourceEntry, sizeof(GENSVC_RESOURCE) ); resourceEntry->ResourceHandle = ResourceHandle; resourceEntry->ResourceKey = resKey; resourceEntry->ParametersKey = parametersKey; resourceEntry->hSem= NULL; status = ResUtilGetPropertiesToParameterBlock( resourceEntry->ParametersKey, GenSvcResourcePrivateProperties, (LPBYTE) &resourceEntry->Params, TRUE, // CheckForRequiredProperties &nameOfPropInError ); if ( status == ERROR_SUCCESS ) { // Create the Semaphore - this will be executed only if this is not a new // GenericService Type resource being created for the first time lpwTemp = (LPWSTR)LocalAlloc(LMEM_FIXED, (lstrlenW(resourceEntry->Params.ServiceName)+ lstrlenW(L"GenSvc$")+1)*sizeof(WCHAR)); if (lpwTemp==NULL) { status=GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Service '%1!ls!': Not enough memory for storing semaphore name. Error: %2!u!.\n", resourceEntry->Params.ServiceName, status ); goto error_exit; } lstrcpyW(lpwTemp,L"GenSvc$"); lstrcat(lpwTemp,resourceEntry->Params.ServiceName); resourceEntry->hSem=CreateSemaphore(NULL,0,1,lpwTemp); status=GetLastError(); if(resourceEntry->hSem) { // Check if there is another resource controlling the same service if(status==ERROR_OBJECT_ALREADY_EXISTS) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Service '%1!ls!' is controlled by another resource. Error: %2!u!.\n", resourceEntry->Params.ServiceName, status ); CloseHandle(resourceEntry->hSem); resourceEntry->hSem = NULL; goto error_exit; } } else { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to create semaphore for Service '%1!ls!' . Error: %2!u!.\n", resourceEntry->Params.ServiceName, status ); goto error_exit; } } else { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to read parameters from registry for Service '%1!ls!' . Error: %2!u!, property in error is '%3!ls!' .\n", resourceEntry->Params.ServiceName, status, nameOfPropInError); } status = ERROR_SUCCESS; hCluster = OpenCluster(NULL); if (hCluster == NULL) { status = GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to open cluster, error %1!u!.\n", 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!.\n", status ); goto error_exit; } svcResid = (RESID)resourceEntry; return(svcResid); error_exit: if ( parametersKey != NULL ) { ClusterRegCloseKey( parametersKey ); } if ( resKey != NULL) { ClusterRegCloseKey( resKey ); } if ( resourceEntry != NULL) { LocalFree( resourceEntry ); } if (lpwTemp) { LocalFree(lpwTemp); } SetLastError( status ); return((RESID)NULL); } // GenSvcOpen DWORD WINAPI GenSvcOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ) /*++ Routine Description: Online routine for Generic Service resource. Arguments: ResourceId - 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. --*/ { DWORD status; PGENSVC_RESOURCE resourceEntry; resourceEntry = (PGENSVC_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "GenSvc: Online request for a nonexistent resource id 0x%p.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } ClusWorkerTerminate( &resourceEntry->PendingThread ); status = ClusWorkerCreate( &resourceEntry->PendingThread, GenSvcOnlineThread, resourceEntry ); if ( status == ERROR_SUCCESS ) { status = ERROR_IO_PENDING; } return(status); } // GenSvcOnline VOID WINAPI GenSvcTerminate( IN RESID ResourceId ) /*++ Routine Description: Terminate routine for Generic Application resource. Arguments: ResourceId - Supplies resource id to be terminated Return Value: None. --*/ { SERVICE_STATUS ServiceStatus; PGENSVC_RESOURCE resourceEntry; resourceEntry = (PGENSVC_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "GenSvc: Terminate request for a nonexistent resource id 0x%p.\n", ResourceId ); return; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Terminate request.\n" ); #ifdef TEST_TERMINATE_ON_SHUTDOWN { DWORD dwStatus; BOOLEAN fWasEnabled; // // Test terminate on shutdown code. // (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"GenSvcTerminate: TEST_TERMINATE_ON_SHUTDOWN - enabling shutdown privilege.\n" ); dwStatus = ClRtlEnableThreadPrivilege( SE_SHUTDOWN_PRIVILEGE, &fWasEnabled ); if ( dwStatus != ERROR_SUCCESS ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"GetSvcTerminate: TEST_TERMINATE_ON_SHUTDOWN - Unable to enable shutdown privilege. Error: %1!u!...\n", dwStatus ); } else { AbortSystemShutdown( NULL ); (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"GenSvcTerminate: TEST_TERMINATE_ON_SHUTDOWN - initiating system shutdown.\n" ); if ( ! InitiateSystemShutdown( NULL, // lpMachineName L"Testing Generic Service cluster resource DLL", 0, // dwTimeout TRUE, // bForceAppsClosed TRUE // bRebootAfterShutdown ) ) { dwStatus = GetLastError(); (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"GenSvcTerminate: TEST_TERMINATE_ON_SHUTDOWN - Unable to shutdown the system. Error: %1!u!.\n", dwStatus ); } else { Sleep( 30000 ); } ClRtlRestoreThreadPrivilege( SE_SHUTDOWN_PRIVILEGE, fWasEnabled ); } } #endif //if there is a pending thread close it //if the online pending thread is active, the service may be online //if the offline pending thread is active, the service might be offline ClusWorkerTerminate( &resourceEntry->PendingThread ); //if the service isnt gone by now, terminate it forcibly 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"GenSvcTerminate : calling SCM (didStop=%1!d!)\n", didStop ); 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; } } // // Chittur Subbaraman (chitturs) - 2/21/2000 // // Since SCM doesn't accept any control requests during // windows shutdown, don't send any more control // requests. Just exit from this loop and terminate // the process brute force. // if ( dwStatus == ERROR_SHUTDOWN_IN_PROGRESS ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"System shutdown in progress. Will try to terminate process brute force...\n" ); 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"GenSvcTerminate: retrying...\n" ); Sleep(dwRetryTick); } //declare this service is offline //if there is a pid for this, try and terminate that process //note that terminating a process doesnt terminate all //the child processes //also if it is running in a system process, we do nothing //about it if (resourceEntry->dwServicePid) { DWORD dwResourceState; (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"GenSvcTerminate: Attempting to terminate process with pid=%1!u!...\n", resourceEntry->dwServicePid ); ResUtilTerminateServiceProcessFromResDll( resourceEntry->dwServicePid, FALSE, // bOffline &dwResourceState, g_LogEvent, resourceEntry->ResourceHandle ); } CloseServiceHandle( resourceEntry->ServiceHandle ); resourceEntry->ServiceHandle = NULL; resourceEntry->dwServicePid = 0; } resourceEntry->Online = FALSE; return; } // GenSvcTerminate DWORD WINAPI GenSvcOffline( IN RESID ResourceId ) /*++ Routine Description: Offline routine for Generic Service resource. Arguments: ResourceId - Supplies the resource to be taken offline Return Value: ERROR_SUCCESS - always successful. --*/ { PGENSVC_RESOURCE resourceEntry; DWORD status; resourceEntry = (PGENSVC_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "GenSvc: Offline request for a nonexistent resource id 0x%p.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } ClusWorkerTerminate( &resourceEntry->PendingThread ); status = ClusWorkerCreate( &resourceEntry->PendingThread, GenSvcOfflineThread, resourceEntry ); if ( status == ERROR_SUCCESS ) { status = ERROR_IO_PENDING; } return(status); } // GenSvcOffline DWORD GenSvcOfflineThread( PCLUS_WORKER pWorker, IN PGENSVC_RESOURCE ResourceEntry ) /*++ Routine Description: Brings a generic 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; DWORD dwRetryCount = 2000; // Try 10 min max. ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; resourceStatus.CheckPoint = 1; //check if the service has gone offline or was never brought online if ( ResourceEntry->ServiceHandle == NULL ) { resourceStatus.ResourceState = ClusterResourceOffline; goto FnExit; } //try to stop the cluster service, wait for it to be terminated //as long as we are not asked to terminate while (!ClusWorkerCheckTerminate(pWorker) && dwRetryCount--) { // // Tell the Resource Monitor that we are still working. // resourceStatus.ResourceState = ClusterResourceOfflinePending; resourceStatus.CheckPoint++; (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; // // Request that the service be stopped, or if we already did that, // request the current status of the service. // 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); //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; } // // Chittur Subbaraman (chitturs) - 2/21/2000 // // Handle the case in which the SCM refuses to accept control // requests since windows is shutting down. // if ( status == ERROR_SHUTDOWN_IN_PROGRESS ) { DWORD dwResourceState; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"GenSvcOfflineThread: System shutting down. Attempt to terminate service process %1!u!...\n", ResourceEntry->dwServicePid ); status = ResUtilTerminateServiceProcessFromResDll( ResourceEntry->dwServicePid, TRUE, // bOffline &dwResourceState, g_LogEvent, ResourceEntry->ResourceHandle ); if ( status == ERROR_SUCCESS ) { CloseServiceHandle( ResourceEntry->ServiceHandle ); ResourceEntry->ServiceHandle = NULL; ResourceEntry->dwServicePid = 0; ResourceEntry->Online = FALSE; } resourceStatus.ResourceState = dwResourceState; break; } (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline: retrying...\n" ); Sleep(retryTick); } FnExit: (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); return(status); } // GenSvcOfflineThread BOOL WINAPI GenSvcIsAlive( IN RESID ResourceId ) /*++ Routine Description: IsAlive routine for Generic service resource. Arguments: ResourceId - Supplies the resource id to be polled. Return Value: TRUE - if service is running FALSE - if service is in any other state --*/ { return( VerifyService( ResourceId, TRUE ) ); } // GenSvcIsAlive BOOL VerifyService( IN RESID ResourceId, IN BOOL IsAliveFlag ) /*++ Routine Description: Verify that a specified service is running Arguments: ResourceId - Supplies the resource id IsAliveFlag - Says this is an IsAlive call - used only for debug print Return Value: TRUE - if service is running or starting FALSE - service is in any other state --*/ { SERVICE_STATUS ServiceStatus; PGENSVC_RESOURCE resourceEntry; DWORD status = TRUE; resourceEntry = (PGENSVC_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "GenSvc: IsAlive request for a nonexistent resource id 0x%p.\n", ResourceId ); return(FALSE); } #ifdef TEST_TERMINATE_ON_SHUTDOWN // // Test terminate on shutdown. // if ( IsAliveFlag ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"GenSvcIsAlive: TEST_TERMINATE_ON_SHUTDOWN - Artificially failing IsAlive call.\n", GetLastError() ); return FALSE; } #endif if ( !QueryServiceStatus( resourceEntry->ServiceHandle, &ServiceStatus ) ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Query Service Status failed %1!u!.\n", GetLastError() ); return(FALSE); } // // Now check the status of the service // 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); } // Verify Service BOOL WINAPI GenSvcLooksAlive( IN RESID ResourceId ) /*++ Routine Description: LooksAlive routine for Generic Service resource. Arguments: ResourceId - 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. --*/ { return( VerifyService( ResourceId, FALSE ) ); } // GenSvcLooksAlive VOID WINAPI GenSvcClose( IN RESID ResourceId ) /*++ Routine Description: Close routine for Generic Applications resource. This routine will stop the service, and delete the cluster information regarding that service. Arguments: ResourceId - Supplies resource id to be closed Return Value: None. --*/ { PGENSVC_RESOURCE resourceEntry; resourceEntry = (PGENSVC_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "GenSvc: Close request for a nonexistent resource id 0x%p\n", ResourceId ); return; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Close request.\n" ); // // Shut it down if it's on line // GenSvcTerminate( ResourceId ); ResUtilFreeParameterBlock( (LPBYTE) &(resourceEntry->Params), NULL, GenSvcResourcePrivateProperties ); ClusterRegCloseKey( resourceEntry->ParametersKey ); ClusterRegCloseKey( resourceEntry->ResourceKey ); CloseClusterResource( resourceEntry->hResource ); CloseHandle(resourceEntry->hSem); LocalFree( resourceEntry ); } // GenSvcClose // // Following logic stolen from the CRTs so that our command line parsing // works the same as the standard CRT parsing. // void wparse_cmdline ( WCHAR *cmdstart, WCHAR **argv, WCHAR *args, int *numargs, int *numchars ) { WCHAR *p; WCHAR c; int inquote; /* 1 = inside quotes */ int copychar; /* 1 = copy char to *args */ unsigned numslash; /* num of backslashes seen */ *numchars = 0; *numargs = 0; p = cmdstart; inquote = 0; /* loop on each argument */ for(;;) { if ( *p ) { while (*p == L' ' || *p == L'\t') ++p; } if (*p == L'\0') break; /* end of args */ /* scan an argument */ if (argv) *argv++ = args; /* store ptr to arg */ ++*numargs; /* loop through scanning one argument */ for (;;) { copychar = 1; /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote 2N+1 backslashes + " ==> N backslashes + literal " N backslashes ==> N backslashes */ numslash = 0; while (*p == L'\\') { /* count number of backslashes for use below */ ++p; ++numslash; } if (*p == L'\"') { /* if 2N backslashes before, start/end quote, otherwise copy literally */ if (numslash % 2 == 0) { if (inquote) { if (p[1] == L'\"') p++; /* Double quote inside quoted string */ else /* skip first quote char and copy second */ copychar = 0; } else copychar = 0; /* don't copy quote */ inquote = !inquote; } numslash /= 2; /* divide numslash by two */ } /* copy slashes */ while (numslash--) { if (args) *args++ = L'\\'; ++*numchars; } /* if at end of arg, break loop */ if (*p == L'\0' || (!inquote && (*p == L' ' || *p == L'\t'))) break; /* copy character into argument */ if (copychar) { if (args) *args++ = *p; ++*numchars; } ++p; } /* null-terminate the argument */ if (args) *args++ = L'\0'; /* terminate string */ ++*numchars; } } // wparse_cmdline DWORD GenSvcResourceControl( 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 Generic Service 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; PGENSVC_RESOURCE resourceEntry; DWORD required; resourceEntry = (PGENSVC_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "GenSvc: ResourceControl request for a nonexistent resource id 0x%p\n", ResourceId ); return(FALSE); } switch ( ControlCode ) { case CLUSCTL_RESOURCE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( GenSvcResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( GenSvcResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES: status = GenSvcGetPrivateResProperties( resourceEntry, OutBuffer, OutBufferSize, BytesReturned ); break; case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES: status = GenSvcValidatePrivateResProperties( resourceEntry, InBuffer, InBufferSize, NULL ); break; case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES: status = GenSvcSetPrivateResProperties( resourceEntry, InBuffer, InBufferSize ); break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // GenSvcResourceControl DWORD GenSvcResourceTypeControl( 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 Generic Service resources. Perform the control request specified by ControlCode on this resource type. Arguments: ResourceTypeName - Supplies the resource type name. 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_GET_PRIVATE_RESOURCE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( GenSvcResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( GenSvcResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // GenSvcResourceTypeControl DWORD GenSvcGetPrivateResProperties( IN const PGENSVC_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 Generic Service. 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, GenSvcResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } return(status); } // GenSvcGetPrivateResProperties DWORD GenSvcValidatePrivateResProperties( IN const PGENSVC_RESOURCE ResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize, OUT PGENSVC_PARAMS Params ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control function for resources of type Generic Service. 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_NOT_ENOUGH_MEMORY - An error occurred allocating memory. ERROR_DEPENDENCY_NOT_FOUND - Trying to set UseNetworkName when there is no dependency on a Network Name resource. Win32 error code - The function failed. --*/ { DWORD status; GENSVC_PARAMS currentProps; GENSVC_PARAMS newProps; PGENSVC_PARAMS pParams = NULL; BOOL hResDependency; LPWSTR lpwTemp=NULL; LPWSTR nameOfPropInError; WCHAR netnameBuffer[ MAX_PATH + 1 ]; DWORD netnameBufferSize = sizeof( netnameBuffer ) / sizeof( WCHAR ); // // Check if there is input data. // if ( (InBuffer == NULL) || (InBufferSize < sizeof(DWORD)) ) { return(ERROR_INVALID_DATA); } // // Retrieve the current set of private properties from the // cluster database. // ZeroMemory( ¤tProps, sizeof(currentProps) ); status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey, GenSvcResourcePrivateProperties, (LPBYTE) ¤tProps, FALSE, /*CheckForRequiredProperties*/ &nameOfPropInError ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the '%1!ls!' property. Error: %2!u!.\n", (nameOfPropInError == NULL ? L"" : nameOfPropInError), status ); goto FnExit; } // // Duplicate the resource parameter block. // if ( Params == NULL ) { pParams = &newProps; } else { pParams = Params; } ZeroMemory( pParams, sizeof(GENSVC_PARAMS) ); status = ResUtilDupParameterBlock( (LPBYTE) pParams, (LPBYTE) ¤tProps, GenSvcResourcePrivateProperties ); if ( status != ERROR_SUCCESS ) { return(status); } // // Parse and validate the properties. // status = ResUtilVerifyPropertyTable( GenSvcResourcePrivateProperties, NULL, TRUE, // Allow unknowns InBuffer, InBufferSize, (LPBYTE) pParams ); if ( status != ERROR_SUCCESS ) { goto FnExit; } // // Validate the parameter values. // status = GenSvcIsValidService( ResourceEntry, pParams->ServiceName ); if ( status != ERROR_SUCCESS ) { goto FnExit; } // // If the resource should use the network name as the computer // name, make sure there is a dependency on a Network Name // resource. // if ( pParams->UseNetworkName ) { hResDependency = GetClusterResourceNetworkName( ResourceEntry->hResource, netnameBuffer, &netnameBufferSize ); if ( ! hResDependency ) { status = ERROR_DEPENDENCY_NOT_FOUND; } } if ( status != ERROR_SUCCESS ) { goto FnExit; } if ( ResourceEntry->hSem == NULL ) { // This is executed only if this is a new resource being created lpwTemp = (LPWSTR) LocalAlloc( LMEM_FIXED, ( lstrlenW( pParams->ServiceName ) + lstrlenW( L"GenSvc$" ) + 1 ) * sizeof(WCHAR) ); if ( lpwTemp == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Service '%1!ls!': Not enough memory for storing semaphore name. Error: %2!u!.\n", pParams->ServiceName, status ); goto FnExit; } lstrcpyW( lpwTemp, L"GenSvc$" ); lstrcat( lpwTemp, pParams->ServiceName ); ResourceEntry->hSem = CreateSemaphore( NULL, 0, 1,lpwTemp ); status=GetLastError(); if ( ResourceEntry->hSem ) { // Check if there is another resource controlling the same service if ( status == ERROR_OBJECT_ALREADY_EXISTS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Service '%1!ls!' is controlled by another resource. Error: %2!u!.\n", pParams->ServiceName, status ); CloseHandle( ResourceEntry->hSem ); ResourceEntry->hSem = NULL; goto FnExit; } } else { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"SetPrivateProperties: Unable to create Semaphore %1!ls! for Service '%2!ls!' . Error: %3!u!.\n", lpwTemp, pParams->ServiceName, status ); return(status); } } FnExit: // // Cleanup our parameter block. // if ( ( (status != ERROR_SUCCESS) && (pParams != NULL) ) || ( pParams == &newProps ) ) { ResUtilFreeParameterBlock( (LPBYTE) pParams, (LPBYTE) ¤tProps, GenSvcResourcePrivateProperties ); } ResUtilFreeParameterBlock( (LPBYTE) ¤tProps, NULL, GenSvcResourcePrivateProperties ); if ( lpwTemp ) { LocalFree( lpwTemp ); } return(status); } // GenSvcValidatePrivateResProperties DWORD GenSvcSetPrivateResProperties( IN OUT PGENSVC_RESOURCE ResourceEntry, IN const PVOID InBuffer, IN DWORD InBufferSize ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function for resources of type Generic Service. 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; GENSVC_PARAMS params; ZeroMemory( ¶ms, sizeof(GENSVC_PARAMS) ); // // Parse and validate the properties. // status = GenSvcValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, ¶ms ); if ( status != ERROR_SUCCESS ) { return(status); } // // Save the parameter values. // status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey, GenSvcResourcePrivateProperties, NULL, (LPBYTE) ¶ms, InBuffer, InBufferSize, (LPBYTE) &ResourceEntry->Params ); ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, GenSvcResourcePrivateProperties ); // // If the resource is online, return a non-success status. // if (status == ERROR_SUCCESS) { if ( ResourceEntry->Online ) { status = ERROR_RESOURCE_PROPERTIES_STORED; } else { status = ERROR_SUCCESS; } } return status; } // GenSvcSetPrivateResProperties DWORD GenSvcInvalidGenericServiceCheck( IN OUT PGENSVC_RESOURCE ResourceEntry, IN LPCWSTR ServiceName, IN HKEY ServiceKey ) /*++ Routine Description: Determines if the specified service is an invalid service for use as a generic service. Invalid services include the cluster service and any services upon which it depends. Arguments: ResourceEntry - Supplies the resource entry on which to operate. ServiceName - Service name to verify. ServiceKey - Key for the root to begin checking at. If specified as NULL, the cluster service key will be opened. Return Value: ERROR_SUCCESS - Service is valid for a Generic Service resource. ERROR_NOT_SUPPORTED - Service can not be used for a Generic Service resource. --*/ { DWORD status; DWORD valueType; DWORD valueLength; LPWSTR value = NULL; LPWSTR currentValue; HKEY clusterServiceKey = NULL; HKEY serviceKey = NULL; WCHAR serviceKeyName[2048]; // // If no key specified, check against cluster service. // if ( ServiceKey == NULL ) { if ( lstrcmpiW( ServiceName, L"ClusSvc" ) == 0 ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Resource for ClusSvc is not supported.\n" ); return(ERROR_NOT_SUPPORTED); } status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, SERVICES_ROOT L"ClusSvc", 0, KEY_READ, &clusterServiceKey ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to open the '" SERVICES_ROOT L"ClusSvc' registry key. Error: %1!u!.\n", status ); return(status); } ServiceKey = clusterServiceKey; } // // Read the DependOnService value. // status = RegQueryValueEx( ServiceKey, L"DependOnService", NULL, &valueType, NULL, &valueLength ); if ( status != ERROR_SUCCESS ) { if ( status == ERROR_FILE_NOT_FOUND ) { status = ERROR_SUCCESS; } else { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to get the length of the DependOnService value. Error: %1!u!.\n", status ); } goto done; } value = LocalAlloc( LMEM_FIXED, valueLength ); if ( value == NULL ) { status = GetLastError(); goto done; } status = RegQueryValueEx( ServiceKey, L"DependOnService", NULL, &valueType, (LPBYTE) value, &valueLength ); if ( status != ERROR_SUCCESS ) { if ( status == ERROR_FILE_NOT_FOUND ) { status = ERROR_SUCCESS; } else { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the DependOnService value. Error: %1!u!.\n", status ); } goto done; } // // Loop through each entry in the string. // for ( currentValue = value ; ( ( currentValue-value ) * sizeof ( WCHAR ) < valueLength ); currentValue += lstrlenW( currentValue ) + 1 ) { // // Break out once you reach the end of the REG_MULTI_SZ // if ( *currentValue == L'\0' ) { status = ERROR_SUCCESS; goto done; } // // Compare against this service. // if ( lstrcmpiW( ServiceName, currentValue ) == 0 ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Creating a resource for a service (%1!ls!) upon which ClusSvc is dependent, either directly or indirectly, is not supported.\n", ServiceName ); status = ERROR_NOT_SUPPORTED; goto done; } // // Open this service's key. // if ( (lstrlenW( ServiceName ) * sizeof( WCHAR )) + sizeof( SERVICES_ROOT ) > sizeof( serviceKeyName ) ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Registry key for the '%1!ls!' service is longer than %2!d! bytes.\n", ServiceName, sizeof( serviceKeyName ) ); status = ERROR_NOT_ENOUGH_MEMORY; goto done; } wsprintfW( serviceKeyName, SERVICES_ROOT L"%s", currentValue ); status = RegOpenKey( HKEY_LOCAL_MACHINE, serviceKeyName, &serviceKey ); if ( status != ERROR_SUCCESS ) { if ( status == ERROR_FILE_NOT_FOUND ) { // Ignore services that don't exist in the registry. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Unable to open the '%1!ls!' registry key. Error: %2!u!. Error ignored.\n", serviceKeyName, status ); status = ERROR_SUCCESS; continue; } else { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to open the '%1!ls!' registry key. Error: %2!u!.\n", serviceKeyName, status ); goto done; } } // // Check this service's dependencies. // status = GenSvcInvalidGenericServiceCheck( ResourceEntry, ServiceName, serviceKey ); if ( status != ERROR_SUCCESS ) { goto done; } RegCloseKey( serviceKey ); serviceKey = NULL; } done: if ( clusterServiceKey != NULL ) { RegCloseKey( clusterServiceKey ); } if ( serviceKey != NULL ) { RegCloseKey( serviceKey ); } if ( value != NULL ) { LocalFree( value ); } return(status); } // GenSvcInvalidGenericServiceCheck DWORD GenSvcIsValidService( IN OUT PGENSVC_RESOURCE ResourceEntry, IN LPCWSTR ServiceName ) /*++ Routine Description: Determines if the specified service is a valid service or not. Arguments: ResourceEntry - Supplies the resource entry on which to operate. ServiceName - Service name to verify. Return Value: ERROR_SUCCESS - Service is valid for a Generic Service resource. Any status returned by OpenSCManager(), OpenService(), or GenSvcInvalidGenericServiceCheck. --*/ { DWORD status; HANDLE scManagerHandle; HANDLE serviceHandle; scManagerHandle = OpenSCManager( NULL, // local machine NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS ); // all access if ( scManagerHandle == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Cannot access service controller for validating service '%1!ws!'. Error: %2!u!....\n", ServiceName, status ); return(status); } serviceHandle = OpenService( scManagerHandle, ServiceName, SERVICE_ALL_ACCESS ); if ( serviceHandle == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Cannot open service '%1!ws!'. Error: %2!u!....\n", ServiceName, status ); } else { status = GenSvcInvalidGenericServiceCheck( ResourceEntry, ServiceName, NULL ); } CloseServiceHandle( serviceHandle ); CloseServiceHandle( scManagerHandle ); return(status); } // GenSvcIsValidService //*********************************************************** // // Define Function Table // //*********************************************************** CLRES_V1_FUNCTION_TABLE( GenSvcFunctionTable, // Name CLRES_VERSION_V1_00, // Version GenSvc, // Prefix NULL, // Arbitrate NULL, // Release GenSvcResourceControl,// ResControl GenSvcResourceTypeControl ); // ResTypeControl