/*++

   Copyright    (c)    1996    Microsoft Corporation

   Module  Name :

        iisadmin.cxx

   Abstract:

        Contains the admin functions of the IIS_SERVICE class

   Author:

        Johnson Apacible            (JohnsonA)      24-June-1996

--*/

#include "tcpdllp.hxx"
#include <rpc.h>
#include <tsunami.hxx>
#include <iistypes.hxx>
#include "inetreg.h"
#include "tcpcons.h"
#include "apiutil.h"

BOOL
PopulateSiteArray(
    LPINET_INFO_SITE_LIST * ppSites,
    PVOID    pvUnused,
    IN IIS_SERVER_INSTANCE * pInst
    );

PIIS_SERVICE
IIS_SERVICE::FindFromServiceInfoList(
                IN DWORD dwServiceId
                )
/*++
    Description:

        Finds a given Services Info object from the global list.

    Arguments:

        dwServiceId - Service id of service to look for.

    Returns:
        pointer to service object, if found.
        NULL, otherwise.

--*/
{
    PLIST_ENTRY  listEntry;
    PIIS_SERVICE pInetSvc;

    //
    //  Loop through the list of running internet servers and call the callback
    //  for each server that has one of the service id bits set
    //

    AcquireGlobalLock( );
    for ( listEntry  = sm_ServiceInfoListHead.Flink;
          listEntry != &sm_ServiceInfoListHead;
          listEntry  = listEntry->Flink ) {

        pInetSvc = CONTAINING_RECORD(
                                listEntry,
                                IIS_SERVICE,
                                m_ServiceListEntry );

        if ( dwServiceId == pInetSvc->QueryServiceId() &&
             (pInetSvc->QueryCurrentServiceState() == SERVICE_RUNNING ||
             pInetSvc->QueryCurrentServiceState() == SERVICE_PAUSED ) ) {

            //
            // reference and return
            //

            if ( !pInetSvc->CheckAndReference( ) ) {
                IF_DEBUG( INSTANCE ) {
                    DBGPRINTF((DBG_CONTEXT,
                        "Failed to reference service %d\n", dwServiceId));
                }
                pInetSvc = NULL;
            }

            ReleaseGlobalLock( );
            return pInetSvc;
        }
    }

    ReleaseGlobalLock( );

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "FindFromServiceList cannot find service %d\n", dwServiceId));
    }

    return NULL;

} // IIS_SERVICE::FindFromServiceInfoList



BOOL
IIS_SERVICE::SetServiceAdminInfo(
        IN DWORD        dwLevel,
        IN DWORD        dwServiceId,
        IN DWORD        dwInstance,
        IN BOOL         fCommonConfig,
        IN INETA_CONFIG_INFO * pConfigInfo
        )
/*++
    Description:

        Sets the service configuration.

    Arguments:

        dwLevel - Info level
        dwServiceId - ID of service to set
        dwInstance - ID of instance to set
        fCommonConfig - Determines if we should set the common or the service
            configuration
        pConfigInfo - Configuration structure

    Returns:
        TRUE on sucess and FALSE if there is a failure

--*/
{
    BOOL          fRet = TRUE;
    PIIS_SERVICE  pInetSvc;

    DBG_ASSERT( IIS_SERVICE::sm_fInitialized);

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "SetServiceAdmin called for svc %d inst %d\n",
                dwServiceId, dwInstance));
    }

    pInetSvc = FindFromServiceInfoList( dwServiceId );
    if ( pInetSvc == NULL ) {
        SetLastError( ERROR_SERVICE_NOT_ACTIVE);
        fRet = FALSE;
        goto exit;
    }

    //
    // Set the parameters and update
    //

    fRet = pInetSvc->SetInstanceConfiguration(
                                    dwInstance,
                                    dwLevel,
                                    fCommonConfig,
                                    pConfigInfo );

    //
    // This was referenced in Find
    //

    pInetSvc->Dereference( );
exit:
    return fRet;
}  // IIS_SERVICE::SetServiceAdminInfo





