/*++ 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 #include #include #include #include // // 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= 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= 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)); }