/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    clusutil.cpp

Abstract:
    This file has to be kept in sync with \kinglet\rats\testsrc\kernel\cluster\clusapi\clusutil



Author:

   Sivaprasad Padisetty (sivapad)    6/25/97


Revision History:

--*/
#include "stdafx.h"
//#include <windows.h>
//#include <stdlib.h>
#include <clusapi.h>
#include "clusutil.h"



DWORDTOSTRINGMAP aTypeMap [] =
{
    {L"NODE_STATE", CLUSTER_CHANGE_NODE_STATE},
    {L"NODE_DELETED", CLUSTER_CHANGE_NODE_DELETED},
    {L"NODE_ADDED", CLUSTER_CHANGE_NODE_ADDED},
    {L"NODE_PROPERTY", CLUSTER_CHANGE_NODE_PROPERTY},

    {L"REGISTRY_NAME", CLUSTER_CHANGE_REGISTRY_NAME},
    {L"REGISTRY_ATTRIBUTES", CLUSTER_CHANGE_REGISTRY_ATTRIBUTES},
    {L"REGISTRY_VALUE", CLUSTER_CHANGE_REGISTRY_VALUE},
    {L"REGISTRY_SUBTREE", CLUSTER_CHANGE_REGISTRY_SUBTREE},

    {L"RESOURCE_STATE", CLUSTER_CHANGE_RESOURCE_STATE},
    {L"RESOURCE_DELETED", CLUSTER_CHANGE_RESOURCE_DELETED},
    {L"RESOURCE_ADDED", CLUSTER_CHANGE_RESOURCE_ADDED},
    {L"RESOURCE_PROPERTY", CLUSTER_CHANGE_RESOURCE_PROPERTY},

    {L"GROUP_STATE", CLUSTER_CHANGE_GROUP_STATE},
    {L"GROUP_DELETED", CLUSTER_CHANGE_GROUP_DELETED},
    {L"GROUP_ADDED", CLUSTER_CHANGE_GROUP_ADDED},
    {L"GROUP_PROPERTY", CLUSTER_CHANGE_GROUP_PROPERTY},

    {L"RESOURCE_TYPE_DELETED", CLUSTER_CHANGE_RESOURCE_TYPE_DELETED},
    {L"RESOURCE_TYPE_ADDED", CLUSTER_CHANGE_RESOURCE_TYPE_ADDED},

    {L"NETWORK_STATE", CLUSTER_CHANGE_NETWORK_STATE},
    {L"NETWORK_DELETED", CLUSTER_CHANGE_NETWORK_DELETED},
    {L"NETWORK_ADDED", CLUSTER_CHANGE_NETWORK_ADDED},
    {L"NETWORK_PROPERTY", CLUSTER_CHANGE_NETWORK_PROPERTY},

    {L"NETINTERFACE_STATE", CLUSTER_CHANGE_NETINTERFACE_STATE},
    {L"NETINTERFACE_DELETED", CLUSTER_CHANGE_NETINTERFACE_DELETED},
    {L"NETINTERFACE_ADDED", CLUSTER_CHANGE_NETINTERFACE_ADDED},
    {L"NETINTERFACE_PROPERTY", CLUSTER_CHANGE_NETINTERFACE_PROPERTY},

    {L"QUORUM_STATE", CLUSTER_CHANGE_QUORUM_STATE},
    {L"CLUSTER_STATE", CLUSTER_CHANGE_CLUSTER_STATE},
    {L"CLUSTER_PROPERTY", CLUSTER_CHANGE_CLUSTER_PROPERTY},

    {L"HANDLE_CLOSE", CLUSTER_CHANGE_HANDLE_CLOSE},

    {NULL, 0 }
} ;


LPCWSTR GetType (PDWORDTOSTRINGMAP pTypeMap, ULONG_PTR dwCode)
{
    int i = 0;
    while (pTypeMap [i].pszDesc)
    {
        if (pTypeMap [i].dwCode == dwCode)
            return pTypeMap [i].pszDesc ;
        i++ ;
    }

    return L"Unknown Type" ;
}

