/**********************************************************************/
/**                       Microsoft Windows NT                       **/
/**                Copyright(c) Microsoft Corp., 1993                **/
/**********************************************************************/

/*
    perfftp.cxx

    This file implements the Extensible Performance Objects for
    the FTP Server service.


    FILE HISTORY:
        KeithMo     07-Jun-1993 Created, based on RussBl's sample code.

        MuraliK     16-Nov-1995 Modified dependencies and removed NetApi
        SophiaC     06-Nov-1996 Supported mutlitiple instances
*/

#define INITGUID

#include <windows.h>
#include <winperf.h>
#include <lm.h>
#include <string.h>
#include <stdlib.h>
#include <ole2.h>

#include "iis64.h"
#include "dbgutil.h"
#include "iisinfo.h"
#include "ftpd.h"
#include "ftpctrs.h"
#include "ftpmsg.h"
#include "iadm.h"

extern "C" {
#include "perfutil.h"
#include "apiutil.h"
#include "ftpdata.h"
} // extern "C"

#define APP_NAME                     (TEXT("FTPCtrs"))
#define MAX_SIZEOF_INSTANCE_NAME     METADATA_MAX_NAME_LEN
#define TOTAL_INSTANCE_NAME          L"_Total"

//
//  Private globals.
//

DWORD   cOpens    = 0;                  // Active "opens" reference count.
BOOL    fInitOK   = FALSE;              // TRUE if DLL initialized OK.

HANDLE  hEventLog = NULL;               // event log handle

//
//  Public prototypes.
//

PM_OPEN_PROC    OpenFtpPerformanceData;
PM_COLLECT_PROC CollectFtpPerformanceData;
PM_CLOSE_PROC   CloseFtpPerformanceData;

//
//  Private prototypes.
//
VOID
CopyStatisticsData(
    IN FTP_STATISTICS_0         * pFTPStats,
    OUT FTPD_COUNTER_BLOCK      * pCounterBlock
    );

VOID
Update_TotalStatisticsData(
    IN FTPD_COUNTER_BLOCK       * pCounterBlock,
    OUT FTPD_COUNTER_BLOCK      * pTotal
    );

//
//  Public functions.
//

/*******************************************************************

    NAME:       OpenFtpPerformanceData

    SYNOPSIS:   Initializes the data structures used to communicate
                performance counters with the registry.

    ENTRY:      lpDeviceNames - Poitner to object ID of each device
                    to be opened.

    RETURNS:    DWORD - Win32 status code.

    HISTORY:
        KeithMo     07-Jun-1993 Created.

********************************************************************/
DWORD OpenFtpPerformanceData( LPWSTR lpDeviceNames )
{
    DWORD err  = NO_ERROR;
    HKEY  hkey = NULL;
    DWORD size;
    DWORD type;
    DWORD dwFirstCounter;
    DWORD dwFirstHelp;
    PERF_COUNTER_DEFINITION * pctr;
    FTPD_COUNTER_BLOCK        ftpc;
    DWORD                     i;

    //
    //  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( !fInitOK )
    {

        //
        //  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
            if (hEventLog == NULL)
            {
                return GetLastError();
            }
        }

        //
        //  Open the FTP Server service's Performance key.
        //

        err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                            FTPD_PERFORMANCE_KEY,
                            0,
                            KEY_READ,
                            &hkey );

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

            size = sizeof(DWORD);

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

                size = sizeof(DWORD);

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

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

                    FtpdDataDefinition.FtpdObjectType.ObjectNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdObjectType.ObjectHelpTitleIndex
                        += dwFirstHelp;

                    FtpdDataDefinition.FtpdBytesSent.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdBytesSent.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdBytesSent.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.BytesSent - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdBytesReceived.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdBytesReceived.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdBytesReceived.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.BytesReceived - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdBytesTotal.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdBytesTotal.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdBytesTotal.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.BytesTotal - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdFilesSent.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdFilesSent.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdFilesSent.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.FilesSent - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdFilesReceived.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdFilesReceived.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdFilesReceived.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.FilesReceived - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdFilesTotal.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdFilesTotal.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdFilesTotal.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.FilesTotal - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdCurrentAnonymous.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdCurrentAnonymous.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdCurrentAnonymous.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.CurrentAnonymous - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdCurrentNonAnonymous.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdCurrentNonAnonymous.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdCurrentNonAnonymous.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.CurrentNonAnonymous - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdTotalAnonymous.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdTotalAnonymous.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdTotalAnonymous.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.TotalAnonymous - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdTotalNonAnonymous.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdTotalNonAnonymous.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdTotalNonAnonymous.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.TotalNonAnonymous - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdMaxAnonymous.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdMaxAnonymous.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdMaxAnonymous.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.MaxAnonymous - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdMaxNonAnonymous.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdMaxNonAnonymous.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdMaxNonAnonymous.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.MaxNonAnonymous - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdCurrentConnections.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdCurrentConnections.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdCurrentConnections.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.CurrentConnections - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdMaxConnections.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdMaxConnections.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdMaxConnections.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.MaxConnections - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdConnectionAttempts.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdConnectionAttempts.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdConnectionAttempts.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.ConnectionAttempts - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdLogonAttempts.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdLogonAttempts.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdLogonAttempts.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.LogonAttempts - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdServiceUptime.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdServiceUptime.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdServiceUptime.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.ServiceUptime - (LPBYTE)&ftpc);

