2025-11-28 00:35:46 +09:00

2575 lines
72 KiB
C++

/////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2003 <company name>
//
// Module Name:
// ClipBook Server.cpp
//
// Description:
// Resource DLL for ClipBook Server (ClipBook Server).
//
// Author:
// <name> (<e-mail name>) Mmmm DD, 2003
//
// Revision History:
//
// Notes:
//
/////////////////////////////////////////////////////////////////////////////
#include "ClRes.h"
//
// Type and constant definitions.
//
typedef struct _CLIPBOOKSERVER_RESOURCE
{
RESID resid; // For validation.
HCLUSTER hCluster;
HRESOURCE hResource;
SC_HANDLE hService;
DWORD dwProcessId;
HKEY hkeyParameters;
RESOURCE_HANDLE hResourceHandle;
LPWSTR pwszResourceName;
CLUS_WORKER cwWorkerThread;
CLUSTER_RESOURCE_STATE state;
} CLIPBOOKSERVER_RESOURCE, * PCLIPBOOKSERVER_RESOURCE;
///////////////////////////////////////////////////////////////////////////////
// Global data.
///////////////////////////////////////////////////////////////////////////////
//
// Forward reference to our RESAPI function table.
//
extern CLRES_FUNCTION_TABLE g_ClipBookServerFunctionTable;
//
// Single instance semaphore.
//
#define CLIPBOOKSERVER_SINGLE_INSTANCE_SEMAPHORE L"Cluster_ClipBookServer_Semaphore"
static HANDLE g_hSingleInstanceSemaphoreClipBookServer = NULL;
static PCLIPBOOKSERVER_RESOURCE g_pSingleInstanceResourceClipBookServer = NULL;
//
// Function prototypes.
//
RESID WINAPI
ClipBookServerOpen(
LPCWSTR pwszResourceNameIn
, HKEY hkeyResourceKeyIn
, RESOURCE_HANDLE hResourceHandleIn
);
void WINAPI
ClipBookServerClose(
RESID residIn
);
DWORD WINAPI
ClipBookServerOnline(
RESID residIn
, PHANDLE phEventHandleInout
);
DWORD WINAPI
ClipBookServerOnlineThread(
PCLUS_WORKER pWorkerIn
, PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
);
DWORD WINAPI
ClipBookServerOffline(
RESID residIn
);
DWORD WINAPI
ClipBookServerOfflineThread(
PCLUS_WORKER pWorkerIn
, PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
);
void WINAPI
ClipBookServerTerminate(
RESID residIn
);
BOOL WINAPI
ClipBookServerLooksAlive(
RESID residIn
);
BOOL WINAPI
ClipBookServerIsAlive(
RESID residIn
);
BOOL
ClipBookServerCheckIsAlive(
PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
, BOOL fFullCheckIn
);
DWORD WINAPI
ClipBookServerResourceControl(
RESID residIn
, DWORD nControlCodeIn
, PVOID pInBufferIn
, DWORD cbInBufferSizeIn
, PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
);
DWORD
ClipBookServerGetRequiredDependencies(
PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
);
DWORD
ClipBookServerVerifyRequiredDependencies(
PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
);
DWORD
ClipBookServerSetNameHandler(
PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
, LPWSTR pwszNameIn
);
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerDllMain
//
// Description:
// Main DLL entry point for the ClipBook Server resource type.
//
// Arguments:
// hDllHandleIn
// DLL instance handle.
//
// nReasonIn
// Reason for being called.
//
// ReservedIn
// Reserved argument.
//
// Return Value:
// TRUE
// Success.
//
// FALSE
// Failure.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL WINAPI
ClipBookServerDllMain(
HINSTANCE hDllHandleIn
, DWORD nReasonIn
, LPVOID ReservedIn
)
{
BOOL fSuccess = TRUE;
DWORD sc = ERROR_SUCCESS;
UNREFERENCED_PARAMETER( hDllHandleIn );
UNREFERENCED_PARAMETER( ReservedIn );
switch ( nReasonIn )
{
case DLL_PROCESS_ATTACH:
g_hSingleInstanceSemaphoreClipBookServer = CreateSemaphoreW(
NULL
, 0
, 1
, CLIPBOOKSERVER_SINGLE_INSTANCE_SEMAPHORE
);
sc = GetLastError();
if ( g_hSingleInstanceSemaphoreClipBookServer == NULL )
{
fSuccess = FALSE;
goto Cleanup;
} // if: error creating semaphore
if ( sc != ERROR_ALREADY_EXISTS )
{
// If the semaphore didnt exist, set its initial count to 1.
ReleaseSemaphore( g_hSingleInstanceSemaphoreClipBookServer, 1, NULL );
} // if: semaphore didn't already exist
break;
case DLL_PROCESS_DETACH:
if ( g_hSingleInstanceSemaphoreClipBookServer != NULL )
{
CloseHandle( g_hSingleInstanceSemaphoreClipBookServer );
g_hSingleInstanceSemaphoreClipBookServer = NULL;
} // if: single instance semaphore was created
if ( g_schSCMHandle != NULL )
{
CloseServiceHandle( g_schSCMHandle );
g_schSCMHandle = NULL;
} // if: global SCM handle was opened
break;
} // switch: nReason
Cleanup:
return fSuccess;
} //*** ClipBookServerDllMain
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerStartup
//
// Description:
// Startup the resource DLL for the ClipBook Server resource type.
// This routine verifies that at least one currently supported version
// of the resource DLL is between nMinVersionSupported and
// nMaxVersionSupported. If not, then the resource DLL should return
// ERROR_REVISION_MISMATCH.
//
// If more than one version of the resource DLL interface is supported
// by the resource DLL, then the highest version (up to
// nMaxVersionSupported) should be returned as the resource DLL's
// interface. If the returned version is not within range, then startup
// fails.
//
// The Resource Type is passed in so that if the resource DLL supports
// more than one Resource Type, it can pass back the correct function
// table associated with the Resource Type.
//
// Arguments:
// pwszResourceTypeIn
// Type of resource requesting a function table.
//
// nMinVersionSupportedIn
// Minimum resource DLL interface version supported by the cluster
// software.
//
// nMaxVersionSupportedIn
// Maximum resource DLL interface version supported by the cluster
// software.
//
// pfnSetResourceStatusIn
// Pointer to a routine that the resource DLL should call to update
// the state of a resource after the Online or Offline routine
// have returned a status of ERROR_IO_PENDING.
//
// pfnLogEventIn
// Pointer to a routine that handles the reporting of events from
// the resource DLL.
//
// pFunctionTableIn
// Returns a pointer to the function table defined for the version
// of the resource DLL interface returned by the resource DLL.
//
// Return Value:
// ERROR_SUCCESS
// The operation was successful.
//
// ERROR_CLUSTER_RESNAME_NOT_FOUND
// The resource type name is unknown by this DLL.
//
// ERROR_REVISION_MISMATCH
// The version of the cluster service doesn't match the version of
// the DLL.
//
// Win32 error code
// The operation failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
ClipBookServerStartup(
LPCWSTR pwszResourceTypeIn
, DWORD nMinVersionSupportedIn
, DWORD nMaxVersionSupportedIn
, PSET_RESOURCE_STATUS_ROUTINE pfnSetResourceStatusIn
, PLOG_EVENT_ROUTINE pfnLogEventIn
, PCLRES_FUNCTION_TABLE * pFunctionTableOut
)
{
DWORD sc = ERROR_SUCCESS;
// These are stored in the calling DllMain.
UNREFERENCED_PARAMETER( pfnSetResourceStatusIn );
UNREFERENCED_PARAMETER( pfnLogEventIn );
if ( (nMinVersionSupportedIn > CLRES_VERSION_V1_00)
|| (nMaxVersionSupportedIn < CLRES_VERSION_V1_00) )
{
sc = ERROR_REVISION_MISMATCH;
} // if: version not supported
else if ( 0 == CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
pwszResourceTypeIn,
-1,
RESTYPE_NAME,
-1
)
)
{
*pFunctionTableOut = &g_ClipBookServerFunctionTable;
sc = ERROR_SUCCESS;
} // else if: we support this type of resource
else
{
//
// We don't support this resource type.
//
sc = ERROR_CLUSTER_RESNAME_NOT_FOUND;
} // else: resource type name not supported
return sc;
} //*** ClipBookServerStartup
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerOpen
//
// Description:
// Open routine for ClipBook Server resources.
//
// Open the specified resource (create an instance of the resource).
// Allocate all structures necessary to bring the specified resource
// online.
//
// Arguments:
// pwszResourceNameIn
// Supplies the name of the resource to open.
//
// hkeyResourceKeyIn
// Supplies handle to the resource's cluster database key.
//
// hResourceHandleIn
// A handle that is passed back to the Resource Monitor when the
// SetResourceStatus or LogEvent method is called. See the
// description of the pfnSetResourceStatus and pfnLogEvent arguments
// to the ClipBookServerStartup routine. This handle should never be
// closed or used for any purpose other than passing it as an
// argument back to the Resource Monitor in the SetResourceStatus or
// LogEvent callbacks.
//
// Return Value:
// resid
// RESID of opened resource.
//
// NULL
// Error occurred opening the resource. Resource Monitor may call
// GetLastError() to get more details on the error.
//
//--
/////////////////////////////////////////////////////////////////////////////
RESID WINAPI
ClipBookServerOpen(
LPCWSTR pwszResourceNameIn
, HKEY hkeyResourceKeyIn
, RESOURCE_HANDLE hResourceHandleIn
)
{
DWORD sc = ERROR_SUCCESS;
size_t cchBuffer = 0;
RESID resid = NULL;
HKEY hkeyParameters = NULL;
PCLIPBOOKSERVER_RESOURCE pResourceEntry = NULL;
SC_HANDLE hService = NULL;
HRESULT hr = S_OK;
//
// Check if more than one resource of this type.
//
if ( WaitForSingleObject( g_hSingleInstanceSemaphoreClipBookServer, 0 ) == WAIT_TIMEOUT )
{
//
// A resource of this type is already running.
//
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"A resource of this type is already running.\n"
);
sc = ERROR_OBJECT_ALREADY_EXISTS;
goto Cleanup;
} // if: semaphore for resources of this type already already locked
if ( g_pSingleInstanceResourceClipBookServer != NULL )
{
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Single instance resource already set!\n"
);
sc = ERROR_OBJECT_ALREADY_EXISTS;
goto Cleanup;
} // if: resource of this type already exists
//
// Get a global handle to the Service Control Manager (SCM).
//
if ( g_schSCMHandle == NULL )
{
g_schSCMHandle = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
if ( g_schSCMHandle == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Failed to open Service Control Manager. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error opening the Service Control Manager
} // if: Service Control Manager not open yet
//
// Make sure the service is ready to be controlled by the cluster.
//
//
// Note: We do this in Open so that as soon as the resource is created
// the service is configured for cluster service control.
//
sc = ResUtilSetResourceServiceStartParameters(
CLIPBOOKSERVER_SVCNAME
, g_schSCMHandle
, &hService
, g_pfnLogEvent
, hResourceHandleIn
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Failed to set the service start parameters for the '%1!s!' service. Error: %2!u!.\n"
, CLIPBOOKSERVER_SVCNAME
, sc
);
goto Cleanup;
} // if: error setting service start parameters
if ( hService != NULL )
{
CloseServiceHandle( hService );
hService = NULL;
} // if: hService is not NULL
//
// Make sure the service has been stopped.
//
sc = ResUtilStopResourceService( CLIPBOOKSERVER_SVCNAME );
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Failed to stop the '%1!s!' service. Error: %2!u!.\n"
, CLIPBOOKSERVER_SVCNAME
, sc
);
goto Cleanup;
} // if: error stopping the service
//
// Open the Parameters registry key for this resource.
//
sc = ClusterRegOpenKey(
hkeyResourceKeyIn
, L"Parameters"
, KEY_ALL_ACCESS
, &hkeyParameters
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to open Parameters key. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error creating the Parameters key for the resource
//
// Allocate a resource entry.
//
pResourceEntry = new CLIPBOOKSERVER_RESOURCE;
if ( pResourceEntry == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to allocate resource entry structure. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error allocating memory for the resource
//
// Initialize the resource entry..
//
ZeroMemory( pResourceEntry, sizeof( *pResourceEntry ) );
pResourceEntry->resid = static_cast< RESID >( pResourceEntry ); // for validation
pResourceEntry->hResourceHandle = hResourceHandleIn;
pResourceEntry->hkeyParameters = hkeyParameters;
pResourceEntry->state = ClusterResourceOffline;
//
// Save the name of the resource.
//
cchBuffer = lstrlenW( pwszResourceNameIn ) + 1;
pResourceEntry->pwszResourceName = new WCHAR[ cchBuffer ];
if ( pResourceEntry->pwszResourceName == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to allocate the resource name buffer. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error allocating memory for the name.
hr = StringCchCopyW( pResourceEntry->pwszResourceName, cchBuffer, pwszResourceNameIn );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to allocate the resource name buffer. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if:
//
// Open the cluster.
//
pResourceEntry->hCluster = OpenCluster( NULL );
if ( pResourceEntry->hCluster == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to open the cluster. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error opening the cluster
//
// Open the resource.
//
pResourceEntry->hResource = OpenClusterResource( pResourceEntry->hCluster, pwszResourceNameIn );
if ( pResourceEntry->hResource == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: Unable to open the resource. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error opening the resource
//
// Initialization for the resource.
//
//
// TODO: Add your custom resource open code here.
//
resid = static_cast< RESID >( pResourceEntry );
g_pSingleInstanceResourceClipBookServer = pResourceEntry;
sc = ERROR_SUCCESS;
Cleanup:
if ( resid == NULL )
{
assert( sc != ERROR_SUCCESS );
(g_pfnLogEvent)(
hResourceHandleIn
, LOG_ERROR
, L"Open: failed with error %1!u!.\n"
, sc
);
if ( hkeyParameters != NULL )
{
ClusterRegCloseKey( hkeyParameters );
} // if:
if ( pResourceEntry != NULL )
{
if ( pResourceEntry->hResource != NULL )
{
CloseClusterResource( pResourceEntry->hResource );
} // if:
if ( pResourceEntry->hCluster != NULL )
{
CloseCluster( pResourceEntry->hCluster );
} // if:
delete [] pResourceEntry->pwszResourceName;
delete pResourceEntry;
} // if: resource entry allocated
} // if: something failed
if ( hService != NULL )
{
CloseServiceHandle( hService );
hService = NULL;
} // if: hService is not NULL
SetLastError( sc );
return resid;
} //*** ClipBookServerOpen
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerClose
//
// Description:
// Close routine for ClipBook Server resources.
//
// Close the specified resource and deallocate all structures, etc.,
// allocated in the Open call. If the resource is not in the offline
// state, then the resource should be taken offline (by calling
// Terminate) before the close operation is performed.
//
// Arguments:
// residIn
// Supplies the resource ID of the resource to close.
//
// Return Value:
// None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void WINAPI
ClipBookServerClose(
RESID residIn
)
{
PCLIPBOOKSERVER_RESOURCE pResourceEntry = NULL;
DWORD sc = ERROR_SUCCESS;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PCLIPBOOKSERVER_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "ClipBookServer: Close request for a nonexistent resource id.\n" );
sc = ERROR_RESOURCE_NOT_FOUND;
goto Cleanup;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Close resource sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
sc = ERROR_RESOURCE_NOT_FOUND;
goto Cleanup;
} // if: invalid resource ID
if ( pResourceEntry->pwszResourceName == NULL )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Close request for resource with resid 0x%1!08x!.\n"
, residIn
);
} // if: resource name is null...
else
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Close request for resource '%1!s!'.\n"
, pResourceEntry->pwszResourceName
);
} // else: resource name is not null...
//
// Close the Parameters key.
//
if ( pResourceEntry->hkeyParameters != NULL )
{
ClusterRegCloseKey( pResourceEntry->hkeyParameters );
pResourceEntry->hkeyParameters = NULL;
} // if: parameters key is open
//
// Close the cluster handle.
//
if ( pResourceEntry->hCluster != NULL )
{
CloseCluster( pResourceEntry->hCluster );
pResourceEntry->hCluster = NULL;
} // if: cluster handle is open
//
// Close the resource handle.
//
if ( pResourceEntry->hResource != NULL )
{
CloseClusterResource( pResourceEntry->hResource );
pResourceEntry->hResource = NULL;
} // if: resource handle is open
//
// Clean up the semaphore if this is the single resource instance.
//
if ( pResourceEntry == g_pSingleInstanceResourceClipBookServer )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Close: Setting semaphore '%1!s!'.\n"
, CLIPBOOKSERVER_SINGLE_INSTANCE_SEMAPHORE
);
g_pSingleInstanceResourceClipBookServer = NULL;
ReleaseSemaphore( g_hSingleInstanceSemaphoreClipBookServer, 1 , NULL );
} // if: this is the single resource instance
//
// Release the service handle in case it is still open.
//
if ( pResourceEntry->hService != NULL )
{
CloseServiceHandle( pResourceEntry->hService );
pResourceEntry->hService = NULL;
} // if:
// ADDPARAM: Add new properties here.
//
// The ResUtil API's use LocalAlloc, so use LocalFree on the prop list members.
//
//
// Deallocate the resource entry.
//
delete [] pResourceEntry->pwszResourceName;
delete pResourceEntry;
sc = ERROR_SUCCESS;
Cleanup:
SetLastError( sc );
return;
} //*** ClipBookServerClose
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerOnline
//
// Description:
// Online routine for ClipBook Server resources.
//
// Bring the specified resource online (available for use). The resource
// DLL should attempt to arbitrate for the resource if it is present on
// a shared medium, like a shared SCSI bus.
//
// Arguments:
// residIn
// Supplies the resource ID of the resource to be brought online
// (available for use).
//
// phEventHandleOut
// Returns a signalable handle that is signaled when the resource DLL
// detects a failure on the resource. This argument derferences as NULL
// on input, and the resource DLL returns NULL if asynchronous
// notification of failurs is not supported. Otherwise this must be
// the address of a handle that is signaled on resource failures.
//
// Return Value:
// ERROR_SUCCESS
// The operation was successful, and the resource is now online.
//
// ERROR_RESOURCE_NOT_FOUND
// Resource ID is not valid.
//
// ERROR_RESOURCE_NOT_AVAILABLE
// If the resource was arbitrated with some other systems and one of
// the other systems won the arbitration.
//
// ERROR_IO_PENDING
// The request is pending. A thread has been activated to process
// the online request. The thread that is processing the online
// request will periodically report status by calling the
// SetResourceStatus callback method until the resource is placed
// into the ClusterResourceOnline state (or the resource monitor
// decides to timeout the online request and Terminate the resource.
// This pending timeout value is settable and has a default value of
// 3 minutes.).
//
// Win32 error code
// The operation failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
ClipBookServerOnline(
RESID residIn
, PHANDLE phEventHandleOut
)
{
PCLIPBOOKSERVER_RESOURCE pResourceEntry = NULL;
DWORD sc = ERROR_SUCCESS;
UNREFERENCED_PARAMETER( phEventHandleOut );
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PCLIPBOOKSERVER_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "ClipBook Server: Online request for a nonexistent resource id.\n" );
return ERROR_RESOURCE_NOT_FOUND;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Online sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return ERROR_RESOURCE_NOT_FOUND;
} // if: invalid resource ID
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Online request.\n"
);
//
// Start the Online thread to perform the online operation.
//
pResourceEntry->state = ClusterResourceOnlinePending;
ClusWorkerTerminate( &pResourceEntry->cwWorkerThread );
sc = ClusWorkerCreate(
&pResourceEntry->cwWorkerThread
, reinterpret_cast< PWORKER_START_ROUTINE >( ClipBookServerOnlineThread )
, pResourceEntry
);
if ( sc != ERROR_SUCCESS )
{
pResourceEntry->state = ClusterResourceFailed;
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Online: Unable to start thread. Error: %1!u!.\n"
, sc
);
} // if: error creating the worker thread
else
{
sc = ERROR_IO_PENDING;
} // if: worker thread created successfully
return sc;
} //*** ClipBookServerOnline
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerOnlineThread
//
// Description:
// Worker function which brings a resource online.
// This function is executed in a separate thread.
//
// Arguments:
// pWorkerIn
// Supplies the worker thread structure.
//
// pResourceEntryIn
// A pointer to the CLIPBOOKSERVER_RESOURCE block for this resource.
//
// Return Value:
// ERROR_SUCCESS
// The operation completed successfully.
//
// Win32 error code
// The operation failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
ClipBookServerOnlineThread(
PCLUS_WORKER pWorkerIn
, PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
)
{
RESOURCE_STATUS resourceStatus;
DWORD sc = ERROR_SUCCESS;
DWORD nPrevCheckpoint;
DWORD cbBytesNeeded;
SERVICE_STATUS_PROCESS ServiceStatus = { 0 };
RESOURCE_EXIT_STATE resExitState;
UNREFERENCED_PARAMETER( pWorkerIn );
ResUtilInitializeResourceStatus( &resourceStatus );
resourceStatus.ResourceState = ClusterResourceFailed;
resourceStatus.WaitHint = 0;
resourceStatus.CheckPoint = 1;
//
// Parameter checking.
//
if ( pResourceEntryIn == NULL )
{
sc = ERROR_INVALID_PARAMETER;
goto Cleanup;
} // if:
//
// If we require dependencies them make sure we have them set.
//
sc = ClipBookServerVerifyRequiredDependencies( pResourceEntryIn );
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Required dependency not found. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: required dependencies are missing
//
// Create the new environment with the simulated net name when the
// services queries GetComputerName.
//
if ( ClusWorkerCheckTerminate( pWorkerIn ) == TRUE )
{
goto Cleanup;
} // if: terminating
sc = ResUtilSetResourceServiceEnvironment(
CLIPBOOKSERVER_SVCNAME
, pResourceEntryIn->hResource
, g_pfnLogEvent
, pResourceEntryIn->hResourceHandle
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Failed to set service environment. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error setting the environment for the service
//
// Stop the service if it's running since we are about to change
// its parameters.
//
sc = ResUtilStopResourceService( CLIPBOOKSERVER_SVCNAME );
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Failed to stop the '%1!s!' service. Error: %2!u!.\n"
, CLIPBOOKSERVER_SVCNAME
, sc
);
goto Cleanup;
} // if: error stopping the service
//
// Make sure the service is ready to be controlled by the cluster.
//
if ( ClusWorkerCheckTerminate( pWorkerIn ) == TRUE )
{
goto Cleanup;
} // if: terminating
sc = ResUtilSetResourceServiceStartParameters(
CLIPBOOKSERVER_SVCNAME
, g_schSCMHandle
, &pResourceEntryIn->hService
, g_pfnLogEvent
, pResourceEntryIn->hResourceHandle
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Failed to set service start parameters. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: error setting service start parameters
//
// Perform resource-specific initialization before starting the service.
//
//
// TODO: Add code to initialize the resource before starting the service.
//
//
// Start the service.
//
if ( ! StartServiceW( pResourceEntryIn->hService, 0, NULL ) )
{
sc = GetLastError();
if ( sc != ERROR_SERVICE_ALREADY_RUNNING )
{
//
// TODO: Log event to the event log.
//
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Failed to start the '%1!s!' service. Error: %2!u!.\n"
, CLIPBOOKSERVER_SVCNAME
, sc
);
sc = ERROR_SERVICE_NEVER_STARTED;
goto Cleanup;
} // if: error other than service already running occurred
} // if: error starting the service
//
// Query the status of the service in a loop until it leaves
// the pending state.
//
sc = ERROR_SUCCESS;
resourceStatus.ResourceState = ClusterResourceOnlinePending;
nPrevCheckpoint = 0;
while ( ! ClusWorkerCheckTerminate( pWorkerIn ) )
{
//
// Query the service status.
//
if ( ! QueryServiceStatusEx(
pResourceEntryIn->hService
, SC_STATUS_PROCESS_INFO
, reinterpret_cast< LPBYTE >( &ServiceStatus )
, sizeof( SERVICE_STATUS_PROCESS )
, &cbBytesNeeded
)
)
{
sc = GetLastError();
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Failed to query service status for the '%1!s!' service. Error: %2!u!.\n"
, CLIPBOOKSERVER_SVCNAME
, sc
);
resourceStatus.ResourceState = ClusterResourceFailed;
goto Cleanup;
} // if: error querying service status
//
// If the service is no longer pending, we are done.
//
if ( ServiceStatus.dwCurrentState != SERVICE_START_PENDING )
{
break;
} // if: service state is not pending
//
// If the service checkpoint value changed, use it when notifying
// the Resource Monitor of our current status.
//
if ( nPrevCheckpoint != ServiceStatus.dwCheckPoint )
{
nPrevCheckpoint = ServiceStatus.dwCheckPoint;
resourceStatus.CheckPoint++;
} // if:
//
// Notify the Resource Monitor of our current state.
//
resExitState = static_cast< RESOURCE_EXIT_STATE >(
(g_pfnSetResourceStatus)(
pResourceEntryIn->hResourceHandle
, &resourceStatus
)
);
if ( resExitState != ResourceExitStateContinue )
{
goto Cleanup;
} // if: resource is being terminated
//
// Check again in 1/2 second.
//
Sleep( 500 );
} // while: not terminating while querying the status of the service
//
// Assume that we failed.
//
resourceStatus.ResourceState = ClusterResourceFailed;
//
// If we exited the loop before setting ServiceStatus, then return now.
//
if ( ClusWorkerCheckTerminate( pWorkerIn ) == TRUE )
{
goto Cleanup;
} // if: being terminated
if ( ServiceStatus.dwCurrentState != SERVICE_RUNNING )
{
sc = ERROR_SERVICE_NEVER_STARTED;
//
// TODO: Log event to the event log
//
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Failed to start the '%1!s!' service. Error: %2!u!.\n"
, CLIPBOOKSERVER_SVCNAME
, sc
);
goto Cleanup;
} // if: service not running when loop exited
//
// Set status to online and save process ID of the service.
// This is used to enable us to terminate the resource more
// effectively.
//
resourceStatus.ResourceState = ClusterResourceOnline;
if ( ! (ServiceStatus.dwServiceFlags & SERVICE_RUNS_IN_SYSTEM_PROCESS) )
{
pResourceEntryIn->dwProcessId = ServiceStatus.dwProcessId;
} // if: not running in the system process
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_INFORMATION
, L"The '%1!s!' service is now online.\n"
, CLIPBOOKSERVER_SVCNAME
);
Cleanup:
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"OnlineThread: Error %1!u! bringing resource online.\n"
, sc
);
if ( pResourceEntryIn->hService != NULL )
{
CloseServiceHandle( pResourceEntryIn->hService );
pResourceEntryIn->hService = NULL;
} // if: service handle was opened
} // if: error occurred
pResourceEntryIn->state = resourceStatus.ResourceState;
g_pfnSetResourceStatus( pResourceEntryIn->hResourceHandle, &resourceStatus );
return sc;
} //*** ClipBookServerOnlineThread
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerOffline
//
// Description:
// Offline routine for ClipBook Server resources.
//
// Take the specified resource offline (unavailable for use). Wait
// for any cleanup operations to complete before returning.
//
// Arguments:
// residIn
// Supplies the resource ID of the resource to be shutdown
// gracefully.
//
// Return Value:
// ERROR_SUCCESS
// The operation was successful, and the resource is now offline.
//
// ERROR_RESOURCE_NOT_FOUND
// Resource ID is not valid.
//
// ERROR_RESOURCE_NOT_AVAILABLE
// If the resource was arbitrated with some other systems and one of
// the other systems won the arbitration.
//
// ERROR_IO_PENDING
// The request is still pending. A thread has been activated to
// process the offline request. The thread that is processing the
// offline request will periodically report status by calling the
// SetResourceStatus callback method until the resource is placed
// into the ClusterResourceOffline state (or the resource monitor
// decides to timeout the offline request and Terminate the
// resource).
//
// Win32 error code
// The operation failed. This will cause the Resource Monitor to
// log an event and call the Terminate routine.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
ClipBookServerOffline(
RESID residIn
)
{
PCLIPBOOKSERVER_RESOURCE pResourceEntry = NULL;
DWORD sc = ERROR_SUCCESS;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PCLIPBOOKSERVER_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "ClipBook Server: Offline request for a nonexistent resource id.\n" );
sc = ERROR_RESOURCE_NOT_FOUND;
goto Cleanup;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Offline resource sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
sc = ERROR_RESOURCE_NOT_FOUND;
goto Cleanup;
} // if: invalid resource ID
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Offline request.\n"
);
//
// Start the Offline thread to perform the offline operation.
//
pResourceEntry->state = ClusterResourceOfflinePending;
ClusWorkerTerminate( &pResourceEntry->cwWorkerThread );
sc = ClusWorkerCreate(
&pResourceEntry->cwWorkerThread
, reinterpret_cast< PWORKER_START_ROUTINE >( ClipBookServerOfflineThread )
, pResourceEntry
);
if ( sc != ERROR_SUCCESS )
{
pResourceEntry->state = ClusterResourceFailed;
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Offline: Unable to start thread. Error: %1!u!.\n"
, sc
);
} // if: error creating the worker thread
else
{
sc = ERROR_IO_PENDING;
} // if: worker thread created successfully
Cleanup:
return sc;
} //*** ClipBookServerOffline
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerOfflineThread
//
// Description:
// Worker function which takes a resource offline.
// This function is executed in a separate thread.
//
// Arguments:
// pWorkerIn
// Supplies the worker thread structure.
//
// pResourceEntryIn
// A pointer to the CLIPBOOKSERVER_RESOURCE block for this resource.
//
// Return Value:
// ERROR_SUCCESS
// The operation completed successfully.
//
// Win32 error code
// The operation failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI
ClipBookServerOfflineThread(
PCLUS_WORKER pWorkerIn
, PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
)
{
RESOURCE_STATUS resourceStatus;
DWORD sc = ERROR_SUCCESS;
DWORD nRetryTime = 300; // 300 msec at a time
BOOL fDidStop = FALSE;
SERVICE_STATUS ServiceStatus;
ResUtilInitializeResourceStatus( &resourceStatus );
resourceStatus.ResourceState = ClusterResourceFailed;
resourceStatus.WaitHint = 0;
resourceStatus.CheckPoint = 1;
//
// If the service has gone offline or was never brought online then we're done.
//
if ( pResourceEntryIn->hService == NULL )
{
resourceStatus.ResourceState = ClusterResourceOffline;
goto Cleanup;
}
//
// Try to stop the service. Wait for it to terminate as long as we're not asked to terminate.
//
while ( ClusWorkerCheckTerminate( pWorkerIn ) == FALSE )
{
sc = (ControlService(
pResourceEntryIn->hService
, (fDidStop
? SERVICE_CONTROL_INTERROGATE
: SERVICE_CONTROL_STOP)
, &ServiceStatus
)
? ERROR_SUCCESS
: GetLastError()
);
if ( sc == ERROR_SUCCESS )
{
fDidStop = TRUE;
if ( ServiceStatus.dwCurrentState == SERVICE_STOPPED )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_INFORMATION
, L"OfflineThread: The '%1!s!' service stopped.\n"
, CLIPBOOKSERVER_SVCNAME
);
//
// Set the status to offline.
//
resourceStatus.ResourceState = ClusterResourceOffline;
CloseServiceHandle( pResourceEntryIn->hService );
pResourceEntryIn->hService = NULL;
pResourceEntryIn->dwProcessId = 0;
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_INFORMATION
, L"OfflineThread: Service is now offline.\n"
);
break;
} // if: current service state is STOPPED
} // if: ControlService completed successfully
if ( ( sc == ERROR_EXCEPTION_IN_SERVICE )
|| ( sc == ERROR_PROCESS_ABORTED )
|| ( sc == ERROR_SERVICE_NOT_ACTIVE )
)
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_INFORMATION
, L"OfflineThread: The '%1!s!' service died or is not active any more; status = %2!u!.\n"
, CLIPBOOKSERVER_SVCNAME
, sc
);
//
// Set the status to offline.
//
resourceStatus.ResourceState = ClusterResourceOffline;
CloseServiceHandle( pResourceEntryIn->hService );
pResourceEntryIn->hService = NULL;
pResourceEntryIn->dwProcessId = 0;
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_INFORMATION
, L"OfflineThread: Service is now offline.\n"
);
break;
} // if: service stopped abnormally
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_INFORMATION
, L"OfflineThread: retrying...\n"
);
Sleep( nRetryTime );
} // while: not asked to terminate
//
// Undo the temporary changes to the service config.
//
ResUtilRemoveResourceServiceEnvironment(
CLIPBOOKSERVER_SVCNAME
, g_pfnLogEvent
, pResourceEntryIn->hResourceHandle
);
Cleanup:
pResourceEntryIn->state = resourceStatus.ResourceState;
g_pfnSetResourceStatus( pResourceEntryIn->hResourceHandle, &resourceStatus );
return sc;
} //*** ClipBookServerOfflineThread
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerTerminate
//
// Description:
// Terminate routine for ClipBook Server resources.
//
// Take the specified resource offline immediately (the resource is
// unavailable for use).
//
// Arguments:
// residIn
// Supplies the resource ID of the resource to be shutdown
// ungracefully.
//
// Return Value:
// None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void WINAPI
ClipBookServerTerminate(
RESID residIn
)
{
PCLIPBOOKSERVER_RESOURCE pResourceEntry = NULL;
DWORD sc = ERROR_SUCCESS;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PCLIPBOOKSERVER_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "ClipBook Server: Terminate request for a nonexistent resource id.\n" );
return;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Terminate resource sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return;
} // if: invalid resource ID
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Terminate request.\n"
);
//
// Kill off any pending threads.
//
ClusWorkerTerminate( &pResourceEntry->cwWorkerThread );
if ( pResourceEntry->hService != NULL )
{
DWORD nTotalRetryTime = 30*1000; // Wait 30 secs for shutdown
DWORD nRetryTime = 300; // 300 msec at a time
BOOL fDidStop = FALSE;
SERVICE_STATUS ServiceStatus;
for ( ;; )
{
sc = (ControlService(
pResourceEntry->hService
, (fDidStop
? SERVICE_CONTROL_INTERROGATE
: SERVICE_CONTROL_STOP)
, &ServiceStatus
)
? ERROR_SUCCESS
: GetLastError()
);
if ( sc == ERROR_SUCCESS )
{
fDidStop = TRUE;
if ( ServiceStatus.dwCurrentState == SERVICE_STOPPED )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Terminate: The '%1!s!' service stopped.\n"
, CLIPBOOKSERVER_SVCNAME
);
break;
} // if: current service state is STOPPED
} // if: ControlService completed successfully
if ( ( sc == ERROR_EXCEPTION_IN_SERVICE )
|| ( sc == ERROR_PROCESS_ABORTED )
|| ( sc == ERROR_SERVICE_NOT_ACTIVE )
)
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Terminate: Service died; status = %1!u!.\n"
, sc
);
break;
} // if: service stopped abnormally
if ( (nTotalRetryTime -= nRetryTime) == 0 )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"Terminate: Service did not stop; giving up.\n"
);
break;
} // if: retried too many times
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Terminate: retrying...\n"
);
Sleep( nRetryTime );
} // for: forever
//
// Declare the service offline. It may not truly be offline, so
// if there is a pid for this service, try and terminate that process.
// Note that terminating a process doesnt terminate all the child
// processes.
//
if ( pResourceEntry->dwProcessId != 0 )
{
HANDLE hSvcProcess = NULL;
hSvcProcess = OpenProcess(
PROCESS_TERMINATE
, FALSE
, pResourceEntry->dwProcessId
);
if ( hSvcProcess != NULL )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"Terminate: Terminating processid %1!u!\n"
, pResourceEntry->dwProcessId
);
TerminateProcess( hSvcProcess, 0 );
CloseHandle( hSvcProcess );
} // if: opened process successfully
} // if: service process ID available
CloseServiceHandle( pResourceEntry->hService );
pResourceEntry->hService = NULL;
pResourceEntry->dwProcessId = 0;
} // if: service was started
//
// Undo the temporary changes to the service config.
//
ResUtilRemoveResourceServiceEnvironment(
CLIPBOOKSERVER_SVCNAME
, g_pfnLogEvent
, pResourceEntry->hResourceHandle
);
pResourceEntry->state = ClusterResourceFailed;
return;
} //*** ClipBookServerTerminate
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerLooksAlive
//
// Description:
// LooksAlive routine for ClipBook Server resources.
//
// Perform a quick check to determine if the specified resource is
// probably online (available for use). This call should not block for
// more than 300 ms, preferably less than 50 ms.
//
// Arguments:
// residIn
// Supplies the resource ID for the resource to be polled.
//
// Return Value:
// TRUE
// The specified resource is probably online and available for use.
//
// FALSE
// The specified resource is not functioning normally. The IsAlive
// function will be called to perform a more thorough check.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL WINAPI
ClipBookServerLooksAlive(
RESID residIn
)
{
PCLIPBOOKSERVER_RESOURCE pResourceEntry = NULL;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PCLIPBOOKSERVER_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "ClipBook Server: LooksAlive request for a nonexistent resource id.\n" );
return FALSE;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"LooksAlive sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return FALSE;
} // if: invalid resource ID
#ifdef LOG_VERBOSE
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"LooksAlive request.\n"
);
#endif
//
// TODO: LooksAlive code
//
//
// NOTE: LooksAlive should be a quick check to see if the resource is
// available or not, whereas IsAlive should be a thorough check. If
// there are no differences between a quick check and a thorough check,
// IsAlive can be called for LooksAlive, as it is below. However, if there
// are differences, replace the call to IsAlive below with your quick
// check code.
//
//
// Check to see if the resource is alive.
//
return ClipBookServerCheckIsAlive( pResourceEntry, FALSE /* fFullCheck */ );
} //*** ClipBookServerLooksAlive
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerIsAlive
//
// Description:
// IsAlive routine for ClipBook Server resources.
//
// Perform a thorough check to determine if the specified resource is
// online (available for use). This call should not block for more
// more than 300 ms, preferably less than 50 ms. If it must block for
// longer than this, create a separate thread dedicated to polling for
// this information and have this routine return the status of the last
// poll performed.
//
// Arguments:
// residIn
// Supplies the resource ID for the resource to be polled.
//
// Return Value:
// TRUE
// The specified resource is online and functioning normally.
//
// FALSE
// The specified resource is not functioning normally. The resource
// will be terminated and then Online will be called.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL WINAPI
ClipBookServerIsAlive(
RESID residIn
)
{
PCLIPBOOKSERVER_RESOURCE pResourceEntry = NULL;
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PCLIPBOOKSERVER_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "ClipBook Server: IsAlive request for a nonexistent resource id.\n" );
return FALSE;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"IsAlive sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return FALSE;
} // if: invalid resource ID
#ifdef LOG_VERBOSE
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_INFORMATION
, L"IsAlive request.\n"
);
#endif
//
// Check to see if the resource is alive.
//
return ClipBookServerCheckIsAlive( pResourceEntry, TRUE /* fFullCheck */ );
} //** ClipBookServerIsAlive
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerCheckIsAlive
//
// Description:
// Check to see if the resource is alive for ClipBook Server
// resources.
//
// Arguments:
// pResourceEntryIn
// Supplies the resource entry for the resource to polled.
//
// fFullCheckIn
// TRUE = Perform a full check.
// FALSE = Perform a cursory check.
//
// Return Value:
// TRUE
// The specified resource is online and functioning normally.
//
// FALSE
// The specified resource is not functioning normally.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL
ClipBookServerCheckIsAlive(
PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
, BOOL fFullCheckIn
)
{
BOOL fIsAlive = TRUE;
DWORD sc;
//
// Check to see if the resource is alive.
//
sc = ResUtilVerifyService( pResourceEntryIn->hService );
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"CheckIsAlive: Verification of the '%1!s!' service failed. Error: %2!u!.\n"
, CLIPBOOKSERVER_SVCNAME
, sc
);
fIsAlive = FALSE;
goto Cleanup;
} // if: error verifying service
if ( fFullCheckIn )
{
//
// TODO: Add code to perform a full check.
//
} // if: performing a full check
Cleanup:
return fIsAlive;
} //*** ClipBookServerCheckIsAlive
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerResourceControl
//
// Description:
// ResourceControl routine for ClipBook Server resources.
//
// Perform the control request specified by nControlCode on the specified
// resource.
//
// Arguments:
// residIn
// Supplies the resource ID for the specific resource.
//
// nControlCodeIn
// Supplies the control code that defines the action to be performed.
//
// pInBufferIn
// Supplies a pointer to a buffer containing input data.
//
// cbInBufferSizeIn
// Supplies the size, in bytes, of the data pointed to by pInBufferIn.
//
// pOutBufferOut
// Supplies a pointer to the output buffer to be filled in.
//
// cbOutBufferSizeIn
// Supplies the size, in bytes, of the available space pointed to by
// pOutBufferOut
//
// pcbBytesReturnedOut
// Returns the number of bytes of pOutBufferOut actually filled in by
// the resource. If pOutBufferOut is too small, pcbBytesReturnedOut
// contains the total number of bytes for the operation to succeed.
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// ERROR_RESOURCE_NOT_FOUND
// Resource ID is not valid.
//
// ERROR_MORE_DATA
// The output buffer is too small to return the data.
// pcbBytesReturnedOut contains the required size.
//
// 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 WINAPI
ClipBookServerResourceControl(
RESID residIn
, DWORD nControlCodeIn
, PVOID pInBufferIn
, DWORD cbInBufferSizeIn
, PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
)
{
DWORD sc = ERROR_SUCCESS;
PCLIPBOOKSERVER_RESOURCE pResourceEntry = NULL;
UNREFERENCED_PARAMETER( cbInBufferSizeIn );
//
// Verify we have a valid resource ID.
//
pResourceEntry = static_cast< PCLIPBOOKSERVER_RESOURCE >( residIn );
if ( pResourceEntry == NULL )
{
DBG_PRINT( "ClipBook Server: ResourceControl request for a nonexistent resource id.\n" );
return ERROR_RESOURCE_NOT_FOUND;
} // if: NULL resource ID
if ( pResourceEntry->resid != residIn )
{
(g_pfnLogEvent)(
pResourceEntry->hResourceHandle
, LOG_ERROR
, L"ResourceControl sanity check failed! resid = 0x%1!08x!.\n"
, residIn
);
return ERROR_RESOURCE_NOT_FOUND;
} // if: invalid resource ID
switch ( nControlCodeIn )
{
case CLUSCTL_RESOURCE_UNKNOWN:
*pcbBytesReturnedOut = 0;
sc = ERROR_SUCCESS;
break;
case CLUSCTL_RESOURCE_SET_NAME:
sc = ClipBookServerSetNameHandler( pResourceEntry, static_cast< LPWSTR >( pInBufferIn ) );
break;
case CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES:
sc = ClipBookServerGetRequiredDependencies( pOutBufferOut, cbOutBufferSizeIn, pcbBytesReturnedOut );
break;
case CLUSCTL_RESOURCE_GET_CHARACTERISTICS:
case CLUSCTL_RESOURCE_GET_CLASS_INFO:
case CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO:
case CLUSCTL_RESOURCE_STORAGE_IS_PATH_VALID:
case CLUSCTL_RESOURCE_DELETE:
case CLUSCTL_RESOURCE_INSTALL_NODE:
case CLUSCTL_RESOURCE_EVICT_NODE:
case CLUSCTL_RESOURCE_ADD_DEPENDENCY:
case CLUSCTL_RESOURCE_REMOVE_DEPENDENCY:
case CLUSCTL_RESOURCE_ADD_OWNER:
case CLUSCTL_RESOURCE_REMOVE_OWNER:
case CLUSCTL_RESOURCE_CLUSTER_NAME_CHANGED:
case CLUSCTL_RESOURCE_CLUSTER_VERSION_CHANGED:
default:
sc = ERROR_INVALID_FUNCTION;
break;
} // switch: nControlCodeIn
return sc;
} //*** ClipBookServerResourceControl
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerGetRequiredDependencies
//
// Description:
// Processes the CLUSCTL_RESOURCE_GET_REQUIRED_DEPENDENCIES control
// function for resources of type ClipBook Server.
//
// Arguments:
// pOutBufferOut
// Supplies a pointer to the output buffer to be filled in.
//
// cbOutBufferSizeIn
// Supplies the size, in bytes, of the available space pointed to by
// pOutBuffer.
//
// pcbBytesReturnedOut
// Returns the number of bytes of pOutBufferOut actually filled in by
// the resource. If pOutBufferOut is too small, pcbBytesReturnedOut
// contains the total number of bytes for the operation to succeed.
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// ERROR_MORE_DATA
// The output buffer is too small to return the data.
// pcbBytesReturnedOut contains the required size.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
ClipBookServerGetRequiredDependencies(
PVOID pOutBufferOut
, DWORD cbOutBufferSizeIn
, LPDWORD pcbBytesReturnedOut
)
{
//
// TODO: Specify your resource's required dependencies here.
// The default is that the resource requires a dependency on a
// network name resource.
//
struct DEP_DATA
{
//
// TODO: Uncomment the following line to enable a dependency on a storage device.
//
//CLUSPROP_RESOURCE_CLASS rcStorage;
CLUSPROP_SZ_DECLARE( netnameEntry, RTL_NUMBER_OF( RESOURCE_TYPE_NETWORK_NAME ) );
CLUSPROP_SYNTAX endmark;
};
DEP_DATA * pdepdata = static_cast< DEP_DATA * >( pOutBufferOut );
DWORD sc = ERROR_SUCCESS;
HRESULT hr = S_OK;
*pcbBytesReturnedOut = sizeof( *pdepdata );
if ( cbOutBufferSizeIn < sizeof( *pdepdata ) )
{
if ( pOutBufferOut == NULL )
{
sc = ERROR_SUCCESS;
} // if: no buffer specified
else
{
sc = ERROR_MORE_DATA;
} // if: buffer specified
} // if: output buffer is too small
else
{
ZeroMemory( pdepdata, sizeof( *pdepdata ) );
//
// TODO: Uncomment the following lines to enable a dependency on a storage device.
//
//
// Add the storage device entry.
//
//pdepdata->rcStorage.Syntax.dw = CLUSPROP_SYNTAX_RESCLASS;
//pdepdata->rcStorage.cbLength = sizeof( pdepdata->rcStorage.rc );
//pdepdata->rcStorage.rc = CLUS_RESCLASS_STORAGE;
//
// Add the netname entry.
//
pdepdata->netnameEntry.Syntax.dw = CLUSPROP_SYNTAX_NAME;
pdepdata->netnameEntry.cbLength = sizeof( RESOURCE_TYPE_NETWORK_NAME );
hr = StringCchCopyW(
pdepdata->netnameEntry.sz
, RTL_NUMBER_OF( pdepdata->netnameEntry.sz )
, RESOURCE_TYPE_NETWORK_NAME
);
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
goto Cleanup;
} // if:
//
// Add the endmark.
//
pdepdata->endmark.dw = CLUSPROP_SYNTAX_ENDMARK;
sc = ERROR_SUCCESS;
} // else: output buffer is large enough
Cleanup:
return sc;
} //*** ClipBookServerGetRequiredDependencies
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerVerifyRequiredDependencies
//
// Description:
// Verifies that all of the required dependencies have been set. Called
// from OnlineThread.
//
// Arguments:
// pResourceEntryIn
// Supplies the resource entry on which to operate.
//
// Return Value:
// ERROR_SUCCESS
// All required dependencies have been met.
//
// ERROR_DEPENDENCY_NOT_FOUND
// A required dependency is missing.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD ClipBookServerVerifyRequiredDependencies(
PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
)
{
DWORD sc = ERROR_SUCCESS;
DWORD cbBuffer = 0;
PBYTE pbBuffer = NULL;
CLUSPROP_BUFFER_HELPER props;
HRESOURCE hResource = NULL;
//
// Get the list of required dependencies.
//
sc = ClipBookServerGetRequiredDependencies(
NULL
, 0
, &cbBuffer
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"VerifyRequiredDependencies: Failed to retrieve list of required dependencies. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if:
pbBuffer = new BYTE[ cbBuffer ];
if ( pbBuffer == NULL )
{
sc = ERROR_OUTOFMEMORY;
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"VerifyRequiredDependencies: Failed to allocate memory. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if:
sc = ClipBookServerGetRequiredDependencies(
pbBuffer
, cbBuffer
, &cbBuffer
);
if ( sc != ERROR_SUCCESS )
{
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"VerifyRequiredDependencies: Failed to retrieve list of required dependencies. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if:
//
// Assign the list.
//
props.pRequiredDependencyValue = (PCLUSPROP_REQUIRED_DEPENDENCY) pbBuffer;
//
// Loop through each required dependency and make sure
// there is a dependency on a resource of that type.
//
while ( props.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK )
{
//
// Check whether the dependency exists.
//
if ( props.pSyntax->dw == CLUSPROP_SYNTAX_RESCLASS )
{
//
// We have a dependency on a class of resource, such as storage devices.
// This allows us to depend on a storage resource type from any vendor
// and not just on Physical Disk.
//
hResource = ResUtilGetResourceDependencyByClass(
pResourceEntryIn->hCluster
, pResourceEntryIn->hResource
, props.pResourceClassInfoValue
, TRUE // bRecurse
);
if ( hResource == NULL )
{
sc = GetLastError();
if ( sc == ERROR_NO_MORE_ITEMS )
{
//
// If we failed with ERROR_NO_MORE_ITEMS then the resource enumeration
// completed without finding the dependency. Change sc to reflect the
// actual meaning of the error.
//
sc = ERROR_DEPENDENCY_NOT_FOUND;
} // if:
if ( props.pResourceClassInfoValue->rc == CLUS_RESCLASS_STORAGE )
{
//
// We require a storage class resource dependency.
//
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"VerifyRequiredDependencies: Missing storage resource dependency. Error: %1!u!.\n"
, sc
);
} // if: storage resclass dependency
else
{
//
// We require a custom-defined resource class dependency.
//
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"VerifyRequiredDependencies: Missing resource class dependency. Class: %1!u!. Error: %2!u!.\n"
, props.pResourceClassInfoValue->rc
, sc
);
} // else: custom resclass dependency
goto Cleanup;
} // if: hResource == NULL
props.pb += sizeof( *props.pResourceClassValue );
} // if: by resclass
else if ( props.pSyntax->dw == CLUSPROP_SYNTAX_NAME )
{
//
// We have a dependency on a particular resource type.
//
hResource = ResUtilGetResourceDependencyByName(
pResourceEntryIn->hCluster
, pResourceEntryIn->hResource
, props.pStringValue->sz
, TRUE // bRecurse
);
if ( hResource == NULL )
{
sc = GetLastError();
if ( sc == ERROR_NO_MORE_ITEMS )
{
//
// If we failed with ERROR_NO_MORE_ITEMS then the resource enumeration
// completed without finding the dependency. Change sc to reflect the
// actual meaning of the error.
//
sc = ERROR_DEPENDENCY_NOT_FOUND;
} // if:
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"VerifyRequiredDependencies: Missing a required depencency on '%1!s!'. Error: %2!u!.\n"
, props.pStringValue->sz
, sc
);
goto Cleanup;
} // if: hResource == NULL
props.pb += sizeof( *props.pStringValue ) + ALIGN_CLUSPROP( props.pStringValue->cbLength );
} // else if: by name
else
{
//
// If we got here then our list of required dependencies contains an invalid entry.
//
//
sc = ERROR_DEPENDENCY_NOT_FOUND;
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"VerifyRequiredDependencies: Invalid dependency type. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // else: unknown
CloseClusterResource( hResource );
hResource = NULL;
//
// Test to make sure we haven't gone past the end of the buffer.
//
if ( props.pb > (pbBuffer + cbBuffer) )
{
sc = ERROR_INVALID_DATA;
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"VerifyRequiredDependencies: Invalid property list detected. Error: %1!u!.\n"
, sc
);
goto Cleanup;
} // if: we've gone beyond the end of the buffer
} // while: !endmark
sc = ERROR_SUCCESS;
Cleanup:
if ( hResource != NULL )
{
CloseClusterResource( hResource );
} // if:
delete [] pbBuffer;
return sc;
} //*** ClipBookServerVerifyRequiredDependencies
/////////////////////////////////////////////////////////////////////////////
//++
//
// ClipBookServerSetNameHandler
//
// Description:
// Handle the CLUSCTL_RESOURCE_SET_NAME control code by saving the new
// name of the resource.
//
// Arguments:
// pResourceEntryIn
// Supplies the resource entry on which to operate.
//
// pwszNameIn
// The new name of the resource.
//
// Return Value:
// ERROR_SUCCESS
// The function completed successfully.
//
// Win32 error code
// The function failed.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
ClipBookServerSetNameHandler(
PCLIPBOOKSERVER_RESOURCE pResourceEntryIn
, LPWSTR pwszNameIn
)
{
DWORD sc = ERROR_SUCCESS;
DWORD cchNameBuffer = 0;
HRESULT hr = S_OK;
WCHAR * pwszTemp = NULL;
//
// Allocate a buffer for the new name.
//
cchNameBuffer = lstrlenW( pwszNameIn ) + 1;
pwszTemp = new WCHAR[ cchNameBuffer ];
if ( pwszTemp == NULL )
{
sc = GetLastError();
(g_pfnLogEvent)(
pResourceEntryIn->hResourceHandle
, LOG_ERROR
, L"Failed to allocate memory for the new resource name '%1!s!'. Error: %2!u!.\n"
, pwszNameIn
, sc
);
goto Cleanup;
} // if: error allocating memory for the name.
//
// Copy the new name to the new buffer.
//
hr = StringCchCopyW( pwszTemp, cchNameBuffer, pwszNameIn );
if ( FAILED( hr ) )
{
sc = HRESULT_CODE( hr );
goto Cleanup;
} // if:
//
// All went well, free the old buffer and assign the new one.
//
delete [] pResourceEntryIn->pwszResourceName;
pResourceEntryIn->pwszResourceName = pwszTemp;
pwszTemp = NULL;
Cleanup:
delete [] pwszTemp;
return sc;
} //*** ClipBookServerSetNameHandler
/////////////////////////////////////////////////////////////////////////////
//
// Define Function Table
//
/////////////////////////////////////////////////////////////////////////////
CLRES_V1_FUNCTION_TABLE(
g_ClipBookServerFunctionTable, // Name
CLRES_VERSION_V1_00, // Version
ClipBookServer, // Prefix
NULL, // Arbitrate
NULL, // Release
ClipBookServerResourceControl, // ResControl
NULL // ResTypeControl
);