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

643 lines
24 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000 Microsoft Corporation
//
// Module Name:
// ClusComp.cpp
//
// Description:
// This file implements that function that is called by WinNT32.exe before
// an upgrade to ensure that no incompatibilities occur as a result of the
// upgrade. For example, in a cluster of two NT4 nodes, one node cannot
// be upgraded to Whistler while the other is still at NT4. The user is
// warned about such problems by this function.
//
// NOTE: This function is called by WinNT32.exe on the OS *before* an
// upgrade. If OS version X is being upgraded to OS version X+1, then
// the X+1 verion of this DLL is loaded on OS version X. To make sure
// that this DLL can function properly in an downlevel OS, it is linked
// against only the indispensible libraries.
//
// Header File:
// There is no header file for this source file.
//
// Maintained By:
// Vij Vasu (Vvasu) 25-JUL-2000
// Created the original version.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Include Files
//////////////////////////////////////////////////////////////////////////////
// Precompiled header for this DLL
#include "pch.h"
// For the compatibility check function and types
#include <comp.h>
// For the cluster API
#include <clusapi.h>
// For the names of several cluster service related registry keys and values
#include "clusudef.h"
//////////////////////////////////////////////////////////////////////////////
// Forward declarations
//////////////////////////////////////////////////////////////////////////////
DWORD DwIsClusterServiceRegistered( bool & rfIsRegisteredOut );
DWORD DwLoadString( UINT nStringIdIn, WCHAR *& rpszDestOut );
DWORD DwWriteOSVersionInfo( const OSVERSIONINFO & rcosviOSVersionInfoIn );
//////////////////////////////////////////////////////////////////////////////
//++
//
// extern "C"
// BOOL
// ClusterUpgradeCompatibilityCheck()
//
// Description:
// This function is called by WinNT32.exe before an upgrade to ensure that
// no incompatibilities occur as a result of the upgrade. For example,
// in a cluster of two NT4 nodes, one node cannot be upgraded to Whistler
// while the other is still at NT4.
//
// Arguments:
// PCOMPAIBILITYCALLBACK pfnCompatibilityCallbackIn
// Points to the callback function used to supply compatibility
// information to WinNT32.exe
//
// LPVOID pvContextIn
// Pointer to the context buffer supplied by WinNT32.exe
//
// Return Value:
// TRUE if there were no errors or no compatibility problems.
// FALSE otherwise.
//
//--
//////////////////////////////////////////////////////////////////////////////
extern "C"
BOOL ClusterUpgradeCompatibilityCheck(
PCOMPAIBILITYCALLBACK pfnCompatibilityCallbackIn
, LPVOID pvContextIn
)
{
TraceFunc( "" );
LogMsg( "Entering function " __FUNCTION__ "()" );
BOOL fReturnValue = TRUE;
bool fWarningRequired = true;
DWORD dwError = ERROR_SUCCESS;
do
{
typedef CSmartResource< CHandleTrait< HCLUSTER, BOOL, CloseCluster > > SmartClusterHandle;
OSVERSIONINFO osviOSVersionInfo;
SmartClusterHandle schClusterHandle;
DWORD cchBufferSize = 256;
osviOSVersionInfo.dwOSVersionInfoSize = sizeof( osviOSVersionInfo );
//
// First of all, get and store the OS version info into the registry.
//
// Cannot call VerifyVerionInfo as this requires Win2k.
if ( GetVersionEx( &osviOSVersionInfo ) == FALSE )
{
// We could not get OS version info.
// Show the warning, just in case.
dwError = TW32( GetLastError() );
TraceFlow1( "Error %#x occurred trying to get the OS version info.", dwError );
LogMsg( "Error %#x occurred trying to get the OS version info.", dwError );
break;
} // if: GetVersionEx() failed
// Write the OS version info to the registry. This data will be used later by ClusOCM
// to figure out which OS version we are upgrading from.
dwError = TW32( DwWriteOSVersionInfo( osviOSVersionInfo ) );
if ( dwError != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x occurred trying to store the OS version info. This is not a fatal error.", dwError );
LogMsg( "Error %#x occurred trying to store the OS version info. This is not a fatal error.", dwError );
// This is not a fatal error. So reset the error code.
dwError = ERROR_SUCCESS;
} // if: there was an error writing the OS version info
else
{
TraceFlow( "The OS version info was successfully written to the registry." );
} // else: the OS version info was successfully written to the registry
// Check if the cluster service is registered.
dwError = TW32( DwIsClusterServiceRegistered( fWarningRequired ) );
if ( dwError != ERROR_SUCCESS )
{
// We could not get the state of the cluster service
// Show the warning, just in case.
TraceFlow1( "Error %#x occurred trying to check if the cluster service is registered.", dwError );
LogMsg( "Error %#x occurred trying to check if the cluster service is registered.", dwError );
break;
} // if: DwIsClusterServiceRegistered() returned an error
if ( !fWarningRequired )
{
// If the cluster service was not registered, no warning is needed.
TraceFlow( "The cluster service is not registered." );
LogMsg( "The cluster service is not registered." );
break;
} // if: no warning is required
TraceFlow( "The cluster service is registered. Checking the node versions." );
LogMsg( "The cluster service is registered. Checking the node versions." );
// Check if this is an NT4 node
if ( osviOSVersionInfo.dwMajorVersion < 5 )
{
TraceFlow( "This is an NT4 node." );
LogMsg( "This is an NT4 node." );
fWarningRequired = true;
break;
} // if: this is an NT4 node
TraceFlow( "This is not an NT4 node." );
// Check if the OS version is Whistler or if it is a non-NT OS
if ( ( osviOSVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT )
|| ( ( osviOSVersionInfo.dwMajorVersion >= 5 )
&& ( osviOSVersionInfo.dwMinorVersion >= 1 )
)
)
{
// If the OS not of the NT family or if the OS version of this
// node is Whistler or greater, no warning is required.
TraceFlow2(
"The version of the OS on this node is %d.%d. So, this is Windows Whister or later (or is not running NT)."
, osviOSVersionInfo.dwMajorVersion
, osviOSVersionInfo.dwMinorVersion
);
TraceFlow( "No NT4 nodes can exist in this cluster." );
LogMsg( "The version of the OS on this node is Windows Whister or later (or is not running NT)." );
LogMsg( "No NT4 nodes can exist in this cluster." );
fWarningRequired = false;
break;
} // if: the OS is not NT or if it is Win2k or greater
TraceFlow( "This is not a Whistler node - this has to be a Windows 2000 node." );
TraceFlow( "Trying to check if there are any NT4 nodes in the cluster." );
//
// Get the cluster version information
//
// Open a handle to the local cluster
schClusterHandle.Assign( OpenCluster( NULL ) );
if ( schClusterHandle.HHandle() == NULL )
{
// Show the warning, just to be safe.
dwError = TW32( GetLastError() );
TraceFlow1( "Error %#x occurred trying to get a handle to the cluster.", dwError );
LogMsg( "Error %#x occurred trying to get information about the cluster.", dwError );
break;
} // if: we could not get the cluster handle
TraceFlow( "OpenCluster() was successful." );
// Get the cluster version info
do
{
// Allocate the buffer - this memory is automatically freed when this object
// goes out of scope ( or during the next iteration ).
SmartSz sszClusterName( new WCHAR[ cchBufferSize ] );
CLUSTERVERSIONINFO cviClusterVersionInfo;
if ( sszClusterName.FIsEmpty() )
{
dwError = TW32( ERROR_NOT_ENOUGH_MEMORY );
TraceFlow1( "Error %#x occurred while allocating a buffer for the cluster name.", dwError );
LogMsg( "Error %#x occurred while allocating a buffer for the cluster name.", dwError );
break;
} // if: memory allocation failed
TraceFlow( "Memory for the cluster name has been allocated." );
cviClusterVersionInfo.dwVersionInfoSize = sizeof( cviClusterVersionInfo );
dwError = GetClusterInformation(
schClusterHandle.HHandle()
, sszClusterName.PMem()
, &cchBufferSize
, &cviClusterVersionInfo
);
if ( dwError == ERROR_SUCCESS )
{
// A warning is required if this node version is less than Win2k or
// if there is a node in the cluster whose version is less than Win2k
// NOTE: cviClusterVersionInfo.MajorVersion is the OS version
// while cviClusterVersionInfo.dwClusterHighestVersion is the cluster version.
fWarningRequired =
( ( cviClusterVersionInfo.MajorVersion < 5 )
|| ( CLUSTER_GET_MAJOR_VERSION( cviClusterVersionInfo.dwClusterHighestVersion ) < NT5_MAJOR_VERSION )
);
if ( fWarningRequired )
{
TraceFlow( "There is at least one node in the cluster whose OS version is earlier than Windows 2000." );
LogMsg( "There is at least one node in the cluster whose OS version is earlier than Windows 2000." );
} // if: a warning will be shown
else
{
TraceFlow( "The OS versions of all the nodes in the cluster are Windows 2000 or later." );
LogMsg( "The OS versions of all the nodes in the cluster are Windows 2000 or later." );
} // else: a warning will not be shown
break;
} // if: we got the cluster version info
else
{
if ( dwError == ERROR_MORE_DATA )
{
// Insufficient buffer - try again
++cchBufferSize;
dwError = ERROR_SUCCESS;
TraceFlow1( "The buffer size is insufficient. Need %d bytes. Reallocating.", cchBufferSize );
continue;
} // if: the size of the buffer was insufficient
// If we are here, something has gone wrong - show the warning
TW32( dwError );
TraceFlow1( "Error %#x occurred trying to get cluster information.", dwError );
LogMsg( "Error %#x occurred trying to get cluster information.", dwError );
break;
} // else: we could not get the cluster version info
}
while( true ); // loop infinitely
// We are done.
break;
}
while( false ); // Dummy do-while loop to avoid gotos
while ( fWarningRequired )
{
SmartSz sszWarningTitle;
COMPATIBILITY_ENTRY ceCompatibilityEntry;
TraceFlow( "The compatibility warning is required." );
LogMsg( "The compatibility warning is required." );
{
WCHAR * pszWarningTitle = NULL;
dwError = TW32( DwLoadString( IDS_ERROR_UPGRADE_OTHER_NODES, pszWarningTitle ) );
if ( dwError != ERROR_SUCCESS )
{
// We cannot show the warning
TraceFlow1( "Error %#x occurred trying to load the warning string.", dwError );
LogMsg( "Error %#x occurred trying to show the warning.", dwError );
break;
} // if: the load string failed
sszWarningTitle.Assign( pszWarningTitle );
}
//
// Call the callback function
//
ceCompatibilityEntry.Description = sszWarningTitle.PMem();
ceCompatibilityEntry.HtmlName = L"CompData\\ClusComp.htm";
ceCompatibilityEntry.TextName = L"CompData\\ClusComp.txt";
ceCompatibilityEntry.RegKeyName = NULL;
ceCompatibilityEntry.RegValName = NULL ;
ceCompatibilityEntry.RegValDataSize = 0;
ceCompatibilityEntry.RegValData = NULL;
ceCompatibilityEntry.SaveValue = NULL;
ceCompatibilityEntry.Flags = 0;
ceCompatibilityEntry.InfName = NULL;
ceCompatibilityEntry.InfSection = NULL;
TraceFlow( "About to call the compatibility callback function." );
// This function returns TRUE if the compatibility warning data was successfully set.
fReturnValue = pfnCompatibilityCallbackIn( &ceCompatibilityEntry, pvContextIn );
TraceFlow1( "The compatibility callback function returned %d.", fReturnValue );
break;
} // while: we need to show the warning
if ( !fWarningRequired )
{
TraceFlow( "The compatibility warning need not be shown." );
LogMsg( "The compatibility warning need not be shown." );
} // if: we did not need to show the warning
LogMsg( "Exiting function ClusterUpgradeCompatibilityCheck(). Return value is %d.", fReturnValue );
RETURN( fReturnValue );
} //*** ClusterUpgradeCompatibilityCheck()
/////////////////////////////////////////////////////////////////////////////
//++
//
// DWORD
// DwIsClusterServiceRegistered()
//
// Description:
// This function determines whether the Cluster Service has been registered
// with the Service Control Manager or not. It is not possible to use the
// GetNodeClusterState() API to see if this node is a member of a cluster
// or not, since this API was not available on NT4 SP3.
//
// Arguments:
// bool & rfIsRegisteredOut
// 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
DwIsClusterServiceRegistered( bool & rfIsRegisteredOut )
{
TraceFunc( "" );
DWORD dwError = ERROR_SUCCESS;
// Initialize the output
rfIsRegisteredOut = false;
// dummy do-while loop to avoid gotos
do
{
// Instantiate the SmartServiceHandle smart handle class.
typedef CSmartResource< CHandleTrait< SC_HANDLE, BOOL, CloseServiceHandle, NULL > > SmartServiceHandle;
// 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 )
{
dwError = TW32( GetLastError() );
TraceFlow1( "Error %#x occurred trying open a handle to the service control manager.", dwError );
LogMsg( "Error %#x occurred trying open a handle to the service control manager.", dwError );
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( "Successfully opened a handle to the cluster service. Therefore, it is registered." );
rfIsRegisteredOut = true;
break;
} // if: handle to clussvc could be opened
dwError = GetLastError();
if ( dwError == ERROR_SERVICE_DOES_NOT_EXIST )
{
TraceFlow( "The cluster service is not registered." );
dwError = ERROR_SUCCESS;
break;
} // if: the handle could not be opened because the service did not exist.
// If we are here, then some error occurred.
TW32( dwError );
TraceFlow1( "Error %#x occurred trying open a handle to the cluster service.", dwError );
LogMsg( "Error %#x occurred trying open a handle to the cluster service.", dwError );
// Handles are closed by the CSmartHandle destructor.
}
while ( false ); // dummy do-while loop to avoid gotos
RETURN( dwError );
} //*** DwIsClusterServiceRegistered()
//////////////////////////////////////////////////////////////////////////////
//++
//
// DwLoadString()
//
// Description:
// Allocate memory for and load a string from the string table.
//
// Arguments:
// uiStringIdIn
// Id of the string to look up
//
// rpszDestOut
// Reference to the pointer that will hold the address of the
// loaded string. The memory will have to be freed by the caller
// by using the delete operator.
//
// Return Value:
// S_OK
// If the call succeeded
//
// Other Win32 error codes
// If the call failed.
//
// Remarks:
// This function cannot load a zero length string.
//--
//////////////////////////////////////////////////////////////////////////////
DWORD
DwLoadString(
UINT nStringIdIn
, WCHAR *& rpszDestOut
)
{
TraceFunc( "" );
DWORD dwError = ERROR_SUCCESS;
UINT uiCurrentSize = 0;
SmartSz sszCurrentString;
UINT uiReturnedStringLen = 0;
// Initialize the output.
rpszDestOut = NULL;
do
{
// Grow the current string by an arbitrary amount.
uiCurrentSize += 256;
sszCurrentString.Assign( new WCHAR[ uiCurrentSize ] );
if ( sszCurrentString.FIsEmpty() )
{
dwError = TW32( ERROR_NOT_ENOUGH_MEMORY );
TraceFlow2( "Error %#x occurred trying allocate memory for string (string id is %d).", dwError, nStringIdIn );
LogMsg( "Error %#x occurred trying allocate memory for string (string id is %d).", dwError, nStringIdIn );
break;
} // if: the memory allocation has failed
uiReturnedStringLen = ::LoadString(
g_hInstance
, nStringIdIn
, sszCurrentString.PMem()
, uiCurrentSize
);
if ( uiReturnedStringLen == 0 )
{
dwError = TW32( GetLastError() );
TraceFlow2( "Error %#x occurred trying load string (string id is %d).", dwError, nStringIdIn );
LogMsg( "Error %#x occurred trying load string (string id is %d).", dwError, nStringIdIn );
break;
} // if: LoadString() had an error
++uiReturnedStringLen;
}
while( uiCurrentSize <= uiReturnedStringLen );
if ( dwError == ERROR_SUCCESS )
{
// Detach the smart pointer from the string, so that it is not freed by this function.
// Store the string pointer in the output.
rpszDestOut = sszCurrentString.PRelease();
} // if: there were no errors in this function
else
{
rpszDestOut = NULL;
} // else: something went wrong
RETURN( dwError );
} //*** DwLoadString()
/////////////////////////////////////////////////////////////////////////////
//++
//
// DWORD
// DwWriteOSVersionInfo()
//
// Description:
// This function writes the OS major and minor version information into the
// registry. This information will be used later by ClusOCM to determine the
// OS version before the upgrade.
//
// Arguments:
// const OSVERSIONINFO & rcosviOSVersionInfoIn
// Reference to the OSVERSIONINFO structure that has information about the
// OS version of this node.
//
// Return Value:
// ERROR_SUCCESS if all went well.
// Other Win32 error codes on failure.
//
//--
/////////////////////////////////////////////////////////////////////////////
DWORD
DwWriteOSVersionInfo( const OSVERSIONINFO & rcosviOSVersionInfoIn )
{
TraceFunc( "" );
DWORD dwError = ERROR_SUCCESS;
// dummy do-while loop to avoid gotos
do
{
// Instantiate the SmartRegistryKey smart handle class.
typedef CSmartResource< CHandleTrait< HKEY, LONG, RegCloseKey, NULL > > SmartRegistryKey;
SmartRegistryKey srkOSInfoKey;
{
HKEY hTempKey = NULL;
// Open the node version info registry key
dwError = TW32(
RegCreateKeyEx(
HKEY_LOCAL_MACHINE
, CLUSREG_KEYNAME_NODE_DATA L"\\" CLUSREG_KEYNAME_PREV_OS_INFO
, 0
, L""
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &hTempKey
, NULL
)
);
if ( dwError != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x occurred trying create the registry key where the node OS info is stored.", dwError );
LogMsg( "Error %#x occurred trying create the registry key where the node OS info is stored.", dwError );
break;
} // if: RegCreateKeyEx() failed
srkOSInfoKey.Assign( hTempKey );
}
// Write the OS major version
dwError = TW32(
RegSetValueEx(
srkOSInfoKey.HHandle()
, CLUSREG_NAME_NODE_MAJOR_VERSION
, 0
, REG_DWORD
, reinterpret_cast< const BYTE * >( &rcosviOSVersionInfoIn.dwMajorVersion )
, sizeof( rcosviOSVersionInfoIn.dwMajorVersion )
)
);
if ( dwError != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x occurred trying to store the OS major version info.", dwError );
LogMsg( "Error %#x occurred trying to store the OS major version info.", dwError );
break;
} // if: RegSetValueEx() failed while writing rcosviOSVersionInfoIn.dwMajorVersion
// Write the OS minor version
dwError = TW32(
RegSetValueEx(
srkOSInfoKey.HHandle()
, CLUSREG_NAME_NODE_MINOR_VERSION
, 0
, REG_DWORD
, reinterpret_cast< const BYTE * >( &rcosviOSVersionInfoIn.dwMinorVersion )
, sizeof( rcosviOSVersionInfoIn.dwMinorVersion )
)
);
if ( dwError != ERROR_SUCCESS )
{
TraceFlow1( "Error %#x occurred trying to store the OS minor version info.", dwError );
LogMsg( "Error %#x occurred trying to store the OS minor version info.", dwError );
break;
} // if: RegSetValueEx() failed while writing rcosviOSVersionInfoIn.dwMinorVersion
TraceFlow( "OS version information successfully stored in the registry." );
LogMsg( "OS version information successfully stored in the registry." );
}
while ( false ); // dummy do-while loop to avoid gotos
RETURN( dwError );
} //*** DwWriteOSVersionInfo()