LPCWSTR GetType (ULONG_PTR dwFilter)
{
    return GetType (aTypeMap, dwFilter) ;
}


DWORDTOSUBSTRINGMAP aSubTypeMap [] =
{
    {L"Unknown", CLUSTER_CHANGE_RESOURCE_STATE, ClusterResourceStateUnknown},
    {L"Online", CLUSTER_CHANGE_RESOURCE_STATE, ClusterResourceOnline},
    {L"Offline", CLUSTER_CHANGE_RESOURCE_STATE, ClusterResourceOffline},
    {L"Failed", CLUSTER_CHANGE_RESOURCE_STATE, ClusterResourceFailed},
    {L"OnlinePending", CLUSTER_CHANGE_RESOURCE_STATE, ClusterResourceOnlinePending},
    {L"OfflinePending", CLUSTER_CHANGE_RESOURCE_STATE, ClusterResourceOfflinePending},

    {L"Unknown", CLUSTER_CHANGE_GROUP_STATE, ClusterGroupStateUnknown},
    {L"Online", CLUSTER_CHANGE_GROUP_STATE, ClusterGroupOnline},
    {L"Offline", CLUSTER_CHANGE_GROUP_STATE, ClusterGroupOffline},
    {L"Failed", CLUSTER_CHANGE_GROUP_STATE, ClusterGroupFailed},
    {L"PartialOnline", CLUSTER_CHANGE_GROUP_STATE, ClusterGroupPartialOnline},
    {L"Pending", CLUSTER_CHANGE_GROUP_STATE, ClusterGroupPending},

    {L"Unknown", CLUSTER_CHANGE_NODE_STATE, ClusterNodeStateUnknown},
    {L"Up", CLUSTER_CHANGE_NODE_STATE, ClusterNodeUp},
    {L"Down", CLUSTER_CHANGE_NODE_STATE, ClusterNodeDown},
    {L"Paused", CLUSTER_CHANGE_NODE_STATE, ClusterNodePaused},
    {L"Joining", CLUSTER_CHANGE_NODE_STATE, ClusterNodeJoining},

    {L"Unknown", CLUSTER_CHANGE_NETWORK_STATE, ClusterNetworkStateUnknown},
    {L"Unavailable", CLUSTER_CHANGE_NETWORK_STATE, ClusterNetworkUnavailable},
    {L"Down", CLUSTER_CHANGE_NETWORK_STATE, ClusterNetworkDown},
    {L"Partitioned", CLUSTER_CHANGE_NETWORK_STATE, ClusterNetworkPartitioned},
    {L"Up", CLUSTER_CHANGE_NETWORK_STATE, ClusterNetworkUp},
    
    {L"Unknown", CLUSTER_CHANGE_NETINTERFACE_STATE, ClusterNetInterfaceStateUnknown},
    {L"Unavailable", CLUSTER_CHANGE_NETINTERFACE_STATE, ClusterNetInterfaceUnavailable},
    {L"Failed", CLUSTER_CHANGE_NETINTERFACE_STATE, ClusterNetInterfaceFailed},
    {L"Unreachable", CLUSTER_CHANGE_NETINTERFACE_STATE, ClusterNetInterfaceUnreachable},
    {L"Up", CLUSTER_CHANGE_NETINTERFACE_STATE, ClusterNetInterfaceUp},

    {NULL, 0, 0 }
} ;

LPCWSTR GetSubType (PDWORDTOSUBSTRINGMAP pTypeMap, DWORD dwCode, DWORD dwSubCode)
{
    int i = 0;
    while (pTypeMap [i].pszDesc)
    {
        if (pTypeMap [i].dwCode == dwCode &&
            pTypeMap [i].dwSubCode == dwSubCode )
            return pTypeMap [i].pszDesc ;
        i++ ;
    }

    return L"Unknown Type" ;
}


/*
Ini File Manipulating routines.

*/

const LPCWSTR pszDefaultIni = L".\\CLUSMAN.INI" ;

