/*++

Copyright (c) 1998-1999 Microsoft Corporation

Module Name:

    ulperf.cpp

Abstract:

    This is the main code for the ul.sys performance counter support object

Author:

    Paul McDaniel (paulmcd)       11-May-1999

Revision History:

--*/

#include "precomp.h"
#include "ulperf.h"


//
// Globals
//

LONG                g_lInit;
HINSTANCE           g_hInstance;

//
// Active "opens" reference count for Open/Close PerformanceData
//
ULONG OpenCount = 0;

//
// An open handle to the event log for error logging
//
HANDLE hEventLog;

//
//  Initialize the constant portions of the HKEY_PERFORMANCE data structure.
//  Certain parts (especially the name/help indices) will be
//  updated at initialization time.
//

const UL_COUNTER_BLOCK CounterBlock;

UL_PERF_OBJECT_DEFINITION ObjectDefinition =
{
    {   // ObjectType
        sizeof(UL_PERF_OBJECT_DEFINITION) + sizeof(UL_COUNTER_BLOCK),
        sizeof(UL_PERF_OBJECT_DEFINITION),
        sizeof(PERF_OBJECT_TYPE),
        UL_COUNTER_OBJECT,
        NULL,
        UL_COUNTER_OBJECT,
        NULL,
        PERF_DETAIL_ADVANCED,
        NUMBER_OF_UL_COUNTERS,
        -1,                                     // DefaultCounter
        PERF_NO_INSTANCES,
        0,
        { 0, 0 },
        { 0, 0 }
    },

    {   // BytesSent
        sizeof(PERF_COUNTER_DEFINITION),
        UL_BYTES_SENT_COUNTER,
        NULL,                           // assigned in OpenPerformanceData()
        UL_BYTES_SENT_COUNTER,
        NULL,                           // assigned in OpenPerformanceData()
        0,
        PERF_DETAIL_ADVANCED,
        PERF_COUNTER_COUNTER,
        0,                              // assigned in OpenPerformanceData()
        0                               // assigned in OpenPerformanceData()
    },

    {   // BytesReceived
        sizeof(PERF_COUNTER_DEFINITION),
        UL_BYTES_RECEIVED_COUNTER,
        NULL,
        UL_BYTES_RECEIVED_COUNTER,
        NULL,
        0,
        PERF_DETAIL_ADVANCED,
        PERF_COUNTER_COUNTER,
        0,
        0
    },

    {   // CurrentConnections
        sizeof(PERF_COUNTER_DEFINITION),
        UL_CURRENT_CONNECTIONS_NUMBER,
        NULL,
        UL_CURRENT_CONNECTIONS_NUMBER,
        NULL,
        0,
        PERF_DETAIL_ADVANCED,
        PERF_COUNTER_RAWCOUNT,
        0,
        0
    },
    
    {   // CurrentRequests
        sizeof(PERF_COUNTER_DEFINITION),
        UL_CURRENT_REQUESTS_NUMBER,
        NULL,
        UL_CURRENT_REQUESTS_NUMBER,
        NULL,
        0,
        PERF_DETAIL_ADVANCED,
        PERF_COUNTER_RAWCOUNT,
        0,
        0
    },

    {   // QueuedRequests
        sizeof(PERF_COUNTER_DEFINITION),
        UL_QUEUED_REQUESTS_NUMBER,
        NULL,
        UL_QUEUED_REQUESTS_NUMBER,
        NULL,
        0,
        PERF_DETAIL_ADVANCED,
        PERF_COUNTER_RAWCOUNT,
        0,
        0
    },
    
    {   // AttachedProcesses
        sizeof(PERF_COUNTER_DEFINITION),
        UL_ATTACHED_PROCESSES_NUMBER,
        NULL,
        UL_ATTACHED_PROCESSES_NUMBER,
        NULL,
        0,
        PERF_DETAIL_ADVANCED,
        PERF_COUNTER_RAWCOUNT,
        0,
        0
    }

};