BOOL
IIS_SERVICE::GetServiceAdminInfo(
        IN DWORD        dwLevel,
        IN DWORD        dwServiceId,
        IN DWORD        dwInstance,
        IN BOOL         fCommonConfig,
        OUT PDWORD      nRead,
        OUT LPINETA_CONFIG_INFO * ppConfigInfo
        )
/*++
    Description:

        Gets the service configuration.

    Arguments:

        dwLevel - Info level of this operation
        dwServiceId - ID of service to get
        dwInstance - ID of instance to get
        fCommonConfig - Determines if we should get the common or the service
            configuration
        pBuffer     - on return, will contains a pointer to
            the configuration block

    Returns:
        TRUE on sucess and FALSE if there is a failure

--*/
{
    BOOL fRet = FALSE;
    PIIS_SERVICE  pInetSvc;

    DBG_ASSERT( IIS_SERVICE::sm_fInitialized);

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "GetServiceAdmin called for svc %d inst %x\n",
                dwServiceId, dwInstance));
    }

    pInetSvc = FindFromServiceInfoList( dwServiceId );
    if ( pInetSvc == NULL ) {
        SetLastError( ERROR_SERVICE_NOT_ACTIVE);
        goto exit;
    }

    //
    // Get the params
    //

    fRet = pInetSvc->GetInstanceConfiguration(
                                        dwInstance,
                                        dwLevel,
                                        fCommonConfig,
                                        nRead,
                                        ppConfigInfo
                                        );

    //
    // This was referenced in Find
    //

    pInetSvc->Dereference( );

exit:
    return fRet;
}  // IIS_SERVICE::GetServiceAdminInfo



BOOL
IIS_SERVICE::GetServiceSiteInfo(
                    IN  DWORD                   dwServiceId,
                    OUT LPINET_INFO_SITE_LIST * ppSites
                    )
/*++
    Description:

        Gets the list of service instances.

    Arguments:

        dwServiceId - ID of service to get
        ppSites     - on return, will contain a pointer to
                      the array of sites

    Returns:
        TRUE on sucess and FALSE if there is a failure

--*/
{
    BOOL fRet = FALSE;
    PIIS_SERVICE  pInetSvc;
    DWORD cInstances = 0;
    LPINET_INFO_SITE_LIST pSites;
    
    DBG_ASSERT( IIS_SERVICE::sm_fInitialized);

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "GetServiceSiteInfo called for svc %d\n",
                dwServiceId));
    }

    pInetSvc = FindFromServiceInfoList( dwServiceId );
    
    if ( pInetSvc == NULL ) {
        SetLastError( ERROR_SERVICE_NOT_ACTIVE);
        return FALSE;
    }

    //
    // Get the params
    //

    pInetSvc->AcquireServiceLock(TRUE);

    cInstances = pInetSvc->QueryInstanceCount();

    //allocate enough memory to hold the Site Info Arrary
    
    pSites = (LPINET_INFO_SITE_LIST) 
                midl_user_allocate (sizeof(INET_INFO_SITE_LIST) + sizeof(INET_INFO_SITE_ENTRY)*cInstances);

    if (!pSites) {
    
        pInetSvc->ReleaseServiceLock(TRUE);

    	//This was referenced in Find
        pInetSvc->Dereference( );    
        
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }
        
    *ppSites = pSites;
    
    pSites->cEntries = 0;
    
    fRet = pInetSvc->EnumServiceInstances(
                                        (PVOID) ppSites,
                                        NULL,
                                        (PFN_INSTANCE_ENUM) PopulateSiteArray
                                        );
                                        
    if (!fRet) {
    
        for (DWORD i=0; i<pSites->cEntries; i++) {
            midl_user_free(pSites->aSiteEntry[i].pszComment);
        }
        
        midl_user_free(pSites);

        *ppSites = NULL;
    }
    
    pInetSvc->ReleaseServiceLock(TRUE);

    //This was referenced in Find
    
    pInetSvc->Dereference( );
                                        
    return fRet;
}  // IIS_SERVICE::GetServiceSiteInfo



BOOL
PopulateSiteArray(
    LPINET_INFO_SITE_LIST * ppSites,
    PVOID    pvUnused,
    IN IIS_SERVER_INSTANCE * pInst
    )
