//////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 1999-2000 Microsoft Corporation
//
//  Module Name:
//      CClusSvc.cpp
//
//  Description:
//      Contains the definition of the CClusSvc class.
//
//  Maintained By:
//      Vij Vasu (Vvasu) 08-MAR-2000
//
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
// Include Files
//////////////////////////////////////////////////////////////////////////////

// The precompiled header.
#include "pch.h"

// The header for this file
#include "CClusSvc.h"

// For DwRemoveDirectory()
#include "Common.h"

// To set the failure actions of the cluster service.
#include "clusrtl.h"


//////////////////////////////////////////////////////////////////////////////
// Macros
//////////////////////////////////////////////////////////////////////////////

// Name of the NodeId cluster service parameter registry value.
#define CLUSSVC_NODEID_VALUE   L"NodeId"


//////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusSvc::CClusSvc()
//
//  Description:
//      Constructor of the CClusSvc 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
//
    //--
//////////////////////////////////////////////////////////////////////////////
CClusSvc::CClusSvc(
      CBaseClusterAction *  pbcaParentActionIn
    )
    : m_cservClusSvc( CLUSTER_SERVICE_NAME )
    , m_pbcaParentAction( pbcaParentActionIn )
{

    BCATraceScope( "" );

    if ( m_pbcaParentAction == NULL) 
    {
        BCATraceMsg( "Pointers to the parent action is NULL. Throwing exception." );
        THROW_ASSERT( 
              E_INVALIDARG
            , "CClusSvc::CClusSvc() => Required input pointer in NULL"
            );
    } // if: the parent action pointer is NULL

} //*** CClusSvc::CClusSvc()


//////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusSvc::~CClusSvc( void )
//
//  Description:
//      Destructor of the CClusSvc class.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None. 
//
//  Exceptions Thrown:
//      Any exceptions thrown by underlying functions
//
//--
//////////////////////////////////////////////////////////////////////////////
CClusSvc::~CClusSvc( void )
{
    BCATraceScope( "" );

} //*** CClusSvc::~CClusSvc()


//////////////////////////////////////////////////////////////////////////////
//++
//
//  void
//  CClusSvc::ConfigureService()
//
//  Description:
//      Create the service, set the failure actions and the service account.
//      Then start the service.
//
//  Arguments:
//      pszClusterDomainAccountNameIn
//      pszClusterAccountPwdIn
//          Information about the account to be used as the cluster service
//          account.
//
//      pszNodeIdString
//          String containing the Id of this node.
//
//  Return Value:
//      None. 
//
//  Exceptions Thrown:
//      CRuntimeError
//          If any of the APIs fail.
//
//      Any that are thrown by the underlying functions.
//
//--
//////////////////////////////////////////////////////////////////////////////
void
CClusSvc::ConfigureService(
      const WCHAR *     pszClusterDomainAccountNameIn
    , const WCHAR *     pszClusterAccountPwdIn
    , const WCHAR *     pszNodeIdStringIn
    , bool              fIsVersionCheckingDisabledIn
    )
{
    BCATraceScope( "" );

    DWORD           dwError = ERROR_SUCCESS;

    CStatusReport   srCreatingClusSvc(
          PbcaGetParent()->PBcaiGetInterfacePointer()
        , TASKID_Major_Configure_Cluster_Services
        , TASKID_Minor_Creating_Cluster_Service
        , 0, 2
        , IDS_TASK_CREATING_CLUSSVC
        );

    LogMsg( "Configuring the Cluster service." );

    // Send the next step of this status report.
    srCreatingClusSvc.SendNextStep( S_OK );

    // Create the cluster service.
    m_cservClusSvc.Create( m_pbcaParentAction->HGetMainInfFileHandle() );

    do
    {
        LogMsg( "Setting the Cluster service account information." );


        // Open a smart handle to the cluster service.
        SmartSCMHandle  sscmhClusSvcHandle(
            OpenService(
                  m_pbcaParentAction->HGetSCMHandle()
                , CLUSTER_SERVICE_NAME
                , SERVICE_CHANGE_CONFIG
                )
            );


        if ( sscmhClusSvcHandle.FIsInvalid() )
        {
            dwError = TW32( GetLastError() );
            BCATraceMsg( "OpenService() failed." );
            break;
        } // if: we could not open a handle to the cluster service.

        //
        // Set the service account information.
        //
        {
            if ( 
                 ChangeServiceConfig(
                      sscmhClusSvcHandle
                    , SERVICE_NO_CHANGE
                    , SERVICE_NO_CHANGE
                    , SERVICE_NO_CHANGE
                    , NULL
                    , NULL
                    , NULL
                    , NULL
                    , pszClusterDomainAccountNameIn
                    , pszClusterAccountPwdIn
                    , NULL
                    ) 
                 == FALSE
               )
            {
                dwError = TW32( GetLastError() );
                BCATraceMsg1( 
                      "ChangeServiceConfig() failed. Account = '%ws'."
                    , pszClusterDomainAccountNameIn
                    );
                break;
            } // if: we could not set the account information.
        }

        LogMsg( "Setting the Cluster service failure actions." );

        // Set the failure actions of the cluster service service.
        dwError = TW32( ClRtlSetSCMFailureActions( NULL ) );
        if ( dwError != ERROR_SUCCESS )
        {
            BCATraceMsg( "ClRtlSetSCMFailureActions() failed." );
            break;
        } // if: the service failure actions couldn't be set

        LogMsg( "Setting the Cluster service parameters." );

        // Send the next step of this status report.
        srCreatingClusSvc.SendNextStep( S_OK );

        {
            CRegistryKey rkClusSvcParams;
            
            // Open the parameters key or create it if it does not exist.
            rkClusSvcParams.CreateKey(
                  HKEY_LOCAL_MACHINE
                , CLUSREG_KEYNAME_CLUSSVC_PARAMETERS
                , KEY_WRITE
                );

            // Set the NodeId string.
            rkClusSvcParams.SetValue(
                  CLUSSVC_NODEID_VALUE
                , REG_SZ
                , reinterpret_cast< const BYTE * >( pszNodeIdStringIn )
                , ( wcslen( pszNodeIdStringIn ) + 1 ) * sizeof( *pszNodeIdStringIn )
                );

            // If version checking has been disabled, set a flag in the service parameters
            // to indicate this.
            if ( fIsVersionCheckingDisabledIn )
            {
                DWORD   dwNoVersionCheck = 1;

                rkClusSvcParams.SetValue(
                      CLUSREG_NAME_SVC_PARAM_NOVER_CHECK
                    , REG_DWORD
                    , reinterpret_cast< const BYTE * >( &dwNoVersionCheck )
                    , sizeof( dwNoVersionCheck )
                    );

                BCATraceMsg( "Cluster version checking has been disabled on this computer." );
                LogMsg( "Cluster version checking has been disabled on this computer." );
            } // if: version checking has been disabled
        }

        //
        // Set the cluster installation state.
        //
        if ( ClRtlSetClusterInstallState( eClusterInstallStateConfigured ) == FALSE )
        {
            dwError = TW32( GetLastError() );
            LogMsg( "Could not set the cluster installation state." );
            BCATraceMsg( "Could not set the cluster installation state. Throwing exception." );

            break;
        } // ClRtlSetClusterInstallState() failed.

    }
    while( false ); // dummy do-while loop to avoid gotos

    if ( dwError != ERROR_SUCCESS )
    {
        LogMsg( "Error %#08x occurred trying configure the ClusSvc service.", dwError );
        BCATraceMsg1( "Error %#08x occurred trying configure the ClusSvc service. Throwing exception.", dwError );
        THROW_RUNTIME_ERROR(
              HRESULT_FROM_WIN32( dwError )
            , IDS_ERROR_CLUSSVC_CONFIG
            );
    } // if; there was an error getting the handle.

    // Send the next step of this status report.
    srCreatingClusSvc.SendNextStep( S_OK );

    {
        UINT    cQueryCount = 200;

        CStatusReport   srStartingClusSvc(
              PbcaGetParent()->PBcaiGetInterfacePointer()
            , TASKID_Major_Configure_Cluster_Services
            , TASKID_Minor_Starting_Cluster_Service
            , 0, cQueryCount + 2    // we will send at most cQueryCount reports while waiting for the service to start (the two extra sends are below)
            , IDS_TASK_STARTING_CLUSSVC
            );

        // Send the next step of this status report.
        srStartingClusSvc.SendNextStep( S_OK );

        // Start the service.
        m_cservClusSvc.Start(
              m_pbcaParentAction->HGetSCMHandle()
            , true                  // wait for the service to start
            , 1500                  // wait 1.5 seconds between queries for status.
            , cQueryCount           // query cQueryCount times
            , &srStartingClusSvc    // status report to be sent while waiting for the service to start
            );

        // Send the last step of this status report.
        srStartingClusSvc.SendLastStep( S_OK );
    }

} //*** CClusSvc::ConfigureService()