DWORD 
OpenPerformanceData(
    LPWSTR lpDeviceNames
    )
{
    DWORD   Error = NO_ERROR;
    HKEY    hkey = NULL;
    DWORD   size;
    DWORD   type;
    DWORD   dwFirstCounter;
    DWORD   dwFirstHelp;
    DWORD   i;
    
    PERF_COUNTER_DEFINITION *   pctr;

    //
    //  Since WINLOGON is multi-threaded and will call this routine in
    //  order to service remote performance queries, this library
    //  must keep track of how many times it has been opened (i.e.
    //  how many threads have accessed it). The registry routines will
    //  limit access to the initialization routine to only one thread
    //  at a time so synchronization (i.e. reentrancy) should not be
    //  a problem.
    //

    if (OpenCount == 0)
    {

        //
        //  This is the *first* open.
        //

        // open event log interface

        if (hEventLog == NULL)
        {
            hEventLog = RegisterEventSource(
                            (LPTSTR)NULL,       // Use Local Machine
                            APP_NAME            // event log app name 
                                                // to find in registry
                            );               
        }

        //
        //  open the performance key
        //

        Error = RegOpenKeyEx( 
                    HKEY_LOCAL_MACHINE,
                    REGISTRY_UL_INFORMATION L"\\Performance",
                    0,
                    KEY_ALL_ACCESS,
                    &hkey 
                    );

        if( err == NO_ERROR )
        {
            //
            //  Read the first counter DWORD.
            //

            size = sizeof(DWORD);

            err = RegQueryValueEx( 
                        hkey,
                        L"First Counter",
                        NULL,
                        &type,
                        (LPBYTE)&dwFirstCounter,
                        &size 
                        );
                        
            if( err == NO_ERROR )
            {
                //
                //  Read the first help DWORD.
                //

                size = sizeof(DWORD);

                err = RegQueryValueEx( hkey,
                                    L"First Help",
                                    NULL,
                                    &type,
                                    (LPBYTE)&dwFirstHelp,
                                    &size );

                if ( err == NO_ERROR )
                {
                    //
                    //  Update the object & counter name & help indicies.
                    //

                    ObjectDefinition.ObjectType.ObjectNameTitleIndex
                        += dwFirstCounter;
                    ObjectDefinition.ObjectType.ObjectHelpTitleIndex
                        += dwFirstHelp;

#define ADJUST_VALUES(CounterName) \
do {\
    ObjectDefinition.##CounterName.CounterNameTitleIndex \
        += dwFirstCounter; \
    ObjectDefinition.##CounterName.CounterHelpTitleIndex \
        += dwFirstHelp; \
    ObjectDefinition.##CounterName.CounterSize \
        = sizeof(CounterBlock.##CounterName); \
    ObjectDefinition.##CounterName.CounterOffset = \
        DIFF((LPBYTE)&CounterBlock.##CounterName - (LPBYTE)&CounterBlock);\
} while (0)

                    ADJUST_VALUES(BytesSent);
                    ADJUST_VALUES(BytesReceived);
                    ADJUST_VALUES(CurrentConnections);
                    ADJUST_VALUES(CurrentRequests);
                    ADJUST_VALUES(QueuedRequests);
                    ADJUST_VALUES(AttachedProcesses);

                    //
                    //  Remember that we initialized OK.
                    //

                    fInitOK = TRUE;

                } else {
                    ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE,
                        0, W3_UNABLE_READ_FIRST_HELP,
                        (PSID)NULL, 0,
                        sizeof(err), NULL,
                        (PVOID)(&err));
                }
            } else {
                ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE,
                    0, W3_UNABLE_READ_FIRST_COUNTER,
                    (PSID)NULL, 0,
                    sizeof(err), NULL,
                    (PVOID)(&err));
            }

            //
            //  Close the registry if we managed to actually open it.
            //

            if( hkey != NULL )
            {
                RegCloseKey( hkey );
                hkey = NULL;
            }
        } else {
            ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE,
                0, W3_UNABLE_OPEN_W3SVC_PERF_KEY,
                (PSID)NULL, 0,
                sizeof(err), NULL,
                (PVOID)(&err));
        }
    }

    //
    //  Bump open counter.
    //

    OpenCount += 1;

end:
    return Error;

}   // OpenPerformanceData

DWORD
CollectPerformanceData(
    LPWSTR lpwszValue, 
    LPVOID *lppData, 
    LPDWORD lpcbBytes, 
    LPDWORD lpcObjectTypes
    )
{

}

DWORD
WINAPI
ClosePerformanceData(
    )
{
    //
    //  Clean up
    //

    OpenCount -= 1;
    if (OpenCount == 0)
    {
        if (hEventLog != NULL) 
        {
            DeregisterEventSource(hEventLog);
            hEventLog = NULL;
        }
    }

    return NO_ERROR;
    
}   // ClosePerformanceData




CUlPerfCounters::CUlPerfCounters()
{

}

CUlPerfCounters::~CUlPerfCounters()
{

}


//
// IWbemProviderInit
//

STDMETHODIMP CUlPerfCounters::Initialize(LPWSTR wszUser,LONG lFlags,LPWSTR wszNamespace,LPWSTR wszLocale,IWbemServices* pNamespace,IWbemContext* pCtx,IWbemProviderInitSink* pInitSink)
{ 
    HRESULT Result;

    ASSERT(pInitSink != NULL);

    TRACE(L"ulperf!CUlPerfCounters::Initialize called\n");

    //
    // tell wbem we are ready to go.
    //
    
    Result = pInitSink->SetStatus(WBEM_S_INITIALIZED, 0);
    

    return Result; 
    
}   // CUlPerfCounters::Initialize


//
// IWbemServices
//

// Context.
// ========