void
RecurseEvalParam (LPCWSTR pszParam, LPWSTR pszValue, LPCWSTR pszSection)
{
    LPCWSTR pszIniFile = _wgetenv (L"CLUSMANINI") ;
    if (!pszIniFile)
        pszIniFile = pszDefaultIni ;

    LPCWSTR pszTmp, pszStart, pszEnd ;
    WCHAR    szVariable [PARAM_LEN], szTmpParam [PARAM_LEN], szTmpValue [PARAM_LEN] ;
    while (*pszParam)
    {
        if (*pszParam != L'%')
            *pszValue++ = *pszParam++ ;
        else  // Has to substitute for the parameter.
        {
            pszEnd = pszStart = pszParam + 1 ;

            while (*pszEnd && *pszEnd != L'%')pszEnd++ ;

            if (!*pszEnd) // Only one % is found. Just copy the string to the value
            {
                while (*pszParam)
                    *pszValue++ = *pszParam++ ;
            }
            else
            {
                pszParam = pszEnd + 1 ;

                wcsncpy (szVariable, pszStart, (UINT)(pszEnd - pszStart)) ;
                szVariable [pszEnd-pszStart] = L'\0' ;

                if (wcslen (szVariable) == 0)
                    continue ;

                pszTmp = _wgetenv (szVariable) ;

                if (pszTmp)  // Env variable found, copy it.
                {
                    RecurseEvalParam (pszTmp, szTmpValue) ;
                    pszTmp = szTmpValue ;

                    while (*pszTmp)
                        *pszValue++ = *pszTmp++ ;
                }
                else  // Now try in the Ini File
                {
                    if (GetPrivateProfileString (pszSection, szVariable, L"", szTmpParam, sizeof (szTmpParam)/sizeof(szTmpParam[0]), pszIniFile) > 0)
                    { // Copied some stuff.

                        RecurseEvalParam (szTmpParam, szTmpValue) ;
                        pszTmp = szTmpValue ;

                        while (*pszTmp)
                            *pszValue++ = *pszTmp++ ;
                    }
                }
            }
        }
    }
    *pszValue = L'\0' ;
}

int RecurseEvalIntParam (LPCWSTR pszParam)
{
    WCHAR szBuf [PARAM_LEN] ;

    RecurseEvalParam (pszParam, szBuf) ;

    return szBuf [0] == L'\0'?0:_wtoi (szBuf) ;
}

void
SaveParam (LPCWSTR pszKey, LPCWSTR pszValue)
{
    LPCWSTR pszIniFile = _wgetenv (L"CLUSMANINI") ;
    if (!pszIniFile)
        pszIniFile = pszDefaultIni ;

    WritePrivateProfileString (L"PARAMETER", pszKey, pszValue, pszIniFile) ;
}


void
SaveIntParam (LPCWSTR pszKey, int iValue)
{
    LPCWSTR pszIniFile = _wgetenv (L"CLUSMANINI") ;
    if (!pszIniFile)
        pszIniFile = pszDefaultIni ;

    WCHAR szBuf [80] ;

    wsprintf (szBuf, L"%d", iValue) ;

    WritePrivateProfileString (L"PARAMETER", pszKey, szBuf, pszIniFile) ;
}

void MemSet (LPWSTR pszBuf, WCHAR c, DWORD cb)
{
    while (cb--)
        *pszBuf++ = c ;
}

int MemCheck (LPCWSTR pszBuf, WCHAR c, DWORD cb)
{
    while (cb--)
        if (*pszBuf++ != c)
            return 1 ;
    return 0 ;
}

// Get the state of the resource of the name of the resource
CLUSTER_RESOURCE_STATE MyGetClusterResourceState (HCLUSTER hCluster, LPCWSTR pszResourceName, LPWSTR pszNodeName, LPWSTR pszGroupName)
{
    HRESOURCE hResource ;
    CLUSTER_RESOURCE_STATE dwState ;
    DWORD cbNodeName, cbGroupName ;

    if (hResource = OpenClusterResource (hCluster, pszResourceName))
    {
        // This is a bug of hard coding the length. Hopefully the code does not crash.
        cbGroupName = (pszGroupName)?80:0 ;
        cbNodeName = (pszNodeName)?80:0 ;
        dwState = GetClusterResourceState (hResource, pszNodeName, &cbNodeName, pszGroupName, &cbGroupName) ;
        CloseClusterResource (hResource) ;
    }
    else
        dwState = ClusterResourceStateUnknown ;

    return dwState ;
}

