windows-nt/Source/XPSP1/NT/base/cluster/mgmt/clusocm/cclusocmapp.cpp

1312 lines
49 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000 Microsoft Corporation
//
// Module Name:
// CClusOCMApp.cpp
//
// Description:
// ClusOCM.DLL is an Optional Components Manager DLL for installation of
// Microsoft Cluster Server. This file contains the definition of the
// class ClusOCMApp, which is the main class of the ClusOCM DLL.
//
// Documentation:
// [1] 2001 Setup - Architecture.doc
// Architecture of the DLL for Whistler (Windows 2001)
//
// [2] 2000 Setup - FuncImpl.doc
// Contains description of the previous version of this DLL (Windows 2000)
//
// [3] http://winweb/setup/ocmanager/OcMgr1.doc
// Documentation about the OC Manager API
//
// Header File:
// CClusOCMApp.h
//
// Maintained By:
// C. Brent Thomas (a-brentt) 01-JAN-1998
// Created the original version.
//
// Vij Vasu (Vvasu) 03-MAR-2000
// Adapted for Whistler (Windows 2001). See documentation for more
// complete details. Major changes are:
//
// - This DLL no longer uses MFC. Class structure has changed.
//
// - Cluster binaries are always installed. So, uninstall functionality
// has been removed from this DLL.
//
// - Upgrade on a computer that does not have the cluster binaries
// should now install the binaries.
//
// - CluAdmin completely functional by the end of install of binaries.
//
// - Time Service no longer installed.
//
// - Complete change in coding and commenting style.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Include Files
//////////////////////////////////////////////////////////////////////////////
// Precompiled header for this DLL.
#include "pch.h"
// The header file for this module.
#include "CClusOCMApp.h"
// For the CTaskCleanInstall class
#include "CTaskCleanInstall.h"
// For the CTaskUpgradeNT4 class
#include "CTaskUpgradeNT4.h"
// For the CTaskUpgradeWin2k class
#include "CTaskUpgradeWin2k.h"
// For the CTaskUpgradeWhistler class
#include "CTaskUpgradeWhistler.h"
//////////////////////////////////////////////////////////////////////////////
// Macro Definitions
//////////////////////////////////////////////////////////////////////////////
// Needed for tracing.
DEFINE_THISCLASS( "CClusOCMApp" )
/////////////////////////////////////////////////////////////////////////////
//++
//
// CClusOCMApp::CClusOCMApp
//
// Description:
// Constructor of the CClusOCMApp class.
//
// Arguments:
// None.
//
// Return Value:
// None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CClusOCMApp::CClusOCMApp( void )
: m_fIsUnattendedSetup( false )
, m_fIsUpgrade( false )
, m_fIsGUIModeSetup( false )
, m_fAttemptedTaskCreation( false )
, m_cisCurrentInstallState( eClusterInstallStateUnknown )
, m_dwFirstError( NO_ERROR )
{
TraceFunc( "" );
memset( &m_sicSetupInitComponent, 0, sizeof( m_sicSetupInitComponent ) );
TraceFuncExit();
} //*** CClusOCMApp::CClusOCMApp()
/////////////////////////////////////////////////////////////////////////////
//++
//
// CClusOCMApp::~CClusOCMApp
//
// Description:
// Destructor of the CClusOCMApp class.
//
// Arguments:
// None.
//
// Return Value:
// None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CClusOCMApp::~CClusOCMApp( void )
{
TraceFunc( "" );
TraceFuncExit();
} //*** CClusOCMApp::CClusOCMApp()
/////////////////////////////////////////////////////////////////////////////
//++
//
// DWORD
// CClusOCMApp::DwClusOcmSetupProc
//
// Description:
// This function is called by the entry point of this DLL, DwClusOcmSetupProc.
// See document [3] in the header of this file for details.
//
// This function determines what actions need to be taken (upgrade, clean
// install, etc., and pass control accordingly to the correct routines.
//
// Arguments:
// LPCVOID pvComponentIdIn
// Pointer to a string that uniquely identifies the component.
//
// LPCVOID pvSubComponentIdIn
// Pointer to a string that uniquely identifies a sub-component in
// the component's hiearchy.
//
// UINT uiFunctionCodeIn
// A numeric value indicating which function is to be perfomed.
// See ocmanage.h for the macro definitions.
//
// UINT uiParam1In
// Supplies a function specific parameter.
//
// PVOID pvParam2Inout
// Pointer to a function specific parameter (either input or output).
//
// Return Value:
// A function specific value is returned to OC Manager.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
CClusOCMApp::DwClusOcmSetupProc(
LPCVOID pvComponentIdIn
, LPCVOID pvSubComponentIdIn
, UINT uiFunctionCodeIn
, UINT uiParam1In
, PVOID pvParam2Inout
)
{
TraceFunc( "" );
LogMsg( "Entering " __FUNCTION__ "()" );
DWORD dwReturnValue = NO_ERROR;
// Switch based on the function code passed in by OC Manager.
switch ( uiFunctionCodeIn )
{
// This is the first function that OC Manager calls.
case OC_PREINITIALIZE:
{
TraceFlow( "OC Manager called OC_PREINITIALIZE." );
LogMsg( "OC Manager called OC_PREINITIALIZE." );
// Return OCFLAG_UNICODE to indicate that only UNICODE is to be used.
dwReturnValue = OCFLAG_UNICODE;
} // case OC_PREINITIALIZE
break;
//
// This function is called soon after the component's setup dll is
// loaded. This function provides initialization information to the
// dll, instructs the dll to initialize itself, and provides a
// mechanism for the dll to return information to OC Manager.
//
case OC_INIT_COMPONENT:
{
TraceFlow1(
"OC Manager called OC_INIT_COMPONENT for the component '%s'."
, reinterpret_cast< LPCWSTR >( pvComponentIdIn )
);
LogMsg(
"OC Manager called OC_INIT_COMPONENT for the component '%s'."
, reinterpret_cast< LPCWSTR >( pvComponentIdIn )
);
dwReturnValue = TW32( DwOcInitComponentHandler( reinterpret_cast< PSETUP_INIT_COMPONENT >( pvParam2Inout ) ) );
} // case OC_INIT_COMPONENT
break;
// Get the initial, current and final state of the component.
case OC_QUERY_STATE:
{
TraceFlow( "OC Manager called OC_QUERY_STATE." );
LogMsg( "OC Manager called OC_QUERY_STATE." );
dwReturnValue = DwOcQueryStateHandler( uiParam1In );
} // case OC_QUERY_STATE
break;
// OC Manager is asking approval for a user selection of installation state.
case OC_QUERY_CHANGE_SEL_STATE:
{
TraceFlow( "OC Manager called OC_QUERY_CHANGE_SEL_STATE." );
LogMsg( "OC Manager called OC_QUERY_CHANGE_SEL_STATE." );
//
// The cluster service has to be always installed. So, disallow any state
// change that deselects the cluster service (by returning FALSE).
//
// The component has been deselected if uiParam1In is 0.
if ( uiParam1In == 0 )
{
TraceFlow( "Disallowing deselection of the Cluster Service." );
LogMsg( "Disallowing deselection of the Cluster Service." );
dwReturnValue = FALSE;
}
else
{
TraceFlow( "Allowing selection of the Cluster Service." );
LogMsg( "Allowing selection of the Cluster Service." );
dwReturnValue = TRUE;
}
} // case OC_QUERY_CHANGE_SEL_STATE
break;
// Instructs the component to change to a given language if it can.
case OC_SET_LANGUAGE:
{
TraceFlow( "OC Manager called OC_SET_LANGUAGE." );
LogMsg( "OC Manager called OC_SET_LANGUAGE." );
dwReturnValue = SetThreadLocale( MAKELCID( PRIMARYLANGID( uiParam1In ), SORT_DEFAULT ) );
} // case OC_SET_LANGUAGE
break;
//
// Directs the component to manipulate a Setup API Disk Space List,
// placing files on it or removing files from it, to mirror what will be
// actually installed later via a Setup API file queue.
//
case OC_CALC_DISK_SPACE:
{
CClusOCMTask * pCurrentTask = NULL;
TraceFlow( "OC Manager called OC_CALC_DISK_SPACE." );
LogMsg( "OC Manager called OC_CALC_DISK_SPACE." );
dwReturnValue = TW32( DwGetCurrentTask( pCurrentTask ) );
if ( dwReturnValue != NO_ERROR )
{
DwSetError( dwReturnValue );
TraceFlow1( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
LogMsg( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
break;
} // if: we could not get the current task pointer
if ( pCurrentTask != NULL )
{
dwReturnValue = TW32(
pCurrentTask->DwOcCalcDiskSpace(
( uiParam1In != 0 ) // non-zero uiParam1In means "add to disk space requirements"
, reinterpret_cast< HDSKSPC >( pvParam2Inout )
)
);
// Note: Do not call DwSetError() here if the above function failed. Failure to calculate disk space
// is to be expected if the binaries are not accessible at this time (for example, they are on a
// network share and the credentials for this share have not been entered yet). This is not fatal and
// hence should not trigger a cleanup.
} // if: there is something to do
else
{
TraceFlow( "There is no task to be performed." );
LogMsg( "There is no task to be performed." );
} // else: there is nothing to do.
} // case OC_CALC_DISK_SPACE
break;
//
// Directs the component to queue file operations for installation, based on
// user interaction with the wizard pages and other component-specific factors.
//
case OC_QUEUE_FILE_OPS:
{
CClusOCMTask * pCurrentTask = NULL;
TraceFlow( "OC Manager called OC_QUEUE_FILE_OPS." );
LogMsg( "OC Manager called OC_QUEUE_FILE_OPS." );
if ( DwGetError() != NO_ERROR )
{
// If an error has occurred previously, do not do this operation.
TraceFlow( "An error has occurred earlier in this task. Nothing will be done here." );
LogMsg( "An error has occurred earlier in this task. Nothing will be done here." );
break;
} // if: an error has occurred previously
dwReturnValue = TW32( DwGetCurrentTask( pCurrentTask ) );
if ( dwReturnValue != NO_ERROR )
{
DwSetError( dwReturnValue );
TraceFlow1( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
LogMsg( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
break;
} // if: we could not get the current task pointer
if ( pCurrentTask != NULL )
{
dwReturnValue = TW32(
DwSetError(
pCurrentTask->DwOcQueueFileOps(
reinterpret_cast< HSPFILEQ >( pvParam2Inout )
)
)
);
} // if: there is something to do
else
{
TraceFlow( "There is no task to be performed." );
LogMsg( "There is no task to be performed." );
} // else: there is nothing to do.
} // case OC_QUEUE_FILE_OPS
break;
//
// Allows the component to perform any additional operations needed
// to complete installation, for example registry manipulations, and
// so forth.
//
case OC_COMPLETE_INSTALLATION:
{
CClusOCMTask * pCurrentTask = NULL;
TraceFlow( "OC Manager called OC_COMPLETE_INSTALLATION." );
LogMsg( "OC Manager called OC_COMPLETE_INSTALLATION." );
if ( DwGetError() != NO_ERROR )
{
// If an error has occurred previously, do not do this operation.
TraceFlow( "An error has occurred earlier in this task. Nothing will be done here." );
LogMsg( "An error has occurred earlier in this task. Nothing will be done here." );
break;
} // if: an error has occurred previously
dwReturnValue = TW32( DwGetCurrentTask( pCurrentTask ) );
if ( dwReturnValue != NO_ERROR )
{
DwSetError( dwReturnValue );
TraceFlow1( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
LogMsg( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
break;
} // if: we could not get the current task pointer
if ( pCurrentTask != NULL )
{
dwReturnValue = TW32( DwSetError( pCurrentTask->DwOcCompleteInstallation() ) );
} // if: there is something to do
else
{
TraceFlow( "There is no task to be performed." );
LogMsg( "There is no task to be performed." );
} // else: there is nothing to do.
} // case OC_COMPLETE_INSTALLATION
break;
//
// Informs the component that it is about to be unloaded.
//
case OC_CLEANUP:
{
CClusOCMTask * pCurrentTask = NULL;
TraceFlow( "OC Manager called OC_CLEANUP." );
LogMsg( "OC Manager called OC_CLEANUP." );
dwReturnValue = TW32( DwGetCurrentTask( pCurrentTask ) );
if ( dwReturnValue != NO_ERROR )
{
DwSetError( dwReturnValue );
TraceFlow1( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
LogMsg( "Error %#x occurred trying to get a pointer to the current task.", dwReturnValue );
break;
} // if: we could not get the current task pointer
if ( pCurrentTask != NULL )
{
dwReturnValue = TW32( DwSetError( pCurrentTask->DwOcCleanup() ) );
// Once the cleanup is done, we have nothing else to do. Free the task object.
ResetCurrentTask();
} // if: there is something to do
else
{
TraceFlow( "There is no task to be performed." );
LogMsg( "There is no task to be performed." );
} // else: there is nothing to do.
} // case OC_CLEANUP
break;
default:
{
TraceFlow1( "OC Manager called unknown function. Function code is %#x.", uiFunctionCodeIn );
LogMsg( "OC Manager called unknown function. Function code is %#x.", uiFunctionCodeIn );
} // case: default
} // switch( uiFunctionCodeIn )
TraceFlow1( "Return Value is %#x.", dwReturnValue );
LogMsg( "Return Value is %#x.", dwReturnValue );
RETURN( dwReturnValue );
} //*** CClusOCMApp::DwClusOcmSetupProc()
/////////////////////////////////////////////////////////////////////////////
//++
//
// DWORD
// CClusOCMApp::DwOcInitComponentHandler
//
// Description:
// This function handles the OC_INIT_COMPONENT messages from the Optional
// Components Manager.
//
// This function is called soon after the component's setup dll is
// loaded. This checks OS and OC Manager versions, initializes CClusOCMApp
// data members, determines the cluster service installation state, etc.
//
// Arguments:
// PSETUP_INIT_COMPONENT pSetupInitComponentInout
// Pointer to a SETUP_INIT_COMPONENT structure.
//
// Return Value:
// NO_ERROR
// Call was successful.
//
// ERROR_CALL_NOT_IMPLEMENTED
// The OC Manager and this DLLs versions are not compatible.
//
// ERROR_CANCELLED
// Any other error occurred. No other error codes are returned.
// The actual error is logged.
//
// Remarks:
// The SETUP_INIT_COMPONENT structure pointed to by pSetupInitComponentInout
// is not persistent. It is therefore necessary to save a copy locally.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
CClusOCMApp::DwOcInitComponentHandler(
PSETUP_INIT_COMPONENT pSetupInitComponentInout
)
{
TraceFunc( "" );
LogMsg( "Entering " __FUNCTION__ "()" );
DWORD dwReturnValue = NO_ERROR;
UINT uiStatus;
eClusterInstallState cisTempState = eClusterInstallStateUnknown;
// Dummy do-while loop to avoid gotos.
do
{
if ( pSetupInitComponentInout == NULL )
{
TraceFlow( "Error: Pointer to the SETUP_INIT_COMPONENT structure is NULL." );
LogMsg( "Error: Pointer to the SETUP_INIT_COMPONENT structure is NULL." );
dwReturnValue = TW32( ERROR_CANCELLED );
break;
} // if: pSetupInitComponentInout is NULL
// Indicate to OC Manager which version of OC Manager this dll expects.
pSetupInitComponentInout->ComponentVersion = OCMANAGER_VERSION;
// Save the SETUP_INIT_COMPONENT structure.
SetSetupState( *pSetupInitComponentInout );
//
// Determine if the OC Manager version is correct.
//
if ( OCMANAGER_VERSION > RsicGetSetupInitComponent().OCManagerVersion )
{
// Indicate failure.
TraceFlow2(
"Error: OC Manager version mismatch. Version %d is required, Version %d was reported.",
OCMANAGER_VERSION,
RsicGetSetupInitComponent().OCManagerVersion
);
LogMsg(
"Error: OC Manager version mismatch. Version %d is required, Version %d was reported.",
OCMANAGER_VERSION,
RsicGetSetupInitComponent().OCManagerVersion
);
dwReturnValue = TW32( ERROR_CALL_NOT_IMPLEMENTED );
break;
} // if: the OC Manager version is incorrect.
TraceFlow( "The OC Manager version matches with the version of this component." );
LogMsg( "The OC Manager version matches with the version of this component." );
#if 0
/*
// KB: 06-DEC-2000 DavidP
// Since ClusOCM only copies files and registers some COM objects,
// there is no longer any reason to perform an OS check. We now
// depend on this happening in the service when the node is added
// to a cluster.
//
// Check OS version and suite information.
//
TraceFlow( "Checking if OS version and Product Suite are compatible..." );
LogMsg( "Checking if OS version and Product Suite are compatible..." );
if ( ClRtlIsOSValid() == FALSE )
{
// The OS version and/or Product Suite are not compatible
DWORD dwErrorCode = TW32( GetLastError() );
TraceFlow( "Cluster Service cannot be installed on this computer. The version or product suite of the operating system is incorrect." );
LogMsg( "Cluster Service cannot be installed on this computer. The version or product suite of the operating system is incorrect." );
TraceFlow1( "ClRtlIsOSValid failed with error code %#x.", dwErrorCode );
LogMsg( "ClRtlIsOSValid failed with error code %#x.", dwErrorCode );
dwReturnValue = ERROR_CANCELLED;
break;
} // if: OS version and/or Product Suite are not compatible
TraceFlow( "OS version and product suite are correct." );
LogMsg( "OS version and product suite are correct." );
*/
#endif
// Is the handle to the component INF valid?
if ( ( RsicGetSetupInitComponent().ComponentInfHandle == INVALID_HANDLE_VALUE )
|| ( RsicGetSetupInitComponent().ComponentInfHandle == NULL )
)
{
// Indicate failure.
LogMsg( "Error: ComponentInfHandle is invalid." );
TraceFlow( "Error: ComponentInfHandle is invalid." );
dwReturnValue = TW32( ERROR_CANCELLED );
break;
} // if: the INF file handle is not valid.
//
// The following call to SetupOpenAppendInfFile ensures that layout.inf
// gets appended to ClusOCM.inf. This is required for several reasons
// dictated by the Setup API. In theory OC Manager should do this, but
// as per Andrew Ritz, 8/24/98, OC manager neglects to do it and it is
// harmless to do it here after OC Manager is revised.
//
// Note that passing NULL as the first parameter causes SetupOpenAppendInfFile
// to append the file(s) listed on the LayoutFile entry in clusocm.inf.
//
// The above comment was written by Brent.
// TODO: Check if this is still needed. (Vij Vasu, 05-MAR-2000)
//
SetupOpenAppendInfFile(
NULL,
RsicGetSetupInitComponent().ComponentInfHandle,
&uiStatus
);
//
// Determine the current installation state of the cluster service. This can
// be done by calling the function ClRtlGetClusterInstallState.
// However, on machines that are upgrading from NT4, this will not work and
// the correct installation state can be found out only by checking if the
// cluster service is registered (ClRtlGetClusterInstallState will return
// eClusterInstallStateUnknown in this case)
//
dwReturnValue = ClRtlGetClusterInstallState( NULL, &cisTempState );
if ( dwReturnValue != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x occurred calling ClRtlGetClusterInstallState(). Cluster Service installation state cannot be determined.", dwReturnValue );
LogMsg( "Error %#x occurred calling ClRtlGetClusterInstallState(). Cluster Service installation state cannot be determined.", dwReturnValue );
dwReturnValue = TW32( ERROR_CANCELLED );
break;
} // if: ClRtlGetClusterInstallState failed
if ( cisTempState == eClusterInstallStateUnknown )
{
bool fRegistered = false;
dwReturnValue = TW32( DwIsClusterServiceRegistered( &fRegistered ) );
if ( dwReturnValue != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x: Could not check if the cluster service was registered or not.", dwReturnValue );
LogMsg( "Error %#x: Could not check if the cluster service was registered or not.", dwReturnValue );
dwReturnValue = ERROR_CANCELLED;
break;
}
TraceFlow( "ClRtlGetClusterInstallState() returned eClusterInstallStateUnknown. Trying to see if the service is registered." );
LogMsg( "ClRtlGetClusterInstallState() returned eClusterInstallStateUnknown. Trying to see if the service is registered." );
if ( fRegistered )
{
TraceFlow( "The Cluster Service is registered. Setting current installation state to eClusterInstallStateConfigured." );
LogMsg( "The Cluster Service is registered. Setting current installation state to eClusterInstallStateConfigured." );
cisTempState = eClusterInstallStateConfigured;
}
else
{
TraceFlow( "The Cluster Service is not registered." );
LogMsg( "The Cluster Service is not registered." );
} // else: Cluster Service is not registered.
} // if: ClRtlGetClusterInstallState returned eClusterInstallStateUnknown
TraceFlow1( "The current installation state of the cluster service is %#x.", cisTempState );
LogMsg( "The current installation state of the cluster service is %#x.", cisTempState );
// Store the current cluster installation state.
CisStoreClusterInstallState( cisTempState );
}
while ( false ); // dummy do-while loop to avoid gotos
TraceFlow1( "Return Value is %#x.", dwReturnValue );
LogMsg( "Return Value is %#x.", dwReturnValue );
RETURN( dwReturnValue );
} //*** CClusOCMApp::DwOcInitComponentHandler()
/////////////////////////////////////////////////////////////////////////////
//++
//
// DWORD
// CClusOCMApp::DwOcQueryStateHandler
//
// Description:
// This function handles the OC_QUERY_STATE messages from the Optional
// Components Manager.
//
// This function is called called at least thrice, once each to get the
// initial, current and the final installation states.
//
// The initial state is the state before ClusOCM was called.
//
// The current state is the current selection state. This is always
// 'On' because the cluster binaries are always installed.
//
// The final state is the state after ClusOCM has done its tasks.
//
// Arguments:
// UINT uiSelStateQueryTypeIn
// The type of query - OCSELSTATETYPE_ORIGINAL, OCSELSTATETYPE_CURRENT
// or OCSELSTATETYPE_FINAL.
//
// Return Value:
// SubcompOn
// Indicates that the checkbox next to the component in the OC
// Manager UI should be set.
//
// SubcompOff
// Indicates that the checkbox should be cleared.
//
// SubcompUseOcManagerDefault
// OC Manager should set the state of the checkbox.
//
// Remarks:
// This function has to be called after DwOcInitComponentHandler, otherwise
// the initial installation state may not be set correctly.
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
CClusOCMApp::DwOcQueryStateHandler( UINT uiSelStateQueryTypeIn )
{
TraceFunc( "" );
LogMsg( "Entering " __FUNCTION__ "()" );
DWORD dwReturnValue = SubcompUseOcManagerDefault;
switch( uiSelStateQueryTypeIn )
{
case OCSELSTATETYPE_ORIGINAL:
{
TraceFlow( "OC Manager is querying for the original state." );
LogMsg( "OC Manager is querying for the original state." );
//
// If the cluster binaries have been installed or if cluster service
// has been configured, then the original installation state is on.
//
dwReturnValue = ( CisGetClusterInstallState() == eClusterInstallStateUnknown )
? SubcompOff
: SubcompOn;
} // case: OCSELSTATETYPE_ORIGINAL
break;
case OCSELSTATETYPE_CURRENT:
{
// The current state is always on.
TraceFlow( "OC Manager is querying for the current state." );
LogMsg( "OC Manager is querying for the current state." );
dwReturnValue = SubcompOn;
} // case: OCSELSTATETYPE_CURRENT
break;
case OCSELSTATETYPE_FINAL:
{
TraceFlow( "OC Manager is querying for the final state." );
LogMsg( "OC Manager is querying for the final state." );
//
// If we are here, then the OC_COMPLETE_INSTALLATION has already
// been called. At this stage CisStoreClusterInstallState() reflects
// the state after ClusOCM has done its tasks.
//
dwReturnValue = ( CisGetClusterInstallState() == eClusterInstallStateUnknown )
? SubcompOff
: SubcompOn;
} // case: OCSELSTATETYPE_FINAL
break;
}; // switch: based on uiSelStateQueryTypeIn
TraceFlow1( "Return Value is %#x.", dwReturnValue );
LogMsg( "Return Value is %#x.", dwReturnValue );
RETURN( dwReturnValue );
} //*** CClusOCMApp::DwOcQueryStateHandler()
/////////////////////////////////////////////////////////////////////////////
//++
//
// void
// CClusOCMApp::SetSetupState
//
// Description:
// Set the SETUP_INIT_COMPONENT structure. Use this structure and set
// various setup state variables.
//
// Arguments:
// const SETUP_INIT_COMPONENT & sicSourceIn
// The source SETUP_INIT_COMPONENT structure, usually passed in by
// the OC Manager.
//
// Return Value:
// None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void
CClusOCMApp::SetSetupState( const SETUP_INIT_COMPONENT & sicSourceIn )
{
TraceFunc( "" );
m_sicSetupInitComponent = sicSourceIn;
m_fIsUnattendedSetup = (
( m_sicSetupInitComponent.SetupData.OperationFlags
& (DWORDLONG) SETUPOP_BATCH
)
!=
(DWORDLONG) 0L
);
m_fIsUpgrade = (
( m_sicSetupInitComponent.SetupData.OperationFlags
& (DWORDLONG) SETUPOP_NTUPGRADE
)
!=
(DWORDLONG) 0L
);
m_fIsGUIModeSetup = (
( m_sicSetupInitComponent.SetupData.OperationFlags
& (DWORDLONG) SETUPOP_STANDALONE
)
==
(DWORDLONG) 0L
);
// Log setup state.
TraceFlow3(
"This is an %s, %s setup session. This is%s an upgrade."
, FIsUnattendedSetup() ? L"unattended" : L"attended"
, FIsGUIModeSetup() ? L"GUI mode" : L"standalone"
, FIsUpgrade() ? L"" : L" not"
);
LogMsg(
"This is an %s, %s setup session. This is%s an upgrade."
, FIsUnattendedSetup() ? L"unattended" : L"attended"
, FIsGUIModeSetup() ? L"GUI mode" : L"standalone"
, FIsUpgrade() ? L"" : L" not"
);
TraceFuncExit();
} //*** CClusOCMApp::SetSetupState()
/////////////////////////////////////////////////////////////////////////////
//++
//
// DWORD
// CClusOCMApp::DwIsClusterServiceRegistered
//
// Description:
// This function determines whether the Cluster Service has been registered
// with the Service Control Manager. If it is, it means that it has already
// been configured. This check is required in nodes being upgraded from NT4
// where ClRtlGetClusterInstallState() will not work.
//
// Arguments:
// bool * pfIsRegisteredOut
// If true, Cluster Service (ClusSvc) is registered with the Service
// Control Manager (SCM). Else, Cluster Service (ClusSvc) is not
// registered with SCM
//
// Return Value:
// ERROR_SUCCESS if all went well.
// Other Win32 error codes on failure.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
CClusOCMApp::DwIsClusterServiceRegistered( bool * pfIsRegisteredOut ) const
{
TraceFunc( "" );
LogMsg( "Entering " __FUNCTION__ "()" );
bool fIsRegistered = false;
DWORD dwReturnValue = ERROR_SUCCESS;
// dummy do-while loop to avoid gotos
do
{
// Connect to the Service Control Manager
SmartServiceHandle shServiceMgr( OpenSCManager( NULL, NULL, GENERIC_READ ) );
// Was the service control manager database opened successfully?
if ( shServiceMgr.HHandle() == NULL )
{
dwReturnValue = TW32( GetLastError() );
TraceFlow1( "Error %#x occurred trying to open a connection to the local service control manager.", dwReturnValue );
LogMsg( "Error %#x occurred trying to open a connection to the local service control manager.", dwReturnValue );
break;
} // if: opening the SCM was unsuccessful
// Open a handle to the Cluster Service.
SmartServiceHandle shService( OpenService( shServiceMgr, L"ClusSvc", GENERIC_READ ) );
// Was the handle to the service opened?
if ( shService.HHandle() != NULL )
{
TraceFlow( "The cluster service is registered." );
LogMsg( "The cluster service is registered." );
fIsRegistered = true;
break;
} // if: handle to clussvc could be opened
dwReturnValue = GetLastError();
if ( dwReturnValue == ERROR_SERVICE_DOES_NOT_EXIST )
{
dwReturnValue = ERROR_SUCCESS;
TraceFlow( "ClusSvc does not exist as a service." );
LogMsg( "ClusSvc does not exist as a service." );
break;
} // if: the handle could not be opened because the service did not exist.
// Some error occurred.
TW32( dwReturnValue);
TraceFlow1( "Error %#x occurred trying to open a handle to the cluster service.", dwReturnValue );
LogMsg( "Error %#x occurred trying to open a handle to the cluster service.", dwReturnValue );
// Handles are closed by the CSmartHandle destructor.
}
while ( false ); // dummy do-while loop to avoid gotos
if ( pfIsRegisteredOut != NULL )
{
*pfIsRegisteredOut = fIsRegistered;
}
TraceFlow2( "fIsRegistered is %d. Return Value is %#x.", fIsRegistered, dwReturnValue );
LogMsg( "fIsRegistered is %d. Return Value is %#x.", fIsRegistered, dwReturnValue );
RETURN( dwReturnValue );
} //*** CClusOCMApp::DwIsClusterServiceRegistered()
/////////////////////////////////////////////////////////////////////////////
//++
//
// DWORD
// CClusOCMApp::DwGetCurrentTask
//
// Description:
// This function returns a pointer to the current task object. If a task
// object has not been created yet, it creates the appropriate task.
//
// Arguments:
// CClusOCMTask *& rpTaskOut
// Reference to the pointer to the current task. Do not try to
// free this memory.
//
// If no task needs to be performed, a NULL pointer is returned.
//
// Return Value:
// NO_ERROR if all went well.
// Other Win32 error codes on failure.
//
//
// Remarks:
// This function will work properly only after the member variables which
// indicate which task will be performed have been initialized correctly.
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
CClusOCMApp::DwGetCurrentTask( CClusOCMTask *& rpTaskOut )
{
TraceFunc( "" );
LogMsg( "Entering " __FUNCTION__ "()" );
DWORD dwReturnValue = NO_ERROR;
// Initialize the output.
rpTaskOut = NULL;
do
{
eClusterInstallState ecisCurrentState;
if ( m_fAttemptedTaskCreation )
{
// A task object already exists - just return it.
TraceFlow( "A task object already exists. Returning it." );
LogMsg( "A task object already exists. Returning it." );
rpTaskOut = m_sptaskCurrentTask.PMem();
break;
} // if: the task object has already been created.
TraceFlow( "Creating a new task object." );
LogMsg( "Creating a new task object." );
// Make note of the fact that we have started our attempt to create a task object.
m_fAttemptedTaskCreation = true;
// Reset the task pointer.
m_sptaskCurrentTask.Assign( NULL );
// Get the current installation state to deduce what operation to perform.
ecisCurrentState = CisGetClusterInstallState();
// The task object has not been created yet - create one now.
if ( ecisCurrentState == eClusterInstallStateUnknown )
{
TraceFlow( "The cluster installation state is not known. Assuming that a clean install is required." );
LogMsg( "The cluster installation state is not known. Assuming that a clean install is required." );
// If the installation state is unknown, assume that the cluster binaries
// are not installed.
rpTaskOut = new CTaskCleanInstall( *this );
if ( rpTaskOut == NULL )
{
TraceFlow( "Error: There was not enough memory to create a clean install task." );
LogMsg( "Error: There was not enough memory to start a clean install." );
dwReturnValue = TW32( ERROR_NOT_ENOUGH_MEMORY );
break;
} // if: memory allocation failed
} // if: the cluster installation state is eClusterInstallStateUnknown
else if ( m_fIsUpgrade )
{
//
// If we are here, it means that an upgrade is in progress and the cluster binaries
// have already been installed on the OS being upgraded. Additionally, this node may
// already be part of a cluster.
//
DWORD dwNodeClusterMajorVersion = 0;
// Find out which version of the cluster service we are upgrading.
dwReturnValue = TW32( DwGetNodeClusterMajorVersion( dwNodeClusterMajorVersion ) );
if ( dwReturnValue != NO_ERROR )
{
TraceFlow1( "Error %#x occurred trying to determine the version of the cluster service that we are upgrading.", dwReturnValue );
LogMsg( "Error %#x occurred trying to determine the version of the cluster service that we are upgrading.", dwReturnValue );
break;
} // if: an error occurred trying to determine the version of the cluster service that we are upgrading
// Check if the returned cluster version is valid
if ( ( dwNodeClusterMajorVersion != NT51_MAJOR_VERSION )
&& ( dwNodeClusterMajorVersion != NT5_MAJOR_VERSION )
&& ( dwNodeClusterMajorVersion != NT4SP4_MAJOR_VERSION )
&& ( dwNodeClusterMajorVersion != NT4_MAJOR_VERSION )
)
{
TraceFlow1( "The version of the cluster service before the upgrade (%d) is invalid.", dwNodeClusterMajorVersion );
LogMsg( "The version of the cluster service before the upgrade (%d) is invalid.", dwNodeClusterMajorVersion );
break;
} // if: the cluster version is not valid
// Based on the previous version of the cluster service, create the correct task object.
if ( dwNodeClusterMajorVersion == NT5_MAJOR_VERSION )
{
TraceFlow( "We are upgrading a Windows 2000 node." );
LogMsg( "We are upgrading a Windows 2000 node." );
rpTaskOut = new CTaskUpgradeWin2k( *this );
} // if: we are upgrading from Windows 2000
else if ( dwNodeClusterMajorVersion == NT51_MAJOR_VERSION )
{
TraceFlow( "We are upgrading a Whistler node." );
LogMsg( "We are upgrading a Whistler node." );
rpTaskOut = new CTaskUpgradeWhistler( *this );
} // else if: we are upgrading from Whistler
else
{
TraceFlow( "We are upgrading an NT4 node." );
LogMsg( "We are upgrading an NT4 node." );
rpTaskOut = new CTaskUpgradeNT4( *this );
} // else: we are upgrading from NT4 (either SP3 or SP4)
if ( rpTaskOut == NULL )
{
TraceFlow( "Error: There was not enough memory to create the required task object." );
LogMsg( "Error: There was not enough memory to create the required task." );
dwReturnValue = TW32( ERROR_NOT_ENOUGH_MEMORY );
break;
} // if: memory allocation failed
} // else if: an upgrade is in progress
if ( rpTaskOut != NULL )
{
TraceFlow( "A task object was successfully created." );
LogMsg( "A task object was successfully created." );
// Store the pointer to the newly created task in the member variable.
m_sptaskCurrentTask.Assign( rpTaskOut );
} // if: the task object was successfully created
else
{
TraceFlow( "No task object was created." );
LogMsg( "No task object was created." );
} // else: no task object was created
}
while( false ); // dummy do-while loop to avoid gotos
LogMsg( "Return Value is %#x.", dwReturnValue );
TraceFlow1( "Return Value is %#x.", dwReturnValue );
RETURN( dwReturnValue );
} //*** CClusOCMApp::DwGetCurrentTask()
/////////////////////////////////////////////////////////////////////////////
//++
//
// DWORD
// CClusOCMApp::DwGetNodeClusterMajorVersion
//
// Description:
// This function returns the major version of the cluster service that
// we are upgrading. The version that this function returns is the version
// of the service before the upgrade. If there was a problem reading this
// information, this function lies and says that the previous version was
// NT4, since this is the safest thing to say and is better than aborting
// the upgrade.
//
// Note: This function can only be called during an upgrade.
//
// Arguments:
// DWORD & rdwNodeClusterMajorVersionOut
// Reference to DWORD that will hold the major version of the cluster
// service that we are upgrading.
//
// Return Value:
// NO_ERROR if all went well.
// E_UNEXPECTED if an upgrade is not in progress.
// Other Win32 error codes on failure.
//
//
// Remarks:
// This function will work properly only after the member variables which
// indicate which task will be performed have been initialized correctly.
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
CClusOCMApp::DwGetNodeClusterMajorVersion( DWORD & rdwNodeClusterMajorVersionOut )
{
TraceFunc( "" );
LogMsg( "Entering " __FUNCTION__ "()" );
DWORD dwReturnValue = NO_ERROR;
DWORD dwPrevOSMajorVersion = 0;
DWORD dwPrevOSMinorVersion = 0;
do
{
SmartRegistryKey srkOSInfoKey;
DWORD dwRegValueType = 0;
DWORD cbBufferSize = 0;
// Initialize the output.
rdwNodeClusterMajorVersionOut = 0;
if ( !m_fIsUpgrade )
{
TraceFlow( "Error: This function cannot be called when an upgrade is not in progress." );
LogMsg( "Error: This function cannot be called when an upgrade is not in progress." );
dwReturnValue = THR( E_UNEXPECTED );
break;
} // if: an upgrade is not in progress
//
// Read the registry to get what the OS version was before the upgrade.
// This information was written here by ClusComp.dll. From the OS version information,
// try and deduce the cluster version info.
// NOTE: At this point, it is not possible to differentiate between NT4_MAJOR_VERSION
// and NT4SP4_MAJOR_VERSION, and, for the purposes of the upgrade, I don't think we need
// to either - so, just treat all NT4 cluster nodes the same.
//
{
HKEY hTempKey = NULL;
// Open the node version info registry key
dwReturnValue = TW32(
RegOpenKeyEx(
HKEY_LOCAL_MACHINE
, CLUSREG_KEYNAME_NODE_DATA L"\\" CLUSREG_KEYNAME_PREV_OS_INFO
, 0
, KEY_READ
, &hTempKey
)
);
if ( dwReturnValue != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x occurred trying open the registry key where info about the previous OS is stored.", dwReturnValue );
LogMsg( "Error %#x occurred trying open the registry key where where info about the previous OS is stored.", dwReturnValue );
break;
} // if: RegOpenKeyEx() failed
// Store the opened key in a smart pointer for automatic close.
srkOSInfoKey.Assign( hTempKey );
}
// Read the OS major version
cbBufferSize = sizeof( dwPrevOSMajorVersion );
dwReturnValue = TW32(
RegQueryValueEx(
srkOSInfoKey.HHandle()
, CLUSREG_NAME_NODE_MAJOR_VERSION
, 0
, &dwRegValueType
, reinterpret_cast< LPBYTE >( &dwPrevOSMajorVersion )
, &cbBufferSize
)
);
if ( dwReturnValue != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x occurred trying to read the previous major version info.", dwReturnValue );
LogMsg( "Error %#x occurred trying to read the previous OS major version info.", dwReturnValue );
break;
} // if: RegQueryValueEx() failed while reading dwPrevOSMajorVersion
// Read the OS minor version
cbBufferSize = sizeof( dwPrevOSMinorVersion );
dwReturnValue = TW32(
RegQueryValueEx(
srkOSInfoKey.HHandle()
, CLUSREG_NAME_NODE_MINOR_VERSION
, 0
, &dwRegValueType
, reinterpret_cast< LPBYTE >( &dwPrevOSMinorVersion )
, &cbBufferSize
)
);
if ( dwReturnValue != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x occurred trying to read the previous minor version info.", dwReturnValue );
LogMsg( "Error %#x occurred trying to read the previous OS minor version info.", dwReturnValue );
break;
} // if: RegQueryValueEx() failed while reading dwPrevOSMinorVersion
TraceFlow2( "Previous OS major and minor versions were %d and %d respectively.", dwPrevOSMajorVersion, dwPrevOSMinorVersion );
LogMsg( "Previous OS major and minor versions were %d and %d respectively.", dwPrevOSMajorVersion, dwPrevOSMinorVersion );
}
while( false ); // dummy do-while loop to avoid gotos
if ( dwReturnValue != NO_ERROR )
{
TraceFlow( "An error occurred trying to read the version information of the previous OS. Proceeding assuming that it was NT4." );
LogMsg( "An error occurred trying to read the version information of the previous OS. Proceeding assuming that it was NT4." );
dwReturnValue = NO_ERROR;
rdwNodeClusterMajorVersionOut = NT4_MAJOR_VERSION;
} // if: an error occurred trying to determine the previous OS version
else
{
if ( dwPrevOSMajorVersion == 4 )
{
// The previous OS version was NT4 (it does not matter if it was SP3 or SP4 - we will treat
// both the same way.
TraceFlow( "The previous OS was NT4. We are going to treat NT4SP3 and NT4SP4 nodes the same way for upgrades." );
LogMsg( "The previous OS was NT4. We are going to treat NT4SP3 and NT4SP4 nodes the same way for upgrades." );
rdwNodeClusterMajorVersionOut = NT4_MAJOR_VERSION;
} // if: the previous OS version was NT4
else if ( dwPrevOSMajorVersion == 5 )
{
if ( dwPrevOSMinorVersion == 0 )
{
TraceFlow( "The previous OS was Windows 2000." );
LogMsg( "The previous OS was Windows 2000." );
rdwNodeClusterMajorVersionOut = NT5_MAJOR_VERSION;
} // if: this was a Windows 2000 node
else if ( dwPrevOSMinorVersion == 1 )
{
TraceFlow( "The previous OS was Whistler." );
LogMsg( "The previous OS was Whistler." );
rdwNodeClusterMajorVersionOut = NT51_MAJOR_VERSION;
} // else if: this was a Whistler node
else
{
TraceFlow( "The previous OS was neither NT4, Windows 2000 nor Whistler. An error must have occurred." );
LogMsg( "The previous OS was neither NT4, Windows 2000 nor Whistler. An error must have occurred." );
dwReturnValue = THR( E_UNEXPECTED );
} // else: the previous OS was neither NT4, Windows 2000 nor Whistler
} // else if: the previous OS major version is 5
else
{
TraceFlow( "The previous OS was neither NT4, Windows 2000 nor Whistler. An error must have occurred." );
LogMsg( "The previous OS was neither NT4, Windows 2000 nor Whistler. An error must have occurred." );
dwReturnValue = THR( E_UNEXPECTED );
} // else: the previous OS was neither NT4, Windows 2000 nor Whistler
} // if; we read the previous OS version info
LogMsg( "Return Value is %#x.", dwReturnValue );
TraceFlow1( "Return Value is %#x.", dwReturnValue );
RETURN( dwReturnValue );
} //*** CClusOCMApp::DwGetNodeClusterMajorVersion()