////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1999-2001 Microsoft Corporation // // Module Name: // CClusDBJoin.cpp // // Description: // Contains the definition of the CClusDBJoin class. // // Maintained By: // David Potter (DavidP) 14-JUN-2001 // Vij Vasu (Vvasu) 08-MAR-2000 // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Include Files ////////////////////////////////////////////////////////////////////////////// // The precompiled header. #include "Pch.h" // The header for this file #include "CClusDBJoin.h" // For the CBaseClusterJoin class. #include "CBaseClusterJoin.h" // For the CImpersonateUser class. #include "CImpersonateUser.h" ////////////////////////////////////////////////////////////////////////////// //++ // // CClusDBJoin::CClusDBJoin // // Description: // Constructor of the CClusDBJoin class // // Arguments: // m_pcjClusterJoinIn // Pointer to the base cluster action of which this action is a part. // // Return Value: // None. // // Exceptions Thrown: // Any exceptions thrown by underlying functions // //-- ////////////////////////////////////////////////////////////////////////////// CClusDBJoin::CClusDBJoin( CBaseClusterJoin * pcjClusterJoinIn ) : BaseClass( pcjClusterJoinIn ) , m_pcjClusterJoin( pcjClusterJoinIn ) , m_fHasNodeBeenAddedToSponsorDB( false ) { TraceFunc( "" ); SetRollbackPossible( true ); TraceFuncExit(); } //*** CClusDBJoin::CClusDBJoin ////////////////////////////////////////////////////////////////////////////// //++ // // CClusDBJoin::~CClusDBJoin // // Description: // Destructor of the CClusDBJoin class. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any exceptions thrown by underlying functions // //-- ////////////////////////////////////////////////////////////////////////////// CClusDBJoin::~CClusDBJoin( void ) { TraceFunc( "" ); TraceFuncExit(); } //*** CClusDBJoin::~CClusDBJoin ////////////////////////////////////////////////////////////////////////////// //++ // // CClusDBJoin::Commit // // Description: // Create the cluster database. If anything goes wrong with the creation, // cleanup the tasks already done. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any that are thrown by the contained actions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusDBJoin::Commit( void ) { TraceFunc( "" ); // Call the base class commit method. BaseClass::Commit(); // // Perform a ClusDB cleanup just to make sure that we do not use some files left over // from a previous install, aborted uninstall, etc. // LogMsg( "[BC-ClusDB-Commit] Cleaning up old cluster database files that may already exist before starting creation." ); { CStatusReport srCleanDB( PbcaGetParent()->PBcaiGetInterfacePointer() , TASKID_Major_Configure_Cluster_Services , TASKID_Minor_Cleaning_Up_Cluster_Database , 0, 1 , IDS_TASK_CLEANINGUP_CLUSDB ); // Send the next step of this status report. srCleanDB.SendNextStep( S_OK ); CleanupHive(); // Send the last step of this status report. srCleanDB.SendNextStep( S_OK ); } try { // Create the cluster database Create(); } // try: catch( ... ) { // If we are here, then something went wrong with the create. LogMsg( "[BC-ClusDB-Commit] Caught exception during commit." ); // // Cleanup anything that the failed add operation might have done. // Catch any exceptions thrown during Cleanup to make sure that there // is no collided unwind. // try { // Cleanup the database. Cleanup(); } catch( ... ) { // // The rollback of the committed action has failed. // There is nothing that we can do. // We certainly cannot rethrow this exception, since // the exception that caused the rollback is more important. // TW32( ERROR_CLUSCFG_ROLLBACK_FAILED ); LogMsg( "[BC-ClusDB-Commit] THIS COMPUTER MAY BE IN AN INVALID STATE. Caught an exception during cleanup." ); } // catch: all // Rethrow the exception thrown by commit. throw; } // catch: all // If we are here, then everything went well. SetCommitCompleted( true ); TraceFuncExit(); } //*** CClusDBJoin::Commit ////////////////////////////////////////////////////////////////////////////// //++ // // CClusDBJoin::Rollback // // Description: // Unload the cluster hive and cleanup any associated files. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any that are thrown by the underlying functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusDBJoin::Rollback( void ) { TraceFunc( "" ); // Call the base class rollback method. BaseClass::Rollback(); // Undo the actions performed by. Cleanup(); SetCommitCompleted( false ); TraceFuncExit(); } //*** CClusDBJoin::Rollback ////////////////////////////////////////////////////////////////////////////// //++ // // CClusDBJoin::Create // // Description: // Create the cluster database. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // CRuntimeError // If any of the APIs fail. // // Any that are thrown by the called functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusDBJoin::Create( void ) { TraceFunc( "" ); LogMsg( "[BC-ClusDB-Create] Attempting to create the cluster database required to add the node to a cluster." ); DWORD sc = ERROR_SUCCESS; SmartFileHandle sfhClusDBFile; { // // Get the full path and name of the cluster database file. // CStr strClusterHiveFileName( PbcaGetParent()->RStrGetClusterInstallDirectory() ); strClusterHiveFileName += L"\\" CLUSTER_DATABASE_NAME; LogMsg( "[BC-ClusDB-Create] The cluster hive backing file is '%s'.", strClusterHiveFileName.PszData() ); // // Create the cluster database file. // sfhClusDBFile.Assign( CreateFile( strClusterHiveFileName.PszData() , GENERIC_READ | GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , 0 , NULL ) ); if ( sfhClusDBFile.FIsInvalid() ) { sc = TW32( GetLastError() ); LogMsg( "[BC-ClusDB-Create] Error %#08x occurred trying to create the cluster database file. Throwing an exception.", sc ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_JOIN_SYNC_DB ); } // if: CreateFile() failed // Store the file handle just obtained in a member variable so that it can be used during Synchronize() // Note, this file is closed when sfhClusDBFile goes out of scope, so m_hClusDBFile should not be used // outside this function or any function that this function calls. m_hClusDBFile = sfhClusDBFile.HHandle(); } // // In the scope below, the cluster service account is impersonated, so that we can communicate with the // sponsor cluster // { LogMsg( "[BC-ClusDB-Create] Attempting to impersonate the cluster service account." ); // Impersonate the cluster service account, so that we can contact the sponsor cluster. // The impersonation is automatically ended when this object is destroyed. CImpersonateUser ciuImpersonateClusterServiceAccount( m_pcjClusterJoin->HGetClusterServiceAccountToken() ); // Add this node to the sponsor cluster database do { DWORD dwSuiteType = ClRtlGetSuiteType(); BOOL bJoinerRunningWin64; SYSTEM_INFO SystemInfo; m_fHasNodeBeenAddedToSponsorDB = false; LogMsg( "[BC-ClusDB-Create] Trying to add node '%s' (suite type %d) to the sponsor cluster database." , m_pcjClusterJoin->PszGetNodeName() , dwSuiteType ); bJoinerRunningWin64 = ClRtlIsProcessRunningOnWin64(GetCurrentProcess()); GetSystemInfo(&SystemInfo); sc = TW32( JoinAddNode4( m_pcjClusterJoin->RbhGetJoinBindingHandle() , m_pcjClusterJoin->PszGetNodeName() , m_pcjClusterJoin->DwGetNodeHighestVersion() , m_pcjClusterJoin->DwGetNodeLowestVersion() , dwSuiteType , bJoinerRunningWin64 , SystemInfo.wProcessorArchitecture ) ); if (sc == RPC_S_PROCNUM_OUT_OF_RANGE) { LogMsg( "[BC-ClusDB-Create] Error %#08x returned from JoinAddNode4. Sponser must be Windows 2000.", sc ); //this happens when the sponsorer is win2K sc = TW32( JoinAddNode3( m_pcjClusterJoin->RbhGetJoinBindingHandle() , m_pcjClusterJoin->PszGetNodeName() , m_pcjClusterJoin->DwGetNodeHighestVersion() , m_pcjClusterJoin->DwGetNodeLowestVersion() , dwSuiteType ) ); } if ( sc != ERROR_SUCCESS ) { LogMsg( "[BC-ClusDB-Create] Error %#08x returned from JoinAddNodeN.", sc ); break; } // if: JoinAddNodeN() failed // Set the flag that indicates that the sponsor database has been modified, so that // we can undo this if we need to rollback or cleanup. m_fHasNodeBeenAddedToSponsorDB = true; } while( false ); // dummy do while loop to avoid gotos. if ( sc != ERROR_SUCCESS ) { LogMsg( "[BC-ClusDB-Create] Error %#08x occurred trying to add this node to the sponsor cluster database. Throwing an exception.", sc ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_JOINING_SPONSOR_DB ); } // if: something has gone wrong LogMsg( "[BC-ClusDB-Create] This node has been successfully added to the sponsor cluster database." ); // Get the node id of the newly formed node. do { // Smart handle to sponsor cluster SmartClusterHandle schSponsorCluster; // Smart handle to this node SmartNodeHandle snhThisNodeHandle; // // Get a handle to the sponsor cluster. // { LogMsg( "[BC-ClusDB-Create] Opening a cluster handle to the sponsor cluster with the '%ws' binding string.", m_pcjClusterJoin->RStrGetClusterBindingString().PszData() ); // Open a handle to the sponsor cluster. HCLUSTER hSponsorCluster = OpenCluster( m_pcjClusterJoin->RStrGetClusterBindingString().PszData() ); // Assign it to a smart handle for safe release. schSponsorCluster.Assign( hSponsorCluster ); } // Did we succeed in opening a handle to the sponsor cluster? if ( schSponsorCluster.FIsInvalid() ) { sc = TW32( GetLastError() ); LogMsg( "[BC-ClusDB-Create] Error %#08x occurred trying to open a cluster handle to the sponsor cluster with the '%ws' binding string." , sc , m_pcjClusterJoin->RStrGetClusterBindingString().PszData() ); break; } // if: OpenCluster() failed // // Get a handle to this node. // { LogMsg( "[BC-ClusDB-Create] Opening a cluster handle to the local node with the '%ws' binding string.", m_pcjClusterJoin->PszGetNodeName() ); // Open a handle to this node. HNODE hThisNode = OpenClusterNode( schSponsorCluster.HHandle(), m_pcjClusterJoin->PszGetNodeName() ); // Assign it to a smart handle for safe release. snhThisNodeHandle.Assign( hThisNode ); } // Did we succeed in opening a handle to this node? if ( snhThisNodeHandle.FIsInvalid() ) { sc = TW32( GetLastError() ); LogMsg( "[BC-ClusDB-Create] Error %#08x occurred trying to open a cluster handle to the local node with the '%ws' binding string.", sc, m_pcjClusterJoin->PszGetNodeName() ); break; } // if: OpenClusterNode() failed // Get the node id string. { DWORD cchIdSize = 0; SmartSz sszNodeId; sc = GetClusterNodeId( snhThisNodeHandle.HHandle() , NULL , &cchIdSize ); if ( ( sc != ERROR_SUCCESS ) && ( sc != ERROR_MORE_DATA ) ) { TW32( sc ); LogMsg( "[BC-ClusDB-Create] Error %#08x returned from GetClusterNodeId() trying to get the required length of the node id buffer.", sc ); break; } // if: GetClusterNodeId() failed // cchIdSize returned by the above call is the count of characters and does not include the space for // the terminating NULL. ++cchIdSize; sszNodeId.Assign( new WCHAR[ cchIdSize ] ); if ( sszNodeId.FIsEmpty() ) { sc = TW32( ERROR_OUTOFMEMORY ); LogMsg( "[BC-ClusDB-Create] A memory allocation failure occurred trying to allocate %d characters.", cchIdSize ); break; } // if: memory allocation failed sc = TW32( GetClusterNodeId( snhThisNodeHandle.HHandle() , sszNodeId.PMem() , &cchIdSize ) ); if ( sc != ERROR_SUCCESS ) { LogMsg( "Error %#08x returned from GetClusterNodeId() trying to get the node id of this node.", sc ); break; } // if: GetClusterNodeId() failed LogMsg( "[BC-ClusDB-Create] The node id of this node is '%s'.", sszNodeId.PMem() ); // Set the node id for later use. m_pcjClusterJoin->SetNodeIdString( sszNodeId.PMem() ); } } while( false ); // dummy do while loop to avoid gotos. if ( sc != ERROR_SUCCESS ) { LogMsg( "[BC-ClusDB-Create] Error %#08x occurred trying to get the node id of this node. Throwing an exception.", sc ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_GET_NEW_NODE_ID ); } // if: something has gone wrong { CStatusReport srSyncDB( PbcaGetParent()->PBcaiGetInterfacePointer() , TASKID_Major_Configure_Cluster_Services , TASKID_Minor_Join_Sync_Cluster_Database , 0, 1 , IDS_TASK_JOIN_SYNC_CLUSDB ); // Send the next step of this status report. srSyncDB.SendNextStep( S_OK ); // Synchronize the cluster database. Synchronize(); // Send the last step of this status report. srSyncDB.SendNextStep( S_OK ); } } LogMsg( "[BC-ClusDB-Create] The cluster database has been successfully created and synchronized with the sponsor cluster." ); TraceFuncExit(); } //*** CClusDBJoin::Create ////////////////////////////////////////////////////////////////////////////// //++ // // CClusDBJoin::Cleanup // // Description: // Cleanup the effects of Create() // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // CRuntimeError // If any of the APIs fail. // // Any that are thrown by the called functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusDBJoin::Cleanup( void ) { TraceFunc( "" ); LogMsg( "[BC-ClusDB-Cleanup] Attempting to cleanup the cluster database." ); DWORD sc = ERROR_SUCCESS; DWORD cRetryCount = 0; // // Check if we added this node to the sponsor cluster database. If so, remove it from there. // if ( m_fHasNodeBeenAddedToSponsorDB ) { LogMsg( "[BC-ClusDB-Cleanup] Attempting to impersonate the cluster service account." ); // Impersonate the cluster service account, so that we can contact the sponsor cluster. // The impersonation is automatically ended when this object is destroyed. CImpersonateUser ciuImpersonateClusterServiceAccount( m_pcjClusterJoin->HGetClusterServiceAccountToken() ); // // Remove this node from the sponsor cluster database // do { // Smart handle to sponsor cluster SmartClusterHandle schSponsorCluster; // Smart handle to this node SmartNodeHandle snhThisNodeHandle; // // Get a handle to the sponsor cluster. // { LogMsg( "[BC-ClusDB-Cleanup] Opening a clusterhandle to the sponsor cluster with the '%ws' binding string.", m_pcjClusterJoin->RStrGetClusterBindingString().PszData() ); // Open a handle to the sponsor cluster. HCLUSTER hSponsorCluster = OpenCluster( m_pcjClusterJoin->RStrGetClusterBindingString().PszData() ); // Assign it to a smart handle for safe release. schSponsorCluster.Assign( hSponsorCluster ); } // Did we succeed in opening a handle to the sponsor cluster? if ( schSponsorCluster.FIsInvalid() ) { sc = TW32( GetLastError() ); LogMsg( "[BC-ClusDB-Cleanup] Error %#08x occurred trying to open a cluster handle to the sponsor cluster with the '%ws' binding string.", sc, m_pcjClusterJoin->RStrGetClusterBindingString().PszData() ); break; } // if: OpenCluster() failed // // Get a handle to this node. // { LogMsg( "[BC-ClusDB-Cleanup] Open a clusterhandle to the local node with the '%ws' binding string.", m_pcjClusterJoin->PszGetNodeName() ); // Open a handle to this node. HNODE hThisNode = OpenClusterNode( schSponsorCluster.HHandle(), m_pcjClusterJoin->PszGetNodeName() ); if ( hThisNode == NULL ) { sc = TW32( GetLastError() ); LogMsg( "[BC-ClusDB-Cleanup] Error %#08x occurred trying to open a cluster handle to the local node with the '%ws' binding string.", sc, m_pcjClusterJoin->PszGetNodeName() ); break; } // if: OpenClusterNode() failed. // Assign it to a smart handle for safe release. snhThisNodeHandle.Assign( hThisNode ); } // // If the cluster is still dealing with the join process we'll get ERROR_CLUSTER_JOIN_IN_PROGRESS. // After the join finishes/fails/stabilizes we'll be able to make the evict call without the // join process getting in the way. // cRetryCount = 1; sc = EvictClusterNode( snhThisNodeHandle.HHandle() ); while( sc == ERROR_CLUSTER_JOIN_IN_PROGRESS && cRetryCount < 25 ) // Allow a two minute wait. (24 * 5 seconds) { LogMsg( "[BC-ClusDB-Cleanup] EvictClusterNode returned ERROR_CLUSTER_JOIN_IN_PROGRESS. Retry attempt %d.", cRetryCount++ ); // Sleep for a few seconds. Sleep( 5000 ); // Try again. sc = EvictClusterNode( snhThisNodeHandle.HHandle() ); } if ( sc != ERROR_SUCCESS ) { TW32( sc ); LogMsg( "[BC-ClusDB-Cleanup] Error %#08x occurred trying to evict this node from the sponsor cluster.", sc ); break; } // if: EvictClusterNode() failed } while( false ); // dummy do while loop to avoid gotos. if ( sc != ERROR_SUCCESS ) { LogMsg( "[BC-ClusDB-Cleanup] Error %#08x occurred trying to remove this node from the sponsor cluster database. Throwing exception.", sc ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_EVICTING_NODE ); } // if: something has gone wrong LogMsg( "[BC-ClusDB-Cleanup] This node has been successfully removed from the sponsor cluster database." ); } // if: we had added this node to the sponsor cluster database // Cleanup the cluster hive CleanupHive(); TraceFuncExit(); } //*** CClusDBJoin::Cleanup ////////////////////////////////////////////////////////////////////////////// //++ // // CClusDBJoin::Synchronize // // Description: // Synchronize the cluster database with the sponsor cluster. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // CRuntimeError // If any of the APIs fail. // // Any that are thrown by the called functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusDBJoin::Synchronize( void ) { TraceFunc( "" ); LogMsg( "[BC-ClusDB-Synchronize] Attempting to synchronize the cluster database with the sponsor cluster." ); DWORD sc = ERROR_SUCCESS; // // Initialize the byte pipe. // m_bpBytePipe.state = reinterpret_cast< char * >( this ); m_bpBytePipe.alloc = S_BytePipeAlloc; m_bpBytePipe.push = S_BytePipePush; m_bpBytePipe.pull = S_BytePipePull; // // Synchronize the database // sc = TW32( DmSyncDatabase( m_pcjClusterJoin->RbhGetJoinBindingHandle(), m_bpBytePipe ) ); if ( sc != ERROR_SUCCESS ) { LogMsg( "[BC-ClusDB-Synchronize] Error %#08x occurred trying to suck the database down from the sponsor cluster.", sc ); goto Cleanup; } // if: DmSyncDatabase() failed Cleanup: if ( sc != ERROR_SUCCESS ) { LogMsg( "[BC-ClusDB-Synchronize] Error %#08x occurred trying to synchronize the cluster database with the sponsor cluster. Throwing an exception.", sc ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_JOIN_SYNC_DB ); } // if: something has gone wrong LogMsg( "[BC-ClusDB-Synchronize] The cluster database has been synchronized with the sponsor cluster." ); TraceFuncExit(); } //*** CClusDBJoin::Synchronize ////////////////////////////////////////////////////////////////////////////// //++ // // static // CClusDBJoin::S_BytePipePush // // Description: // Callback function used by RPC to push data. // // Arguments: // pchStateIn // State of the byte pipe // // pchBufferIn // ulBufferSizeIn // Buffer contained the pushed data and its size. // // Return Value: // None. // // Exceptions Thrown: // RPC Exceptions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusDBJoin::S_BytePipePush( char * pchStateIn , unsigned char * pchBufferIn , unsigned long ulBufferSizeIn ) { TraceFunc( "" ); CClusDBJoin * pThis = reinterpret_cast< CClusDBJoin * >( pchStateIn ); if ( ulBufferSizeIn != 0 ) { DWORD dwBytesWritten; if ( WriteFile( pThis->m_hClusDBFile , pchBufferIn , ulBufferSizeIn , &dwBytesWritten , NULL ) == 0 ) { DWORD sc = TW32( GetLastError() ); RpcRaiseException( sc ); } // if: WriteFile() failed Assert( dwBytesWritten == ulBufferSizeIn ); } // if: the buffer is non-empty TraceFuncExit(); } //*** CClusDBJoin::S_BytePipePush ////////////////////////////////////////////////////////////////////////////// //++ // // static // CClusDBJoin::S_BytePipePull // // Description: // Callback function used by RPC to pull data. // // Arguments: // pchStateIn // State of the byte pipe // // pchBufferIn // ulBufferSizeIn // Buffer contained the pushed data and its size. // // pulWrittenOut // Pointer to the number of bytes actually filled into the buffer. // // Return Value: // None. // // Exceptions Thrown: // RPC Exceptions. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusDBJoin::S_BytePipePull( char * pchStateIn , unsigned char * pchBufferIn , unsigned long ulBufferSizeIn , unsigned long * pulWrittenOut ) { TraceFunc( "" ); CClusDBJoin * pThis = reinterpret_cast< CClusDBJoin * >( pchStateIn ); if ( ulBufferSizeIn != 0 ) { if ( ReadFile( pThis->m_hClusDBFile , pchBufferIn , ulBufferSizeIn , pulWrittenOut , NULL ) == 0 ) { DWORD sc = TW32( GetLastError() ); RpcRaiseException( sc ); } // if: ReadFile() failed Assert( *pulWrittenOut <= ulBufferSizeIn ); } // if: the buffer is non-empty TraceFuncExit(); } //*** CClusDBJoin::S_BytePipePull ////////////////////////////////////////////////////////////////////////////// //++ // // static // CClusDBJoin::S_BytePipeAlloc // // Description: // Callback function used by RPC to allocate a buffer. // // Arguments: // pchStateIn // State of the file pipe // // ulRequestedSizeIn // Requested size of the buffer // // ppchBufferOut // Pointer to the buffer pointer // // pulActualSizeOut // Pointer to the actual size of the allocated buffer // // Return Value: // None. // // Exceptions Thrown: // None. // //-- ////////////////////////////////////////////////////////////////////////////// void CClusDBJoin::S_BytePipeAlloc( char * pchStateIn , unsigned long ulRequestedSizeIn , unsigned char ** ppchBufferOut , unsigned long * pulActualSizeOut ) { TraceFunc( "" ); CClusDBJoin * pThis = reinterpret_cast< CClusDBJoin * >( pchStateIn ); *ppchBufferOut = reinterpret_cast< unsigned char * >( pThis->m_rgbBytePipeBuffer ); *pulActualSizeOut = ( ulRequestedSizeIn < pThis->ms_nFILE_PIPE_BUFFER_SIZE ) ? ulRequestedSizeIn : pThis->ms_nFILE_PIPE_BUFFER_SIZE; TraceFuncExit(); } //*** CClusDBJoin::S_BytePipeAlloc