// These counters are currently meaningless, but should be restored if we
// ever enable per-FTP-instance bandwidth throttling.
/*
                    FtpdDataDefinition.FtpdBlockedRequests.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdBlockedRequests.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdBlockedRequests.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.BlockedRequests - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdAllowedRequests.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdAllowedRequests.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdAllowedRequests.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.AllowedRequests - (LPBYTE)&ftpc);


                    FtpdDataDefinition.FtpdRejectedRequests.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdRejectedRequests.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdRejectedRequests.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.RejectedRequests - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdCurrentBlockedRequests.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdCurrentBlockedRequests.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdCurrentBlockedRequests.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.CurrentBlockedRequests - (LPBYTE)&ftpc);

                    FtpdDataDefinition.FtpdMeasuredBandwidth.CounterNameTitleIndex
                        += dwFirstCounter;
                    FtpdDataDefinition.FtpdMeasuredBandwidth.CounterHelpTitleIndex
                        += dwFirstHelp;
                    FtpdDataDefinition.FtpdMeasuredBandwidth.CounterOffset =
                        (DWORD)((LPBYTE)&ftpc.MeasuredBandwidth - (LPBYTE)&ftpc);

*/

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

                    fInitOK = TRUE;
                } else {
                    ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE,
                                 0, FTP_UNABLE_QUERY_DATA,
                                 (PSID)NULL, 0,
                                 sizeof(err), NULL,
                                 (PVOID)(&err));
                }
            } else {
                ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE,
                             0, FTP_UNABLE_QUERY_DATA,
                             (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, FTP_UNABLE_QUERY_DATA,
                         (PSID)NULL, 0,
                         sizeof(err), NULL,
                         (PVOID)(&err));
        }
    }

    //
    //  Bump open counter.
    //

    InterlockedIncrement((LPLONG )&cOpens);

    return err;

}   // OpenFTPPerformanceData