/*++
    Description:

        Fills the ppSites array with the instance id and site comment.

    Arguments:
        pvUnused    - not used
        pInst       - pointer to service instance
        ppSites     - on return, will contain a pointer to
                      the array of sites

    Returns:
        TRUE on sucess and FALSE if there is a failure

--*/
{
    DWORD cbComment = 0;
    LPINET_INFO_SITE_LIST pSites = *ppSites;



    //Set the instance ID
    pSites->aSiteEntry[pSites->cEntries].dwInstance = pInst->QueryInstanceId();


    //allocate memory for the site name
    cbComment = (strlen(pInst->QuerySiteName())+1) * sizeof(WCHAR);
    
    pSites->aSiteEntry[pSites->cEntries].pszComment = (LPWSTR)midl_user_allocate(cbComment);

    
    if (!(pSites->aSiteEntry[pSites->cEntries].pszComment)) 
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    //
    //  Need unicode conversion here
    //

    if (!MultiByteToWideChar(  CP_ACP,                                      // code page
                        MB_PRECOMPOSED,                                     // character-type options
                        pInst->QuerySiteName(),                             // address of string to map
                        -1,                                                 // number of bytes in string
                        pSites->aSiteEntry[pSites->cEntries].pszComment,    // address of wide-character buffer
                        cbComment / sizeof(WCHAR)                           // size of buffer
                      )) 
    {
        pSites->aSiteEntry[pSites->cEntries].pszComment[0] = L'\0';
    }

    pSites->cEntries++;

    return TRUE;

} //PopulateSiteArray


DWORD
IIS_SERVICE::GetNewInstanceId(
            VOID
            )
/*++
    Description:

        Returns a new instance ID.

    Arguments:

        None.

    Returns:

        A non zero dword containing the instance ID.
        0 if there was an error.

--*/
{
    DWORD  dwId;
    CHAR   regParamKey[MAX_PATH+1];
    MB     mb( (IMDCOM*) QueryMDObject()  );

    AcquireServiceLock( );

    for (; ; ) {

        dwId = ++m_maxInstanceId;

        if ( m_maxInstanceId > INET_INSTANCE_MAX ) {
            m_maxInstanceId = INET_INSTANCE_MIN;
        }

        //
        // Make sure the metapath does not exist
        //

        GetMDInstancePath( dwId, regParamKey );
        if ( !mb.Open( regParamKey ) ) {

            DBG_ASSERT(GetLastError() == ERROR_FILE_NOT_FOUND);
            if ( GetLastError() != ERROR_FILE_NOT_FOUND ) {
                DBGPRINTF((DBG_CONTEXT,
                    "Error %d trying to open %s.\n", GetLastError(), regParamKey ));

                dwId = 0;
            }
            break;
        }

    }

    ReleaseServiceLock( );

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT, "GetNewInstanceId returns %d\n", dwId));
    }

    return dwId;

} // IIS_SERVICE::GetNewInstanceId




BOOL
IIS_SERVICE::SetInstanceConfiguration(
    IN DWORD            dwInstance,
    IN DWORD            dwLevel,
    IN BOOL             fCommonConfig,
    IN LPINETA_CONFIG_INFO pConfig
    )
/*++
    Description:

        Set the configuration for the instance.

    Arguments:

        dwInstance - Instance number to find - IGNORED FOR SET!!
        dwLevel - level of information to set
        pCommonConfig - Whether info is common or service specific
        pConfig - pointer to the configuration block

    Returns:

        A dword containing the instance ID.
--*/
{
    BOOL fRet = FALSE;
    PLIST_ENTRY  listEntry;
    PIIS_SERVER_INSTANCE pInstance;

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "SetInstanceConfiguration called for instance %d\n", dwInstance));
    }

    //
    // Try to find this instance
    //

    AcquireServiceLock( TRUE );

    //
    //  Find the instance in the instance list
    //

    if ( dwInstance == INET_INSTANCE_ROOT ) {
        DBG_ASSERT( !"Explicit default instance no longer supported inherited from metabase)\n" );
        pInstance = NULL;
    } else {
        pInstance = FindIISInstance( QueryDownLevelInstance() );
    }

    if ( pInstance != NULL ) {
        goto found;
    }

    SetLastError(ERROR_FILE_NOT_FOUND);
    DBG_ASSERT(!fRet);
    goto exit;

