Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1865 lines
49 KiB

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