////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1999-2002 Microsoft Corporation // // Module Name: // CNode.cpp // // Description: // Contains the definition of the CNode class. // // Maintained By: // David Potter (DavidP) 14-JU-2001 // Vij Vasu (Vvasu) 08-MAR-2000 // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Include Files ////////////////////////////////////////////////////////////////////////////// // The precompiled header. #include "Pch.h" // The header for this file #include "CNode.h" // For the CRegistryKey class #include "CRegistryKey.h" // For the CStr class #include "CStr.h" ////////////////////////////////////////////////////////////////////////////// // Macros ////////////////////////////////////////////////////////////////////////////// // Names of the sections in the main INF file which deal with node configuration // and cleanup. #define NODE_CONFIG_INF_SECTION L"Node_Create" #define NODE_CLEANUP_INF_SECTION L"Node_Cleanup" // Registry key storing the list of connections for the cluster administrator #define CLUADMIN_CONNECTIONS_KEY_NAME L"Software\\Microsoft\\Cluster Administrator\\Connections" // Name of the registry value storing the list of connections for the cluster administrator #define CLUADMIN_CONNECTIONS_VALUE_NAME L"Connections" ////////////////////////////////////////////////////////////////////////////// //++ // // CNode::CNode // // Description: // Constructor of the CNode class // // Arguments: // pbcaParentActionIn // Pointer to the base cluster action of which this action is a part. // // Return Value: // None. // // Exceptions Thrown: // CAssert // If the parameters are incorrect. // // Any exceptions thrown by underlying functions // //-- ////////////////////////////////////////////////////////////////////////////// CNode::CNode( CBaseClusterAction * pbcaParentActionIn ) : m_pbcaParentAction( pbcaParentActionIn ) , m_fChangedConnectionsList( false ) { TraceFunc( "" ); if ( m_pbcaParentAction == NULL) { LogMsg( "[BC] Pointers to the parent action is NULL. Throwing an exception." ); THROW_ASSERT( E_INVALIDARG , "CNode::CNode() => Required input pointer in NULL" ); } // if: the parent action pointer is NULL TraceFuncExit(); } //*** CNode::CNode ////////////////////////////////////////////////////////////////////////////// //++ // // CNode::~CNode // // Description: // Destructor of the CNode class. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any exceptions thrown by underlying functions // //-- ////////////////////////////////////////////////////////////////////////////// CNode::~CNode( void ) { TraceFunc( "" ); TraceFuncExit(); } //*** CNode::~CNode ////////////////////////////////////////////////////////////////////////////// //++ // // CNode::Configure // // Description: // Make the changes that need to be made when a node becomes part of a // cluster. // // Arguments: // rcstrClusterNameIn // Name of the cluster being configured. // // Return Value: // None. // // Exceptions Thrown: // CRuntimeError // If any of the APIs fail. // // Any that are thrown by the underlying functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CNode::Configure( const CStr & rcstrClusterNameIn ) { TraceFunc( "" ); WCHAR * pszConnectionsValue = NULL; DWORD cbConnectionsValueSize = 0; DWORD cchOldListLen = 0; CRegistryKey rkConnectionsKey; // // Validate the parameter // if ( rcstrClusterNameIn.FIsEmpty() ) { LogMsg( "[BC] The name of the cluster is empty. Throwing an exception." ); THROW_ASSERT( E_INVALIDARG, "The name of the cluster cannot be empty." ); } // if: the cluster name is not valid LogMsg( "[BC] Attempting to make miscellaneous changes to the node." ); // Process the registry keys. if ( SetupInstallFromInfSection( NULL // optional, handle of a parent window , m_pbcaParentAction->HGetMainInfFileHandle() // handle to the INF file , NODE_CONFIG_INF_SECTION // name of the Install section , SPINST_REGISTRY // which lines to install from section , NULL // optional, key for registry installs , NULL // optional, path for source files , NULL // optional, specifies copy behavior , NULL // optional, specifies callback routine , NULL // optional, callback routine context , NULL // optional, device information set , NULL // optional, device info structure ) == FALSE ) { DWORD sc = TW32( GetLastError() ); LogMsg( "[BC] Error %#08x returned from SetupInstallFromInfSection() while trying to make miscellaneous changes to the node. Throwing an exception.", sc ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_NODE_CONFIG ); } // if: SetupInstallFromInfSection failed // // Add the name of the cluster that this node is a part of to the list of connections // that will be opened when the cluster administrator is started on this node. // The list of connections is a comma separated list of cluster names. // LogMsg( "[BC] Adding the cluster name '%s' to the list of cluadmin connections.", rcstrClusterNameIn.PszData() ); // Reset the state. m_fChangedConnectionsList = false; m_sszOldConnectionsList.PRelease(); LogMsg( "[BC] Trying to read the existing Cluster Administrator remembered connections list." ); // Open the cluster administrator connections key. Create it if it does not exist. rkConnectionsKey.CreateKey( HKEY_CURRENT_USER , CLUADMIN_CONNECTIONS_KEY_NAME ); try { // Try and get the current value rkConnectionsKey.QueryValue( CLUADMIN_CONNECTIONS_VALUE_NAME , reinterpret_cast< LPBYTE * >( &pszConnectionsValue ) , &cbConnectionsValueSize ); } // try: to read the "Connections" value catch( CRuntimeError & crte ) { // Check if this error occurred because the value did not exist if ( crte.HrGetErrorCode() == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ) { LogMsg( "[BC] The registry value '%s' does not exist. This is ok and is not an error.", CLUADMIN_CONNECTIONS_VALUE_NAME ); cchOldListLen = 0; } // if: the value does not exist else { throw; } // else: something else is wrong - rethrow the exception } // catch: the run time error that occurred // Number of characters in the old list, including the terminating NULL. cchOldListLen = cbConnectionsValueSize / sizeof( *pszConnectionsValue ); if ( cchOldListLen <= 1 ) { LogMsg( "[BC] There are no existing Cluster Administrator remembered connections. Creating a new list with just one name in it." ); // Write the cluster name to the value rkConnectionsKey.SetValue( CLUADMIN_CONNECTIONS_VALUE_NAME , REG_SZ , reinterpret_cast< const BYTE * >( rcstrClusterNameIn.PszData() ) , ( rcstrClusterNameIn.NGetLen() + 1 ) * sizeof( WCHAR ) ); // We have changed the connections list. m_fChangedConnectionsList = true; } // if: there are no existing connections else { WCHAR * pszSubString = NULL; bool fIsInList = false; LogMsg( "[BC] The existing list of Cluster Administrator remembered connections is '%s'.", pszConnectionsValue ); // // Is the cluster name already in the list of connections? // pszSubString = wcsstr( pszConnectionsValue, rcstrClusterNameIn.PszData() ); while ( pszSubString != NULL ) { // // The cluster name is a substring of the list. // Make sure that the cluster name is not a proper substring of an cluster name already in the list. // if ( ( ( pszSubString == pszConnectionsValue ) // the substring was found at the beginning of the string || ( *( pszSubString - 1 ) == L',' ) // or the character before the substring is a comma ) // AND && ( ( *( pszSubString + rcstrClusterNameIn.NGetLen() ) == L'\0' ) // the character after the substring is a '\0' || ( *( pszSubString + rcstrClusterNameIn.NGetLen() ) == L',' ) // or character after the substring is a comma ) ) { fIsInList = true; break; } // if: the cluster name is not a proper substring of a cluster name that is already in the list // Continue searching. pszSubString = wcsstr( pszSubString + rcstrClusterNameIn.NGetLen(), rcstrClusterNameIn.PszData() ); } // while: the cluster name is a substring of the list of existing connections if ( fIsInList ) { // Nothing more to be done. LogMsg( "[BC] The '%s' cluster is already in the list of remembered Cluster Administrator connections.", rcstrClusterNameIn.PszData() ); goto Cleanup; } // if: the cluster name is already in the list LogMsg( "[BC] The '%s' cluster is not in the list of remembered Cluster Administrator connections.", rcstrClusterNameIn.PszData() ); // Store the current value in the member variable for restoration in case of error. m_sszOldConnectionsList.Assign( pszConnectionsValue ); // Set the new connections value. { // Define a string to hold the new connections value. Preallocate its buffer. CStr strNewConnectionsValue( cchOldListLen // length of the old list ( including terminating '\0' ) + 1 // for the comma + rcstrClusterNameIn.NGetLen() // length of the cluster name( including terminating '\0' ) ); // // Construct the new list // strNewConnectionsValue = rcstrClusterNameIn; strNewConnectionsValue += L","; strNewConnectionsValue += m_sszOldConnectionsList.PMem(); LogMsg( "[BC] Writing the new list of remembered Cluster Administrator connections '%s'.", strNewConnectionsValue.PszData() ); // Write the new list. rkConnectionsKey.SetValue( CLUADMIN_CONNECTIONS_VALUE_NAME , REG_SZ , reinterpret_cast< const BYTE * >( strNewConnectionsValue.PszData() ) , ( strNewConnectionsValue.NGetLen() + 1 ) * sizeof( WCHAR ) ); // We have changed the connections list. m_fChangedConnectionsList = true; } } // else: there are existing connections Cleanup: LogMsg( "[BC] The changes were made successfully." ); TraceFuncExit(); } //*** CNode::Configure ////////////////////////////////////////////////////////////////////////////// //++ // // CNode::Cleanup // // Description: // Clean up the changes made to this node when it became part of a cluster. // Note that the changes made during Configure() are not really undone here - // we just bring the node back to an acceptable state. This is because, // without a transactional registry, it will be very diffult to get // the registry back to the exact state it was in before Configure() was // called. // // Arguments: // None. // // Return Value: // None. // // Exceptions Thrown: // Any that are thrown by the underlying functions. // //-- ////////////////////////////////////////////////////////////////////////////// void CNode::Cleanup( void ) { TraceFunc( "" ); LogMsg( "[BC] Attempting to cleanup changes made when this node was made a part of a cluster." ); // Process the registry keys. if ( SetupInstallFromInfSection( NULL // optional, handle of a parent window , m_pbcaParentAction->HGetMainInfFileHandle() // handle to the INF file , NODE_CLEANUP_INF_SECTION // name of the Install section , SPINST_REGISTRY // which lines to install from section , NULL // optional, key for registry installs , NULL // optional, path for source files , NULL // optional, specifies copy behavior , NULL // optional, specifies callback routine , NULL // optional, callback routine context , NULL // optional, device information set , NULL // optional, device info structure ) == FALSE ) { DWORD sc = TW32( GetLastError() ); LogMsg( "[BC] Error %#08x was returned from SetupInstallFromInfSection() while trying to clean up miscellaneous changes. Throwing an exception.", sc ); THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_NODE_CLEANUP ); } // if: SetupInstallFromInfSection failed if ( m_fChangedConnectionsList ) { LogMsg( "[BC] Restoring the list of remembered Cluster Administrator connections to '%s'", m_sszOldConnectionsList.PMem() ); // Open the cluster administrator connections key. CRegistryKey rkConnectionsKey( HKEY_CURRENT_USER , CLUADMIN_CONNECTIONS_KEY_NAME ); // If there wasn't a previous value, delete the value we set. // Otherwise, set the value back to the old value. if ( m_sszOldConnectionsList.PMem() == NULL ) { // Delete the value. rkConnectionsKey.DeleteValue( CLUADMIN_CONNECTIONS_VALUE_NAME ); } // if: no old value existed else { // Write the old list back. rkConnectionsKey.SetValue( CLUADMIN_CONNECTIONS_VALUE_NAME , REG_SZ , reinterpret_cast< const BYTE * >( m_sszOldConnectionsList.PMem() ) , ( (UINT) wcslen( m_sszOldConnectionsList.PMem() ) + 1 ) * sizeof( WCHAR ) ); } // else: old value existed } // if: we changed the list of cluadmin connections LogMsg( "[BC] The cleanup was successfully." ); TraceFuncExit(); } //*** CNode::Cleanup