/*******************************************************************

    NAME:       CollectFtpPerformanceData

    SYNOPSIS:   Initializes the data structures used to communicate

    ENTRY:      lpValueName - The name of the value to retrieve.

                lppData - On entry contains a pointer to the buffer to
                    receive the completed PerfDataBlock & subordinate
                    structures.  On exit, points to the first bytes
                    *after* the data structures added by this routine.

                lpcbTotalBytes - On entry contains a pointer to the
                    size (in BYTEs) of the buffer referenced by lppData.
                    On exit, contains the number of BYTEs added by this
                    routine.

                lpNumObjectTypes - Receives the number of objects added
                    by this routine.

    RETURNS:    DWORD - Win32 status code.  MUST be either NO_ERROR
                    or ERROR_MORE_DATA.

    HISTORY:
        KeithMo     07-Jun-1993 Created.

********************************************************************/
DWORD CollectFtpPerformanceData( LPWSTR    lpValueName,
                                 LPVOID  * lppData,
                                 LPDWORD   lpcbTotalBytes,
                                 LPDWORD   lpNumObjectTypes )
{
    PERF_INSTANCE_DEFINITION * pPerfInstanceDefinition;
    DWORD                   dwInstanceIndex = 0;
    DWORD                   dwInstanceCount = 0;
    DWORD                   i = 0;
    DWORD                   dwQueryType;
    ULONG                   cbRequired;
    DWORD                   * pdwCounter;
    LARGE_INTEGER           * pliCounter;
    FTPD_COUNTER_BLOCK      * pCounterBlock;
    FTPD_COUNTER_BLOCK      * pTotal;
    FTPD_DATA_DEFINITION    * pFtpdDataDefinition;
    FTP_STATISTICS_0        * pFTPStats;
    NET_API_STATUS          neterr;
    HRESULT                 hresErr;
    DWORD                   dwBufferSize = 0;

    LPINET_INFO_SITE_LIST   pSites;


    //
    //  No need to even try if we failed to open...
    //

    if( !fInitOK )
    {

        *lpcbTotalBytes   = 0;
        *lpNumObjectTypes = 0;

        //
        //  According to the Performance Counter design, this
        //  is a successful exit.  Go figure.
        //

        return NO_ERROR;
    }

    //
    //  Determine the query type.
    //

    dwQueryType = GetQueryType( lpValueName );

    if (( dwQueryType == QUERY_FOREIGN ) || (dwQueryType == QUERY_COSTLY))
    {
        //
        //  We don't do foreign queries.
        //

        *lpcbTotalBytes   = 0;
        *lpNumObjectTypes = 0;

        return NO_ERROR;
    }

    if( dwQueryType == QUERY_ITEMS )
    {
        //
        //  The registry is asking for a specific object.  Let's
        //  see if we're one of the chosen.
        //

        if( !IsNumberInUnicodeList(
                        FtpdDataDefinition.FtpdObjectType.ObjectNameTitleIndex,
                        lpValueName ) )
        {
            *lpcbTotalBytes   = 0;
            *lpNumObjectTypes = 0;

            return NO_ERROR;
        }
    }

    //
    //  Enumerate and get total number of instances count.
    //

    neterr = InetInfoGetSites(
                NULL,
                INET_FTP_SVC_ID,
                &pSites
                );
    

    if( neterr != NERR_Success )
    {
        //
        //  Only event log once for each server down
        //

        // if the server is down, we don't log an error.
		if ( !( neterr == RPC_S_SERVER_UNAVAILABLE ||
                neterr == RPC_S_UNKNOWN_IF         ||
                neterr == ERROR_SERVICE_NOT_ACTIVE ||
                neterr == RPC_S_CALL_FAILED_DNE ))
        {
            //
            //  Error retrieving statistics.
            //
            
            ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE,
                        0, FTP_UNABLE_QUERY_DATA,
                        (PSID)NULL, 0,
                        sizeof(neterr), NULL,
                        (PVOID)(&neterr));

        }

        *lpcbTotalBytes   = 0;
        *lpNumObjectTypes = 0;

        return NO_ERROR;
    }
        
    //
    //  add 1 to dwInstanceCount for _Total instance
    //

    dwInstanceCount = pSites->cEntries + 1;

    //
    //  always return an "instance sized" buffer after the definition
    //  blocks to prevent perfmon from reading bogus data. This is strictly
    //  a hack to accomodate how PERFMON handles the "0" instance case.
    //  By doing this, perfmon won't choke when there are no instances
    //  and the counter object & counters will be displayed in the list
    //  boxes, even though no instances will be listed.
    //

    pFtpdDataDefinition = (FTPD_DATA_DEFINITION *)*lppData;

    cbRequired = sizeof(FTPD_DATA_DEFINITION) +
                 (dwInstanceCount * (sizeof(PERF_INSTANCE_DEFINITION) +
                 MAX_SIZEOF_INSTANCE_NAME +
                 sizeof (FTPD_COUNTER_BLOCK)));

    //
    //  See if there's enough space.
    //

    if( *lpcbTotalBytes < cbRequired )
    {
        //
        //  Nope.
        //

        *lpcbTotalBytes   = 0;
        *lpNumObjectTypes = 0;

        MIDL_user_free(pSites);
        return ERROR_MORE_DATA;
    }

    //
    //  Copy the (constant, initialized) Object Type and counter definitions
    //  to the caller's data buffer
    //

    memmove( pFtpdDataDefinition,
             &FtpdDataDefinition,
             sizeof(FTPD_DATA_DEFINITION) );

    //
    //  Create data for return for each instance
    //

    pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
                                  &pFtpdDataDefinition[1];

    //
    // Set first block of Buffer for _Total
    //

    MonBuildInstanceDefinition(
        pPerfInstanceDefinition,
        (PVOID *)&pCounterBlock,
        0,
        0,
        (DWORD)-1, // use name
        TOTAL_INSTANCE_NAME );   // pass in instance name

    pTotal = pCounterBlock;
    memset( pTotal, 0, sizeof(FTPD_COUNTER_BLOCK ));
    pTotal->PerfCounterBlock.ByteLength = sizeof (FTPD_COUNTER_BLOCK);
    pPerfInstanceDefinition =
        (PERF_INSTANCE_DEFINITION *)((LPBYTE)pCounterBlock +
         sizeof(FTPD_COUNTER_BLOCK));


    neterr = FtpQueryStatistics2(
                            NULL,
                            0,
                            0,  // instance id, 0 for global stats
                            0,
                            (LPBYTE *)&pFTPStats );

    if( neterr == NERR_Success )
    {
        pTotal->ServiceUptime = pFTPStats->ServiceUptime;
    }

    MIDL_user_free( pFTPStats );

    for ( i = 0; i < pSites->cEntries; i++)
    {
        MonBuildInstanceDefinition(
            pPerfInstanceDefinition,
            (PVOID *)&pCounterBlock,
            0,
            0,
            (DWORD)-1,                          // use name
            pSites->aSiteEntry[i].pszComment    // pass in instance name
            );

        //
        // query for statistics info
        //

        neterr = FtpQueryStatistics2(
                            NULL,
                            0,
                            pSites->aSiteEntry[i].dwInstance,  // instance id
                            0,
                            (LPBYTE *)&pFTPStats );

        if( neterr != NERR_Success )
        {
            //
            //  Only event log once for each server down
            //

            // if the server is down, we don't log an error.
	        if ( !( neterr == RPC_S_SERVER_UNAVAILABLE ||
                    neterr == RPC_S_UNKNOWN_IF         ||
                    neterr == ERROR_SERVICE_NOT_ACTIVE ||
                    neterr == RPC_S_CALL_FAILED_DNE ))
            {
                //
                //  Error retrieving statistics.
                //
                ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE,
                    0, FTP_UNABLE_QUERY_DATA,
                    (PSID)NULL, 0,
                    sizeof(neterr), NULL,
                    (PVOID)(&neterr));
            }

            *lpcbTotalBytes   = 0;
            *lpNumObjectTypes = 0;

            MIDL_user_free(pSites);
            return NO_ERROR;
        }

        //
        //  Format the FTP Server data.
        //

        CopyStatisticsData( pFTPStats,
                            pCounterBlock );

        //
        //  update _total instance counters
        //

        Update_TotalStatisticsData( pCounterBlock,
                                    pTotal );

        pPerfInstanceDefinition =
            (PERF_INSTANCE_DEFINITION *)((LPBYTE)pCounterBlock +
             sizeof(FTPD_COUNTER_BLOCK));

        //
        //  Free the API buffer.
        //

        MIDL_user_free( pFTPStats );
    }

    if (dwInstanceCount == 1) {

        //
        //  zero fill one instance sized block of data if there's no data
        //  instances
        //

        memset (pPerfInstanceDefinition, 0,
            (sizeof(PERF_INSTANCE_DEFINITION) +
            MAX_SIZEOF_INSTANCE_NAME +
            sizeof(FTPD_COUNTER_BLOCK)));

        // adjust pointer to point to end of zeroed block
        pPerfInstanceDefinition += (sizeof(PERF_INSTANCE_DEFINITION) +
            MAX_SIZEOF_INSTANCE_NAME +
            sizeof(FTPD_COUNTER_BLOCK));
    }

    //
    //  Update arguments for return.
    //

    *lppData          = (PVOID)(pPerfInstanceDefinition);

    *lpNumObjectTypes = 1;

    pFtpdDataDefinition->FtpdObjectType.NumInstances = dwInstanceCount;

    pFtpdDataDefinition->FtpdObjectType.TotalByteLength =
        *lpcbTotalBytes   = DIFF((PBYTE)pPerfInstanceDefinition -
                                 (PBYTE)pFtpdDataDefinition);

    //
    //  Success!  Honest!!
    //

    MIDL_user_free(pSites);
    
    return NO_ERROR;

}   // CollectFTPPerformanceData

