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.

1311 lines
49 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2000 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // CClusOCMApp.cpp
  7. //
  8. // Description:
  9. // ClusOCM.DLL is an Optional Components Manager DLL for installation of
  10. // Microsoft Cluster Server. This file contains the definition of the
  11. // class ClusOCMApp, which is the main class of the ClusOCM DLL.
  12. //
  13. // Documentation:
  14. // [1] 2001 Setup - Architecture.doc
  15. // Architecture of the DLL for Whistler (Windows 2001)
  16. //
  17. // [2] 2000 Setup - FuncImpl.doc
  18. // Contains description of the previous version of this DLL (Windows 2000)
  19. //
  20. // [3] http://winweb/setup/ocmanager/OcMgr1.doc
  21. // Documentation about the OC Manager API
  22. //
  23. // Header File:
  24. // CClusOCMApp.h
  25. //
  26. // Maintained By:
  27. // C. Brent Thomas (a-brentt) 01-JAN-1998
  28. // Created the original version.
  29. //
  30. // Vij Vasu (Vvasu) 03-MAR-2000
  31. // Adapted for Whistler (Windows 2001). See documentation for more
  32. // complete details. Major changes are:
  33. //
  34. // - This DLL no longer uses MFC. Class structure has changed.
  35. //
  36. // - Cluster binaries are always installed. So, uninstall functionality
  37. // has been removed from this DLL.
  38. //
  39. // - Upgrade on a computer that does not have the cluster binaries
  40. // should now install the binaries.
  41. //
  42. // - CluAdmin completely functional by the end of install of binaries.
  43. //
  44. // - Time Service no longer installed.
  45. //
  46. // - Complete change in coding and commenting style.
  47. //
  48. //////////////////////////////////////////////////////////////////////////////
  49. //////////////////////////////////////////////////////////////////////////////
  50. // Include Files
  51. //////////////////////////////////////////////////////////////////////////////
  52. // Precompiled header for this DLL.
  53. #include "pch.h"
  54. // The header file for this module.
  55. #include "CClusOCMApp.h"
  56. // For the CTaskCleanInstall class
  57. #include "CTaskCleanInstall.h"
  58. // For the CTaskUpgradeNT4 class
  59. #include "CTaskUpgradeNT4.h"
  60. // For the CTaskUpgradeWin2k class
  61. #include "CTaskUpgradeWin2k.h"
  62. // For the CTaskUpgradeWhistler class
  63. #include "CTaskUpgradeWhistler.h"
  64. //////////////////////////////////////////////////////////////////////////////
  65. // Macro Definitions
  66. //////////////////////////////////////////////////////////////////////////////
  67. // Needed for tracing.
  68. DEFINE_THISCLASS( "CClusOCMApp" )
  69. /////////////////////////////////////////////////////////////////////////////
  70. //++
  71. //
  72. // CClusOCMApp::CClusOCMApp
  73. //
  74. // Description:
  75. // Constructor of the CClusOCMApp class.
  76. //
  77. // Arguments:
  78. // None.
  79. //
  80. // Return Value:
  81. // None.
  82. //
  83. //--
  84. /////////////////////////////////////////////////////////////////////////////
  85. CClusOCMApp::CClusOCMApp( void )
  86. : m_fIsUnattendedSetup( false )
  87. , m_fIsUpgrade( false )
  88. , m_fIsGUIModeSetup( false )
  89. , m_fAttemptedTaskCreation( false )
  90. , m_cisCurrentInstallState( eClusterInstallStateUnknown )
  91. , m_dwFirstError( NO_ERROR )
  92. {
  93. TraceFunc( "" );
  94. memset( &m_sicSetupInitComponent, 0, sizeof( m_sicSetupInitComponent ) );
  95. TraceFuncExit();
  96. } //*** CClusOCMApp::CClusOCMApp()
  97. /////////////////////////////////////////////////////////////////////////////
  98. //++
  99. //
  100. // CClusOCMApp::~CClusOCMApp
  101. //
  102. // Description:
  103. // Destructor of the CClusOCMApp class.
  104. //
  105. // Arguments:
  106. // None.
  107. //
  108. // Return Value:
  109. // None.
  110. //
  111. //--
  112. /////////////////////////////////////////////////////////////////////////////
  113. CClusOCMApp::~CClusOCMApp( void )
  114. {
  115. TraceFunc( "" );
  116. TraceFuncExit();
  117. } //*** CClusOCMApp::CClusOCMApp()
  118. /////////////////////////////////////////////////////////////////////////////
  119. //++
  120. //
  121. // DWORD
  122. // CClusOCMApp::DwClusOcmSetupProc
  123. //
  124. // Description:
  125. // This function is called by the entry point of this DLL, DwClusOcmSetupProc.
  126. // See document [3] in the header of this file for details.
  127. //
  128. // This function determines what actions need to be taken (upgrade, clean
  129. // install, etc., and pass control accordingly to the correct routines.
  130. //
  131. // Arguments:
  132. // LPCVOID pvComponentIdIn
  133. // Pointer to a string that uniquely identifies the component.
  134. //
  135. // LPCVOID pvSubComponentIdIn
  136. // Pointer to a string that uniquely identifies a sub-component in
  137. // the component's hiearchy.
  138. //
  139. // UINT uiFunctionCodeIn
  140. // A numeric value indicating which function is to be perfomed.
  141. // See ocmanage.h for the macro definitions.
  142. //
  143. // UINT uiParam1In
  144. // Supplies a function specific parameter.
  145. //
  146. // PVOID pvParam2Inout
  147. // Pointer to a function specific parameter (either input or output).
  148. //
  149. // Return Value:
  150. // A function specific value is returned to OC Manager.
  151. //
  152. //--
  153. /////////////////////////////////////////////////////////////////////////////
  154. DWORD
  155. CClusOCMApp::DwClusOcmSetupProc(
  156. LPCVOID pvComponentIdIn
  157. , LPCVOID pvSubComponentIdIn
  158. , UINT uiFunctionCodeIn
  159. , UINT uiParam1In
  160. , PVOID pvParam2Inout
  161. )
  162. {
  163. TraceFunc( "" );
  164. LogMsg( "Entering " __FUNCTION__ "()" );
  165. DWORD dwReturnValue = NO_ERROR;
  166. // Switch based on the function code passed in by OC Manager.
  167. switch ( uiFunctionCodeIn )
  168. {
  169. // This is the first function that OC Manager calls.
  170. case OC_PREINITIALIZE:
  171. {
  172. TraceFlow( "OC Manager called OC_PREINITIALIZE." );
  173. LogMsg( "OC Manager called OC_PREINITIALIZE." );
  174. // Return OCFLAG_UNICODE to indicate that only UNICODE is to be used.
  175. dwReturnValue = OCFLAG_UNICODE;
  176. } // case OC_PREINITIALIZE
  177. break;
  178. //
  179. // This function is called soon after the component's setup dll is
  180. // loaded. This function provides initialization information to the
  181. // dll, instructs the dll to initialize itself, and provides a
  182. // mechanism for the dll to return information to OC Manager.
  183. //
  184. case OC_INIT_COMPONENT:
  185. {
  186. TraceFlow1(
  187. "OC Manager called OC_INIT_COMPONENT for the component '%s'."
  188. , reinterpret_cast< LPCWSTR >( pvComponentIdIn )
  189. );
  190. LogMsg(
  191. "OC Manager called OC_INIT_COMPONENT for the component '%s'."
  192. , reinterpret_cast< LPCWSTR >( pvComponentIdIn )
  193. );
  194. dwReturnValue = TW32( DwOcInitComponentHandler( reinterpret_cast< PSETUP_INIT_COMPONENT >( pvParam2Inout ) ) );
  195. } // case OC_INIT_COMPONENT
  196. break;
  197. // Get the initial, current and final state of the component.
  198. case OC_QUERY_STATE:
  199. {
  200. TraceFlow( "OC Manager called OC_QUERY_STATE." );
  201. LogMsg( "OC Manager called OC_QUERY_STATE." );
  202. dwReturnValue = DwOcQueryStateHandler( uiParam1In );
  203. } // case OC_QUERY_STATE
  204. break;
  205. // OC Manager is asking approval for a user selection of installation state.
  206. case OC_QUERY_CHANGE_SEL_STATE:
  207. {
  208. TraceFlow( "OC Manager called OC_QUERY_CHANGE_SEL_STATE." );
  209. LogMsg( "OC Manager called OC_QUERY_CHANGE_SEL_STATE." );
  210. //
  211. // The cluster service has to be always installed. So, disallow any state
  212. // change that deselects the cluster service (by returning FALSE).
  213. //
  214. // The component has been deselected if uiParam1In is 0.
  215. if ( uiParam1In == 0 )
  216. {
  217. TraceFlow( "Disallowing deselection of the Cluster Service." );
  218. LogMsg( "Disallowing deselection of the Cluster Service." );
  219. dwReturnValue = FALSE;
  220. }
  221. else
  222. {
  223. TraceFlow( "Allowing selection of the Cluster Service." );
  224. LogMsg( "Allowing selection of the Cluster Service." );
  225. dwReturnValue = TRUE;
  226. }
  227. } // case OC_QUERY_CHANGE_SEL_STATE
  228. break;
  229. // Instructs the component to change to a given language if it can.
  230. case OC_SET_LANGUAGE:
  231. {
  232. TraceFlow( "OC Manager called OC_SET_LANGUAGE." );
  233. LogMsg( "OC Manager called OC_SET_LANGUAGE." );
  234. dwReturnValue = SetThreadLocale( MAKELCID( PRIMARYLANGID( uiParam1In ), SORT_DEFAULT ) );
  235. } // case OC_SET_LANGUAGE
  236. break;
  237. //
  238. // Directs the component to manipulate a Setup API Disk Space List,
  239. // placing files on it or removing files from it, to mirror what will be
  240. // actually installed later via a Setup API file queue.
  241. //
  242. case OC_CALC_DISK_SPACE:
  243. {
  244. CClusOCMTask * pCurrentTask = NULL;
  245. TraceFlow( "OC Manager called OC_CALC_DISK_SPACE." );
  246. LogMsg( "OC Manager called OC_CALC_DISK_SPACE." );
  247. dwReturnValue = TW32( DwGetCurrentTask( pCurrentTask ) );
  248. if ( dwReturnValue != NO_ERROR )
  249. {
  250. DwSetError( dwReturnValue );
  251. TraceFlow1( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
  252. LogMsg( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
  253. break;
  254. } // if: we could not get the current task pointer
  255. if ( pCurrentTask != NULL )
  256. {
  257. dwReturnValue = TW32(
  258. pCurrentTask->DwOcCalcDiskSpace(
  259. ( uiParam1In != 0 ) // non-zero uiParam1In means "add to disk space requirements"
  260. , reinterpret_cast< HDSKSPC >( pvParam2Inout )
  261. )
  262. );
  263. // Note: Do not call DwSetError() here if the above function failed. Failure to calculate disk space
  264. // is to be expected if the binaries are not accessible at this time (for example, they are on a
  265. // network share and the credentials for this share have not been entered yet). This is not fatal and
  266. // hence should not trigger a cleanup.
  267. } // if: there is something to do
  268. else
  269. {
  270. TraceFlow( "There is no task to be performed." );
  271. LogMsg( "There is no task to be performed." );
  272. } // else: there is nothing to do.
  273. } // case OC_CALC_DISK_SPACE
  274. break;
  275. //
  276. // Directs the component to queue file operations for installation, based on
  277. // user interaction with the wizard pages and other component-specific factors.
  278. //
  279. case OC_QUEUE_FILE_OPS:
  280. {
  281. CClusOCMTask * pCurrentTask = NULL;
  282. TraceFlow( "OC Manager called OC_QUEUE_FILE_OPS." );
  283. LogMsg( "OC Manager called OC_QUEUE_FILE_OPS." );
  284. if ( DwGetError() != NO_ERROR )
  285. {
  286. // If an error has occurred previously, do not do this operation.
  287. TraceFlow( "An error has occurred earlier in this task. Nothing will be done here." );
  288. LogMsg( "An error has occurred earlier in this task. Nothing will be done here." );
  289. break;
  290. } // if: an error has occurred previously
  291. dwReturnValue = TW32( DwGetCurrentTask( pCurrentTask ) );
  292. if ( dwReturnValue != NO_ERROR )
  293. {
  294. DwSetError( dwReturnValue );
  295. TraceFlow1( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
  296. LogMsg( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
  297. break;
  298. } // if: we could not get the current task pointer
  299. if ( pCurrentTask != NULL )
  300. {
  301. dwReturnValue = TW32(
  302. DwSetError(
  303. pCurrentTask->DwOcQueueFileOps(
  304. reinterpret_cast< HSPFILEQ >( pvParam2Inout )
  305. )
  306. )
  307. );
  308. } // if: there is something to do
  309. else
  310. {
  311. TraceFlow( "There is no task to be performed." );
  312. LogMsg( "There is no task to be performed." );
  313. } // else: there is nothing to do.
  314. } // case OC_QUEUE_FILE_OPS
  315. break;
  316. //
  317. // Allows the component to perform any additional operations needed
  318. // to complete installation, for example registry manipulations, and
  319. // so forth.
  320. //
  321. case OC_COMPLETE_INSTALLATION:
  322. {
  323. CClusOCMTask * pCurrentTask = NULL;
  324. TraceFlow( "OC Manager called OC_COMPLETE_INSTALLATION." );
  325. LogMsg( "OC Manager called OC_COMPLETE_INSTALLATION." );
  326. if ( DwGetError() != NO_ERROR )
  327. {
  328. // If an error has occurred previously, do not do this operation.
  329. TraceFlow( "An error has occurred earlier in this task. Nothing will be done here." );
  330. LogMsg( "An error has occurred earlier in this task. Nothing will be done here." );
  331. break;
  332. } // if: an error has occurred previously
  333. dwReturnValue = TW32( DwGetCurrentTask( pCurrentTask ) );
  334. if ( dwReturnValue != NO_ERROR )
  335. {
  336. DwSetError( dwReturnValue );
  337. TraceFlow1( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
  338. LogMsg( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
  339. break;
  340. } // if: we could not get the current task pointer
  341. if ( pCurrentTask != NULL )
  342. {
  343. dwReturnValue = TW32( DwSetError( pCurrentTask->DwOcCompleteInstallation() ) );
  344. } // if: there is something to do
  345. else
  346. {
  347. TraceFlow( "There is no task to be performed." );
  348. LogMsg( "There is no task to be performed." );
  349. } // else: there is nothing to do.
  350. } // case OC_COMPLETE_INSTALLATION
  351. break;
  352. //
  353. // Informs the component that it is about to be unloaded.
  354. //
  355. case OC_CLEANUP:
  356. {
  357. CClusOCMTask * pCurrentTask = NULL;
  358. TraceFlow( "OC Manager called OC_CLEANUP." );
  359. LogMsg( "OC Manager called OC_CLEANUP." );
  360. dwReturnValue = TW32( DwGetCurrentTask( pCurrentTask ) );
  361. if ( dwReturnValue != NO_ERROR )
  362. {
  363. DwSetError( dwReturnValue );
  364. TraceFlow1( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
  365. LogMsg( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
  366. break;
  367. } // if: we could not get the current task pointer
  368. if ( pCurrentTask != NULL )
  369. {
  370. dwReturnValue = TW32( DwSetError( pCurrentTask->DwOcCleanup() ) );
  371. // Once the cleanup is done, we have nothing else to do. Free the task object.
  372. ResetCurrentTask();
  373. } // if: there is something to do
  374. else
  375. {
  376. TraceFlow( "There is no task to be performed." );
  377. LogMsg( "There is no task to be performed." );
  378. } // else: there is nothing to do.
  379. } // case OC_CLEANUP
  380. break;
  381. default:
  382. {
  383. TraceFlow1( "OC Manager called unknown function. Function code is %#x.", uiFunctionCodeIn );
  384. LogMsg( "OC Manager called unknown function. Function code is %#x.", uiFunctionCodeIn );
  385. } // case: default
  386. } // switch( uiFunctionCodeIn )
  387. TraceFlow1( "Return Value is %#x.", dwReturnValue );
  388. LogMsg( "Return Value is %#x.", dwReturnValue );
  389. RETURN( dwReturnValue );
  390. } //*** CClusOCMApp::DwClusOcmSetupProc()
  391. /////////////////////////////////////////////////////////////////////////////
  392. //++
  393. //
  394. // DWORD
  395. // CClusOCMApp::DwOcInitComponentHandler
  396. //
  397. // Description:
  398. // This function handles the OC_INIT_COMPONENT messages from the Optional
  399. // Components Manager.
  400. //
  401. // This function is called soon after the component's setup dll is
  402. // loaded. This checks OS and OC Manager versions, initializes CClusOCMApp
  403. // data members, determines the cluster service installation state, etc.
  404. //
  405. // Arguments:
  406. // PSETUP_INIT_COMPONENT pSetupInitComponentInout
  407. // Pointer to a SETUP_INIT_COMPONENT structure.
  408. //
  409. // Return Value:
  410. // NO_ERROR
  411. // Call was successful.
  412. //
  413. // ERROR_CALL_NOT_IMPLEMENTED
  414. // The OC Manager and this DLLs versions are not compatible.
  415. //
  416. // ERROR_CANCELLED
  417. // Any other error occurred. No other error codes are returned.
  418. // The actual error is logged.
  419. //
  420. // Remarks:
  421. // The SETUP_INIT_COMPONENT structure pointed to by pSetupInitComponentInout
  422. // is not persistent. It is therefore necessary to save a copy locally.
  423. //
  424. //--
  425. /////////////////////////////////////////////////////////////////////////////
  426. DWORD
  427. CClusOCMApp::DwOcInitComponentHandler(
  428. PSETUP_INIT_COMPONENT pSetupInitComponentInout
  429. )
  430. {
  431. TraceFunc( "" );
  432. LogMsg( "Entering " __FUNCTION__ "()" );
  433. DWORD dwReturnValue = NO_ERROR;
  434. UINT uiStatus;
  435. eClusterInstallState cisTempState = eClusterInstallStateUnknown;
  436. // Dummy do-while loop to avoid gotos.
  437. do
  438. {
  439. if ( pSetupInitComponentInout == NULL )
  440. {
  441. TraceFlow( "Error: Pointer to the SETUP_INIT_COMPONENT structure is NULL." );
  442. LogMsg( "Error: Pointer to the SETUP_INIT_COMPONENT structure is NULL." );
  443. dwReturnValue = TW32( ERROR_CANCELLED );
  444. break;
  445. } // if: pSetupInitComponentInout is NULL
  446. // Indicate to OC Manager which version of OC Manager this dll expects.
  447. pSetupInitComponentInout->ComponentVersion = OCMANAGER_VERSION;
  448. // Save the SETUP_INIT_COMPONENT structure.
  449. SetSetupState( *pSetupInitComponentInout );
  450. //
  451. // Determine if the OC Manager version is correct.
  452. //
  453. if ( OCMANAGER_VERSION > RsicGetSetupInitComponent().OCManagerVersion )
  454. {
  455. // Indicate failure.
  456. TraceFlow2(
  457. "Error: OC Manager version mismatch. Version %d is required, Version %d was reported.",
  458. OCMANAGER_VERSION,
  459. RsicGetSetupInitComponent().OCManagerVersion
  460. );
  461. LogMsg(
  462. "Error: OC Manager version mismatch. Version %d is required, Version %d was reported.",
  463. OCMANAGER_VERSION,
  464. RsicGetSetupInitComponent().OCManagerVersion
  465. );
  466. dwReturnValue = TW32( ERROR_CALL_NOT_IMPLEMENTED );
  467. break;
  468. } // if: the OC Manager version is incorrect.
  469. TraceFlow( "The OC Manager version matches with the version of this component." );
  470. LogMsg( "The OC Manager version matches with the version of this component." );
  471. #if 0
  472. /*
  473. // KB: 06-DEC-2000 DavidP
  474. // Since ClusOCM only copies files and registers some COM objects,
  475. // there is no longer any reason to perform an OS check. We now
  476. // depend on this happening in the service when the node is added
  477. // to a cluster.
  478. //
  479. // Check OS version and suite information.
  480. //
  481. TraceFlow( "Checking if OS version and Product Suite are compatible..." );
  482. LogMsg( "Checking if OS version and Product Suite are compatible..." );
  483. if ( ClRtlIsOSValid() == FALSE )
  484. {
  485. // The OS version and/or Product Suite are not compatible
  486. DWORD dwErrorCode = TW32( GetLastError() );
  487. TraceFlow( "Cluster Service cannot be installed on this computer. The version or product suite of the operating system is incorrect." );
  488. LogMsg( "Cluster Service cannot be installed on this computer. The version or product suite of the operating system is incorrect." );
  489. TraceFlow1( "ClRtlIsOSValid failed with error code %#x.", dwErrorCode );
  490. LogMsg( "ClRtlIsOSValid failed with error code %#x.", dwErrorCode );
  491. dwReturnValue = ERROR_CANCELLED;
  492. break;
  493. } // if: OS version and/or Product Suite are not compatible
  494. TraceFlow( "OS version and product suite are correct." );
  495. LogMsg( "OS version and product suite are correct." );
  496. */
  497. #endif
  498. // Is the handle to the component INF valid?
  499. if ( ( RsicGetSetupInitComponent().ComponentInfHandle == INVALID_HANDLE_VALUE )
  500. || ( RsicGetSetupInitComponent().ComponentInfHandle == NULL )
  501. )
  502. {
  503. // Indicate failure.
  504. LogMsg( "Error: ComponentInfHandle is invalid." );
  505. TraceFlow( "Error: ComponentInfHandle is invalid." );
  506. dwReturnValue = TW32( ERROR_CANCELLED );
  507. break;
  508. } // if: the INF file handle is not valid.
  509. //
  510. // The following call to SetupOpenAppendInfFile ensures that layout.inf
  511. // gets appended to ClusOCM.inf. This is required for several reasons
  512. // dictated by the Setup API. In theory OC Manager should do this, but
  513. // as per Andrew Ritz, 8/24/98, OC manager neglects to do it and it is
  514. // harmless to do it here after OC Manager is revised.
  515. //
  516. // Note that passing NULL as the first parameter causes SetupOpenAppendInfFile
  517. // to append the file(s) listed on the LayoutFile entry in clusocm.inf.
  518. //
  519. // The above comment was written by Brent.
  520. // TODO: Check if this is still needed. (Vij Vasu, 05-MAR-2000)
  521. //
  522. SetupOpenAppendInfFile(
  523. NULL,
  524. RsicGetSetupInitComponent().ComponentInfHandle,
  525. &uiStatus
  526. );
  527. //
  528. // Determine the current installation state of the cluster service. This can
  529. // be done by calling the function ClRtlGetClusterInstallState.
  530. // However, on machines that are upgrading from NT4, this will not work and
  531. // the correct installation state can be found out only by checking if the
  532. // cluster service is registered (ClRtlGetClusterInstallState will return
  533. // eClusterInstallStateUnknown in this case)
  534. //
  535. dwReturnValue = ClRtlGetClusterInstallState( NULL, &cisTempState );
  536. if ( dwReturnValue != ERROR_SUCCESS )
  537. {
  538. TraceFlow1( "Error %#x occurred calling ClRtlGetClusterInstallState(). Cluster Service installation state cannot be determined.", dwReturnValue );
  539. LogMsg( "Error %#x occurred calling ClRtlGetClusterInstallState(). Cluster Service installation state cannot be determined.", dwReturnValue );
  540. dwReturnValue = TW32( ERROR_CANCELLED );
  541. break;
  542. } // if: ClRtlGetClusterInstallState failed
  543. if ( cisTempState == eClusterInstallStateUnknown )
  544. {
  545. bool fRegistered = false;
  546. dwReturnValue = TW32( DwIsClusterServiceRegistered( &fRegistered ) );
  547. if ( dwReturnValue != ERROR_SUCCESS )
  548. {
  549. TraceFlow1( "Error %#x: Could not check if the cluster service was registered or not.", dwReturnValue );
  550. LogMsg( "Error %#x: Could not check if the cluster service was registered or not.", dwReturnValue );
  551. dwReturnValue = ERROR_CANCELLED;
  552. break;
  553. }
  554. TraceFlow( "ClRtlGetClusterInstallState() returned eClusterInstallStateUnknown. Trying to see if the service is registered." );
  555. LogMsg( "ClRtlGetClusterInstallState() returned eClusterInstallStateUnknown. Trying to see if the service is registered." );
  556. if ( fRegistered )
  557. {
  558. TraceFlow( "The Cluster Service is registered. Setting current installation state to eClusterInstallStateConfigured." );
  559. LogMsg( "The Cluster Service is registered. Setting current installation state to eClusterInstallStateConfigured." );
  560. cisTempState = eClusterInstallStateConfigured;
  561. }
  562. else
  563. {
  564. TraceFlow( "The Cluster Service is not registered." );
  565. LogMsg( "The Cluster Service is not registered." );
  566. } // else: Cluster Service is not registered.
  567. } // if: ClRtlGetClusterInstallState returned eClusterInstallStateUnknown
  568. TraceFlow1( "The current installation state of the cluster service is %#x.", cisTempState );
  569. LogMsg( "The current installation state of the cluster service is %#x.", cisTempState );
  570. // Store the current cluster installation state.
  571. CisStoreClusterInstallState( cisTempState );
  572. }
  573. while ( false ); // dummy do-while loop to avoid gotos
  574. TraceFlow1( "Return Value is %#x.", dwReturnValue );
  575. LogMsg( "Return Value is %#x.", dwReturnValue );
  576. RETURN( dwReturnValue );
  577. } //*** CClusOCMApp::DwOcInitComponentHandler()
  578. /////////////////////////////////////////////////////////////////////////////
  579. //++
  580. //
  581. // DWORD
  582. // CClusOCMApp::DwOcQueryStateHandler
  583. //
  584. // Description:
  585. // This function handles the OC_QUERY_STATE messages from the Optional
  586. // Components Manager.
  587. //
  588. // This function is called called at least thrice, once each to get the
  589. // initial, current and the final installation states.
  590. //
  591. // The initial state is the state before ClusOCM was called.
  592. //
  593. // The current state is the current selection state. This is always
  594. // 'On' because the cluster binaries are always installed.
  595. //
  596. // The final state is the state after ClusOCM has done its tasks.
  597. //
  598. // Arguments:
  599. // UINT uiSelStateQueryTypeIn
  600. // The type of query - OCSELSTATETYPE_ORIGINAL, OCSELSTATETYPE_CURRENT
  601. // or OCSELSTATETYPE_FINAL.
  602. //
  603. // Return Value:
  604. // SubcompOn
  605. // Indicates that the checkbox next to the component in the OC
  606. // Manager UI should be set.
  607. //
  608. // SubcompOff
  609. // Indicates that the checkbox should be cleared.
  610. //
  611. // SubcompUseOcManagerDefault
  612. // OC Manager should set the state of the checkbox.
  613. //
  614. // Remarks:
  615. // This function has to be called after DwOcInitComponentHandler, otherwise
  616. // the initial installation state may not be set correctly.
  617. //--
  618. /////////////////////////////////////////////////////////////////////////////
  619. DWORD
  620. CClusOCMApp::DwOcQueryStateHandler( UINT uiSelStateQueryTypeIn )
  621. {
  622. TraceFunc( "" );
  623. LogMsg( "Entering " __FUNCTION__ "()" );
  624. DWORD dwReturnValue = SubcompUseOcManagerDefault;
  625. switch( uiSelStateQueryTypeIn )
  626. {
  627. case OCSELSTATETYPE_ORIGINAL:
  628. {
  629. TraceFlow( "OC Manager is querying for the original state." );
  630. LogMsg( "OC Manager is querying for the original state." );
  631. //
  632. // If the cluster binaries have been installed or if cluster service
  633. // has been configured, then the original installation state is on.
  634. //
  635. dwReturnValue = ( CisGetClusterInstallState() == eClusterInstallStateUnknown )
  636. ? SubcompOff
  637. : SubcompOn;
  638. } // case: OCSELSTATETYPE_ORIGINAL
  639. break;
  640. case OCSELSTATETYPE_CURRENT:
  641. {
  642. // The current state is always on.
  643. TraceFlow( "OC Manager is querying for the current state." );
  644. LogMsg( "OC Manager is querying for the current state." );
  645. dwReturnValue = SubcompOn;
  646. } // case: OCSELSTATETYPE_CURRENT
  647. break;
  648. case OCSELSTATETYPE_FINAL:
  649. {
  650. TraceFlow( "OC Manager is querying for the final state." );
  651. LogMsg( "OC Manager is querying for the final state." );
  652. //
  653. // If we are here, then the OC_COMPLETE_INSTALLATION has already
  654. // been called. At this stage CisStoreClusterInstallState() reflects
  655. // the state after ClusOCM has done its tasks.
  656. //
  657. dwReturnValue = ( CisGetClusterInstallState() == eClusterInstallStateUnknown )
  658. ? SubcompOff
  659. : SubcompOn;
  660. } // case: OCSELSTATETYPE_FINAL
  661. break;
  662. }; // switch: based on uiSelStateQueryTypeIn
  663. TraceFlow1( "Return Value is %#x.", dwReturnValue );
  664. LogMsg( "Return Value is %#x.", dwReturnValue );
  665. RETURN( dwReturnValue );
  666. } //*** CClusOCMApp::DwOcQueryStateHandler()
  667. /////////////////////////////////////////////////////////////////////////////
  668. //++
  669. //
  670. // void
  671. // CClusOCMApp::SetSetupState
  672. //
  673. // Description:
  674. // Set the SETUP_INIT_COMPONENT structure. Use this structure and set
  675. // various setup state variables.
  676. //
  677. // Arguments:
  678. // const SETUP_INIT_COMPONENT & sicSourceIn
  679. // The source SETUP_INIT_COMPONENT structure, usually passed in by
  680. // the OC Manager.
  681. //
  682. // Return Value:
  683. // None.
  684. //
  685. //--
  686. /////////////////////////////////////////////////////////////////////////////
  687. void
  688. CClusOCMApp::SetSetupState( const SETUP_INIT_COMPONENT & sicSourceIn )
  689. {
  690. TraceFunc( "" );
  691. m_sicSetupInitComponent = sicSourceIn;
  692. m_fIsUnattendedSetup = (
  693. ( m_sicSetupInitComponent.SetupData.OperationFlags
  694. & (DWORDLONG) SETUPOP_BATCH
  695. )
  696. !=
  697. (DWORDLONG) 0L
  698. );
  699. m_fIsUpgrade = (
  700. ( m_sicSetupInitComponent.SetupData.OperationFlags
  701. & (DWORDLONG) SETUPOP_NTUPGRADE
  702. )
  703. !=
  704. (DWORDLONG) 0L
  705. );
  706. m_fIsGUIModeSetup = (
  707. ( m_sicSetupInitComponent.SetupData.OperationFlags
  708. & (DWORDLONG) SETUPOP_STANDALONE
  709. )
  710. ==
  711. (DWORDLONG) 0L
  712. );
  713. // Log setup state.
  714. TraceFlow3(
  715. "This is an %s, %s setup session. This is%s an upgrade."
  716. , FIsUnattendedSetup() ? L"unattended" : L"attended"
  717. , FIsGUIModeSetup() ? L"GUI mode" : L"standalone"
  718. , FIsUpgrade() ? L"" : L" not"
  719. );
  720. LogMsg(
  721. "This is an %s, %s setup session. This is%s an upgrade."
  722. , FIsUnattendedSetup() ? L"unattended" : L"attended"
  723. , FIsGUIModeSetup() ? L"GUI mode" : L"standalone"
  724. , FIsUpgrade() ? L"" : L" not"
  725. );
  726. TraceFuncExit();
  727. } //*** CClusOCMApp::SetSetupState()
  728. /////////////////////////////////////////////////////////////////////////////
  729. //++
  730. //
  731. // DWORD
  732. // CClusOCMApp::DwIsClusterServiceRegistered
  733. //
  734. // Description:
  735. // This function determines whether the Cluster Service has been registered
  736. // with the Service Control Manager. If it is, it means that it has already
  737. // been configured. This check is required in nodes being upgraded from NT4
  738. // where ClRtlGetClusterInstallState() will not work.
  739. //
  740. // Arguments:
  741. // bool * pfIsRegisteredOut
  742. // If true, Cluster Service (ClusSvc) is registered with the Service
  743. // Control Manager (SCM). Else, Cluster Service (ClusSvc) is not
  744. // registered with SCM
  745. //
  746. // Return Value:
  747. // ERROR_SUCCESS if all went well.
  748. // Other Win32 error codes on failure.
  749. //
  750. //--
  751. /////////////////////////////////////////////////////////////////////////////
  752. DWORD
  753. CClusOCMApp::DwIsClusterServiceRegistered( bool * pfIsRegisteredOut ) const
  754. {
  755. TraceFunc( "" );
  756. LogMsg( "Entering " __FUNCTION__ "()" );
  757. bool fIsRegistered = false;
  758. DWORD dwReturnValue = ERROR_SUCCESS;
  759. // dummy do-while loop to avoid gotos
  760. do
  761. {
  762. // Connect to the Service Control Manager
  763. SmartServiceHandle shServiceMgr( OpenSCManager( NULL, NULL, GENERIC_READ ) );
  764. // Was the service control manager database opened successfully?
  765. if ( shServiceMgr.HHandle() == NULL )
  766. {
  767. dwReturnValue = TW32( GetLastError() );
  768. TraceFlow1( "Error %#x occurred trying to open a connection to the local service control manager.", dwReturnValue );
  769. LogMsg( "Error %#x occurred trying to open a connection to the local service control manager.", dwReturnValue );
  770. break;
  771. } // if: opening the SCM was unsuccessful
  772. // Open a handle to the Cluster Service.
  773. SmartServiceHandle shService( OpenService( shServiceMgr, L"ClusSvc", GENERIC_READ ) );
  774. // Was the handle to the service opened?
  775. if ( shService.HHandle() != NULL )
  776. {
  777. TraceFlow( "The cluster service is registered." );
  778. LogMsg( "The cluster service is registered." );
  779. fIsRegistered = true;
  780. break;
  781. } // if: handle to clussvc could be opened
  782. dwReturnValue = GetLastError();
  783. if ( dwReturnValue == ERROR_SERVICE_DOES_NOT_EXIST )
  784. {
  785. dwReturnValue = ERROR_SUCCESS;
  786. TraceFlow( "ClusSvc does not exist as a service." );
  787. LogMsg( "ClusSvc does not exist as a service." );
  788. break;
  789. } // if: the handle could not be opened because the service did not exist.
  790. // Some error occurred.
  791. TW32( dwReturnValue);
  792. TraceFlow1( "Error %#x occurred trying to open a handle to the cluster service.", dwReturnValue );
  793. LogMsg( "Error %#x occurred trying to open a handle to the cluster service.", dwReturnValue );
  794. // Handles are closed by the CSmartHandle destructor.
  795. }
  796. while ( false ); // dummy do-while loop to avoid gotos
  797. if ( pfIsRegisteredOut != NULL )
  798. {
  799. *pfIsRegisteredOut = fIsRegistered;
  800. }
  801. TraceFlow2( "fIsRegistered is %d. Return Value is %#x.", fIsRegistered, dwReturnValue );
  802. LogMsg( "fIsRegistered is %d. Return Value is %#x.", fIsRegistered, dwReturnValue );
  803. RETURN( dwReturnValue );
  804. } //*** CClusOCMApp::DwIsClusterServiceRegistered()
  805. /////////////////////////////////////////////////////////////////////////////
  806. //++
  807. //
  808. // DWORD
  809. // CClusOCMApp::DwGetCurrentTask
  810. //
  811. // Description:
  812. // This function returns a pointer to the current task object. If a task
  813. // object has not been created yet, it creates the appropriate task.
  814. //
  815. // Arguments:
  816. // CClusOCMTask *& rpTaskOut
  817. // Reference to the pointer to the current task. Do not try to
  818. // free this memory.
  819. //
  820. // If no task needs to be performed, a NULL pointer is returned.
  821. //
  822. // Return Value:
  823. // NO_ERROR if all went well.
  824. // Other Win32 error codes on failure.
  825. //
  826. //
  827. // Remarks:
  828. // This function will work properly only after the member variables which
  829. // indicate which task will be performed have been initialized correctly.
  830. //--
  831. /////////////////////////////////////////////////////////////////////////////
  832. DWORD
  833. CClusOCMApp::DwGetCurrentTask( CClusOCMTask *& rpTaskOut )
  834. {
  835. TraceFunc( "" );
  836. LogMsg( "Entering " __FUNCTION__ "()" );
  837. DWORD dwReturnValue = NO_ERROR;
  838. // Initialize the output.
  839. rpTaskOut = NULL;
  840. do
  841. {
  842. eClusterInstallState ecisCurrentState;
  843. if ( m_fAttemptedTaskCreation )
  844. {
  845. // A task object already exists - just return it.
  846. TraceFlow( "A task object already exists. Returning it." );
  847. LogMsg( "A task object already exists. Returning it." );
  848. rpTaskOut = m_sptaskCurrentTask.PMem();
  849. break;
  850. } // if: the task object has already been created.
  851. TraceFlow( "Creating a new task object." );
  852. LogMsg( "Creating a new task object." );
  853. // Make note of the fact that we have started our attempt to create a task object.
  854. m_fAttemptedTaskCreation = true;
  855. // Reset the task pointer.
  856. m_sptaskCurrentTask.Assign( NULL );
  857. // Get the current installation state to deduce what operation to perform.
  858. ecisCurrentState = CisGetClusterInstallState();
  859. // The task object has not been created yet - create one now.
  860. if ( ecisCurrentState == eClusterInstallStateUnknown )
  861. {
  862. TraceFlow( "The cluster installation state is not known. Assuming that a clean install is required." );
  863. LogMsg( "The cluster installation state is not known. Assuming that a clean install is required." );
  864. // If the installation state is unknown, assume that the cluster binaries
  865. // are not installed.
  866. rpTaskOut = new CTaskCleanInstall( *this );
  867. if ( rpTaskOut == NULL )
  868. {
  869. TraceFlow( "Error: There was not enough memory to create a clean install task." );
  870. LogMsg( "Error: There was not enough memory to start a clean install." );
  871. dwReturnValue = TW32( ERROR_NOT_ENOUGH_MEMORY );
  872. break;
  873. } // if: memory allocation failed
  874. } // if: the cluster installation state is eClusterInstallStateUnknown
  875. else if ( m_fIsUpgrade )
  876. {
  877. //
  878. // If we are here, it means that an upgrade is in progress and the cluster binaries
  879. // have already been installed on the OS being upgraded. Additionally, this node may
  880. // already be part of a cluster.
  881. //
  882. DWORD dwNodeClusterMajorVersion = 0;
  883. // Find out which version of the cluster service we are upgrading.
  884. dwReturnValue = TW32( DwGetNodeClusterMajorVersion( dwNodeClusterMajorVersion ) );
  885. if ( dwReturnValue != NO_ERROR )
  886. {
  887. TraceFlow1( "Error %#x occurred trying to determine the version of the cluster service that we are upgrading.", dwReturnValue );
  888. LogMsg( "Error %#x occurred trying to determine the version of the cluster service that we are upgrading.", dwReturnValue );
  889. break;
  890. } // if: an error occurred trying to determine the version of the cluster service that we are upgrading
  891. // Check if the returned cluster version is valid
  892. if ( ( dwNodeClusterMajorVersion != NT51_MAJOR_VERSION )
  893. && ( dwNodeClusterMajorVersion != NT5_MAJOR_VERSION )
  894. && ( dwNodeClusterMajorVersion != NT4SP4_MAJOR_VERSION )
  895. && ( dwNodeClusterMajorVersion != NT4_MAJOR_VERSION )
  896. )
  897. {
  898. TraceFlow1( "The version of the cluster service before the upgrade (%d) is invalid.", dwNodeClusterMajorVersion );
  899. LogMsg( "The version of the cluster service before the upgrade (%d) is invalid.", dwNodeClusterMajorVersion );
  900. break;
  901. } // if: the cluster version is not valid
  902. // Based on the previous version of the cluster service, create the correct task object.
  903. if ( dwNodeClusterMajorVersion == NT5_MAJOR_VERSION )
  904. {
  905. TraceFlow( "We are upgrading a Windows 2000 node." );
  906. LogMsg( "We are upgrading a Windows 2000 node." );
  907. rpTaskOut = new CTaskUpgradeWin2k( *this );
  908. } // if: we are upgrading from Windows 2000
  909. else if ( dwNodeClusterMajorVersion == NT51_MAJOR_VERSION )
  910. {
  911. TraceFlow( "We are upgrading a Whistler node." );
  912. LogMsg( "We are upgrading a Whistler node." );
  913. rpTaskOut = new CTaskUpgradeWhistler( *this );
  914. } // else if: we are upgrading from Whistler
  915. else
  916. {
  917. TraceFlow( "We are upgrading an NT4 node." );
  918. LogMsg( "We are upgrading an NT4 node." );
  919. rpTaskOut = new CTaskUpgradeNT4( *this );
  920. } // else: we are upgrading from NT4 (either SP3 or SP4)
  921. if ( rpTaskOut == NULL )
  922. {
  923. TraceFlow( "Error: There was not enough memory to create the required task object." );
  924. LogMsg( "Error: There was not enough memory to create the required task." );
  925. dwReturnValue = TW32( ERROR_NOT_ENOUGH_MEMORY );
  926. break;
  927. } // if: memory allocation failed
  928. } // else if: an upgrade is in progress
  929. if ( rpTaskOut != NULL )
  930. {
  931. TraceFlow( "A task object was successfully created." );
  932. LogMsg( "A task object was successfully created." );
  933. // Store the pointer to the newly created task in the member variable.
  934. m_sptaskCurrentTask.Assign( rpTaskOut );
  935. } // if: the task object was successfully created
  936. else
  937. {
  938. TraceFlow( "No task object was created." );
  939. LogMsg( "No task object was created." );
  940. } // else: no task object was created
  941. }
  942. while( false ); // dummy do-while loop to avoid gotos
  943. LogMsg( "Return Value is %#x.", dwReturnValue );
  944. TraceFlow1( "Return Value is %#x.", dwReturnValue );
  945. RETURN( dwReturnValue );
  946. } //*** CClusOCMApp::DwGetCurrentTask()
  947. /////////////////////////////////////////////////////////////////////////////
  948. //++
  949. //
  950. // DWORD
  951. // CClusOCMApp::DwGetNodeClusterMajorVersion
  952. //
  953. // Description:
  954. // This function returns the major version of the cluster service that
  955. // we are upgrading. The version that this function returns is the version
  956. // of the service before the upgrade. If there was a problem reading this
  957. // information, this function lies and says that the previous version was
  958. // NT4, since this is the safest thing to say and is better than aborting
  959. // the upgrade.
  960. //
  961. // Note: This function can only be called during an upgrade.
  962. //
  963. // Arguments:
  964. // DWORD & rdwNodeClusterMajorVersionOut
  965. // Reference to DWORD that will hold the major version of the cluster
  966. // service that we are upgrading.
  967. //
  968. // Return Value:
  969. // NO_ERROR if all went well.
  970. // E_UNEXPECTED if an upgrade is not in progress.
  971. // Other Win32 error codes on failure.
  972. //
  973. //
  974. // Remarks:
  975. // This function will work properly only after the member variables which
  976. // indicate which task will be performed have been initialized correctly.
  977. //--
  978. /////////////////////////////////////////////////////////////////////////////
  979. DWORD
  980. CClusOCMApp::DwGetNodeClusterMajorVersion( DWORD & rdwNodeClusterMajorVersionOut )
  981. {
  982. TraceFunc( "" );
  983. LogMsg( "Entering " __FUNCTION__ "()" );
  984. DWORD dwReturnValue = NO_ERROR;
  985. DWORD dwPrevOSMajorVersion = 0;
  986. DWORD dwPrevOSMinorVersion = 0;
  987. do
  988. {
  989. SmartRegistryKey srkOSInfoKey;
  990. DWORD dwRegValueType = 0;
  991. DWORD cbBufferSize = 0;
  992. // Initialize the output.
  993. rdwNodeClusterMajorVersionOut = 0;
  994. if ( !m_fIsUpgrade )
  995. {
  996. TraceFlow( "Error: This function cannot be called when an upgrade is not in progress." );
  997. LogMsg( "Error: This function cannot be called when an upgrade is not in progress." );
  998. dwReturnValue = THR( E_UNEXPECTED );
  999. break;
  1000. } // if: an upgrade is not in progress
  1001. //
  1002. // Read the registry to get what the OS version was before the upgrade.
  1003. // This information was written here by ClusComp.dll. From the OS version information,
  1004. // try and deduce the cluster version info.
  1005. // NOTE: At this point, it is not possible to differentiate between NT4_MAJOR_VERSION
  1006. // and NT4SP4_MAJOR_VERSION, and, for the purposes of the upgrade, I don't think we need
  1007. // to either - so, just treat all NT4 cluster nodes the same.
  1008. //
  1009. {
  1010. HKEY hTempKey = NULL;
  1011. // Open the node version info registry key
  1012. dwReturnValue = TW32(
  1013. RegOpenKeyEx(
  1014. HKEY_LOCAL_MACHINE
  1015. , CLUSREG_KEYNAME_NODE_DATA L"\\" CLUSREG_KEYNAME_PREV_OS_INFO
  1016. , 0
  1017. , KEY_READ
  1018. , &hTempKey
  1019. )
  1020. );
  1021. if ( dwReturnValue != ERROR_SUCCESS )
  1022. {
  1023. TraceFlow1( "Error %#x occurred trying open the registry key where info about the previous OS is stored.", dwReturnValue );
  1024. LogMsg( "Error %#x occurred trying open the registry key where where info about the previous OS is stored.", dwReturnValue );
  1025. break;
  1026. } // if: RegOpenKeyEx() failed
  1027. // Store the opened key in a smart pointer for automatic close.
  1028. srkOSInfoKey.Assign( hTempKey );
  1029. }
  1030. // Read the OS major version
  1031. cbBufferSize = sizeof( dwPrevOSMajorVersion );
  1032. dwReturnValue = TW32(
  1033. RegQueryValueEx(
  1034. srkOSInfoKey.HHandle()
  1035. , CLUSREG_NAME_NODE_MAJOR_VERSION
  1036. , 0
  1037. , &dwRegValueType
  1038. , reinterpret_cast< LPBYTE >( &dwPrevOSMajorVersion )
  1039. , &cbBufferSize
  1040. )
  1041. );
  1042. if ( dwReturnValue != ERROR_SUCCESS )
  1043. {
  1044. TraceFlow1( "Error %#x occurred trying to read the previous major version info.", dwReturnValue );
  1045. LogMsg( "Error %#x occurred trying to read the previous OS major version info.", dwReturnValue );
  1046. break;
  1047. } // if: RegQueryValueEx() failed while reading dwPrevOSMajorVersion
  1048. // Read the OS minor version
  1049. cbBufferSize = sizeof( dwPrevOSMinorVersion );
  1050. dwReturnValue = TW32(
  1051. RegQueryValueEx(
  1052. srkOSInfoKey.HHandle()
  1053. , CLUSREG_NAME_NODE_MINOR_VERSION
  1054. , 0
  1055. , &dwRegValueType
  1056. , reinterpret_cast< LPBYTE >( &dwPrevOSMinorVersion )
  1057. , &cbBufferSize
  1058. )
  1059. );
  1060. if ( dwReturnValue != ERROR_SUCCESS )
  1061. {
  1062. TraceFlow1( "Error %#x occurred trying to read the previous minor version info.", dwReturnValue );
  1063. LogMsg( "Error %#x occurred trying to read the previous OS minor version info.", dwReturnValue );
  1064. break;
  1065. } // if: RegQueryValueEx() failed while reading dwPrevOSMinorVersion
  1066. TraceFlow2( "Previous OS major and minor versions were %d and %d respectively.", dwPrevOSMajorVersion, dwPrevOSMinorVersion );
  1067. LogMsg( "Previous OS major and minor versions were %d and %d respectively.", dwPrevOSMajorVersion, dwPrevOSMinorVersion );
  1068. }
  1069. while( false ); // dummy do-while loop to avoid gotos
  1070. if ( dwReturnValue != NO_ERROR )
  1071. {
  1072. TraceFlow( "An error occurred trying to read the version information of the previous OS. Proceeding assuming that it was NT4." );
  1073. LogMsg( "An error occurred trying to read the version information of the previous OS. Proceeding assuming that it was NT4." );
  1074. dwReturnValue = NO_ERROR;
  1075. rdwNodeClusterMajorVersionOut = NT4_MAJOR_VERSION;
  1076. } // if: an error occurred trying to determine the previous OS version
  1077. else
  1078. {
  1079. if ( dwPrevOSMajorVersion == 4 )
  1080. {
  1081. // The previous OS version was NT4 (it does not matter if it was SP3 or SP4 - we will treat
  1082. // both the same way.
  1083. TraceFlow( "The previous OS was NT4. We are going to treat NT4SP3 and NT4SP4 nodes the same way for upgrades." );
  1084. LogMsg( "The previous OS was NT4. We are going to treat NT4SP3 and NT4SP4 nodes the same way for upgrades." );
  1085. rdwNodeClusterMajorVersionOut = NT4_MAJOR_VERSION;
  1086. } // if: the previous OS version was NT4
  1087. else if ( dwPrevOSMajorVersion == 5 )
  1088. {
  1089. if ( dwPrevOSMinorVersion == 0 )
  1090. {
  1091. TraceFlow( "The previous OS was Windows 2000." );
  1092. LogMsg( "The previous OS was Windows 2000." );
  1093. rdwNodeClusterMajorVersionOut = NT5_MAJOR_VERSION;
  1094. } // if: this was a Windows 2000 node
  1095. else if ( dwPrevOSMinorVersion == 1 )
  1096. {
  1097. TraceFlow( "The previous OS was Whistler." );
  1098. LogMsg( "The previous OS was Whistler." );
  1099. rdwNodeClusterMajorVersionOut = NT51_MAJOR_VERSION;
  1100. } // else if: this was a Whistler node
  1101. else
  1102. {
  1103. TraceFlow( "The previous OS was neither NT4, Windows 2000 nor Whistler. An error must have occurred." );
  1104. LogMsg( "The previous OS was neither NT4, Windows 2000 nor Whistler. An error must have occurred." );
  1105. dwReturnValue = THR( E_UNEXPECTED );
  1106. } // else: the previous OS was neither NT4, Windows 2000 nor Whistler
  1107. } // else if: the previous OS major version is 5
  1108. else
  1109. {
  1110. TraceFlow( "The previous OS was neither NT4, Windows 2000 nor Whistler. An error must have occurred." );
  1111. LogMsg( "The previous OS was neither NT4, Windows 2000 nor Whistler. An error must have occurred." );
  1112. dwReturnValue = THR( E_UNEXPECTED );
  1113. } // else: the previous OS was neither NT4, Windows 2000 nor Whistler
  1114. } // if; we read the previous OS version info
  1115. LogMsg( "Return Value is %#x.", dwReturnValue );
  1116. TraceFlow1( "Return Value is %#x.", dwReturnValue );
  1117. RETURN( dwReturnValue );
  1118. } //*** CClusOCMApp::DwGetNodeClusterMajorVersion()