found:

    if ( fCommonConfig ) {

        fRet = pInstance->SetCommonConfig( pConfig, TRUE);

    } else {

        fRet = pInstance->SetServiceConfig( (CHAR *) pConfig);
    }

exit:
    ReleaseServiceLock( TRUE );
    return fRet;

} // IIS_SERVICE::SetInstanceConfiguration


BOOL
IIS_SERVICE::GetInstanceConfiguration(
        IN DWORD dwInstance,
        IN DWORD dwLevel,
        IN BOOL fCommonConfig,
        OUT PDWORD nRead,
        OUT LPINETA_CONFIG_INFO * pBuffer
        )
/*++
    Description:

        Get the configuration for the instance.

    Arguments:

        dwInstance - Instance number to find - IGNORED!!
        dwLevel - level of information to get
        pCommonConfig - Whether info is common or service specific
        nRead - pointer to a DWORD where the number of entries to be
            returned is set.
        pBuffer - pointer to the configuration block

    Returns:

        A dword containing the instance ID.
--*/
{

    PLIST_ENTRY  listEntry;
    PIIS_SERVER_INSTANCE pInstance;
    DWORD nInstances = 1;
    DWORD nSize;
    PCHAR pConfig;

    //
    // Set and Get only work against the default instance
    //

    dwInstance = QueryDownLevelInstance();

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "GetInstanceConfiguration [%x][%d] called for instance %x L%d\n",
            this, fCommonConfig, dwInstance, dwLevel));
    }

    //
    // We support only 1
    //

    *nRead = 0;
    *pBuffer = NULL;

    if ( fCommonConfig ) {

        DBG_ASSERT( dwLevel == 1 );

        nSize = sizeof( INETA_CONFIG_INFO );

    } else {

        DWORD err;

        nSize = GetServiceConfigInfoSize(dwLevel);
        if ( nSize == 0 ) {
            SetLastError(ERROR_INVALID_LEVEL);
            return(FALSE);
        }
    }

    //
    // Try to find this instance
    //

    AcquireServiceLock( );

    if ( dwInstance == INET_INSTANCE_ALL ) {
        nInstances = QueryInstanceCount( );
        if ( nInstances == 0 ) {
            goto exit;
        }
        IF_DEBUG(INSTANCE) {
            DBGPRINTF((DBG_CONTEXT,"%d instances found\n",nInstances));
        }
    }

    *pBuffer = (LPINETA_CONFIG_INFO) MIDL_user_allocate(nInstances * nSize);
    if ( *pBuffer == NULL ) {
        goto error_exit;
    }

    ZeroMemory( *pBuffer, nInstances * nSize );

    //
    //  Loop through the list of running internet servers and call the callback
    //  for each server that has one of the service id bits set
    //

    pConfig = (CHAR *) *pBuffer;

    if ( dwInstance != INET_INSTANCE_ROOT ) {

        for ( listEntry  = m_InstanceListHead.Flink;
              listEntry != &m_InstanceListHead;
              listEntry  = listEntry->Flink ) {

            pInstance = CONTAINING_RECORD(
                                    listEntry,
                                    IIS_SERVER_INSTANCE,
                                    m_InstanceListEntry );

            if ( (dwInstance == pInstance->QueryInstanceId()) ||
                 (dwInstance == INET_INSTANCE_ALL)   ||
                 (dwInstance == INET_INSTANCE_FIRST) ) {

                if ( fCommonConfig ) {
                    if ( !pInstance->GetCommonConfig(pConfig,dwLevel) ) {
                        goto error_exit;
                    }
                } else {
                    if ( !pInstance->GetServiceConfig(pConfig,dwLevel) ) {
                        goto error_exit;
                    }
                }

                //
                // if not get all, return.
                //

                (*nRead)++;
                pConfig += nSize;

                if ( dwInstance != INET_INSTANCE_ALL ) {
                    break;
                }
            }
        }
    } else {

        DBG_ASSERT( !"Default instance no longer supported!\n" );
    }

