/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    perfnet.c

Abstract:


Author:

    Bob Watson (a-robw) Aug 95

Revision History:

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winperf.h>
#include <ntprfctr.h>
#include <assert.h>
#include <perfutil.h>
#include "perfnet.h"
#include "netsvcmc.h"

// bit field definitions for collect function flags

#define POS_COLLECT_SERVER_DATA         ((DWORD)0x00000001)
#define POS_COLLECT_SERVER_QUEUE_DATA   ((DWORD)0x00000002)
#define POS_COLLECT_REDIR_DATA          ((DWORD)0x00000004)
#define POS_COLLECT_BROWSER_DATA        ((DWORD)0x00000008)

#define POS_COLLECT_GLOBAL_DATA         ((DWORD)0x0000000F)
#define POS_COLLECT_FOREIGN_DATA        ((DWORD)0)
#define POS_COLLECT_COSTLY_DATA         ((DWORD)0)

// global variables to this DLL

HANDLE  ThisDLLHandle = NULL;
HANDLE  hEventLog     = NULL;
HANDLE  hLibHeap      = NULL;

// variables local to this module

static POS_FUNCTION_INFO    posDataFuncInfo[] = {
    {SERVER_OBJECT_TITLE_INDEX,         POS_COLLECT_SERVER_DATA,    0, CollectServerObjectData},
    {SERVER_QUEUE_OBJECT_TITLE_INDEX,   POS_COLLECT_SERVER_QUEUE_DATA,     0, CollectServerQueueObjectData},
    {REDIRECTOR_OBJECT_TITLE_INDEX,     POS_COLLECT_REDIR_DATA,     0, CollectRedirObjectData},
    {BROWSER_OBJECT_TITLE_INDEX,        POS_COLLECT_BROWSER_DATA,   0, CollectBrowserObjectData}
};

#define POS_NUM_FUNCS   (sizeof(posDataFuncInfo) / sizeof(posDataFuncInfo[1]))

static  bInitOk  = FALSE;
static  DWORD   dwOpenCount = 0;

static  BOOL    bReportedNotOpen = FALSE;

PM_OPEN_PROC    OpenNetSvcsObject;
PM_COLLECT_PROC CollecNetSvcsObjectData;
PM_CLOSE_PROC   CloseNetSvcsObject;

static
BOOL
DllProcessAttach (
    IN  HANDLE DllHandle
)
/*++

Description:

    perform any initialization function that apply to all object
    modules
   
--*/
{
    BOOL    bReturn = TRUE;

    UNREFERENCED_PARAMETER (DllHandle);

    // create heap for this library
    if (hLibHeap == NULL) hLibHeap = HeapCreate (0, 1, 0);

    assert (hLibHeap != NULL);

    if (hLibHeap == NULL) {
        return FALSE;
    }
    // open handle to the event log
    if (hEventLog == NULL) hEventLog = MonOpenEventLog((LPWSTR)L"PerfNet");
    assert (hEventLog != NULL);

    return bReturn;
}

static
BOOL
DllProcessDetach (
    IN  HANDLE DllHandle
)
{
    UNREFERENCED_PARAMETER (DllHandle);

    if (dwOpenCount != 0) {
        // make sure the object has been closed before the
        // library is deleted.
        // setting dwOpenCount to 1 insures that all
        // the objects will be closed on this call
        if (dwOpenCount > 1) dwOpenCount = 1;
        CloseNetSvcsObject();
        dwOpenCount = 0;
    }

    if (hLibHeap != NULL) {
        HeapDestroy (hLibHeap); 
        hLibHeap = NULL;
    }

    if (hEventLog != NULL) {
        MonCloseEventLog ();
        hEventLog = NULL;
    }
    return TRUE;
}

BOOL
__stdcall
DllInit(
    IN HANDLE DLLHandle,
    IN DWORD  Reason,
    IN LPVOID ReservedAndUnused
)
{
    ReservedAndUnused;

    // this will prevent the DLL from getting
    // the DLL_THREAD_* messages
    DisableThreadLibraryCalls (DLLHandle);

    switch(Reason) {
        case DLL_PROCESS_ATTACH:
            return DllProcessAttach (DLLHandle);

        case DLL_PROCESS_DETACH:
            return DllProcessDetach (DLLHandle);

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        default:
            return TRUE;
    }
}

DWORD APIENTRY
OpenNetSvcsObject (
    LPWSTR lpDeviceNames
    )
/*++

Routine Description:

    This routine will initialize the data structures used to pass
    data back to the registry

Arguments:

    Pointer to object ID of each device to be opened (PerfGen)

Return Value:

    None.

--*/
{
    DWORD   status = ERROR_SUCCESS;
    DWORD   dwErrorCount = 0;

    if (dwOpenCount == 0) {

        status = OpenServerObject (lpDeviceNames);
        // if this didn't open, it's not fatal, just no 
        // server stats will be returned
        if (status != ERROR_SUCCESS) {
            dwErrorCount++;
            status = ERROR_SUCCESS;
        }

        status = OpenServerQueueObject (lpDeviceNames);
        // if this didn't open, it's not fatal, just no 
        // server queue stats will be returned
        if (status != ERROR_SUCCESS) {
            dwErrorCount++;
            status = ERROR_SUCCESS;
        }

        status = OpenRedirObject (lpDeviceNames);
        // if this didn't open, it's not fatal, just no 
        // Redir stats will be returned
        if (status != ERROR_SUCCESS) {
            dwErrorCount++;
            status = ERROR_SUCCESS;
        }

        status = OpenBrowserObject (lpDeviceNames);
        // if this didn't open, it's not fatal, just no 
        // Browser stats will be returned
        if (status != ERROR_SUCCESS) {
            dwErrorCount++;
            status = ERROR_SUCCESS;
        }

        if (dwErrorCount < POS_NUM_FUNCS) {
            // then at least one object opened OK so continue
            bInitOk = TRUE;
            dwOpenCount++;
        } else {
            // none of the objects opened, so give up.
            ReportEvent (hEventLog,
                EVENTLOG_ERROR_TYPE,
                0,
                PERFNET_UNABLE_OPEN,
                NULL,
                0,
                sizeof(DWORD),
                NULL,
                (LPVOID)&status);
        }
    } else {
        // already opened so bump the refcount
        dwOpenCount++;
    }

    return  status;
}

