1491 lines
40 KiB
C
1491 lines
40 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1995-1999 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
service.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Service control functions for the Cluster Service.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Mike Massa (mikemas) 2-Jan-1996
|
|||
|
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <initp.h>
|
|||
|
#include <shellapi.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include <clusverp.h>
|
|||
|
|
|||
|
//
|
|||
|
// Public data
|
|||
|
//
|
|||
|
#define CLUSTER_SERVICE_CONTROLS (SERVICE_ACCEPT_STOP | \
|
|||
|
SERVICE_ACCEPT_SHUTDOWN )
|
|||
|
|
|||
|
ULONG CsLogLevel=LOG_UNUSUAL;
|
|||
|
PCLRTL_WORK_QUEUE CsDelayedWorkQueue = NULL;
|
|||
|
PCLRTL_WORK_QUEUE CsCriticalWorkQueue = NULL;
|
|||
|
LPWSTR CsClusterName = NULL;
|
|||
|
SERVICE_STATUS CsServiceStatus = {
|
|||
|
SERVICE_WIN32_OWN_PROCESS, // dwServiceType
|
|||
|
SERVICE_STOPPED, // dwCurrentState
|
|||
|
CLUSTER_SERVICE_CONTROLS, // dwControlsAccepted
|
|||
|
ERROR_SUCCESS, // dwWin32ExitCode
|
|||
|
ERROR_SUCCESS, // dwServiceSpecificExitCode
|
|||
|
1, // dwCheckPoint
|
|||
|
90000 // dwWaitHint - 90 seconds -nm uses 90 sec timeout
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// internal cluster versions. The major version is bumped during
|
|||
|
// product releases (which could include service pack releases).
|
|||
|
//
|
|||
|
DWORD CsMyHighestVersion = CLUSTER_MAKE_VERSION(
|
|||
|
CLUSTER_INTERNAL_CURRENT_MAJOR_VERSION,
|
|||
|
VER_PRODUCTBUILD);
|
|||
|
|
|||
|
DWORD CsMyLowestVersion = CLUSTER_INTERNAL_PREVIOUS_HIGHEST_VERSION;
|
|||
|
|
|||
|
//initialize by calling an rtl funcion
|
|||
|
SUITE_TYPE CsMyProductSuite;
|
|||
|
|
|||
|
DWORD CsClusterHighestVersion;
|
|||
|
|
|||
|
DWORD CsClusterLowestVersion;
|
|||
|
|
|||
|
DWORD CsClusterNodeLimit;
|
|||
|
|
|||
|
SHUTDOWN_TYPE CsShutdownRequest = CsShutdownTypeStop;
|
|||
|
|
|||
|
//
|
|||
|
// used to turn authenticated RPC on or off
|
|||
|
//
|
|||
|
// This should always be defined, since none of the other code has
|
|||
|
// the checks for this variable conditionalized
|
|||
|
//
|
|||
|
BOOL CsUseAuthenticatedRPC = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// domain and user account under which the service is run
|
|||
|
//
|
|||
|
LPWSTR CsServiceDomainAccount;
|
|||
|
|
|||
|
//
|
|||
|
// security packages to use during the join for authenticated RPC; JoinVersion
|
|||
|
// determines which package will be used by the ExtroCluster interface.
|
|||
|
// CsRPCSecurityPackageInUse reflects that choice. The package used for the
|
|||
|
// Intracluster interface is negotiated separately.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// when using kerberos with RPC, RPC calls fail with 1825 (sec. pkg error)
|
|||
|
// somewhere between 30 minutes and 12 hours. For beta 2, we'll revert back to
|
|||
|
// NTLM where expiration is not a problem.
|
|||
|
//
|
|||
|
|
|||
|
//DWORD CsRPCSecurityPackage[] = { RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_WINNT };
|
|||
|
//LPWSTR CsRPCSecurityPackageName[] = { L"Kerberos", L"NTLM" };
|
|||
|
|
|||
|
DWORD CsRPCSecurityPackage[] = { RPC_C_AUTHN_WINNT };
|
|||
|
LPWSTR CsRPCSecurityPackageName[] = { L"NTLM" };
|
|||
|
DWORD CsNumberOfRPCSecurityPackages = sizeof( CsRPCSecurityPackage ) / sizeof( CsRPCSecurityPackage[0] );
|
|||
|
LONG CsRPCSecurityPackageIndex = -1;
|
|||
|
|
|||
|
//
|
|||
|
// Public Debug Data
|
|||
|
//
|
|||
|
#if 1 // CLUSTER_BETA
|
|||
|
|
|||
|
BOOL CsDebugResmon = FALSE;
|
|||
|
LPWSTR CsResmonDebugCmd;
|
|||
|
|
|||
|
BOOL CsNoVersionCheck = FALSE;
|
|||
|
#endif
|
|||
|
|
|||
|
#if DBG // DBG
|
|||
|
|
|||
|
ULONG CsDebugFlags = CS_DBG_ALL;
|
|||
|
|
|||
|
#endif // DBG
|
|||
|
|
|||
|
#ifdef CLUSTER_TESTPOINT
|
|||
|
|
|||
|
DWORD CsTestPoint = 0;
|
|||
|
DWORD CsTestTrigger = TestTriggerNever;
|
|||
|
DWORD CsTestAction = TestActionTrue;
|
|||
|
BOOL CsPersistentTestPoint = FALSE;
|
|||
|
|
|||
|
#endif // CLUSTER_TESTPOINT
|
|||
|
|
|||
|
BOOL CsUpgrade = FALSE;
|
|||
|
BOOL CsFirstRun = FALSE;
|
|||
|
BOOL CsNoQuorumLogging = FALSE;
|
|||
|
BOOL CsUserTurnedOffQuorumLogging = FALSE;
|
|||
|
BOOL CsNoQuorum = FALSE;
|
|||
|
BOOL CsResetQuorumLog = FALSE;
|
|||
|
BOOL CsForceQuorum = FALSE;
|
|||
|
LPWSTR CsForceQuorumNodes = NULL;
|
|||
|
BOOL CsCommandLineForceQuorum = FALSE;
|
|||
|
BOOL CsNoRepEvtLogging = FALSE;
|
|||
|
LPWSTR CsDatabaseRestorePath = NULL;
|
|||
|
BOOL CsDatabaseRestore = FALSE;
|
|||
|
BOOL CsForceDatabaseRestore = FALSE;
|
|||
|
LPWSTR CsQuorumDriveLetter = NULL;
|
|||
|
DWORD CspInitStatus;
|
|||
|
BOOL CsRunningAsService = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Private Data
|
|||
|
//
|
|||
|
SERVICE_STATUS_HANDLE CspServiceStatusHandle = 0;
|
|||
|
HANDLE CspStopEvent = NULL;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Private service initialization & cleanup routines.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
CspSetErrorCode(
|
|||
|
IN DWORD ErrorCode,
|
|||
|
OUT LPSERVICE_STATUS ServiceStatus
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Sets the correct error return for the Service Control Manager.
|
|||
|
|
|||
|
Problem:
|
|||
|
|
|||
|
The original cluster error codes overlap with many of the network error
|
|||
|
codes. For those overlaps, this function will return the error code as a
|
|||
|
service specific error code.
|
|||
|
|
|||
|
Inputs:
|
|||
|
|
|||
|
EerrorCode - the correct error code to set.
|
|||
|
ServiceStatus - pointer to the service status for SCM
|
|||
|
|
|||
|
Outputs:
|
|||
|
|
|||
|
ServiceStatus - Sets the correct error code in the service status.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
|
|||
|
if ( ( ErrorCode > 5000 ) && ( ErrorCode < 5090 ) ) {
|
|||
|
ServiceStatus->dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
|
|||
|
ServiceStatus->dwServiceSpecificExitCode = ErrorCode;
|
|||
|
status = ERROR_SERVICE_SPECIFIC_ERROR;
|
|||
|
} else {
|
|||
|
ServiceStatus->dwWin32ExitCode = ErrorCode;
|
|||
|
ServiceStatus->dwServiceSpecificExitCode = ErrorCode;
|
|||
|
status = ErrorCode;
|
|||
|
}
|
|||
|
|
|||
|
return (status);
|
|||
|
|
|||
|
} // CspSetErrorCode
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CspCleanup(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Main Cluster Manager cleanup routine. Called when the service is
|
|||
|
stopping.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Cleanup & shutdown the service
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(CLEANUP) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] Cleaning up\n");
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the stop event
|
|||
|
//
|
|||
|
if (CspStopEvent != NULL) {
|
|||
|
CloseHandle(CspStopEvent);
|
|||
|
CspStopEvent = NULL;
|
|||
|
}
|
|||
|
CsServiceStatus.dwCheckPoint++;
|
|||
|
CsAnnounceServiceStatus();
|
|||
|
|
|||
|
if ( CsDatabaseRestorePath != NULL ) {
|
|||
|
LocalFree ( CsDatabaseRestorePath );
|
|||
|
}
|
|||
|
|
|||
|
if ( CsQuorumDriveLetter != NULL ) {
|
|||
|
LocalFree ( CsQuorumDriveLetter );
|
|||
|
}
|
|||
|
|
|||
|
if ( CsForceQuorumNodes != NULL && !CsCommandLineForceQuorum ) {
|
|||
|
LocalFree ( CsForceQuorumNodes );
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(CLEANUP) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] Cleanup complete.\n");
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
} // CspCleanup
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Public service control routines.
|
|||
|
//
|
|||
|
VOID
|
|||
|
CsWaitForStopEvent(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Main body of the Cluster Manager service. Called when the service
|
|||
|
has been successfully started.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A Win32 status code.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
|
|||
|
|
|||
|
CL_ASSERT(CsRunningAsService);
|
|||
|
if (CsRunningAsService) {
|
|||
|
//
|
|||
|
// Announce that we're up and running.
|
|||
|
//
|
|||
|
CsServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
|||
|
CsServiceStatus.dwControlsAccepted = CLUSTER_SERVICE_CONTROLS;
|
|||
|
CsServiceStatus.dwCheckPoint = 0;
|
|||
|
CsServiceStatus.dwWaitHint = 0;
|
|||
|
|
|||
|
CsAnnounceServiceStatus();
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(INIT) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] Service Started.\n\n");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Wait for the service to be stopped.
|
|||
|
//
|
|||
|
WaitForSingleObject(CspStopEvent, // handle
|
|||
|
INFINITE // no timeout
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
return;
|
|||
|
} // CsWaitForStopEvent
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CsStopService(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Handler for a service controller STOP message. Initiates the process
|
|||
|
of stopping the Cluster Manager service.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if (CsRunningAsService) {
|
|||
|
//
|
|||
|
// Announce that we are stopping.
|
|||
|
//
|
|||
|
CsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
CsServiceStatus.dwCheckPoint = 1;
|
|||
|
CsServiceStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
CsAnnounceServiceStatus();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Wake up the main service thread.
|
|||
|
//
|
|||
|
SetEvent(CspStopEvent);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CsAnnounceServiceStatus (
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Announces the service's status to the service controller.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Don't announce our status if running as a console app.
|
|||
|
//
|
|||
|
if (!CsRunningAsService) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Service status handle is NULL if RegisterServiceCtrlHandler failed.
|
|||
|
//
|
|||
|
if ( CspServiceStatusHandle == 0 ) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call SetServiceStatus, ignoring any errors.
|
|||
|
//
|
|||
|
SetServiceStatus(CspServiceStatusHandle, &CsServiceStatus);
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // CsAnnounceServiceStatus
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Private routines for executing as a Win32 service.
|
|||
|
//
|
|||
|
VOID WINAPI
|
|||
|
CspControlHandler(
|
|||
|
DWORD ControlCode
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Handler for Service Controller messages.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ControlCode - The code indicating the Service Controller's request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
switch(ControlCode){
|
|||
|
|
|||
|
case SERVICE_CONTROL_SHUTDOWN:
|
|||
|
|
|||
|
CsShutdownRequest = CsShutdownTypeShutdown;
|
|||
|
|
|||
|
// Fall Through
|
|||
|
|
|||
|
case SERVICE_CONTROL_STOP:
|
|||
|
IF_DEBUG(CLEANUP) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CS] Received %1!ws! command\n",
|
|||
|
(ControlCode == SERVICE_CONTROL_STOP ? L"STOP" : L"SHUTDOWN"));
|
|||
|
}
|
|||
|
|
|||
|
CsStopService();
|
|||
|
break;
|
|||
|
|
|||
|
case SERVICE_CONTROL_INTERROGATE:
|
|||
|
CsAnnounceServiceStatus();
|
|||
|
break;
|
|||
|
|
|||
|
case SERVICE_CONTROL_CONTINUE:
|
|||
|
case SERVICE_CONTROL_PAUSE:
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CS] Received unknown service command %1!u!\n",
|
|||
|
ControlCode
|
|||
|
);
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // CspControlHandler
|
|||
|
|
|||
|
DWORD CspGetFirstRunState(
|
|||
|
OUT LPDWORD pdwFirstRun
|
|||
|
)
|
|||
|
{
|
|||
|
HKEY hKey = NULL;
|
|||
|
DWORD dwStatus; // returned by registry API functions
|
|||
|
DWORD dwClusterInstallState;
|
|||
|
DWORD dwValueType;
|
|||
|
DWORD dwDataBufferSize = sizeof( DWORD );
|
|||
|
|
|||
|
*pdwFirstRun = 0;
|
|||
|
// Read the registry key that indicates whether cluster files are installed.
|
|||
|
|
|||
|
dwStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
|
|||
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
|
|||
|
0, // reserved
|
|||
|
KEY_READ,
|
|||
|
&hKey );
|
|||
|
|
|||
|
// Was the registry key opened successfully ?
|
|||
|
if ( dwStatus != ERROR_SUCCESS )
|
|||
|
{
|
|||
|
if ( dwStatus == ERROR_FILE_NOT_FOUND )
|
|||
|
{
|
|||
|
*pdwFirstRun = 1;
|
|||
|
dwStatus = ERROR_SUCCESS;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Read the entry.
|
|||
|
dwStatus = RegQueryValueExW( hKey,
|
|||
|
L"ClusterFirstRun",
|
|||
|
0, // reserved
|
|||
|
&dwValueType,
|
|||
|
(LPBYTE) pdwFirstRun,
|
|||
|
&dwDataBufferSize );
|
|||
|
|
|||
|
// Was the value read successfully ?
|
|||
|
if ( dwStatus != ERROR_SUCCESS )
|
|||
|
{
|
|||
|
if ( dwStatus == ERROR_FILE_NOT_FOUND )
|
|||
|
{
|
|||
|
*pdwFirstRun = 1;
|
|||
|
dwStatus = ERROR_SUCCESS;
|
|||
|
goto FnExit;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FnExit:
|
|||
|
// Close the registry key.
|
|||
|
if ( hKey )
|
|||
|
{
|
|||
|
RegCloseKey( hKey );
|
|||
|
}
|
|||
|
|
|||
|
return ( dwStatus );
|
|||
|
|
|||
|
} //*** CspGetFirstRunState
|
|||
|
|
|||
|
DWORD CspGetServiceParams()
|
|||
|
{
|
|||
|
HKEY hClusSvcKey = NULL;
|
|||
|
DWORD Length;
|
|||
|
DWORD Type;
|
|||
|
DWORD Status;
|
|||
|
eClusterInstallState eState;
|
|||
|
|
|||
|
//
|
|||
|
// Open key to SYSTEM\CurrentControlSet\Services\ClusSvc\Parameters
|
|||
|
//
|
|||
|
Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
|
|||
|
CLUSREG_KEYNAME_CLUSSVC_PARAMETERS,
|
|||
|
&hClusSvcKey);
|
|||
|
|
|||
|
Length = sizeof(DWORD);
|
|||
|
Status = RegQueryValueExW(hClusSvcKey,
|
|||
|
CLUSREG_NAME_SVC_PARAM_NOVER_CHECK,
|
|||
|
0,
|
|||
|
&Type,
|
|||
|
(LPBYTE)&CsNoVersionCheck,
|
|||
|
&Length);
|
|||
|
|
|||
|
// by default, version checking is turned on
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
CsNoVersionCheck = FALSE;
|
|||
|
Status = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
Length = sizeof(DWORD);
|
|||
|
Status = RegQueryValueExW(hClusSvcKey,
|
|||
|
CLUSREG_NAME_SVC_PARAM_NOREP_EVTLOGGING,
|
|||
|
0,
|
|||
|
&Type,
|
|||
|
(LPBYTE)&CsNoRepEvtLogging,
|
|||
|
&Length);
|
|||
|
//For now, default is to turn eventlogging on
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
CsNoRepEvtLogging = FALSE;
|
|||
|
Status = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//figure out if this is the first run on upgrade or fresh install
|
|||
|
Status = CspGetFirstRunState((LPDWORD)&CsFirstRun);
|
|||
|
CL_ASSERT(Status == ERROR_SUCCESS);
|
|||
|
|
|||
|
//if there is upgrade, this must be the first run
|
|||
|
Status = ClRtlGetClusterInstallState( NULL, &eState );
|
|||
|
if (eState == eClusterInstallStateUpgraded)
|
|||
|
{
|
|||
|
CsUpgrade = TRUE;
|
|||
|
CsFirstRun = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check the registry to see whether RestoreDatabase option is
|
|||
|
// chosen. If so, get the params and save them in global variables.
|
|||
|
//
|
|||
|
RdbGetRestoreDbParams( hClusSvcKey );
|
|||
|
|
|||
|
//
|
|||
|
// See if the force quorum option has been set. Unfortunately we
|
|||
|
// need two calls to get the size and do the alloc. Note that if
|
|||
|
// we have command line stuff already then this overrides registry
|
|||
|
// parameters. If we have command line stuff then CsForceQuorum
|
|||
|
// will be set. Care is needed since we could be unlucky with the
|
|||
|
// time between the two calls.
|
|||
|
//
|
|||
|
if ( !CsForceQuorum ) {
|
|||
|
GetForceQuorum:
|
|||
|
Length = 0;
|
|||
|
Status = RegQueryValueExW( hClusSvcKey,
|
|||
|
CLUSREG_NAME_SVC_PARAM_FORCE_QUORUM,
|
|||
|
0,
|
|||
|
&Type,
|
|||
|
NULL,
|
|||
|
&Length);
|
|||
|
if (Status == ERROR_SUCCESS) {
|
|||
|
|
|||
|
// Got the length, check the type before allocating
|
|||
|
//
|
|||
|
if ( Type != REG_SZ ) {
|
|||
|
ClRtlLogPrint( LOG_ERROR,"[CS] Error in startup param, type was not REG_SZ.\r\n" );
|
|||
|
Status = ERROR_INVALID_PARAMETER;
|
|||
|
goto ret;
|
|||
|
}
|
|||
|
// Got a valid type so force quorum is set, check the length.
|
|||
|
// If the length is 0 then we have the key but no data which
|
|||
|
// is OK. Otherwise alloc and read the data.
|
|||
|
//
|
|||
|
CsForceQuorum = TRUE;
|
|||
|
if ( Length == 0 )
|
|||
|
goto ret;
|
|||
|
CsForceQuorumNodes = (LPWSTR) LocalAlloc( LMEM_FIXED, Length );
|
|||
|
Status = RegQueryValueExW( hClusSvcKey,
|
|||
|
CLUSREG_NAME_SVC_PARAM_FORCE_QUORUM,
|
|||
|
0,
|
|||
|
&Type,
|
|||
|
(LPBYTE) CsForceQuorumNodes,
|
|||
|
&Length);
|
|||
|
if ( Status == ERROR_MORE_DATA || Type != REG_SZ ) {
|
|||
|
LocalFree( CsForceQuorumNodes );
|
|||
|
CsForceQuorumNodes = NULL;
|
|||
|
CsForceQuorum = FALSE;
|
|||
|
goto GetForceQuorum;
|
|||
|
}
|
|||
|
if ( Status != ERROR_SUCCESS )
|
|||
|
goto ret;
|
|||
|
} else {
|
|||
|
Status = ERROR_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
ret:
|
|||
|
//close the key
|
|||
|
if (hClusSvcKey) RegCloseKey(hClusSvcKey);
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL CspResetFirstRunState(DWORD dwFirstRunState)
|
|||
|
{
|
|||
|
//initialize return to FALSE
|
|||
|
BOOL fReturnValue = FALSE;
|
|||
|
|
|||
|
// Set the state of the ClusterInstallationState registry key to indicate
|
|||
|
// that Cluster Server has been configured.
|
|||
|
|
|||
|
HKEY hKey;
|
|||
|
|
|||
|
DWORD dwStatus; // returned by registry API functions
|
|||
|
|
|||
|
// Attempt to open an existing key in the registry.
|
|||
|
|
|||
|
dwStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
|
|||
|
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
|
|||
|
0, // reserved
|
|||
|
KEY_WRITE,
|
|||
|
&hKey );
|
|||
|
|
|||
|
// Was the regustry key opened successfully ?
|
|||
|
|
|||
|
if ( dwStatus == ERROR_SUCCESS )
|
|||
|
{
|
|||
|
// set the first run state to 0.
|
|||
|
|
|||
|
DWORD dwFirstRun = 0;
|
|||
|
|
|||
|
DWORD dwValueType = REG_DWORD;
|
|||
|
DWORD dwDataBufferSize = sizeof( DWORD );
|
|||
|
|
|||
|
dwStatus = RegSetValueExW( hKey,
|
|||
|
L"ClusterFirstRun",
|
|||
|
0, // reserved
|
|||
|
dwValueType,
|
|||
|
(LPBYTE) &dwFirstRun,
|
|||
|
dwDataBufferSize );
|
|||
|
|
|||
|
// Close the registry key.
|
|||
|
|
|||
|
RegCloseKey( hKey );
|
|||
|
|
|||
|
// Was the value set successfully?
|
|||
|
|
|||
|
if ( dwStatus == ERROR_SUCCESS )
|
|||
|
{
|
|||
|
fReturnValue = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return ( fReturnValue );
|
|||
|
|
|||
|
} //*** CspResetFirstRunState
|
|||
|
|
|||
|
DWORD
|
|||
|
CspSetInstallAndFirstRunState(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Sets the cluster state to Configured. Called
|
|||
|
after the service has started running after the first upgrade.
|
|||
|
If it is a fresh install, Cluscfg sets the state of this to
|
|||
|
Configured before starting the cluster service
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if everything worked ok
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD Status = ERROR_SUCCESS;
|
|||
|
|
|||
|
if (CsUpgrade)
|
|||
|
{
|
|||
|
|
|||
|
if (!ClRtlSetClusterInstallState(eClusterInstallStateConfigured))
|
|||
|
{
|
|||
|
Status = GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (CsFirstRun)
|
|||
|
{
|
|||
|
CspResetFirstRunState(0);
|
|||
|
}
|
|||
|
return(Status);
|
|||
|
|
|||
|
} // CspResetUpgradeBit
|
|||
|
|
|||
|
|
|||
|
void CspGetServiceCmdLineParams(
|
|||
|
DWORD argc,
|
|||
|
LPTSTR argv[]
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD i;
|
|||
|
|
|||
|
if ((argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')))
|
|||
|
{
|
|||
|
for (i=1; i<argc; i++)
|
|||
|
{
|
|||
|
|
|||
|
if (!lstrcmpi(argv[i]+1,L"noquorumlogging"))
|
|||
|
{
|
|||
|
CsNoQuorumLogging = TRUE;
|
|||
|
CsUserTurnedOffQuorumLogging = TRUE;
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] quorum logging is off\r\n");
|
|||
|
}
|
|||
|
else if (!lstrcmpi(argv[i]+1,L"fixquorum"))
|
|||
|
{
|
|||
|
CsNoQuorum = TRUE;
|
|||
|
CsNoQuorumLogging = TRUE;
|
|||
|
CsUserTurnedOffQuorumLogging = TRUE;
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CS] quorum is not arbitrated or brought online\r\n");
|
|||
|
}
|
|||
|
else if (!lstrcmpi(argv[i]+1,L"resetquorumlog"))
|
|||
|
{
|
|||
|
CsResetQuorumLog = TRUE;
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
"[CS] force reset quorum log\r\n");
|
|||
|
}
|
|||
|
else if (!lstrcmpi(argv[i]+1,L"forcequorum"))
|
|||
|
{
|
|||
|
CsForceQuorum = TRUE;
|
|||
|
CsCommandLineForceQuorum = TRUE;
|
|||
|
if (( argc < i+2 )
|
|||
|
|| ( *argv[i+1] == L'-' )
|
|||
|
|| ( *argv[i+1] == L'/' )) {
|
|||
|
|
|||
|
CsForceQuorumNodes = NULL;
|
|||
|
} else {
|
|||
|
CsForceQuorumNodes = argv[++i]; /* increment i to ensure we skip the node list. */
|
|||
|
}
|
|||
|
ClRtlLogPrint(LOG_NOISE, "[CS] force node quorum for nodes %1\r\n", CsForceQuorumNodes);
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"debugresmon", argv[i]+1 ) == 0 ) {
|
|||
|
CsDebugResmon = TRUE;
|
|||
|
//
|
|||
|
// check for optional, non-NULL command string
|
|||
|
//
|
|||
|
if ( argc >= i+2 ) {
|
|||
|
if ( *argv[i+1] != L'-' && *argv[i+1] != L'/' && *argv[i+1] != UNICODE_NULL ) {
|
|||
|
CsResmonDebugCmd = argv[++i];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID WINAPI
|
|||
|
CspServiceMain(
|
|||
|
DWORD argc,
|
|||
|
LPTSTR argv[]
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] Service Starting...\n");
|
|||
|
|
|||
|
if ( CspInitStatus == ERROR_SUCCESS ) {
|
|||
|
CsServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|||
|
} else {
|
|||
|
CsServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
CsServiceStatus.dwWin32ExitCode = CspInitStatus;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize server to receive service requests by registering the
|
|||
|
// control handler.
|
|||
|
//
|
|||
|
|
|||
|
CspServiceStatusHandle = RegisterServiceCtrlHandler(
|
|||
|
CLUSTER_SERVICE_NAME,
|
|||
|
CspControlHandler
|
|||
|
);
|
|||
|
|
|||
|
if ( CspServiceStatusHandle == 0 ) {
|
|||
|
status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] Service Registration failed, %1!u!\n", status);
|
|||
|
CL_UNEXPECTED_ERROR( status );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(INIT) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] Service control handler registered\n");
|
|||
|
}
|
|||
|
|
|||
|
CsAnnounceServiceStatus();
|
|||
|
|
|||
|
if ( CspInitStatus != ERROR_SUCCESS ) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
CspGetServiceCmdLineParams(argc, argv);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the cluster. If this succeeds, wait for
|
|||
|
// the SC mgr to stop us
|
|||
|
//
|
|||
|
status = ClusterInitialize();
|
|||
|
if (status != ERROR_SUCCESS) {
|
|||
|
ClRtlLogPrint(LOG_CRITICAL, "[CS] ClusterInitialize failed %1!d!\n",
|
|||
|
status);
|
|||
|
} else {
|
|||
|
CspSetInstallAndFirstRunState();
|
|||
|
CsWaitForStopEvent();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Announce that we're stopping
|
|||
|
//
|
|||
|
IF_DEBUG(CLEANUP) {
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] Service Stopping...\n");
|
|||
|
}
|
|||
|
|
|||
|
CsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
CsServiceStatus.dwCheckPoint = 1;
|
|||
|
CsServiceStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
CspSetErrorCode( status, &CsServiceStatus );
|
|||
|
|
|||
|
CsAnnounceServiceStatus();
|
|||
|
|
|||
|
//
|
|||
|
// ClusterShutdown currently never returns
|
|||
|
//
|
|||
|
|
|||
|
ClusterShutdown(status);
|
|||
|
|
|||
|
#if 0
|
|||
|
CspCleanup();
|
|||
|
|
|||
|
//
|
|||
|
// Announce that we are stopped.
|
|||
|
//
|
|||
|
CsServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
CsServiceStatus.dwControlsAccepted = 0;
|
|||
|
CsServiceStatus.dwCheckPoint = 0;
|
|||
|
CsServiceStatus.dwWaitHint = 0;
|
|||
|
CspSetErrorCode( status, &CsServiceStatus );
|
|||
|
|
|||
|
CsAnnounceServiceStatus();
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_NOISE,"[CS] Service Stopped.\n\n");
|
|||
|
|
|||
|
//
|
|||
|
// Can't call ClRtlLogPrint after this point.
|
|||
|
//
|
|||
|
ClRtlCleanup();
|
|||
|
|
|||
|
return;
|
|||
|
#endif
|
|||
|
} // CspServiceMain
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Private routines for executing as a console application.
|
|||
|
//
|
|||
|
BOOL WINAPI
|
|||
|
CspConsoleHandler(
|
|||
|
DWORD dwCtrlType
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Handler for console control events when running the service as
|
|||
|
a console application.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
dwCtrlType - Indicates the console event to handle.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if the event was handled, FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
switch( dwCtrlType )
|
|||
|
{
|
|||
|
case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
|
|||
|
case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
|
|||
|
printf("Stopping service...\n");
|
|||
|
CsStopService();
|
|||
|
return TRUE;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
CspDebugService(
|
|||
|
int argc,
|
|||
|
wchar_t ** argv
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Runs the service as a console application
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Standard command-line arguments.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
|
|||
|
SetConsoleCtrlHandler( CspConsoleHandler, TRUE );
|
|||
|
|
|||
|
status = ClusterInitialize();
|
|||
|
|
|||
|
if (status == ERROR_SUCCESS) {
|
|||
|
|
|||
|
CspSetInstallAndFirstRunState();
|
|||
|
|
|||
|
//
|
|||
|
// Wait for ctrl-c to initiate shutdown.
|
|||
|
//
|
|||
|
WaitForSingleObject(CspStopEvent, INFINITE);
|
|||
|
|
|||
|
} else {
|
|||
|
ClRtlLogPrint(LOG_CRITICAL,
|
|||
|
"[CS] ClusterInitialize failed %1!d!\n",
|
|||
|
status);
|
|||
|
}
|
|||
|
|
|||
|
ClusterShutdown(status);
|
|||
|
|
|||
|
CspCleanup();
|
|||
|
|
|||
|
//
|
|||
|
// Can't call ClRtlLogPrint after this point.
|
|||
|
//
|
|||
|
ClRtlCleanup();
|
|||
|
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Main program routines
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
CspUsage(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
#if DBG
|
|||
|
|
|||
|
printf("\nCluster Service\n");
|
|||
|
printf("\n");
|
|||
|
printf("Start with 'net start' to run as a Win32 service\n");
|
|||
|
printf("\n");
|
|||
|
printf("Command line options:\n");
|
|||
|
printf("\t-loglevel N set the debugging log level.\n");
|
|||
|
printf("\t-debug run as a console app.\n");
|
|||
|
printf("\t-debugresmon [dbgcmd] enable debugging of resrcmon process using optional command.\n");
|
|||
|
printf("\t use quotes to include args, i.e., -debugresmon \"ntsd -d\"\n");
|
|||
|
printf("\t-fixquorum no quorum device, no quorum logging.\n");
|
|||
|
printf("\t-noquorumlogging no quorum logging.\n");
|
|||
|
printf("\t-forcequorum N1,...,Nn force a quorum of nodes for node N1 up to Nn inclusive.\n");
|
|||
|
printf("\t-restoredatabase D restore cluster DB to quorum disk from dir D.\n");
|
|||
|
printf("\t-forcerestore force a restore DB operation by performing fixups.\n");
|
|||
|
printf("\t-resetquorumlog force a form despite a missing quorum log file.\n");
|
|||
|
printf("\t-quodriveletter Q drive letter for a replacement quorum disk\n");
|
|||
|
printf("\t-norepevtlogging no replication of event log entries.\n");
|
|||
|
printf("\t-novercheck ignore join version checking.\n");
|
|||
|
printf("\t-testpt N enable test point N.\n");
|
|||
|
printf("\t-persistent make test points persistent.\n");
|
|||
|
printf("\t-trigger N sets test point trigger type.\n");
|
|||
|
printf("\t (0-never (default), 1-always, 2-once, 3-count)\n");
|
|||
|
printf("\t-action N sets trigger action.\n");
|
|||
|
printf("\t (0-true (default), 1-exit, 2-break)\n");
|
|||
|
printf("\n");
|
|||
|
|
|||
|
#else // DBG
|
|||
|
|
|||
|
ClRtlMsgPrint(CS_COMMAND_LINE_HELP);
|
|||
|
|
|||
|
#endif // DBG
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
int __cdecl
|
|||
|
wmain(
|
|||
|
int argc,
|
|||
|
wchar_t **argv
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD Status;
|
|||
|
int i;
|
|||
|
LPWSTR LogLevel;
|
|||
|
BOOLEAN debugFlagFound = FALSE;
|
|||
|
OSVERSIONINFOEXW Version;
|
|||
|
DWORD dwLen;
|
|||
|
BOOL success;
|
|||
|
PWCHAR suiteInfo;
|
|||
|
SYSTEMTIME localTime;
|
|||
|
BOOLEAN dbgOutputToConsole;
|
|||
|
UINT errorMode;
|
|||
|
DWORD dwMask;
|
|||
|
|
|||
|
SERVICE_TABLE_ENTRY dispatchTable[] = {
|
|||
|
{ CLUSTER_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)CspServiceMain },
|
|||
|
{ NULL, NULL }
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// BUGBUG - 06/23/2000
|
|||
|
//
|
|||
|
// This is a temporary change to let the cluster service and resource monitor process run
|
|||
|
// despite 64-bit alignment faults. This will be removed as soon as all alignment issues
|
|||
|
// are fixed.
|
|||
|
//
|
|||
|
errorMode = SetErrorMode( SEM_NOALIGNMENTFAULTEXCEPT );
|
|||
|
|
|||
|
SetErrorMode( SEM_NOALIGNMENTFAULTEXCEPT | errorMode );
|
|||
|
|
|||
|
LogLevel = _wgetenv(L"ClusterLogLevel");
|
|||
|
|
|||
|
if (LogLevel != NULL) {
|
|||
|
swscanf(LogLevel, L"%u", &CsLogLevel);
|
|||
|
}
|
|||
|
|
|||
|
if ( (argc > 1) && ((*argv[1] == L'-') || (*argv[1] == L'/')) ) {
|
|||
|
//
|
|||
|
// Invoked from the command line.
|
|||
|
//
|
|||
|
CsRunningAsService = FALSE;
|
|||
|
dbgOutputToConsole = TRUE;
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Invoked by the Service Controller
|
|||
|
//
|
|||
|
CsRunningAsService = TRUE;
|
|||
|
dbgOutputToConsole = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// initialize the run time library
|
|||
|
//
|
|||
|
Status = ClRtlInitialize( dbgOutputToConsole, &CsLogLevel );
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
if (Status == ERROR_PATH_NOT_FOUND) {
|
|||
|
CsLogEvent( LOG_CRITICAL, SERVICE_CLUSRTL_BAD_PATH );
|
|||
|
} else {
|
|||
|
PWCHAR msgString;
|
|||
|
DWORD msgStatus;
|
|||
|
|
|||
|
msgStatus = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|||
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|||
|
NULL,
|
|||
|
Status,
|
|||
|
0,
|
|||
|
(LPWSTR)&msgString,
|
|||
|
0,
|
|||
|
NULL);
|
|||
|
|
|||
|
if ( msgStatus != 0 ) {
|
|||
|
CsLogEventData1(LOG_CRITICAL,
|
|||
|
SERVICE_CLUSRTL_ERROR,
|
|||
|
sizeof(Status),
|
|||
|
(PVOID)&Status,
|
|||
|
msgString);
|
|||
|
LocalFree( msgString);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
goto init_failed;
|
|||
|
}
|
|||
|
|
|||
|
ClRtlInitWmi(L"Clustering Service");
|
|||
|
//
|
|||
|
// Log the version number
|
|||
|
//
|
|||
|
ClRtlLogPrint( LOG_NOISE, "\n\n");
|
|||
|
ClRtlLogPrint( LOG_NOISE,
|
|||
|
"[CS] Cluster Service started - Cluster Node Version %1!u!.%2!u!\n",
|
|||
|
CLUSTER_GET_MAJOR_VERSION( CsMyHighestVersion ),
|
|||
|
CLUSTER_GET_MINOR_VERSION( CsMyHighestVersion ));
|
|||
|
|
|||
|
Version.dwOSVersionInfoSize = sizeof(Version);
|
|||
|
success = GetVersionExW((POSVERSIONINFOW)&Version);
|
|||
|
|
|||
|
if ( success ) {
|
|||
|
//
|
|||
|
// Log the System version number
|
|||
|
//
|
|||
|
if ( Version.wSuiteMask & VER_SUITE_DATACENTER ) {
|
|||
|
suiteInfo = L"DTC";
|
|||
|
} else if ( Version.wSuiteMask & VER_SUITE_ENTERPRISE ) {
|
|||
|
suiteInfo = L"ADS";
|
|||
|
} else if ( Version.wSuiteMask & VER_SUITE_EMBEDDEDNT ) {
|
|||
|
suiteInfo = L"EMB";
|
|||
|
} else if ( Version.wProductType & VER_NT_WORKSTATION ) {
|
|||
|
suiteInfo = L"WS";
|
|||
|
} else if ( Version.wProductType & VER_NT_DOMAIN_CONTROLLER ) {
|
|||
|
suiteInfo = L"DC";
|
|||
|
} else if ( Version.wProductType & VER_NT_SERVER ) {
|
|||
|
suiteInfo = L"SRV"; // otherwise - some non-descript Server
|
|||
|
} else {
|
|||
|
suiteInfo = L"";
|
|||
|
}
|
|||
|
|
|||
|
dwMask = (Version.wProductType << 24) | Version.wSuiteMask;
|
|||
|
|
|||
|
ClRtlLogPrint(LOG_NOISE,
|
|||
|
" OS Version %1!u!.%2!u!.%3!u!%4!ws!%5!ws! (%6!ws! %7!08XL!)\n",
|
|||
|
Version.dwMajorVersion,
|
|||
|
Version.dwMinorVersion,
|
|||
|
Version.dwBuildNumber,
|
|||
|
*Version.szCSDVersion ? L" - " : L"",
|
|||
|
Version.szCSDVersion,
|
|||
|
suiteInfo,
|
|||
|
dwMask);
|
|||
|
} else {
|
|||
|
ClRtlLogPrint( LOG_UNUSUAL,
|
|||
|
" OS Version not available! (error %1!u!)\n",
|
|||
|
GetLastError()
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// log the local time so we can correlate other logs which show local time
|
|||
|
//
|
|||
|
GetLocalTime( &localTime );
|
|||
|
ClRtlLogPrint( LOG_NOISE,
|
|||
|
" Local Time is "\
|
|||
|
" %1!02d!/%2!02d!/%3!02d!-%4!02d!:%5!02d!:%6!02d!.%7!03d!\n",
|
|||
|
localTime.wYear,
|
|||
|
localTime.wMonth,
|
|||
|
localTime.wDay,
|
|||
|
localTime.wHour,
|
|||
|
localTime.wMinute,
|
|||
|
localTime.wSecond,
|
|||
|
localTime.wMilliseconds);
|
|||
|
|
|||
|
Status = ClRtlBuildClusterServiceSecurityDescriptor( NULL );
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
ClRtlLogPrint(LOG_CRITICAL, "[CS] Failed to build cluster security descriptor %1!x!\n",
|
|||
|
Status);
|
|||
|
goto init_failed;
|
|||
|
}
|
|||
|
|
|||
|
//get params set in the registry
|
|||
|
Status = CspGetServiceParams();
|
|||
|
if (Status != ERROR_SUCCESS) {
|
|||
|
ClRtlLogPrint(LOG_CRITICAL, "[CS] Failed to read service params %1!d!\n",
|
|||
|
Status);
|
|||
|
goto init_failed;
|
|||
|
}
|
|||
|
|
|||
|
//the params on the command line over ride the ones in the registry
|
|||
|
if (CsRunningAsService == FALSE) {
|
|||
|
for (i=1; i<argc; i++) {
|
|||
|
if (lstrcmpiW( L"loglevel", argv[i]+1) == 0) {
|
|||
|
if (argc < i+2) {
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
CsLogLevel = _wtoi(argv[++i]);
|
|||
|
}
|
|||
|
#ifdef CLUSTER_TESTPOINT
|
|||
|
else if (lstrcmpiW( L"testpt", argv[i]+1 ) == 0 ) {
|
|||
|
if (argc < i+2) {
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
CsTestPoint = _wtoi(argv[++i]);
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"persistent", argv[i]+1 ) == 0 ) {
|
|||
|
CsPersistentTestPoint = TRUE;
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"trigger", argv[i]+1 ) == 0 ) {
|
|||
|
if ( argc < i+2 ) {
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
CsTestTrigger = _wtoi(argv[++i]);
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"action", argv[i]+1 ) == 0 ) {
|
|||
|
if ( argc < i+2 ) {
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
CsTestAction = _wtoi(argv[++i]);
|
|||
|
}
|
|||
|
|
|||
|
#endif // CLUSTER_TESTPOINT
|
|||
|
|
|||
|
#if CLUSTER_BETA
|
|||
|
else if ( lstrcmpiW( L"norpcauth", argv[i]+1 ) == 0 ) {
|
|||
|
CsUseAuthenticatedRPC = FALSE;
|
|||
|
}
|
|||
|
#endif // CLUSTER_BETA
|
|||
|
|
|||
|
else if ( lstrcmpiW( L"debugresmon", argv[i]+1 ) == 0 ) {
|
|||
|
CsDebugResmon = TRUE;
|
|||
|
//
|
|||
|
// check for optional, non-NULL command string
|
|||
|
//
|
|||
|
if ( argc >= i+2 ) {
|
|||
|
if ( *argv[i+1] != L'-' && *argv[i+1] != L'/' && *argv[i+1] != UNICODE_NULL ) {
|
|||
|
CsResmonDebugCmd = argv[++i];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"novercheck", argv[i]+1 ) == 0 ) {
|
|||
|
CsNoVersionCheck = TRUE;
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"noquorumlogging", argv[i]+1 ) == 0 ) {
|
|||
|
CsNoQuorumLogging = TRUE;
|
|||
|
CsUserTurnedOffQuorumLogging = TRUE;
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"fixquorum", argv[i]+1 ) == 0 ) {
|
|||
|
CsNoQuorum = TRUE;
|
|||
|
CsNoQuorumLogging = TRUE;
|
|||
|
CsUserTurnedOffQuorumLogging = TRUE;
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"resetquorumlog", argv[i]+1 ) == 0 ) {
|
|||
|
CsResetQuorumLog = TRUE;
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"forcequorum", argv[i]+1 ) == 0 ) {
|
|||
|
CsForceQuorum = TRUE;
|
|||
|
CsCommandLineForceQuorum = TRUE;
|
|||
|
if (( argc < i+2 )
|
|||
|
|| ( *argv[i+1] == L'-' )
|
|||
|
|| ( *argv[i+1] == L'/' )) {
|
|||
|
|
|||
|
CsForceQuorumNodes = NULL;
|
|||
|
} else {
|
|||
|
CsForceQuorumNodes = argv[++i];
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"norepevtlogging", argv[i]+1 ) == 0 ) {
|
|||
|
CsNoRepEvtLogging = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
else if ( lstrcmpiW( L"debug", argv[i]+1 ) == 0 ) {
|
|||
|
debugFlagFound = TRUE;
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"restoredatabase", argv[i]+1 ) == 0 ) {
|
|||
|
if ( ( argc < i+2 ) ||
|
|||
|
( *argv[i+1] == L'-' ) ||
|
|||
|
( *argv[i+1] == L'/' ) )
|
|||
|
{
|
|||
|
printf("\n\n*** restoredatabase option needs a path parameter ***\n\n");
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
if ( !ClRtlIsPathValid( argv[i+1] )) {
|
|||
|
printf( "\n\n*** restoredatabase path is invalid ***\n\n" );
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
if ( !ClRtlPathFileExists( argv[i+1] )) {
|
|||
|
printf( "\n\n*** restoredatabase file cannot be accessed ***\n\n" );
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
dwLen = lstrlenW ( argv[++i] );
|
|||
|
CsDatabaseRestorePath = (LPWSTR) LocalAlloc (LMEM_FIXED,
|
|||
|
( dwLen + 1 ) * sizeof ( WCHAR ) );
|
|||
|
if ( CsDatabaseRestorePath == NULL ) {
|
|||
|
printf("Error %d in allocating storage for restoredatabase path name (%ws)...\n",
|
|||
|
GetLastError(),
|
|||
|
argv[i]);
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
wcscpy( CsDatabaseRestorePath, argv[i] );
|
|||
|
CsDatabaseRestore = TRUE;
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"quodriveletter", argv[i]+1 ) == 0 ) {
|
|||
|
if ( ( argc < i+2 ) ||
|
|||
|
( *argv[i+1] == L'-' ) ||
|
|||
|
( *argv[i+1] == L'/' ) )
|
|||
|
{
|
|||
|
printf("\n\n*** quodriveletter option needs a drive letter parameter ***\n\n");
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
dwLen = lstrlenW ( argv[++i] );
|
|||
|
if ( ( dwLen != 2 ) ||
|
|||
|
!iswalpha( *argv[i] ) ||
|
|||
|
( *( argv[i]+1 ) != L':' ) ) {
|
|||
|
printf("\n\n*** invalid drive letter %ws supplied with quodriveletter option ***\n\n",
|
|||
|
argv[i]);
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
CsQuorumDriveLetter = (LPWSTR) LocalAlloc (LMEM_FIXED,
|
|||
|
( dwLen + 1 ) * sizeof ( WCHAR ) );
|
|||
|
if ( CsQuorumDriveLetter == NULL ) {
|
|||
|
printf("Error %d in allocating storage for quodriveletter option (%ws)...\n\n",
|
|||
|
GetLastError(),
|
|||
|
argv[i]);
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
wcscpy( CsQuorumDriveLetter, argv[i] );
|
|||
|
}
|
|||
|
else if ( lstrcmpiW( L"forcerestore", argv[i]+1 ) == 0 ) {
|
|||
|
CsForceDatabaseRestore = TRUE;
|
|||
|
}
|
|||
|
else {
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!debugFlagFound && !CspStopEvent) {
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
if ( CsDatabaseRestore == TRUE ) {
|
|||
|
if ( CsNoQuorumLogging || CsNoQuorum ) {
|
|||
|
printf("\n\n**** restoredatabase cannot be used with noquorumlogging/fixquorum options ****\n\n");
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
} else if ( CsForceDatabaseRestore ) {
|
|||
|
printf("\n\n**** forcerestore cannot be used without restoredatabase option ****\n\n");
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
|
|||
|
if ( ( CsQuorumDriveLetter != NULL ) && !CsForceDatabaseRestore ) {
|
|||
|
printf("\n\n**** quodriveletter cannot be used without forcerestore option ****\n\n");
|
|||
|
CspUsage();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create our stop event
|
|||
|
//
|
|||
|
Status = ERROR_SUCCESS;
|
|||
|
if (!CspStopEvent)
|
|||
|
{
|
|||
|
CspStopEvent = CreateEvent(
|
|||
|
NULL, // default security
|
|||
|
FALSE, // auto-reset
|
|||
|
FALSE, // initial state is non-signalled
|
|||
|
NULL // unnamed event
|
|||
|
);
|
|||
|
|
|||
|
if (CspStopEvent == NULL) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_CRITICAL,
|
|||
|
"[CS] Unable to create stop event, %1!u!\n",
|
|||
|
Status);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// we can't fire up our main routine if we're running as a service until
|
|||
|
// now (race conditions between reading startup params out of the registry
|
|||
|
// versus whether we're running as a service at all, etc.). Note that we
|
|||
|
// failed initialization so if we are running as a service, we'll detect
|
|||
|
// it in CspServiceMain and issue the stop
|
|||
|
//
|
|||
|
init_failed:
|
|||
|
CspInitStatus = Status;
|
|||
|
|
|||
|
//
|
|||
|
// Run the service.
|
|||
|
//
|
|||
|
if (CsRunningAsService) {
|
|||
|
if (!StartServiceCtrlDispatcher(dispatchTable)) {
|
|||
|
Status = GetLastError();
|
|||
|
ClRtlLogPrint(LOG_CRITICAL,
|
|||
|
"[CS] Unable to dispatch to SC, %1!u!\n",
|
|||
|
Status);
|
|||
|
CL_UNEXPECTED_ERROR( Status );
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( CspInitStatus == ERROR_SUCCESS ) {
|
|||
|
Status = CspDebugService(argc, argv);
|
|||
|
}
|
|||
|
|
|||
|
ClRtlFreeClusterServiceSecurityDescriptor( );
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void CsGetClusterVersionInfo(
|
|||
|
IN PCLUSTERVERSIONINFO pClusterVersionInfo)
|
|||
|
{
|
|||
|
OSVERSIONINFOW OsVersionInfo;
|
|||
|
|
|||
|
pClusterVersionInfo->dwVersionInfoSize = sizeof(CLUSTERVERSIONINFO);
|
|||
|
pClusterVersionInfo->MajorVersion = (WORD)VER_PRODUCTVERSION_W >> 8;
|
|||
|
pClusterVersionInfo->MinorVersion = (WORD)VER_PRODUCTVERSION_W & 0xff;
|
|||
|
pClusterVersionInfo->BuildNumber = (WORD)CLUSTER_GET_MINOR_VERSION(CsMyHighestVersion);
|
|||
|
|
|||
|
mbstowcs(pClusterVersionInfo->szVendorId, VER_CLUSTER_PRODUCTNAME_STR,
|
|||
|
(lstrlenA(VER_CLUSTER_PRODUCTNAME_STR)+1));
|
|||
|
|
|||
|
OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|||
|
|
|||
|
GetVersionExW(&OsVersionInfo);
|
|||
|
lstrcpynW(pClusterVersionInfo->szCSDVersion, OsVersionInfo.szCSDVersion,
|
|||
|
(sizeof(pClusterVersionInfo->szCSDVersion)/sizeof(WCHAR)));
|
|||
|
pClusterVersionInfo->dwReserved = 0;
|
|||
|
NmGetClusterOperationalVersion(&(pClusterVersionInfo->dwClusterHighestVersion),
|
|||
|
&(pClusterVersionInfo->dwClusterLowestVersion),&(pClusterVersionInfo->dwFlags));
|
|||
|
|
|||
|
|
|||
|
}
|