exit:

    if ( *nRead == 0 ) {
        if ( dwInstance != INET_INSTANCE_ALL ) {
            SetLastError(ERROR_FILE_NOT_FOUND);
            goto error_exit;
        }
    }

    ReleaseServiceLock( );

    DBG_ASSERT(nInstances == *nRead);
    return TRUE;

error_exit:

    ReleaseServiceLock( );

    IF_DEBUG(ERROR) {
        DBGPRINTF((DBG_CONTEXT,"Error %x in GetInstanceConfiguration\n",
            GetLastError()));
    }

    if ( *pBuffer != NULL ) {
        MIDL_user_free(*pBuffer);
        *pBuffer = NULL;
    }
    *nRead = 0;
    return(FALSE);

} // IIS_SERVICE::GetInstanceConfiguration




PIIS_SERVER_INSTANCE
IIS_SERVICE::FindIISInstance(
    IN DWORD            dwInstance
    )
/*++
    Description:

        Find the instance
        *** Service lock assumed held ***

    Arguments:

        dwInstance - Instance number to find

    Returns:

        Pointer to the instance.
        NULL if not found.
--*/
{
    BOOL fRet = TRUE;
    PLIST_ENTRY  listEntry;
    PIIS_SERVER_INSTANCE pInstance;

    //
    //  Find the instance in the instance list
    //

    for ( listEntry  = m_InstanceListHead.Flink;
          listEntry != &m_InstanceListHead;
          listEntry  = listEntry->Flink ) {

        pInstance = CONTAINING_RECORD(
                                listEntry,
                                IIS_SERVER_INSTANCE,
                                m_InstanceListEntry );

        if ( (dwInstance == pInstance->QueryInstanceId()) ||
             (dwInstance == INET_INSTANCE_FIRST) ) {

            return pInstance;
        }
    }

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "FindIISInstance: Cannot find instance %d\n", dwInstance));
    }

    SetLastError(ERROR_FILE_NOT_FOUND);
    return(NULL);

} // IIS_SERVICE::FindIISInstance



BOOL
IIS_SERVICE::DeleteInstanceInfo(
                IN DWORD    dwInstance
                )
{
    PIIS_SERVER_INSTANCE pInstance;
    DWORD err = NO_ERROR;

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "DeleteInstanceInfo called for %d\n", dwInstance));
    }

    //
    // Find the instance and close it
    //

    AcquireServiceLock( TRUE );

    //
    //  Find the instance in the instance list
    //

    pInstance = FindIISInstance( dwInstance );

    if( pInstance == NULL ) {

        err = ERROR_FILE_NOT_FOUND;

    } else {

        //
        // Remove it from the list
        //

        RemoveEntryList( &pInstance->m_InstanceListEntry );
        m_nInstance--;

        //
        // Shut it down
        //

        pInstance->CloseInstance();

        //
        // Dereference it
        //

        pInstance->Dereference( );

    }

    ReleaseServiceLock( TRUE );

    if( err == NO_ERROR ) {

        return TRUE;

    }

    SetLastError( err );
    return FALSE;

} // IIS_SERVICE::DeleteInstanceInfo


BOOL
IIS_SERVICE::EnumerateInstanceUsers(
                    IN DWORD dwInstance,
                    OUT PDWORD nRead,
                    OUT PCHAR* pBuffer
                    )
{

    PIIS_SERVER_INSTANCE pInstance;
    BOOL    fRet;

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "EnumerateInstanceUsers called for service %x (Instance %d)\n",
                this, dwInstance ));
    }

    //
    // Find the instance
    //

    AcquireServiceLock( );

    //
    //  Find the instance in the instance list
    //

    pInstance = FindIISInstance( dwInstance );
    if ( pInstance != NULL ) {
        goto found;
    }

    ReleaseServiceLock( );
    SetLastError(ERROR_FILE_NOT_FOUND);
    return(FALSE);

found:

    fRet = pInstance->EnumerateUsers( pBuffer, nRead );
    ReleaseServiceLock( );

    return(fRet);

} // IIS_SERVICE::EnumerateInstanceUsers