DWORD APIENTRY
CollectNetSvcsObjectData (
    IN      LPWSTR  lpValueName,
    IN OUT  LPVOID  *lppData,
    IN OUT  LPDWORD lpcbTotalBytes,
    IN OUT  LPDWORD lpNumObjectTypes
)
/*++

Routine Description:

    This routine will return the data for the processor object

Arguments:

   IN OUT   LPVOID   *lppData
         IN: pointer to the address of the buffer to receive the completed
            PerfDataBlock and subordinate structures. This routine will
            append its data to the buffer starting at the point referenced
            by *lppData.
         OUT: points to the first byte after the data structure added by this
            routine. This routine updated the value at lppdata after appending
            its data.

   IN OUT   LPDWORD  lpcbTotalBytes
         IN: the address of the DWORD that tells the size in bytes of the
            buffer referenced by the lppData argument
         OUT: the number of bytes added by this routine is writted to the
            DWORD pointed to by this argument

   IN OUT   LPDWORD  NumObjectTypes
         IN: the address of the DWORD to receive the number of objects added
            by this routine
         OUT: the number of objects added by this routine is writted to the
            DWORD pointed to by this argument

   Returns:

             0 if successful, else Win 32 error code of failure

--*/
{
    LONG    lReturn = ERROR_SUCCESS;

    // build bit mask of functions to call

    DWORD       dwQueryType;
    DWORD       FunctionCallMask = 0;
    DWORD       FunctionIndex;

    DWORD       dwNumObjectsFromFunction;
    DWORD       dwOrigBuffSize;
    DWORD       dwByteSize;

    if (!bInitOk) {
        if (!bReportedNotOpen) {
            bReportedNotOpen = ReportEvent (hEventLog,
                EVENTLOG_ERROR_TYPE,
                0,
                PERFNET_NOT_OPEN,
                NULL,
                0,
                0,
                NULL,
                NULL);
        }
        *lpcbTotalBytes = (DWORD) 0;
        *lpNumObjectTypes = (DWORD) 0;
        lReturn = ERROR_SUCCESS;
        goto COLLECT_BAIL_OUT;
    }

    dwQueryType = GetQueryType (lpValueName);

    switch (dwQueryType) {
        case QUERY_ITEMS:
            for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
                if (IsNumberInUnicodeList (
                    posDataFuncInfo[FunctionIndex].dwObjectId, lpValueName)) {
                    FunctionCallMask |=
                        posDataFuncInfo[FunctionIndex].dwCollectFunctionBit;
                }
            }
            break;

        case QUERY_GLOBAL:
            FunctionCallMask = POS_COLLECT_GLOBAL_DATA;
            break;

        case QUERY_FOREIGN:
            FunctionCallMask = POS_COLLECT_FOREIGN_DATA;
            break;

        case QUERY_COSTLY:
            FunctionCallMask = POS_COLLECT_COSTLY_DATA;
            break;

        default:
            FunctionCallMask = POS_COLLECT_COSTLY_DATA;
            break;
    }

    // collect data 
    *lpNumObjectTypes = 0;
    dwOrigBuffSize = dwByteSize = *lpcbTotalBytes;
    *lpcbTotalBytes = 0;

    for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
        if (posDataFuncInfo[FunctionIndex].dwCollectFunctionBit &
            FunctionCallMask) {
            dwNumObjectsFromFunction = 0;
            lReturn = (*posDataFuncInfo[FunctionIndex].pCollectFunction) (
                lppData,
                &dwByteSize,
                &dwNumObjectsFromFunction);

            if (lReturn == ERROR_SUCCESS) {
                *lpNumObjectTypes += dwNumObjectsFromFunction;
                *lpcbTotalBytes += dwByteSize;
                dwOrigBuffSize -= dwByteSize;
                dwByteSize = dwOrigBuffSize;
            } else {
                break;
            }
        }
    }

    // *lppData is updated by each function
    // *lpcbTotalBytes is updated after each successful function
    // *lpNumObjects is updated after each successful function

COLLECT_BAIL_OUT:
    
    return lReturn;
}

DWORD APIENTRY
CloseNetSvcsObject (
)
/*++

Routine Description:

    This routine closes the open objects for the net services counters.

Arguments:

    None.


Return Value:

    ERROR_SUCCESS

--*/

{
    if (dwOpenCount > 0) {
        dwOpenCount--;
    }
    if (dwOpenCount == 0) {
        // close stuff here
        CloseServerQueueObject();
        CloseServerObject();
        CloseRedirObject();
        CloseBrowserObject();
    }
    return  ERROR_SUCCESS;
}