/*******************************************************************

    NAME:       CloseFtpPerformanceData

    SYNOPSIS:   Terminates the performance counters.

    RETURNS:    DWORD - Win32 status code.

    HISTORY:
        KeithMo     07-Jun-1993 Created.

********************************************************************/
DWORD CloseFtpPerformanceData( VOID )
{

    DWORD dwCount = InterlockedDecrement((LPLONG )&cOpens);
    
    if ((dwCount) == 0) {
        if (hEventLog != NULL) 
        { 
            DeregisterEventSource (hEventLog);
            hEventLog = NULL;
        };
    }

    return NO_ERROR;

}   // CloseFTPPerformanceData


VOID
CopyStatisticsData(
    IN FTP_STATISTICS_0          * pFTPStats,
    OUT FTPD_COUNTER_BLOCK       * pCounterBlock
    )
{
    //
    //  Format the FTP Server data.
    //

    pCounterBlock->PerfCounterBlock.ByteLength = sizeof (FTPD_COUNTER_BLOCK);

    pCounterBlock->BytesSent       = pFTPStats->TotalBytesSent.QuadPart;
    pCounterBlock->BytesReceived   = pFTPStats->TotalBytesReceived.QuadPart;
    pCounterBlock->BytesTotal      = pFTPStats->TotalBytesSent.QuadPart +
                                     pFTPStats->TotalBytesReceived.QuadPart;

    pCounterBlock->FilesSent        = pFTPStats->TotalFilesSent;
    pCounterBlock->FilesReceived    = pFTPStats->TotalFilesReceived;
    pCounterBlock->FilesTotal       = pFTPStats->TotalFilesSent +
                                      pFTPStats->TotalFilesReceived;

    pCounterBlock->CurrentAnonymous = pFTPStats->CurrentAnonymousUsers;
    pCounterBlock->CurrentNonAnonymous = pFTPStats->CurrentNonAnonymousUsers;

    pCounterBlock->TotalAnonymous   = pFTPStats->TotalAnonymousUsers;
    pCounterBlock->TotalNonAnonymous = pFTPStats->TotalNonAnonymousUsers;

    pCounterBlock->MaxAnonymous     = pFTPStats->MaxAnonymousUsers;
    pCounterBlock->MaxNonAnonymous  = pFTPStats->MaxNonAnonymousUsers;

    pCounterBlock->CurrentConnections = pFTPStats->CurrentConnections;
    pCounterBlock->MaxConnections   = pFTPStats->MaxConnections;
    pCounterBlock->ConnectionAttempts = pFTPStats->ConnectionAttempts;

    pCounterBlock->LogonAttempts    = pFTPStats->LogonAttempts;

    pCounterBlock->ServiceUptime    = pFTPStats->ServiceUptime;

// These counters are currently meaningless, but should be restored if we
// ever enable per-FTP-instance bandwidth throttling.
/*
    pCounterBlock->BlockedRequests  = pFTPStats->TotalBlockedRequests;
    pCounterBlock->AllowedRequests  = pFTPStats->TotalAllowedRequests;
    pCounterBlock->RejectedRequests = pFTPStats->TotalRejectedRequests;
    pCounterBlock->MeasuredBandwidth= pFTPStats->MeasuredBandwidth;
    pCounterBlock->CurrentBlockedRequests = pFTPStats->CurrentBlockedRequests;
*/
}   // CopyStatisticsData