BOOL
IIS_SERVICE::DisconnectInstanceUser(
                    IN DWORD dwInstance,
                    IN DWORD dwIdUser
                    )
{

    PIIS_SERVER_INSTANCE pInstance;
    BOOL    fRet;

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "DisconnectInstanceUsers called for service %x (Instance %d)\n",
                this, dwInstance ));
    }

    //
    // Find the instance
    //

    AcquireServiceLock( );

    //
    //  Find the instance in the instance list
    //

    pInstance = FindIISInstance( dwInstance );
    if ( pInstance != NULL ) {
        goto found;
    }

    ReleaseServiceLock( );
    SetLastError(ERROR_FILE_NOT_FOUND);
    return(FALSE);

found:

    fRet = pInstance->DisconnectUser( dwIdUser );
    ReleaseServiceLock( );

    return(fRet);

} // IIS_SERVICE::DisconnectInstanceUsers




BOOL
IIS_SERVICE::GetInstanceStatistics(
                IN DWORD    dwInstance,
                IN DWORD    dwLevel,
                OUT PCHAR*  pBuffer
                )
{

    PIIS_SERVER_INSTANCE pInstance;
    BOOL                 fRet;

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "GetInstanceStats called for service %x (Instance %d)\n",
                this, dwInstance ));
    }

    //
    // Find the instance and close it
    //

    AcquireServiceLock( TRUE);

    if (dwInstance == 0 ) 
    {
        fRet = GetGlobalStatistics(dwLevel,pBuffer);
    }
    else if ( dwInstance == INET_INSTANCE_GLOBAL)
    {
        //
        // We need to aggregate statistics across all instances
        //
        
        PLIST_ENTRY listEntry;
        PCHAR       pStats;
        BOOL        fFirst = TRUE;

        fRet        = FALSE;
        *pBuffer    = NULL;
        
        //
        // Loop through all the instances and add their counters
        //
            
        for ( listEntry  = m_InstanceListHead.Flink;
              listEntry != &m_InstanceListHead;
              listEntry  = listEntry->Flink) 
        {
        
            pInstance = CONTAINING_RECORD(
                                    listEntry,
                                    IIS_SERVER_INSTANCE,
                                    m_InstanceListEntry );

            pInstance->LockThisForRead();
            fRet = pInstance->GetStatistics(dwLevel, &pStats);
            pInstance->UnlockThis();
            
            if (fRet)
            {
                if (fFirst)
                {
                    //
                    // This is the first successful retrieval.
                    //

                    *pBuffer = pStats;
                    fFirst = FALSE;
                }
                else
                {
                    AggregateStatistics(*pBuffer, pStats);
                    MIDL_user_free(pStats);
                }
            }
        } 
    }
    else 
    {

        //
        //  Find the instance in the instance list
        //

        pInstance = FindIISInstance( dwInstance );
        if ( pInstance != NULL ) 
        {
            fRet = pInstance->GetStatistics(dwLevel,pBuffer);
        }
        else
        {
            SetLastError(ERROR_FILE_NOT_FOUND);
            fRet = FALSE;
        }
    }

    ReleaseServiceLock( TRUE);

    return(fRet);


} // IIS_SERVICE::GetInstanceStatistics


BOOL 
IIS_SERVICE::GetGlobalStatistics(
    IN DWORD dwLevel, 
    OUT PCHAR *pBuffer
    )
{
    return FALSE;
}   // IIS_SERVICE::GetGlobalStatistics


BOOL
IIS_SERVICE::ClearInstanceStatistics(
                IN DWORD    dwInstance
                )
{

    PIIS_SERVER_INSTANCE pInstance;
    BOOL    fRet;

    IF_DEBUG( INSTANCE ) {
        DBGPRINTF((DBG_CONTEXT,
            "ClearInstanceStats called for service %x (instance %d)\n",
                this, dwInstance ));
    }

    //
    // Find the instance and close it
    //

    AcquireServiceLock( );

    //
    //  Find the instance in the instance list
    //

    pInstance = FindIISInstance( dwInstance );
    if ( pInstance != NULL ) {
        goto found;
    }

    ReleaseServiceLock( );
    SetLastError(ERROR_FILE_NOT_FOUND);
    return(FALSE);

found:

    fRet = pInstance->ClearStatistics( );
    ReleaseServiceLock( );

    return(fRet);

} // IIS_SERVICE::ClearInstanceStatistics