Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1490 lines
40 KiB

  1. /*++
  2. Copyright (c) 1995-1999 Microsoft Corporation
  3. Module Name:
  4. service.c
  5. Abstract:
  6. Service control functions for the Cluster Service.
  7. Author:
  8. Mike Massa (mikemas) 2-Jan-1996
  9. Revision History:
  10. --*/
  11. #include <initp.h>
  12. #include <shellapi.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <clusverp.h>
  16. //
  17. // Public data
  18. //
  19. #define CLUSTER_SERVICE_CONTROLS (SERVICE_ACCEPT_STOP | \
  20. SERVICE_ACCEPT_SHUTDOWN )
  21. ULONG CsLogLevel=LOG_UNUSUAL;
  22. PCLRTL_WORK_QUEUE CsDelayedWorkQueue = NULL;
  23. PCLRTL_WORK_QUEUE CsCriticalWorkQueue = NULL;
  24. LPWSTR CsClusterName = NULL;
  25. SERVICE_STATUS CsServiceStatus = {
  26. SERVICE_WIN32_OWN_PROCESS, // dwServiceType
  27. SERVICE_STOPPED, // dwCurrentState
  28. CLUSTER_SERVICE_CONTROLS, // dwControlsAccepted
  29. ERROR_SUCCESS, // dwWin32ExitCode
  30. ERROR_SUCCESS, // dwServiceSpecificExitCode
  31. 1, // dwCheckPoint
  32. 90000 // dwWaitHint - 90 seconds -nm uses 90 sec timeout
  33. };
  34. //
  35. // internal cluster versions. The major version is bumped during
  36. // product releases (which could include service pack releases).
  37. //
  38. DWORD CsMyHighestVersion = CLUSTER_MAKE_VERSION(
  39. CLUSTER_INTERNAL_CURRENT_MAJOR_VERSION,
  40. VER_PRODUCTBUILD);
  41. DWORD CsMyLowestVersion = CLUSTER_INTERNAL_PREVIOUS_HIGHEST_VERSION;
  42. //initialize by calling an rtl funcion
  43. SUITE_TYPE CsMyProductSuite;
  44. DWORD CsClusterHighestVersion;
  45. DWORD CsClusterLowestVersion;
  46. DWORD CsClusterNodeLimit;
  47. SHUTDOWN_TYPE CsShutdownRequest = CsShutdownTypeStop;
  48. //
  49. // used to turn authenticated RPC on or off
  50. //
  51. // This should always be defined, since none of the other code has
  52. // the checks for this variable conditionalized
  53. //
  54. BOOL CsUseAuthenticatedRPC = TRUE;
  55. //
  56. // domain and user account under which the service is run
  57. //
  58. LPWSTR CsServiceDomainAccount;
  59. //
  60. // security packages to use during the join for authenticated RPC; JoinVersion
  61. // determines which package will be used by the ExtroCluster interface.
  62. // CsRPCSecurityPackageInUse reflects that choice. The package used for the
  63. // Intracluster interface is negotiated separately.
  64. //
  65. //
  66. // when using kerberos with RPC, RPC calls fail with 1825 (sec. pkg error)
  67. // somewhere between 30 minutes and 12 hours. For beta 2, we'll revert back to
  68. // NTLM where expiration is not a problem.
  69. //
  70. //DWORD CsRPCSecurityPackage[] = { RPC_C_AUTHN_GSS_KERBEROS, RPC_C_AUTHN_WINNT };
  71. //LPWSTR CsRPCSecurityPackageName[] = { L"Kerberos", L"NTLM" };
  72. DWORD CsRPCSecurityPackage[] = { RPC_C_AUTHN_WINNT };
  73. LPWSTR CsRPCSecurityPackageName[] = { L"NTLM" };
  74. DWORD CsNumberOfRPCSecurityPackages = sizeof( CsRPCSecurityPackage ) / sizeof( CsRPCSecurityPackage[0] );
  75. LONG CsRPCSecurityPackageIndex = -1;
  76. //
  77. // Public Debug Data
  78. //
  79. #if 1 // CLUSTER_BETA
  80. BOOL CsDebugResmon = FALSE;
  81. LPWSTR CsResmonDebugCmd;
  82. BOOL CsNoVersionCheck = FALSE;
  83. #endif
  84. #if DBG // DBG
  85. ULONG CsDebugFlags = CS_DBG_ALL;
  86. #endif // DBG
  87. #ifdef CLUSTER_TESTPOINT
  88. DWORD CsTestPoint = 0;
  89. DWORD CsTestTrigger = TestTriggerNever;
  90. DWORD CsTestAction = TestActionTrue;
  91. BOOL CsPersistentTestPoint = FALSE;
  92. #endif // CLUSTER_TESTPOINT
  93. BOOL CsUpgrade = FALSE;
  94. BOOL CsFirstRun = FALSE;
  95. BOOL CsNoQuorumLogging = FALSE;
  96. BOOL CsUserTurnedOffQuorumLogging = FALSE;
  97. BOOL CsNoQuorum = FALSE;
  98. BOOL CsResetQuorumLog = FALSE;
  99. BOOL CsForceQuorum = FALSE;
  100. LPWSTR CsForceQuorumNodes = NULL;
  101. BOOL CsCommandLineForceQuorum = FALSE;
  102. BOOL CsNoRepEvtLogging = FALSE;
  103. LPWSTR CsDatabaseRestorePath = NULL;
  104. BOOL CsDatabaseRestore = FALSE;
  105. BOOL CsForceDatabaseRestore = FALSE;
  106. LPWSTR CsQuorumDriveLetter = NULL;
  107. DWORD CspInitStatus;
  108. BOOL CsRunningAsService = TRUE;
  109. //
  110. // Private Data
  111. //
  112. SERVICE_STATUS_HANDLE CspServiceStatusHandle = 0;
  113. HANDLE CspStopEvent = NULL;
  114. //
  115. // Private service initialization & cleanup routines.
  116. //
  117. DWORD
  118. CspSetErrorCode(
  119. IN DWORD ErrorCode,
  120. OUT LPSERVICE_STATUS ServiceStatus
  121. )
  122. /*++
  123. Routine Description:
  124. Sets the correct error return for the Service Control Manager.
  125. Problem:
  126. The original cluster error codes overlap with many of the network error
  127. codes. For those overlaps, this function will return the error code as a
  128. service specific error code.
  129. Inputs:
  130. EerrorCode - the correct error code to set.
  131. ServiceStatus - pointer to the service status for SCM
  132. Outputs:
  133. ServiceStatus - Sets the correct error code in the service status.
  134. --*/
  135. {
  136. DWORD status;
  137. if ( ( ErrorCode > 5000 ) && ( ErrorCode < 5090 ) ) {
  138. ServiceStatus->dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  139. ServiceStatus->dwServiceSpecificExitCode = ErrorCode;
  140. status = ERROR_SERVICE_SPECIFIC_ERROR;
  141. } else {
  142. ServiceStatus->dwWin32ExitCode = ErrorCode;
  143. ServiceStatus->dwServiceSpecificExitCode = ErrorCode;
  144. status = ErrorCode;
  145. }
  146. return (status);
  147. } // CspSetErrorCode
  148. VOID
  149. CspCleanup(
  150. VOID
  151. )
  152. /*++
  153. Routine Description:
  154. Main Cluster Manager cleanup routine. Called when the service is
  155. stopping.
  156. Arguments:
  157. None.
  158. Return Value:
  159. None.
  160. --*/
  161. {
  162. //
  163. // Cleanup & shutdown the service
  164. //
  165. IF_DEBUG(CLEANUP) {
  166. ClRtlLogPrint(LOG_NOISE,"[CS] Cleaning up\n");
  167. }
  168. //
  169. // Free the stop event
  170. //
  171. if (CspStopEvent != NULL) {
  172. CloseHandle(CspStopEvent);
  173. CspStopEvent = NULL;
  174. }
  175. CsServiceStatus.dwCheckPoint++;
  176. CsAnnounceServiceStatus();
  177. if ( CsDatabaseRestorePath != NULL ) {
  178. LocalFree ( CsDatabaseRestorePath );
  179. }
  180. if ( CsQuorumDriveLetter != NULL ) {
  181. LocalFree ( CsQuorumDriveLetter );
  182. }
  183. if ( CsForceQuorumNodes != NULL && !CsCommandLineForceQuorum ) {
  184. LocalFree ( CsForceQuorumNodes );
  185. }
  186. IF_DEBUG(CLEANUP) {
  187. ClRtlLogPrint(LOG_NOISE,"[CS] Cleanup complete.\n");
  188. }
  189. return;
  190. } // CspCleanup
  191. //
  192. // Public service control routines.
  193. //
  194. VOID
  195. CsWaitForStopEvent(
  196. VOID
  197. )
  198. /*++
  199. Routine Description:
  200. Main body of the Cluster Manager service. Called when the service
  201. has been successfully started.
  202. Arguments:
  203. None.
  204. Return Value:
  205. A Win32 status code.
  206. --*/
  207. {
  208. DWORD status;
  209. CL_ASSERT(CsRunningAsService);
  210. if (CsRunningAsService) {
  211. //
  212. // Announce that we're up and running.
  213. //
  214. CsServiceStatus.dwCurrentState = SERVICE_RUNNING;
  215. CsServiceStatus.dwControlsAccepted = CLUSTER_SERVICE_CONTROLS;
  216. CsServiceStatus.dwCheckPoint = 0;
  217. CsServiceStatus.dwWaitHint = 0;
  218. CsAnnounceServiceStatus();
  219. }
  220. IF_DEBUG(INIT) {
  221. ClRtlLogPrint(LOG_NOISE,"[CS] Service Started.\n\n");
  222. }
  223. //
  224. // Wait for the service to be stopped.
  225. //
  226. WaitForSingleObject(CspStopEvent, // handle
  227. INFINITE // no timeout
  228. );
  229. return;
  230. } // CsWaitForStopEvent
  231. VOID
  232. CsStopService(
  233. VOID
  234. )
  235. /*++
  236. Routine Description:
  237. Handler for a service controller STOP message. Initiates the process
  238. of stopping the Cluster Manager service.
  239. Arguments:
  240. None.
  241. Return Value:
  242. None.
  243. --*/
  244. {
  245. if (CsRunningAsService) {
  246. //
  247. // Announce that we are stopping.
  248. //
  249. CsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
  250. CsServiceStatus.dwCheckPoint = 1;
  251. CsServiceStatus.dwWaitHint = 20000; // 20 seconds
  252. CsAnnounceServiceStatus();
  253. }
  254. //
  255. // Wake up the main service thread.
  256. //
  257. SetEvent(CspStopEvent);
  258. return;
  259. }
  260. VOID
  261. CsAnnounceServiceStatus (
  262. VOID
  263. )
  264. /*++
  265. Routine Description:
  266. Announces the service's status to the service controller.
  267. Arguments:
  268. None.
  269. Return Value:
  270. None.
  271. --*/
  272. {
  273. //
  274. // Don't announce our status if running as a console app.
  275. //
  276. if (!CsRunningAsService) {
  277. return;
  278. }
  279. //
  280. // Service status handle is NULL if RegisterServiceCtrlHandler failed.
  281. //
  282. if ( CspServiceStatusHandle == 0 ) {
  283. return;
  284. }
  285. //
  286. // Call SetServiceStatus, ignoring any errors.
  287. //
  288. SetServiceStatus(CspServiceStatusHandle, &CsServiceStatus);
  289. return;
  290. } // CsAnnounceServiceStatus
  291. //
  292. // Private routines for executing as a Win32 service.
  293. //
  294. VOID WINAPI
  295. CspControlHandler(
  296. DWORD ControlCode
  297. )
  298. /*++
  299. Routine Description:
  300. Handler for Service Controller messages.
  301. Arguments:
  302. ControlCode - The code indicating the Service Controller's request.
  303. Return Value:
  304. None.
  305. --*/
  306. {
  307. switch(ControlCode){
  308. case SERVICE_CONTROL_SHUTDOWN:
  309. CsShutdownRequest = CsShutdownTypeShutdown;
  310. // Fall Through
  311. case SERVICE_CONTROL_STOP:
  312. IF_DEBUG(CLEANUP) {
  313. ClRtlLogPrint(LOG_NOISE,
  314. "[CS] Received %1!ws! command\n",
  315. (ControlCode == SERVICE_CONTROL_STOP ? L"STOP" : L"SHUTDOWN"));
  316. }
  317. CsStopService();
  318. break;
  319. case SERVICE_CONTROL_INTERROGATE:
  320. CsAnnounceServiceStatus();
  321. break;
  322. case SERVICE_CONTROL_CONTINUE:
  323. case SERVICE_CONTROL_PAUSE:
  324. break;
  325. default:
  326. ClRtlLogPrint(LOG_NOISE,
  327. "[CS] Received unknown service command %1!u!\n",
  328. ControlCode
  329. );
  330. break;
  331. }
  332. return;
  333. } // CspControlHandler
  334. DWORD CspGetFirstRunState(
  335. OUT LPDWORD pdwFirstRun
  336. )
  337. {
  338. HKEY hKey = NULL;
  339. DWORD dwStatus; // returned by registry API functions
  340. DWORD dwClusterInstallState;
  341. DWORD dwValueType;
  342. DWORD dwDataBufferSize = sizeof( DWORD );
  343. *pdwFirstRun = 0;
  344. // Read the registry key that indicates whether cluster files are installed.
  345. dwStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  346. L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
  347. 0, // reserved
  348. KEY_READ,
  349. &hKey );
  350. // Was the registry key opened successfully ?
  351. if ( dwStatus != ERROR_SUCCESS )
  352. {
  353. if ( dwStatus == ERROR_FILE_NOT_FOUND )
  354. {
  355. *pdwFirstRun = 1;
  356. dwStatus = ERROR_SUCCESS;
  357. goto FnExit;
  358. }
  359. }
  360. // Read the entry.
  361. dwStatus = RegQueryValueExW( hKey,
  362. L"ClusterFirstRun",
  363. 0, // reserved
  364. &dwValueType,
  365. (LPBYTE) pdwFirstRun,
  366. &dwDataBufferSize );
  367. // Was the value read successfully ?
  368. if ( dwStatus != ERROR_SUCCESS )
  369. {
  370. if ( dwStatus == ERROR_FILE_NOT_FOUND )
  371. {
  372. *pdwFirstRun = 1;
  373. dwStatus = ERROR_SUCCESS;
  374. goto FnExit;
  375. }
  376. }
  377. FnExit:
  378. // Close the registry key.
  379. if ( hKey )
  380. {
  381. RegCloseKey( hKey );
  382. }
  383. return ( dwStatus );
  384. } //*** CspGetFirstRunState
  385. DWORD CspGetServiceParams()
  386. {
  387. HKEY hClusSvcKey = NULL;
  388. DWORD Length;
  389. DWORD Type;
  390. DWORD Status;
  391. eClusterInstallState eState;
  392. //
  393. // Open key to SYSTEM\CurrentControlSet\Services\ClusSvc\Parameters
  394. //
  395. Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
  396. CLUSREG_KEYNAME_CLUSSVC_PARAMETERS,
  397. &hClusSvcKey);
  398. Length = sizeof(DWORD);
  399. Status = RegQueryValueExW(hClusSvcKey,
  400. CLUSREG_NAME_SVC_PARAM_NOVER_CHECK,
  401. 0,
  402. &Type,
  403. (LPBYTE)&CsNoVersionCheck,
  404. &Length);
  405. // by default, version checking is turned on
  406. if (Status != ERROR_SUCCESS) {
  407. CsNoVersionCheck = FALSE;
  408. Status = ERROR_SUCCESS;
  409. }
  410. Length = sizeof(DWORD);
  411. Status = RegQueryValueExW(hClusSvcKey,
  412. CLUSREG_NAME_SVC_PARAM_NOREP_EVTLOGGING,
  413. 0,
  414. &Type,
  415. (LPBYTE)&CsNoRepEvtLogging,
  416. &Length);
  417. //For now, default is to turn eventlogging on
  418. if (Status != ERROR_SUCCESS) {
  419. CsNoRepEvtLogging = FALSE;
  420. Status = ERROR_SUCCESS;
  421. }
  422. //figure out if this is the first run on upgrade or fresh install
  423. Status = CspGetFirstRunState((LPDWORD)&CsFirstRun);
  424. CL_ASSERT(Status == ERROR_SUCCESS);
  425. //if there is upgrade, this must be the first run
  426. Status = ClRtlGetClusterInstallState( NULL, &eState );
  427. if (eState == eClusterInstallStateUpgraded)
  428. {
  429. CsUpgrade = TRUE;
  430. CsFirstRun = TRUE;
  431. }
  432. //
  433. // Check the registry to see whether RestoreDatabase option is
  434. // chosen. If so, get the params and save them in global variables.
  435. //
  436. RdbGetRestoreDbParams( hClusSvcKey );
  437. //
  438. // See if the force quorum option has been set. Unfortunately we
  439. // need two calls to get the size and do the alloc. Note that if
  440. // we have command line stuff already then this overrides registry
  441. // parameters. If we have command line stuff then CsForceQuorum
  442. // will be set. Care is needed since we could be unlucky with the
  443. // time between the two calls.
  444. //
  445. if ( !CsForceQuorum ) {
  446. GetForceQuorum:
  447. Length = 0;
  448. Status = RegQueryValueExW( hClusSvcKey,
  449. CLUSREG_NAME_SVC_PARAM_FORCE_QUORUM,
  450. 0,
  451. &Type,
  452. NULL,
  453. &Length);
  454. if (Status == ERROR_SUCCESS) {
  455. // Got the length, check the type before allocating
  456. //
  457. if ( Type != REG_SZ ) {
  458. ClRtlLogPrint( LOG_ERROR,"[CS] Error in startup param, type was not REG_SZ.\r\n" );
  459. Status = ERROR_INVALID_PARAMETER;
  460. goto ret;
  461. }
  462. // Got a valid type so force quorum is set, check the length.
  463. // If the length is 0 then we have the key but no data which
  464. // is OK. Otherwise alloc and read the data.
  465. //
  466. CsForceQuorum = TRUE;
  467. if ( Length == 0 )
  468. goto ret;
  469. CsForceQuorumNodes = (LPWSTR) LocalAlloc( LMEM_FIXED, Length );
  470. Status = RegQueryValueExW( hClusSvcKey,
  471. CLUSREG_NAME_SVC_PARAM_FORCE_QUORUM,
  472. 0,
  473. &Type,
  474. (LPBYTE) CsForceQuorumNodes,
  475. &Length);
  476. if ( Status == ERROR_MORE_DATA || Type != REG_SZ ) {
  477. LocalFree( CsForceQuorumNodes );
  478. CsForceQuorumNodes = NULL;
  479. CsForceQuorum = FALSE;
  480. goto GetForceQuorum;
  481. }
  482. if ( Status != ERROR_SUCCESS )
  483. goto ret;
  484. } else {
  485. Status = ERROR_SUCCESS;
  486. }
  487. }
  488. ret:
  489. //close the key
  490. if (hClusSvcKey) RegCloseKey(hClusSvcKey);
  491. return(Status);
  492. }
  493. BOOL CspResetFirstRunState(DWORD dwFirstRunState)
  494. {
  495. //initialize return to FALSE
  496. BOOL fReturnValue = FALSE;
  497. // Set the state of the ClusterInstallationState registry key to indicate
  498. // that Cluster Server has been configured.
  499. HKEY hKey;
  500. DWORD dwStatus; // returned by registry API functions
  501. // Attempt to open an existing key in the registry.
  502. dwStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  503. L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
  504. 0, // reserved
  505. KEY_WRITE,
  506. &hKey );
  507. // Was the regustry key opened successfully ?
  508. if ( dwStatus == ERROR_SUCCESS )
  509. {
  510. // set the first run state to 0.
  511. DWORD dwFirstRun = 0;
  512. DWORD dwValueType = REG_DWORD;
  513. DWORD dwDataBufferSize = sizeof( DWORD );
  514. dwStatus = RegSetValueExW( hKey,
  515. L"ClusterFirstRun",
  516. 0, // reserved
  517. dwValueType,
  518. (LPBYTE) &dwFirstRun,
  519. dwDataBufferSize );
  520. // Close the registry key.
  521. RegCloseKey( hKey );
  522. // Was the value set successfully?
  523. if ( dwStatus == ERROR_SUCCESS )
  524. {
  525. fReturnValue = TRUE;
  526. }
  527. }
  528. return ( fReturnValue );
  529. } //*** CspResetFirstRunState
  530. DWORD
  531. CspSetInstallAndFirstRunState(
  532. VOID
  533. )
  534. /*++
  535. Routine Description:
  536. Sets the cluster state to Configured. Called
  537. after the service has started running after the first upgrade.
  538. If it is a fresh install, Cluscfg sets the state of this to
  539. Configured before starting the cluster service
  540. Arguments:
  541. None
  542. Return Value:
  543. ERROR_SUCCESS if everything worked ok
  544. --*/
  545. {
  546. DWORD Status = ERROR_SUCCESS;
  547. if (CsUpgrade)
  548. {
  549. if (!ClRtlSetClusterInstallState(eClusterInstallStateConfigured))
  550. {
  551. Status = GetLastError();
  552. }
  553. }
  554. if (CsFirstRun)
  555. {
  556. CspResetFirstRunState(0);
  557. }
  558. return(Status);
  559. } // CspResetUpgradeBit
  560. void CspGetServiceCmdLineParams(
  561. DWORD argc,
  562. LPTSTR argv[]
  563. )
  564. {
  565. DWORD i;
  566. if ((argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')))
  567. {
  568. for (i=1; i<argc; i++)
  569. {
  570. if (!lstrcmpi(argv[i]+1,L"noquorumlogging"))
  571. {
  572. CsNoQuorumLogging = TRUE;
  573. CsUserTurnedOffQuorumLogging = TRUE;
  574. ClRtlLogPrint(LOG_NOISE,"[CS] quorum logging is off\r\n");
  575. }
  576. else if (!lstrcmpi(argv[i]+1,L"fixquorum"))
  577. {
  578. CsNoQuorum = TRUE;
  579. CsNoQuorumLogging = TRUE;
  580. CsUserTurnedOffQuorumLogging = TRUE;
  581. ClRtlLogPrint(LOG_NOISE,
  582. "[CS] quorum is not arbitrated or brought online\r\n");
  583. }
  584. else if (!lstrcmpi(argv[i]+1,L"resetquorumlog"))
  585. {
  586. CsResetQuorumLog = TRUE;
  587. ClRtlLogPrint(LOG_NOISE,
  588. "[CS] force reset quorum log\r\n");
  589. }
  590. else if (!lstrcmpi(argv[i]+1,L"forcequorum"))
  591. {
  592. CsForceQuorum = TRUE;
  593. CsCommandLineForceQuorum = TRUE;
  594. if (( argc < i+2 )
  595. || ( *argv[i+1] == L'-' )
  596. || ( *argv[i+1] == L'/' )) {
  597. CsForceQuorumNodes = NULL;
  598. } else {
  599. CsForceQuorumNodes = argv[++i]; /* increment i to ensure we skip the node list. */
  600. }
  601. ClRtlLogPrint(LOG_NOISE, "[CS] force node quorum for nodes %1\r\n", CsForceQuorumNodes);
  602. }
  603. else if ( lstrcmpiW( L"debugresmon", argv[i]+1 ) == 0 ) {
  604. CsDebugResmon = TRUE;
  605. //
  606. // check for optional, non-NULL command string
  607. //
  608. if ( argc >= i+2 ) {
  609. if ( *argv[i+1] != L'-' && *argv[i+1] != L'/' && *argv[i+1] != UNICODE_NULL ) {
  610. CsResmonDebugCmd = argv[++i];
  611. }
  612. }
  613. }
  614. }
  615. }
  616. }
  617. VOID WINAPI
  618. CspServiceMain(
  619. DWORD argc,
  620. LPTSTR argv[]
  621. )
  622. {
  623. DWORD status;
  624. ClRtlLogPrint(LOG_NOISE,"[CS] Service Starting...\n");
  625. if ( CspInitStatus == ERROR_SUCCESS ) {
  626. CsServiceStatus.dwCurrentState = SERVICE_START_PENDING;
  627. } else {
  628. CsServiceStatus.dwCurrentState = SERVICE_STOPPED;
  629. CsServiceStatus.dwWin32ExitCode = CspInitStatus;
  630. }
  631. //
  632. // Initialize server to receive service requests by registering the
  633. // control handler.
  634. //
  635. CspServiceStatusHandle = RegisterServiceCtrlHandler(
  636. CLUSTER_SERVICE_NAME,
  637. CspControlHandler
  638. );
  639. if ( CspServiceStatusHandle == 0 ) {
  640. status = GetLastError();
  641. ClRtlLogPrint(LOG_NOISE,"[CS] Service Registration failed, %1!u!\n", status);
  642. CL_UNEXPECTED_ERROR( status );
  643. return;
  644. }
  645. IF_DEBUG(INIT) {
  646. ClRtlLogPrint(LOG_NOISE,"[CS] Service control handler registered\n");
  647. }
  648. CsAnnounceServiceStatus();
  649. if ( CspInitStatus != ERROR_SUCCESS ) {
  650. return;
  651. }
  652. CspGetServiceCmdLineParams(argc, argv);
  653. //
  654. // Initialize the cluster. If this succeeds, wait for
  655. // the SC mgr to stop us
  656. //
  657. status = ClusterInitialize();
  658. if (status != ERROR_SUCCESS) {
  659. ClRtlLogPrint(LOG_CRITICAL, "[CS] ClusterInitialize failed %1!d!\n",
  660. status);
  661. } else {
  662. CspSetInstallAndFirstRunState();
  663. CsWaitForStopEvent();
  664. }
  665. //
  666. // Announce that we're stopping
  667. //
  668. IF_DEBUG(CLEANUP) {
  669. ClRtlLogPrint(LOG_NOISE,"[CS] Service Stopping...\n");
  670. }
  671. CsServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
  672. CsServiceStatus.dwCheckPoint = 1;
  673. CsServiceStatus.dwWaitHint = 20000; // 20 seconds
  674. CspSetErrorCode( status, &CsServiceStatus );
  675. CsAnnounceServiceStatus();
  676. //
  677. // ClusterShutdown currently never returns
  678. //
  679. ClusterShutdown(status);
  680. #if 0
  681. CspCleanup();
  682. //
  683. // Announce that we are stopped.
  684. //
  685. CsServiceStatus.dwCurrentState = SERVICE_STOPPED;
  686. CsServiceStatus.dwControlsAccepted = 0;
  687. CsServiceStatus.dwCheckPoint = 0;
  688. CsServiceStatus.dwWaitHint = 0;
  689. CspSetErrorCode( status, &CsServiceStatus );
  690. CsAnnounceServiceStatus();
  691. ClRtlLogPrint(LOG_NOISE,"[CS] Service Stopped.\n\n");
  692. //
  693. // Can't call ClRtlLogPrint after this point.
  694. //
  695. ClRtlCleanup();
  696. return;
  697. #endif
  698. } // CspServiceMain
  699. //
  700. // Private routines for executing as a console application.
  701. //
  702. BOOL WINAPI
  703. CspConsoleHandler(
  704. DWORD dwCtrlType
  705. )
  706. /*++
  707. Routine Description:
  708. Handler for console control events when running the service as
  709. a console application.
  710. Arguments:
  711. dwCtrlType - Indicates the console event to handle.
  712. Return Value:
  713. TRUE if the event was handled, FALSE otherwise.
  714. --*/
  715. {
  716. switch( dwCtrlType )
  717. {
  718. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  719. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  720. printf("Stopping service...\n");
  721. CsStopService();
  722. return TRUE;
  723. break;
  724. }
  725. return FALSE;
  726. }
  727. DWORD
  728. CspDebugService(
  729. int argc,
  730. wchar_t ** argv
  731. )
  732. /*++
  733. Routine Description:
  734. Runs the service as a console application
  735. Arguments:
  736. Standard command-line arguments.
  737. Return Value:
  738. None.
  739. --*/
  740. {
  741. DWORD status;
  742. SetConsoleCtrlHandler( CspConsoleHandler, TRUE );
  743. status = ClusterInitialize();
  744. if (status == ERROR_SUCCESS) {
  745. CspSetInstallAndFirstRunState();
  746. //
  747. // Wait for ctrl-c to initiate shutdown.
  748. //
  749. WaitForSingleObject(CspStopEvent, INFINITE);
  750. } else {
  751. ClRtlLogPrint(LOG_CRITICAL,
  752. "[CS] ClusterInitialize failed %1!d!\n",
  753. status);
  754. }
  755. ClusterShutdown(status);
  756. CspCleanup();
  757. //
  758. // Can't call ClRtlLogPrint after this point.
  759. //
  760. ClRtlCleanup();
  761. return(status);
  762. }
  763. //
  764. // Main program routines
  765. //
  766. VOID
  767. CspUsage(
  768. VOID
  769. )
  770. {
  771. #if DBG
  772. printf("\nCluster Service\n");
  773. printf("\n");
  774. printf("Start with 'net start' to run as a Win32 service\n");
  775. printf("\n");
  776. printf("Command line options:\n");
  777. printf("\t-loglevel N set the debugging log level.\n");
  778. printf("\t-debug run as a console app.\n");
  779. printf("\t-debugresmon [dbgcmd] enable debugging of resrcmon process using optional command.\n");
  780. printf("\t use quotes to include args, i.e., -debugresmon \"ntsd -d\"\n");
  781. printf("\t-fixquorum no quorum device, no quorum logging.\n");
  782. printf("\t-noquorumlogging no quorum logging.\n");
  783. printf("\t-forcequorum N1,...,Nn force a quorum of nodes for node N1 up to Nn inclusive.\n");
  784. printf("\t-restoredatabase D restore cluster DB to quorum disk from dir D.\n");
  785. printf("\t-forcerestore force a restore DB operation by performing fixups.\n");
  786. printf("\t-resetquorumlog force a form despite a missing quorum log file.\n");
  787. printf("\t-quodriveletter Q drive letter for a replacement quorum disk\n");
  788. printf("\t-norepevtlogging no replication of event log entries.\n");
  789. printf("\t-novercheck ignore join version checking.\n");
  790. printf("\t-testpt N enable test point N.\n");
  791. printf("\t-persistent make test points persistent.\n");
  792. printf("\t-trigger N sets test point trigger type.\n");
  793. printf("\t (0-never (default), 1-always, 2-once, 3-count)\n");
  794. printf("\t-action N sets trigger action.\n");
  795. printf("\t (0-true (default), 1-exit, 2-break)\n");
  796. printf("\n");
  797. #else // DBG
  798. ClRtlMsgPrint(CS_COMMAND_LINE_HELP);
  799. #endif // DBG
  800. exit(1);
  801. }
  802. int __cdecl
  803. wmain(
  804. int argc,
  805. wchar_t **argv
  806. )
  807. {
  808. DWORD Status;
  809. int i;
  810. LPWSTR LogLevel;
  811. BOOLEAN debugFlagFound = FALSE;
  812. OSVERSIONINFOEXW Version;
  813. DWORD dwLen;
  814. BOOL success;
  815. PWCHAR suiteInfo;
  816. SYSTEMTIME localTime;
  817. BOOLEAN dbgOutputToConsole;
  818. UINT errorMode;
  819. DWORD dwMask;
  820. SERVICE_TABLE_ENTRY dispatchTable[] = {
  821. { CLUSTER_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)CspServiceMain },
  822. { NULL, NULL }
  823. };
  824. //
  825. // BUGBUG - 06/23/2000
  826. //
  827. // This is a temporary change to let the cluster service and resource monitor process run
  828. // despite 64-bit alignment faults. This will be removed as soon as all alignment issues
  829. // are fixed.
  830. //
  831. errorMode = SetErrorMode( SEM_NOALIGNMENTFAULTEXCEPT );
  832. SetErrorMode( SEM_NOALIGNMENTFAULTEXCEPT | errorMode );
  833. LogLevel = _wgetenv(L"ClusterLogLevel");
  834. if (LogLevel != NULL) {
  835. swscanf(LogLevel, L"%u", &CsLogLevel);
  836. }
  837. if ( (argc > 1) && ((*argv[1] == L'-') || (*argv[1] == L'/')) ) {
  838. //
  839. // Invoked from the command line.
  840. //
  841. CsRunningAsService = FALSE;
  842. dbgOutputToConsole = TRUE;
  843. } else {
  844. //
  845. // Invoked by the Service Controller
  846. //
  847. CsRunningAsService = TRUE;
  848. dbgOutputToConsole = FALSE;
  849. }
  850. //
  851. // initialize the run time library
  852. //
  853. Status = ClRtlInitialize( dbgOutputToConsole, &CsLogLevel );
  854. if (Status != ERROR_SUCCESS) {
  855. if (Status == ERROR_PATH_NOT_FOUND) {
  856. CsLogEvent( LOG_CRITICAL, SERVICE_CLUSRTL_BAD_PATH );
  857. } else {
  858. PWCHAR msgString;
  859. DWORD msgStatus;
  860. msgStatus = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  861. FORMAT_MESSAGE_FROM_SYSTEM,
  862. NULL,
  863. Status,
  864. 0,
  865. (LPWSTR)&msgString,
  866. 0,
  867. NULL);
  868. if ( msgStatus != 0 ) {
  869. CsLogEventData1(LOG_CRITICAL,
  870. SERVICE_CLUSRTL_ERROR,
  871. sizeof(Status),
  872. (PVOID)&Status,
  873. msgString);
  874. LocalFree( msgString);
  875. }
  876. }
  877. goto init_failed;
  878. }
  879. ClRtlInitWmi(L"Clustering Service");
  880. //
  881. // Log the version number
  882. //
  883. ClRtlLogPrint( LOG_NOISE, "\n\n");
  884. ClRtlLogPrint( LOG_NOISE,
  885. "[CS] Cluster Service started - Cluster Node Version %1!u!.%2!u!\n",
  886. CLUSTER_GET_MAJOR_VERSION( CsMyHighestVersion ),
  887. CLUSTER_GET_MINOR_VERSION( CsMyHighestVersion ));
  888. Version.dwOSVersionInfoSize = sizeof(Version);
  889. success = GetVersionExW((POSVERSIONINFOW)&Version);
  890. if ( success ) {
  891. //
  892. // Log the System version number
  893. //
  894. if ( Version.wSuiteMask & VER_SUITE_DATACENTER ) {
  895. suiteInfo = L"DTC";
  896. } else if ( Version.wSuiteMask & VER_SUITE_ENTERPRISE ) {
  897. suiteInfo = L"ADS";
  898. } else if ( Version.wSuiteMask & VER_SUITE_EMBEDDEDNT ) {
  899. suiteInfo = L"EMB";
  900. } else if ( Version.wProductType & VER_NT_WORKSTATION ) {
  901. suiteInfo = L"WS";
  902. } else if ( Version.wProductType & VER_NT_DOMAIN_CONTROLLER ) {
  903. suiteInfo = L"DC";
  904. } else if ( Version.wProductType & VER_NT_SERVER ) {
  905. suiteInfo = L"SRV"; // otherwise - some non-descript Server
  906. } else {
  907. suiteInfo = L"";
  908. }
  909. dwMask = (Version.wProductType << 24) | Version.wSuiteMask;
  910. ClRtlLogPrint(LOG_NOISE,
  911. " OS Version %1!u!.%2!u!.%3!u!%4!ws!%5!ws! (%6!ws! %7!08XL!)\n",
  912. Version.dwMajorVersion,
  913. Version.dwMinorVersion,
  914. Version.dwBuildNumber,
  915. *Version.szCSDVersion ? L" - " : L"",
  916. Version.szCSDVersion,
  917. suiteInfo,
  918. dwMask);
  919. } else {
  920. ClRtlLogPrint( LOG_UNUSUAL,
  921. " OS Version not available! (error %1!u!)\n",
  922. GetLastError()
  923. );
  924. }
  925. //
  926. // log the local time so we can correlate other logs which show local time
  927. //
  928. GetLocalTime( &localTime );
  929. ClRtlLogPrint( LOG_NOISE,
  930. " Local Time is "\
  931. " %1!02d!/%2!02d!/%3!02d!-%4!02d!:%5!02d!:%6!02d!.%7!03d!\n",
  932. localTime.wYear,
  933. localTime.wMonth,
  934. localTime.wDay,
  935. localTime.wHour,
  936. localTime.wMinute,
  937. localTime.wSecond,
  938. localTime.wMilliseconds);
  939. Status = ClRtlBuildClusterServiceSecurityDescriptor( NULL );
  940. if (Status != ERROR_SUCCESS) {
  941. ClRtlLogPrint(LOG_CRITICAL, "[CS] Failed to build cluster security descriptor %1!x!\n",
  942. Status);
  943. goto init_failed;
  944. }
  945. //get params set in the registry
  946. Status = CspGetServiceParams();
  947. if (Status != ERROR_SUCCESS) {
  948. ClRtlLogPrint(LOG_CRITICAL, "[CS] Failed to read service params %1!d!\n",
  949. Status);
  950. goto init_failed;
  951. }
  952. //the params on the command line over ride the ones in the registry
  953. if (CsRunningAsService == FALSE) {
  954. for (i=1; i<argc; i++) {
  955. if (lstrcmpiW( L"loglevel", argv[i]+1) == 0) {
  956. if (argc < i+2) {
  957. CspUsage();
  958. }
  959. CsLogLevel = _wtoi(argv[++i]);
  960. }
  961. #ifdef CLUSTER_TESTPOINT
  962. else if (lstrcmpiW( L"testpt", argv[i]+1 ) == 0 ) {
  963. if (argc < i+2) {
  964. CspUsage();
  965. }
  966. CsTestPoint = _wtoi(argv[++i]);
  967. }
  968. else if ( lstrcmpiW( L"persistent", argv[i]+1 ) == 0 ) {
  969. CsPersistentTestPoint = TRUE;
  970. }
  971. else if ( lstrcmpiW( L"trigger", argv[i]+1 ) == 0 ) {
  972. if ( argc < i+2 ) {
  973. CspUsage();
  974. }
  975. CsTestTrigger = _wtoi(argv[++i]);
  976. }
  977. else if ( lstrcmpiW( L"action", argv[i]+1 ) == 0 ) {
  978. if ( argc < i+2 ) {
  979. CspUsage();
  980. }
  981. CsTestAction = _wtoi(argv[++i]);
  982. }
  983. #endif // CLUSTER_TESTPOINT
  984. #if CLUSTER_BETA
  985. else if ( lstrcmpiW( L"norpcauth", argv[i]+1 ) == 0 ) {
  986. CsUseAuthenticatedRPC = FALSE;
  987. }
  988. #endif // CLUSTER_BETA
  989. else if ( lstrcmpiW( L"debugresmon", argv[i]+1 ) == 0 ) {
  990. CsDebugResmon = TRUE;
  991. //
  992. // check for optional, non-NULL command string
  993. //
  994. if ( argc >= i+2 ) {
  995. if ( *argv[i+1] != L'-' && *argv[i+1] != L'/' && *argv[i+1] != UNICODE_NULL ) {
  996. CsResmonDebugCmd = argv[++i];
  997. }
  998. }
  999. }
  1000. else if ( lstrcmpiW( L"novercheck", argv[i]+1 ) == 0 ) {
  1001. CsNoVersionCheck = TRUE;
  1002. }
  1003. else if ( lstrcmpiW( L"noquorumlogging", argv[i]+1 ) == 0 ) {
  1004. CsNoQuorumLogging = TRUE;
  1005. CsUserTurnedOffQuorumLogging = TRUE;
  1006. }
  1007. else if ( lstrcmpiW( L"fixquorum", argv[i]+1 ) == 0 ) {
  1008. CsNoQuorum = TRUE;
  1009. CsNoQuorumLogging = TRUE;
  1010. CsUserTurnedOffQuorumLogging = TRUE;
  1011. }
  1012. else if ( lstrcmpiW( L"resetquorumlog", argv[i]+1 ) == 0 ) {
  1013. CsResetQuorumLog = TRUE;
  1014. }
  1015. else if ( lstrcmpiW( L"forcequorum", argv[i]+1 ) == 0 ) {
  1016. CsForceQuorum = TRUE;
  1017. CsCommandLineForceQuorum = TRUE;
  1018. if (( argc < i+2 )
  1019. || ( *argv[i+1] == L'-' )
  1020. || ( *argv[i+1] == L'/' )) {
  1021. CsForceQuorumNodes = NULL;
  1022. } else {
  1023. CsForceQuorumNodes = argv[++i];
  1024. }
  1025. }
  1026. else if ( lstrcmpiW( L"norepevtlogging", argv[i]+1 ) == 0 ) {
  1027. CsNoRepEvtLogging = TRUE;
  1028. }
  1029. else if ( lstrcmpiW( L"debug", argv[i]+1 ) == 0 ) {
  1030. debugFlagFound = TRUE;
  1031. }
  1032. else if ( lstrcmpiW( L"restoredatabase", argv[i]+1 ) == 0 ) {
  1033. if ( ( argc < i+2 ) ||
  1034. ( *argv[i+1] == L'-' ) ||
  1035. ( *argv[i+1] == L'/' ) )
  1036. {
  1037. printf("\n\n*** restoredatabase option needs a path parameter ***\n\n");
  1038. CspUsage();
  1039. }
  1040. if ( !ClRtlIsPathValid( argv[i+1] )) {
  1041. printf( "\n\n*** restoredatabase path is invalid ***\n\n" );
  1042. CspUsage();
  1043. }
  1044. if ( !ClRtlPathFileExists( argv[i+1] )) {
  1045. printf( "\n\n*** restoredatabase file cannot be accessed ***\n\n" );
  1046. CspUsage();
  1047. }
  1048. dwLen = lstrlenW ( argv[++i] );
  1049. CsDatabaseRestorePath = (LPWSTR) LocalAlloc (LMEM_FIXED,
  1050. ( dwLen + 1 ) * sizeof ( WCHAR ) );
  1051. if ( CsDatabaseRestorePath == NULL ) {
  1052. printf("Error %d in allocating storage for restoredatabase path name (%ws)...\n",
  1053. GetLastError(),
  1054. argv[i]);
  1055. CspUsage();
  1056. }
  1057. wcscpy( CsDatabaseRestorePath, argv[i] );
  1058. CsDatabaseRestore = TRUE;
  1059. }
  1060. else if ( lstrcmpiW( L"quodriveletter", argv[i]+1 ) == 0 ) {
  1061. if ( ( argc < i+2 ) ||
  1062. ( *argv[i+1] == L'-' ) ||
  1063. ( *argv[i+1] == L'/' ) )
  1064. {
  1065. printf("\n\n*** quodriveletter option needs a drive letter parameter ***\n\n");
  1066. CspUsage();
  1067. }
  1068. dwLen = lstrlenW ( argv[++i] );
  1069. if ( ( dwLen != 2 ) ||
  1070. !iswalpha( *argv[i] ) ||
  1071. ( *( argv[i]+1 ) != L':' ) ) {
  1072. printf("\n\n*** invalid drive letter %ws supplied with quodriveletter option ***\n\n",
  1073. argv[i]);
  1074. CspUsage();
  1075. }
  1076. CsQuorumDriveLetter = (LPWSTR) LocalAlloc (LMEM_FIXED,
  1077. ( dwLen + 1 ) * sizeof ( WCHAR ) );
  1078. if ( CsQuorumDriveLetter == NULL ) {
  1079. printf("Error %d in allocating storage for quodriveletter option (%ws)...\n\n",
  1080. GetLastError(),
  1081. argv[i]);
  1082. CspUsage();
  1083. }
  1084. wcscpy( CsQuorumDriveLetter, argv[i] );
  1085. }
  1086. else if ( lstrcmpiW( L"forcerestore", argv[i]+1 ) == 0 ) {
  1087. CsForceDatabaseRestore = TRUE;
  1088. }
  1089. else {
  1090. CspUsage();
  1091. }
  1092. }
  1093. if (!debugFlagFound && !CspStopEvent) {
  1094. CspUsage();
  1095. }
  1096. if ( CsDatabaseRestore == TRUE ) {
  1097. if ( CsNoQuorumLogging || CsNoQuorum ) {
  1098. printf("\n\n**** restoredatabase cannot be used with noquorumlogging/fixquorum options ****\n\n");
  1099. CspUsage();
  1100. }
  1101. } else if ( CsForceDatabaseRestore ) {
  1102. printf("\n\n**** forcerestore cannot be used without restoredatabase option ****\n\n");
  1103. CspUsage();
  1104. }
  1105. if ( ( CsQuorumDriveLetter != NULL ) && !CsForceDatabaseRestore ) {
  1106. printf("\n\n**** quodriveletter cannot be used without forcerestore option ****\n\n");
  1107. CspUsage();
  1108. }
  1109. }
  1110. //
  1111. // Create our stop event
  1112. //
  1113. Status = ERROR_SUCCESS;
  1114. if (!CspStopEvent)
  1115. {
  1116. CspStopEvent = CreateEvent(
  1117. NULL, // default security
  1118. FALSE, // auto-reset
  1119. FALSE, // initial state is non-signalled
  1120. NULL // unnamed event
  1121. );
  1122. if (CspStopEvent == NULL) {
  1123. Status = GetLastError();
  1124. ClRtlLogPrint(LOG_CRITICAL,
  1125. "[CS] Unable to create stop event, %1!u!\n",
  1126. Status);
  1127. }
  1128. }
  1129. //
  1130. // we can't fire up our main routine if we're running as a service until
  1131. // now (race conditions between reading startup params out of the registry
  1132. // versus whether we're running as a service at all, etc.). Note that we
  1133. // failed initialization so if we are running as a service, we'll detect
  1134. // it in CspServiceMain and issue the stop
  1135. //
  1136. init_failed:
  1137. CspInitStatus = Status;
  1138. //
  1139. // Run the service.
  1140. //
  1141. if (CsRunningAsService) {
  1142. if (!StartServiceCtrlDispatcher(dispatchTable)) {
  1143. Status = GetLastError();
  1144. ClRtlLogPrint(LOG_CRITICAL,
  1145. "[CS] Unable to dispatch to SC, %1!u!\n",
  1146. Status);
  1147. CL_UNEXPECTED_ERROR( Status );
  1148. }
  1149. }
  1150. else if ( CspInitStatus == ERROR_SUCCESS ) {
  1151. Status = CspDebugService(argc, argv);
  1152. }
  1153. ClRtlFreeClusterServiceSecurityDescriptor( );
  1154. return(Status);
  1155. }
  1156. void CsGetClusterVersionInfo(
  1157. IN PCLUSTERVERSIONINFO pClusterVersionInfo)
  1158. {
  1159. OSVERSIONINFOW OsVersionInfo;
  1160. pClusterVersionInfo->dwVersionInfoSize = sizeof(CLUSTERVERSIONINFO);
  1161. pClusterVersionInfo->MajorVersion = (WORD)VER_PRODUCTVERSION_W >> 8;
  1162. pClusterVersionInfo->MinorVersion = (WORD)VER_PRODUCTVERSION_W & 0xff;
  1163. pClusterVersionInfo->BuildNumber = (WORD)CLUSTER_GET_MINOR_VERSION(CsMyHighestVersion);
  1164. mbstowcs(pClusterVersionInfo->szVendorId, VER_CLUSTER_PRODUCTNAME_STR,
  1165. (lstrlenA(VER_CLUSTER_PRODUCTNAME_STR)+1));
  1166. OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  1167. GetVersionExW(&OsVersionInfo);
  1168. lstrcpynW(pClusterVersionInfo->szCSDVersion, OsVersionInfo.szCSDVersion,
  1169. (sizeof(pClusterVersionInfo->szCSDVersion)/sizeof(WCHAR)));
  1170. pClusterVersionInfo->dwReserved = 0;
  1171. NmGetClusterOperationalVersion(&(pClusterVersionInfo->dwClusterHighestVersion),
  1172. &(pClusterVersionInfo->dwClusterLowestVersion),&(pClusterVersionInfo->dwFlags));
  1173. }