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));
|
||
|
||
|
||
}
|