//////////////////////////////////////////////////////////////////////////////
//++
//
//  void
//  CClusSvc::CleanupService( void )
//
//  Description:
//      Stop, cleanup and remove the service.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None. 
//
//  Exceptions Thrown:
//      Any that are thrown by the underlying functions.
//
//--
//////////////////////////////////////////////////////////////////////////////
void
CClusSvc::CleanupService( void )
{
    BCATraceScope( "" );

    LogMsg( "Trying to stop the Cluster Service." );

    // Stop the service.
    m_cservClusSvc.Stop(
          m_pbcaParentAction->HGetSCMHandle()
        , 5000      // wait 5 seconds between queries for status.
        , 60        // query 60 times ( 5 minutes )
        );

    //
    // Restore the cluster installation state.
    //
    if ( ClRtlSetClusterInstallState( eClusterInstallStateFilesCopied ) == FALSE )
    {
        DWORD dwError = GetLastError();

        LogMsg( "Could not set the cluster installation state." );
        BCATraceMsg( "Could not set the cluster installation state. Throwing exception." );

        THROW_RUNTIME_ERROR(
              HRESULT_FROM_WIN32( dwError )
            , IDS_ERROR_SETTING_INSTALL_STATE
            );
    } // ClRtlSetClusterInstallState() failed.

    LogMsg( "Cleaning up Cluster Service." );

    m_cservClusSvc.Cleanup( m_pbcaParentAction->HGetMainInfFileHandle() );

    // Cleanup the local quorum directory.
    {
        DWORD           dwError = ERROR_SUCCESS;
        const WCHAR *   pcszQuorumDir = m_pbcaParentAction->RStrGetLocalQuorumDirectory().PszData();

        dwError = TW32( DwRemoveDirectory( pcszQuorumDir ) );
        if ( dwError != ERROR_SUCCESS )
        {
            BCATraceMsg2( "The local quorum directory '%s' cannot be removed. Non-fatal error %#x occurred.\n", pcszQuorumDir, dwError );
            LogMsg( "The local quorum directory '%s' cannot be removed. Non-fatal error %#x occurred.\n", pcszQuorumDir, dwError );
        } // if: we could not remove the local quorum directory
    }

} //*** CClusSvc::CleanupService()