/*++ Copyright (c) 1992-1997 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 "nt.h" #include "ntrtl.h" #include "nturtl.h" #include "windows.h" #include "clusapi.h" #include "clusudef.h" #include "resapi.h" #include "bosvc.h" #include "xchgclus.h" #include "xchgdat.h" extern PLOG_EVENT_ROUTINE g_LogEvent; extern PSET_RESOURCE_STATUS_ROUTINE g_SetResourceStatus; #define DBG_PRINT printf // // Global Data // // Handle to service controller, set by the first create resource call. static SC_HANDLE g_ScHandle = NULL; // // Forward routines // DWORD XchgOnlineThread( IN PCLUS_WORKER pWorker, IN PCOMMON_RESOURCE ResourceEntry ); BOOL XchgVerifyService( IN RESID ResourceId, IN BOOL IsAliveFlag ); HANDLE XchgOpenSvc( IN SERVICE_NAME ServiceName, IN PCOMMON_RESOURCE ResourceEntry ); DWORD XchgSetSvcEnv( IN HKEY ServicesKey, IN SERVICE_NAME ServiceName, IN LPVOID Environment, IN DWORD ValueSize, IN PCOMMON_RESOURCE ResourceEntry ); #ifdef COMMON_ONLINE_THREAD #define COMMON_ONLINE_THREAD_ROUTINE COMMON_ONLINE_THREAD #else #define COMMON_ONLINE_THREAD_ROUTINE XchgOnlineThread #endif // // Local Routines // #ifndef COMMON_ONLINE_THREAD DWORD XchgOnlineThread( IN PCLUS_WORKER pWorker, IN PCOMMON_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 ServiceStatus; DWORD status = ERROR_SUCCESS; DWORD numchars; RESOURCE_STATUS resourceStatus; HANDLE serviceHandle; DWORD valueSize; WCHAR * p; LPVOID Environment = NULL; HKEY ServicesKey; DWORD dwSvcCnt; DWORD dwDependencyCnt; PSERVICE_INFO pSvcInfo; ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; //resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; #if ENVIRONMENT // // Create the new environment with the simulated net name when the // services queries GetComputerName. // Environment = ResUtilGetEnvironmentWithNetName( ResourceEntry->hResource ); if (!Environment) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"XchgOnlineThread:Failed to get environment with netname, error = %1!u!.\n", status ); goto error_exit; } // // 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"XchgOnlineThread:Failed to open services key, error = %1!u!.\n", status ); goto error_exit; } for (dwSvcCnt=0; dwSvcCnt< ServiceInfoList.dwMaxSvcCnt; dwSvcCnt++) { pSvcInfo = &ServiceInfoList.SvcInfo[dwSvcCnt]; status = XchgSetSvcEnv(ServicesKey, pSvcInfo->snSvcName, Environment, valueSize, ResourceEntry); if (status != ERROR_SUCCESS) goto error_exit; for (dwDependencyCnt=0; dwDependencyCnt < pSvcInfo->dwDependencyCnt; dwDependencyCnt++) { status = XchgSetSvcEnv(ServicesKey, pSvcInfo->snProvidorSvc[dwDependencyCnt], Environment, valueSize, ResourceEntry); if (status != ERROR_SUCCESS) goto error_exit; } } RegCloseKey(ServicesKey); #endif //ENVIRONMENT resourceStatus.ResourceState = ClusterResourceFailed; resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; for (dwSvcCnt=0;dwSvcCnt< ServiceInfoList.dwMaxSvcCnt; dwSvcCnt++) { pSvcInfo = &ServiceInfoList.SvcInfo[dwSvcCnt]; // // Now open the requested service and the handle to its providor services // serviceHandle = XchgOpenSvc(pSvcInfo->snSvcName, ResourceEntry); if (serviceHandle == NULL) { status = GetLastError(); goto error_exit; } ResourceEntry->ServiceHandle[dwSvcCnt][0] = serviceHandle; //get the handles for the child services for (dwDependencyCnt=0; dwDependencyCnt < pSvcInfo->dwDependencyCnt; dwDependencyCnt++) { serviceHandle = XchgOpenSvc(pSvcInfo->snSvcName, ResourceEntry); if (serviceHandle == NULL) { status = GetLastError(); goto error_exit; } ResourceEntry->ServiceHandle[dwSvcCnt][dwDependencyCnt+1] = serviceHandle; } //start the top level services // the providor services will be started by scm if ( !StartServiceW( ResourceEntry->ServiceHandle[dwSvcCnt][0], 0, NULL ) ) { status = GetLastError(); if (status != ERROR_SERVICE_ALREADY_RUNNING) { /* ClusResLogEventByKeyData(ResourceEntry->ResourceKey, LOG_CRITICAL, RES_GENSVC_START_FAILED, sizeof(status), &status); */ (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to start %1!ws! service. Error: %2!u!.\n", ServiceInfoList.SvcInfo[dwSvcCnt].snSvcName, status ); status = ERROR_SERVICE_NEVER_STARTED; goto error_exit; } } } for (dwSvcCnt=0;dwSvcCnt< ServiceInfoList.dwMaxSvcCnt; dwSvcCnt++) { while (TRUE) { if ( !QueryServiceStatus( ResourceEntry->ServiceHandle[dwSvcCnt][0], &ServiceStatus ) ) { status = GetLastError(); (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"XchgOnlineThread:Query Service Status failed %1!u!.\n", status ); goto error_exit; } if ( ServiceStatus.dwCurrentState != SERVICE_START_PENDING ) { break; } Sleep(250); } if ( ServiceStatus.dwCurrentState != SERVICE_RUNNING ) { /* ClusResLogEventByKeyData(ResourceEntry->ResourceKey, LOG_CRITICAL, RES_GENSVC_FAILED_AFTER_START, sizeof(status), &status); */ (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to start service %1!ws!, Error: %2!u!.\n", ServiceInfoList.SvcInfo[dwSvcCnt].snSvcName, ERROR_SERVICE_NEVER_STARTED ); status = ERROR_SERVICE_NEVER_STARTED; goto error_exit; } } resourceStatus.ResourceState = ClusterResourceOnline; (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Service is now on line.\n" ); error_exit: #if ENVIRONMENT if ( Environment != NULL ) { RtlDestroyEnvironment( Environment ); } #endif (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); if ( resourceStatus.ResourceState == ClusterResourceOnline ) { ResourceEntry->Online = TRUE; } else { ResourceEntry->Online = FALSE; } return(status); } // CommonOnlineThread #endif RESID WINAPI XchgOpen( 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 COMMON 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; PCOMMON_RESOURCE resourceEntry = NULL; LPWSTR resourceName = NULL; DWORD paramNameSize = 0; DWORD paramNameMaxSize = 0; HCLUSTER hCluster; DWORD returnSize; DWORD i; HANDLE hMutex; DWORD dwDependencyCnt, dwSvcCnt; PSERVICE_INFO pSvcInfo; // // Save the resource name. // resourceName = LocalAlloc( LMEM_FIXED, (wcslen(ResourceName) + 1) * sizeof(WCHAR) ); if ( resourceName == NULL ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to allocate name string.\n" ); status = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } wcscpy( resourceName, ResourceName ); // // Open registry parameters key for this resource. // status = ClusterRegOpenKey( ResourceKey, L"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 ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to open service control manager, error = %1!u!.\n", GetLastError()); goto error_exit; } } resourceEntry = LocalAlloc( LMEM_FIXED, sizeof(COMMON_RESOURCE) ); if ( resourceEntry == NULL ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to allocate a service info structure.\n"); goto error_exit; } ZeroMemory( resourceEntry, sizeof(COMMON_RESOURCE) ); resourceEntry->ResourceHandle = ResourceHandle; resourceEntry->ResourceKey = resKey; resourceEntry->ParametersKey = parametersKey; resourceEntry->ResourceName = resourceName; //initialize the service handles to NULL for (dwSvcCnt=0; dwSvcCnt< ServiceInfoList.dwMaxSvcCnt; dwSvcCnt++) { pSvcInfo = &ServiceInfoList.SvcInfo[dwSvcCnt]; for (dwDependencyCnt=0; dwDependencyCnt <= pSvcInfo->dwDependencyCnt; dwDependencyCnt++) { resourceEntry->ServiceHandle[dwSvcCnt][dwDependencyCnt] = NULL; } } hCluster = OpenCluster(NULL); if (hCluster == NULL) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to open cluster" ); goto error_exit; } resourceEntry->hResource = OpenClusterResource(hCluster, ResourceName); CloseCluster(hCluster); if ( resourceEntry->hResource == NULL ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to open resource" ); goto error_exit; } // // Set any registry checkpoints that we need. // if ( RegSyncCount != 0 ) { returnSize = 0; status = ClusterResourceControl( resourceEntry->hResource, NULL, CLUSCTL_RESOURCE_GET_REGISTRY_CHECKPOINTS, NULL, 0, NULL, 0, &returnSize ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to get registry checkpoints, status %1!u!.\n", status ); goto error_exit; } // // Set registry sync keys if we need them. // if ( returnSize == 0 ) { for ( i = 0; i < RegSyncCount; i++ ) { status = ClusterResourceControl( resourceEntry->hResource, NULL, CLUSCTL_RESOURCE_ADD_REGISTRY_CHECKPOINT, RegSync[i], (lstrlenW(RegSync[i]) + 1) * sizeof(WCHAR), NULL, 0, &returnSize ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to set registry checkpoint, status %1!u!.\n", status ); goto error_exit; } } } } #ifdef COMMON_PARAMS_DEFINED // // Get any parameters... so we can handle the GET_DEPENDENCIES request. // CommonReadParameters( resourceEntry ); // ignore status return #endif // COMMON_PARAMS_DEFINED #ifdef COMMON_MUTEX // // Check if more than one resource of this type. // N.B. Must have a lock for this case! // hMutex = CreateMutexW( NULL, FALSE, COMMON_MUTEX ); if ( hMutex == NULL ) { status = GetLastError(); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to create global mutex, status %1!us!.\n", status ); goto error_exit; } if ( WaitForSingleObject( hMutex, 0 ) == WAIT_TIMEOUT ) { // // A version of this service is already running // CloseHandle( hMutex ); (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Service is already running.\n" ); status = ERROR_SERVICE_ALREADY_RUNNING; goto error_exit; } ASSERT( CommonMutex == NULL ); CommonMutex = hMutex; #endif // COMMON_MUTEX svcResid = (RESID)resourceEntry; return(svcResid); error_exit: LocalFree( resourceName ); LocalFree( resourceEntry ); if ( parametersKey != NULL ) { ClusterRegCloseKey( parametersKey ); } if ( resKey != NULL) { ClusterRegCloseKey( resKey ); } return(FALSE); } // XchgOpen DWORD WINAPI XchgOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ) /*++ Routine Description: Online routine for Common 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; PCOMMON_RESOURCE resourceEntry; resourceEntry = (PCOMMON_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "Common: Online request for a nonexistent resource id 0x%p.\n", ResourceId ); return(ERROR_RESOURCE_NOT_FOUND); } ClusWorkerTerminate( &resourceEntry->OnlineThread ); status = ClusWorkerCreate( &resourceEntry->OnlineThread, COMMON_ONLINE_THREAD_ROUTINE, resourceEntry ); if ( status == ERROR_SUCCESS ) { status = ERROR_IO_PENDING; } return(status); } // XchgOnline VOID WINAPI XchgTerminate( IN RESID ResourceId ) /*++ Routine Description: Terminate routine for Common Service resource. Arguments: ResourceId - Supplies resource id to be terminated Return Value: None. --*/ { SERVICE_STATUS ServiceStatus; PCOMMON_RESOURCE resourceEntry; DWORD dwSvcCnt; PSERVICE_INFO pSvcInfo; DWORD dwDependencyCnt; resourceEntry = (PCOMMON_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "Common: Offline request for a nonexistent resource id 0x%p.\n", ResourceId ); return; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline request.\n" ); ClusWorkerTerminate( &resourceEntry->OnlineThread ); for (dwSvcCnt=0;dwSvcCnt< ServiceInfoList.dwMaxSvcCnt; dwSvcCnt++) { pSvcInfo = &ServiceInfoList.SvcInfo[dwSvcCnt]; //stop the service, lowest order service first for (dwDependencyCnt= 0; dwDependencyCnt<= pSvcInfo->dwDependencyCnt; dwDependencyCnt++) { if ( resourceEntry->ServiceHandle[dwSvcCnt][pSvcInfo->dwDependencyCnt-dwDependencyCnt] != NULL ) { DWORD retryTime = 30*1000; // wait 30 secs for shutdown DWORD retryTick = 300; // 300 msec at a time DWORD status; BOOL didStop = FALSE; //stop the child service for (;;) { status = (ControlService( resourceEntry->ServiceHandle[dwSvcCnt][pSvcInfo->dwDependencyCnt - dwDependencyCnt], (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" ); 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; status = %1!u!.\n", status); break; } if ((retryTime -= retryTick) <= 0) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Service did not stop; giving up.\n" ); break; } (g_LogEvent)( resourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline: retrying...\n" ); Sleep(retryTick); } CloseServiceHandle( resourceEntry->ServiceHandle[dwSvcCnt][pSvcInfo->dwDependencyCnt - dwDependencyCnt] ); resourceEntry->ServiceHandle[dwSvcCnt][pSvcInfo->dwDependencyCnt - dwDependencyCnt] = NULL; } } } resourceEntry->Online = FALSE; } // XchgTerminate static DWORD WINAPI XchgOffline( IN RESID ResourceId ) /*++ Routine Description: Offline routine for Common Service resource. Arguments: ResourceId - Supplies the resource to be taken offline Return Value: ERROR_SUCCESS - always successful. --*/ { XchgTerminate( ResourceId ); return(ERROR_SUCCESS); } // XchgOffline BOOL WINAPI XchgIsAlive( IN RESID ResourceId ) /*++ Routine Description: IsAlive routine for Common 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( XchgVerifyService( ResourceId, TRUE ) ); } // CommonIsAlive BOOL XchgVerifyService( 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; PCOMMON_RESOURCE resourceEntry; DWORD status = ERROR_SUCCESS; BOOL bRet = TRUE; DWORD dwSvcCnt; resourceEntry = (PCOMMON_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "Common: IsAlive request for a nonexistent resource id 0x%p.\n", ResourceId ); bRet = FALSE; goto error_exit; } for (dwSvcCnt=0; dwSvcCnt< ServiceInfoList.dwMaxSvcCnt; dwSvcCnt++) { if ( !QueryServiceStatus( resourceEntry->ServiceHandle[dwSvcCnt][0], &ServiceStatus ) ) { status = GetLastError(); (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"XchgOnlineThread:Query Service Status failed %1!u!.\n", status ); bRet = FALSE; goto error_exit; } if ((ServiceStatus.dwCurrentState != SERVICE_RUNNING)&&(ServiceStatus.dwCurrentState != SERVICE_START_PENDING)) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Failed the IsAlive test. Current State is %1!u!.\n", ServiceStatus.dwCurrentState ); bRet = FALSE; goto error_exit; } } error_exit: return(bRet); } // Verify Service BOOL WINAPI XchgLooksAlive( IN RESID ResourceId ) /*++ Routine Description: LooksAlive routine for Common 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( XchgVerifyService( ResourceId, FALSE ) ); } // CommonLooksAlive VOID WINAPI XchgClose( IN RESID ResourceId ) /*++ Routine Description: Close routine for Common Services 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. --*/ { PCOMMON_RESOURCE resourceEntry; DWORD errorCode; HANDLE hMutex; resourceEntry = (PCOMMON_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { DBG_PRINT( "Common: 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 // XchgTerminate( ResourceId ); ClusterRegCloseKey( resourceEntry->ParametersKey ); ClusterRegCloseKey( resourceEntry->ResourceKey ); CloseClusterResource( resourceEntry->hResource ); #ifdef COMMON_MUTEX hMutex = CommonMutex; CommonMutex = NULL; ReleaseMutex( hMutex ); CloseHandle( hMutex ); #endif LocalFree( resourceEntry->ResourceName ); LocalFree( resourceEntry ); } // XchgClose HANDLE XchgOpenSvc( IN SERVICE_NAME ServiceName, IN PCOMMON_RESOURCE ResourceEntry ) /*++ Routine Description: opens the service and sets it to manual mode. Arguments: ResourceId - Supplies resource id to be closed Return Value: None. --*/ { HANDLE serviceHandle=NULL; DWORD status=ERROR_SUCCESS; serviceHandle = OpenService( g_ScHandle, ServiceName, SERVICE_ALL_ACCESS ); if ( serviceHandle == NULL ) { status = GetLastError(); /* ClusResLogEventByKeyData(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; } // // Make sure service is set to manual start. // ChangeServiceConfig( serviceHandle, SERVICE_NO_CHANGE, SERVICE_DEMAND_START, // Manual start SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); error_exit: if (status != ERROR_SUCCESS) SetLastError(status); return(serviceHandle); }// XchgOpenSvc DWORD XchgSetSvcEnv( IN HKEY ServicesKey, IN SERVICE_NAME ServiceName, IN LPVOID Environment, IN DWORD ValueSize, IN PCOMMON_RESOURCE ResourceEntry ) /*++ Routine Description: opens the service and sets it to manual mode. Arguments: ResourceId - Supplies resource id to be closed Return Value: None. --*/ { DWORD status = ERROR_SUCCESS; HKEY hKey; status = RegOpenKeyExW( ServicesKey, ServiceName, 0, KEY_READ | KEY_WRITE, &hKey ); if (status != ERROR_SUCCESS) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"XchgSet: Failed to open service key for %1!ws!, error = %2!u!.\n", ServiceName, 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"XchgSet:Failed to set service environment value, error = %1!u!.\n", status ); goto error_exit; } error_exit: return(status); } // CommonSetSvcEnv /* VOID ClusResLogEventWithName0( IN HKEY hResourceKey, IN DWORD LogLevel, IN DWORD LogModule, IN LPSTR FileName, IN DWORD LineNumber, IN DWORD MessageId, IN DWORD dwByteCount, IN PVOID lpBytes ) */ /*++ Routine Description: Logs an event to the eventlog. The display name of the resource is retrieved and passed as the first insertion string. Arguments: hResourceKey - Supplies the cluster resource key. LogLevel - Supplies the logging level, one of LOG_CRITICAL 1 LOG_UNUSUAL 2 LOG_NOISE 3 LogModule - Supplies the module ID. FileName - Supplies the filename of the caller LineNumber - Supplies the line number of the caller MessageId - Supplies the message ID to be logged. dwByteCount - Supplies the number of error-specific bytes to log. If this is zero, lpBytes is ignored. lpBytes - Supplies the error-specific bytes to log. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise --*/ /* { DWORD BufSize; DWORD Status; WCHAR ResourceName[80]; DWORD dwType; // // Get the display name for this resource. // BufSize = sizeof(ResourceName); Status = ClusterRegQueryValue( hResourceKey, CLUSREG_NAME_RES_NAME, &dwType, (LPBYTE)ResourceName, &BufSize ); if (Status != ERROR_SUCCESS) { ResourceName[0] = '\0'; } else { ResourceName[79] = '\0'; } ClusterLogEvent1(LogLevel, LogModule, FileName, LineNumber, MessageId, dwByteCount, lpBytes, ResourceName); return; } */ CLRES_V1_FUNCTION_TABLE( ExchangeFunctionTable, // Name CLRES_VERSION_V1_00, // Version Xchg, // Prefix NULL, // Arbitrate NULL, // Release NULL, // ResControl NULL ); // ResTypeControl