STDMETHODIMP CUlPerfCounters::OpenNamespace(BSTR strNamespace, long lFlags, IWbemContext* pCtx, IWbemServices** ppWorkingNamespace, IWbemCallResult** ppResult)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::CancelAsyncCall(IWbemObjectSink* pSink)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


STDMETHODIMP CUlPerfCounters::QueryObjectSink(long lFlags,IWbemObjectSink** ppResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


// Classes and instances.
// ======================

STDMETHODIMP CUlPerfCounters::GetObject(BSTR strObjectPath,long lFlags,IWbemContext* pCtx,IWbemClassObject** ppObject,IWbemCallResult** ppCallResult)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::GetObjectAsync(BSTR strObjectPath,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ 
    IWbemClassObject pObj;
    HRESULT Result;
    CObjectPathParser PathParser;
    ParsedObjectPath *pParsedPath = NULL;

    Result = WBEM_S_NO_ERROR;

    
    //
    // crack the object path
    //

    Result = PathParser.Parse(strObjectPath, &pParsedPath);
    if (Result != CObjectPathParser::NoError)
        return E_FAIL;

    //
    // create a perfctr object
    //

    pObject = new CComObject<CUlPerfCounterObject>;
    if (pObject == NULL)
    {
        Result = E_OUTOFMEMORY
        goto end;
    }

    //
    // init it
    //
    
    Result = pObject->Init(pParsedPath);
    if (FAILED(Result))
        goto end;

    //
    // and hand it off to wbem
    //
    
    Result = pGroup->_InternalQueryInterface(IID_IWbemClassObject, (PVOID*)&pWbemObj);
    if (FAILED(Result))
        goto end;
    
    Result = pHandler->Indicate(1, &pWbemObj);
    if (FAILED(Result))
        goto end;

end:
    if (pParsedPath != NULL)
    {
        PathParser.Free(pParsedPath);
        pParsedPath = NULL;
    }

    if (pWbemObj != NULL)
    {
        pWbemObj->Release();
        pWbemObj = NULL;
    }
    
    pHandler->SetStatus(0, Result, NULL, NULL);
    return Result; 
    
}

// Class manipulation.
// ===================

STDMETHODIMP CUlPerfCounters::PutClass(IWbemClassObject* pObject,long lFlags,IWbemContext* pCtx,IWbemCallResult** ppCallResult)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::PutClassAsync(IWbemClassObject* pObject,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


STDMETHODIMP CUlPerfCounters::DeleteClass(BSTR strClass,long lFlags,IWbemContext* pCtx,IWbemCallResult** ppCallResult)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::DeleteClassAsync(BSTR strClass,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


STDMETHODIMP CUlPerfCounters::CreateClassEnum(BSTR strSuperclass,long lFlags,IWbemContext* pCtx,IEnumWbemClassObject** ppEnum)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::CreateClassEnumAsync(BSTR strSuperclass,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


// Instances.
// ==========

STDMETHODIMP CUlPerfCounters::PutInstance(IWbemClassObject* pInst,long lFlags,IWbemContext* pCtx,IWbemCallResult** ppCallResult)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::PutInstanceAsync(IWbemClassObject* pInst,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


STDMETHODIMP CUlPerfCounters::DeleteInstance(BSTR strObjectPath,long lFlags,IWbemContext* pCtx,IWbemCallResult** ppCallResult)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::DeleteInstanceAsync(BSTR strObjectPath,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


STDMETHODIMP CUlPerfCounters::CreateInstanceEnum(BSTR strClass,long lFlags,IWbemContext* pCtx,IEnumWbemClassObject** ppEnum)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::CreateInstanceEnumAsync(BSTR strClass,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


// Queries.
// ========

STDMETHODIMP CUlPerfCounters::ExecQuery(BSTR strQueryLanguage,BSTR strQuery,long lFlags,IWbemContext* pCtx,IEnumWbemClassObject** ppEnum)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::ExecQueryAsync(BSTR strQueryLanguage,BSTR strQuery,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


STDMETHODIMP CUlPerfCounters::ExecNotificationQuery(BSTR strQueryLanguage,BSTR strQuery,long lFlags,IWbemContext* pCtx,IEnumWbemClassObject** ppEnum)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::ExecNotificationQueryAsync(BSTR strQueryLanguage,BSTR strQuery,long lFlags,IWbemContext* pCtx,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }


// Methods
// =======

STDMETHODIMP CUlPerfCounters::ExecMethod(BSTR strObjectPath,BSTR strMethodName,long lFlags,IWbemContext* pCtx,IWbemClassObject* pInParams,IWbemClassObject** ppOutParams,IWbemCallResult** ppCallResult)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }

STDMETHODIMP CUlPerfCounters::ExecMethodAsync(BSTR strObjectPath,BSTR strMethodName,long lFlags,IWbemContext* pCtx,IWbemClassObject* pInParams,IWbemObjectSink* pResponseHandler)
{ return WBEM_E_METHOD_NOT_IMPLEMENTED; }