Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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