// Get the state of the Group given the name.
CLUSTER_GROUP_STATE MyGetClusterGroupState (HCLUSTER hCluster, LPCWSTR pszGroupName, LPWSTR pszNodeName)
{
    HGROUP hGroup ;
    CLUSTER_GROUP_STATE dwState ;
    DWORD cbNodeName ;

    if (hGroup = OpenClusterGroup (hCluster, pszGroupName))
    {
        // This is a bug of hard coding the length. Hopefully the code does not crash.
        cbNodeName = (pszNodeName)?80:0 ;
        dwState = GetClusterGroupState (hGroup, pszNodeName, &cbNodeName) ;
        CloseClusterGroup (hGroup) ;
    }
    else
        dwState = ClusterGroupStateUnknown ;

    return dwState ;
}

// Get the state of the Node given the name.
CLUSTER_NODE_STATE MyGetClusterNodeState (HCLUSTER hCluster, LPWSTR pszNodeName)
{
    HNODE hNode ;
    CLUSTER_NODE_STATE dwState ;

    if (hNode = OpenClusterNode (hCluster, pszNodeName))
    {
        dwState = GetClusterNodeState (hNode) ;
        CloseClusterNode (hNode) ;
    }
    else
        dwState = ClusterNodeStateUnknown ;

    return dwState ;
}

// Get the state of the Network given the name.
CLUSTER_NETWORK_STATE MyGetClusterNetworkState (HCLUSTER hCluster, LPWSTR pszNetworkName)
{
    HNETWORK hNetwork ;
    CLUSTER_NETWORK_STATE dwState ;

    if (hNetwork = OpenClusterNetwork (hCluster, pszNetworkName))
    {
        dwState = GetClusterNetworkState (hNetwork) ;
        CloseClusterNetwork (hNetwork) ;
    }
    else
        dwState = ClusterNetworkStateUnknown ;

    return dwState ;
}


// Get the state of the NetInterface given the name.
CLUSTER_NETINTERFACE_STATE MyGetClusterNetInterfaceState (HCLUSTER hCluster, LPWSTR pszNetInterfaceName)
{
    HNETINTERFACE hNetInterface ;
    CLUSTER_NETINTERFACE_STATE dwState ;

    if (hNetInterface = OpenClusterNetInterface (hCluster, pszNetInterfaceName))
    {
        dwState = GetClusterNetInterfaceState (hNetInterface) ;
        CloseClusterNetInterface (hNetInterface) ;
    }
    else
        dwState = ClusterNetInterfaceStateUnknown ;

    return dwState ;
}