VOID
Update_TotalStatisticsData(
    IN FTPD_COUNTER_BLOCK        * pCounterBlock,
    OUT FTPD_COUNTER_BLOCK       * pTotal
    )
{
    //
    //  update _total instance counters
    //

    pTotal->BytesSent += pCounterBlock->BytesSent;
    pTotal->BytesReceived += pCounterBlock->BytesReceived;
    pTotal->BytesTotal += pCounterBlock->BytesTotal;

    pTotal->FilesSent += pCounterBlock->FilesSent;
    pTotal->FilesReceived += pCounterBlock->FilesReceived;
    pTotal->FilesTotal += pCounterBlock->FilesTotal;
    pTotal->CurrentAnonymous += pCounterBlock->CurrentAnonymous;
    pTotal->CurrentNonAnonymous += pCounterBlock->CurrentNonAnonymous;
    pTotal->TotalAnonymous += pCounterBlock->TotalAnonymous;
    pTotal->TotalNonAnonymous += pCounterBlock->TotalNonAnonymous;

    pTotal->MaxAnonymous += pCounterBlock->MaxAnonymous;
    pTotal->MaxNonAnonymous += pCounterBlock->MaxNonAnonymous;

    pTotal->CurrentConnections += pCounterBlock->CurrentConnections;
    pTotal->MaxConnections += pCounterBlock->MaxConnections;
    pTotal->ConnectionAttempts = pCounterBlock->ConnectionAttempts;

    pTotal->LogonAttempts += pCounterBlock->LogonAttempts;

// These counters are currently meaningless, but should be restored if we
// ever enable per-FTP-instance bandwidth throttling.
/*
    pTotal->BlockedRequests += pCounterBlock->BlockedRequests;
    pTotal->RejectedRequests += pCounterBlock->RejectedRequests;
    pTotal->AllowedRequests += pCounterBlock->AllowedRequests;
    pTotal->MeasuredBandwidth += pCounterBlock->MeasuredBandwidth;
    pTotal->CurrentBlockedRequests += pCounterBlock->CurrentBlockedRequests;
*/

}   // Update_TotalStatisticsData