mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1267 lines
31 KiB
1267 lines
31 KiB
/*++
|
|
|
|
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
|
|
|