// returns 0 on success.
// hCluster is the handle to the cluster.
// pszFileName is filename where to store the cluster information.
#include <stdio.h>
DumpClusterDetails (HCLUSTER hCluster, LPCWSTR pszFileName, LPCWSTR pszComment)
{
    FILE *fp = _wfopen (pszFileName, L"a") ;
    HCLUSENUM hEnum = NULL ;
    DWORD cbNodeName, cbGroupName, cbResourceName, dwEnumType, dwIndex = 0 ;
    BOOL bMoreData = TRUE ;
    WCHAR szNodeName [512], szGroupName [512], szResourceName [512] ;
    DWORD dwState ;

    fwprintf (fp, L"\n*********************************************************************\n") ;
    fwprintf (fp, pszComment) ;
    fwprintf (fp, L"*********************************************************************\n") ;
    __try
    {
        // Dump all the Node Information
        fwprintf (fp, L"DUMP ALL THE NODE  INFORMATION\n------------------------------\n") ;
        if ((hEnum = ClusterOpenEnum (hCluster, CLUSTER_ENUM_NODE)) == NULL)
        {
            fwprintf (fp, L"Clus Open Enum failed for the node\n") ;
            return 1 ;
        }

        while (bMoreData)
        {
            cbNodeName = sizeof (szNodeName)/sizeof (szNodeName[0]) ;
            switch (ClusterEnum (hEnum, dwIndex, &dwEnumType, szNodeName, &cbNodeName))
            {
            case ERROR_SUCCESS:
                dwState = (DWORD) MyGetClusterNodeState (hCluster, szNodeName) ;
                fwprintf (fp, L"%-30s Status:%-30s\n", szNodeName, GetSubType (aSubTypeMap, CLUSTER_CHANGE_NODE_STATE, dwState)) ;
                break ;
            case ERROR_NO_MORE_ITEMS:
                bMoreData = FALSE ;
                break ;
            default:
                fwprintf (fp, L"ClusterEnum returned Invalid Value\n") ;
                bMoreData = FALSE ;
                return 1 ;
            }
            dwIndex++ ;
        }
        ClusterCloseEnum (hEnum) ;

        // Dump all the Group Information
        dwIndex = 0 ;
        bMoreData = TRUE ;
        fwprintf (fp, L"DUMP ALL THE GROUP INFORMATION\n------------------------------\n") ;
        
        if ((hEnum = ClusterOpenEnum (hCluster, CLUSTER_ENUM_GROUP)) == NULL)
        {
            fwprintf (fp, L"Clus Open Enum failed for the node\n") ;
            return 1 ;
        }

        while (bMoreData)
        {
            cbGroupName = sizeof (szGroupName) / sizeof (szGroupName [0]) ;
            switch (ClusterEnum (hEnum, dwIndex, &dwEnumType, szGroupName, &cbGroupName))
            {
            case ERROR_SUCCESS:
                dwState = (DWORD) MyGetClusterGroupState (hCluster, szGroupName, szNodeName) ;
                fwprintf (fp, L"%-30s Node:%-30s Status:%-30s\n", szGroupName, szNodeName, GetSubType (aSubTypeMap, CLUSTER_CHANGE_GROUP_STATE, dwState)) ;
                break ;
            case ERROR_NO_MORE_ITEMS:
                bMoreData = FALSE ;
                break ;
            default:
                fwprintf (fp, L"ClusterEnum returned Invalid Value\n") ;
                bMoreData = FALSE ;
                return 1 ;
            }
            dwIndex++ ;
        }

        ClusterCloseEnum (hEnum) ;

        // Dump all the Resource Information
        dwIndex = 0 ;
        bMoreData = TRUE ;
        fwprintf (fp, L"DUMP ALL THE RESOURCE INFORMATION\n---------------------------------\n") ;
        
        if ((hEnum = ClusterOpenEnum (hCluster, CLUSTER_ENUM_RESOURCE)) == NULL)
        {
            fwprintf (fp, L"Clus Open Enum failed for the node\n") ;
            return 1 ;
        }

        while (bMoreData)
        {
            cbResourceName = sizeof (szResourceName) / sizeof (szResourceName [0]) ;
            switch (ClusterEnum (hEnum, dwIndex, &dwEnumType, szResourceName, &cbResourceName))
            {
            case ERROR_SUCCESS:
                dwState = (DWORD)MyGetClusterResourceState (hCluster, szResourceName, szNodeName, szGroupName) ;
                fwprintf (fp, L"%-30s Node:%-30s Group:%-30s Status:%-30s\n", szResourceName, szNodeName, szGroupName, GetSubType (aSubTypeMap, CLUSTER_CHANGE_RESOURCE_STATE, dwState)) ;
                break ;
            case ERROR_NO_MORE_ITEMS:
                bMoreData = FALSE ;
                break ;
            default:
                fwprintf (fp, L"ClusterEnum returned Invalid Value\n") ;
                bMoreData = FALSE ;
                return 1 ;
            }
            dwIndex++ ;
        }

        ClusterCloseEnum (hEnum) ;

        hEnum = NULL ;

        return 0 ;
    }
    __finally
    {
        if (hEnum)
            ClusterCloseEnum (hEnum) ;
        fclose (fp) ;
    }
}