/*++

Copyright (c) 1997 - 98, Microsoft Corporation

Module Name:

    rtmregn.c

Abstract:

    Contains routines for managing registration
    of protocol & management entities with RTM.

Author:

    Chaitanya Kodeboyina (chaitk)   20-Aug-1998

Revision History:

--*/

#include "pchrtm.h"

#pragma hdrstop


DWORD
WINAPI
RtmRegisterEntity (
    IN      PRTM_ENTITY_INFO                RtmEntityInfo,
    IN      PRTM_ENTITY_EXPORT_METHODS      ExportMethods OPTIONAL,
    IN      RTM_EVENT_CALLBACK              EventCallback,
    IN      BOOL                            ReserveOpaquePtr,
    OUT     PRTM_REGN_PROFILE               RtmRegProfile,
    OUT     PRTM_ENTITY_HANDLE              RtmRegHandle
    )
/*++

Routine Description:

    Registers an entity with an RTM instance for a specific address
    family.

    A registration handle, and a profile of the RTM instance with
    with supported views, number of equal cost NHops / route etc.
    is returned.

    If registration is with a new instance and/or address family,
    then this instance/address family is created in this process.

Arguments:

    RtmEntityInfo     - Information (RtmInstance, Protocol ID etc.)
                        for the entity that is registering here,

    ExportMethods     - List of methods exported by this entity,

    EventCallback     - Callback invoked to inform of certain events
                        like entity registrations, de-registrations,

    ReserveOpaquePtr  - Reserve a ptr in each destination or not,

    RtmRegProfile     - RTM parameters that the entity will use in
                        RTM API calls [eg: No. of equal cost NHOPs],

    RtmRegHandle      - Identification handle for this entity used
                        in all API calls until its de-registration.

Return Value:

    Status of the operation

--*/

{
    PINSTANCE_INFO Instance;
    PADDRFAM_INFO  AddrFamilyInfo;
    PENTITY_INFO   Entity;
    DWORD          Status;

    CHECK_FOR_RTM_API_INITIALIZED();

    TraceEnter("RtmRegisterEntity");

    ACQUIRE_INSTANCES_WRITE_LOCK();

    do 
    {
        //
        // Search (or create) for an instance with the input RtmInstanceId
        //

        Status = GetInstance(RtmEntityInfo->RtmInstanceId,
                             TRUE,
                             &Instance);

        if (Status != NO_ERROR)
        {
            break;
        }


        //
        // Search (or create) for an address family info with input family
        //

        Status = GetAddressFamily(Instance,
                                  RtmEntityInfo->AddressFamily,
                                  TRUE,
                                  &AddrFamilyInfo);

        if (Status != NO_ERROR)
        {
            break;
        }


        //
        // Search (or create) for an entity with input protocol id, instance
        //

        Status = GetEntity(AddrFamilyInfo,
                           RtmEntityInfo->EntityId.EntityId,
                           TRUE,
                           RtmEntityInfo,
                           ReserveOpaquePtr,
                           ExportMethods,
                           EventCallback,
                           &Entity);

        if (Status != NO_ERROR)
        {
            break;
        }


        //
        // Collect all relevant information and build registration profile
        //

        RtmRegProfile->MaxNextHopsInRoute = AddrFamilyInfo->MaxNextHopsInRoute;

        RtmRegProfile->MaxHandlesInEnum = AddrFamilyInfo->MaxHandlesInEnum;

        RtmRegProfile->ViewsSupported = AddrFamilyInfo->ViewsSupported;

        RtmRegProfile->NumberOfViews = AddrFamilyInfo->NumberOfViews;

        //
        // Return a handle to this entity registration block 
        //

        *RtmRegHandle = MAKE_HANDLE_FROM_POINTER(Entity);
    }
    while (FALSE);

    RELEASE_INSTANCES_WRITE_LOCK();

    TraceLeave("RtmRegisterEntity");

    return Status;
}


DWORD
WINAPI
RtmDeregisterEntity (
    IN      RTM_ENTITY_HANDLE               RtmRegHandle
    )
    
/*++

Routine Description:

    Deregisters an entity with its RTM instance and addr family.

    We assume that the entity is responsible for making sure
    that once this call is made, no other RTM calls will be
    made using this entity registration handle. In case such
    a thing happens, it might result in crashing the process.

    We make this assumption for performance reasons - else we
    we have to make sure that the entity handle passed in is
    valid in a try-except block (same with other handles) and
    this will lead to degradation in performance.

Arguments:

    RtmRegHandle      - RTM registration handle for the entity

Return Value:

    Status of the operation

--*/

{
    PADDRFAM_INFO   AddrFamInfo;
    PENTITY_INFO    Entity;
    HANDLE          Event;
    DWORD           Status;

    TraceEnter("RtmDeregisterEntity");

    VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity);

    //
    // Release all handles opened by entity
    //

    CleanupAfterDeregister(Entity);

    //
    // Mark the entity info as de-registered
    //

    Entity->State = ENTITY_STATE_DEREGISTERED;

    //
    // Make sure no more methods are invoked
    //

    ACQUIRE_ENTITY_METHODS_WRITE_LOCK(Entity);

    // At this time all entity methods are 
    // done - no more methods will be called
    // as we set the state to DEREGISTERED

    RELEASE_ENTITY_METHODS_WRITE_LOCK(Entity);

    //
    // Remove from entity table and inform others
    //

    AddrFamInfo = Entity->OwningAddrFamily;

    ACQUIRE_INSTANCES_WRITE_LOCK();

    //
    // Remove entity from the list of entities
    // even before ref counts on this entity
    // go to zero - this enables the entity to
    // re-register meanwhile as a new entity.
    //

    RemoveEntryList(&Entity->EntityTableLE);

    //
    // Insert in the list of entities to be
    // destroyed on the address family info.
    //

    InsertTailList(&AddrFamInfo->DeregdEntities,
                   &Entity->EntityTableLE);

    InformEntitiesOfEvent(AddrFamInfo->EntityTable,
                          RTM_ENTITY_DEREGISTERED,
                          Entity);

    RELEASE_INSTANCES_WRITE_LOCK();


    DBG_UNREFERENCED_LOCAL_VARIABLE(Event);

#if DBG_REF

    //
    // Create an event on which to block on - this
    // event gets signalled when entity ref is 0.
    //

    Event = CreateEvent(NULL, FALSE, FALSE, NULL);

    ASSERT(Event != NULL);

    Entity->BlockingEvent = Event;

#endif

    //
    // Remove the creation reference on the entity
    //

    DEREFERENCE_ENTITY(Entity, CREATION_REF);


    DBG_UNREFERENCED_LOCAL_VARIABLE(Status);

#if DBG_REF

    //
    // Block until the reference count goes to zero
    //
    
    Status = WaitForSingleObject(Event, INFINITE);

    ASSERT(Status == WAIT_OBJECT_0);

    CloseHandle(Event);

#endif

    TraceLeave("RtmDeregisterEntity");
    
    return NO_ERROR;
}


DWORD
WINAPI
RtmGetRegisteredEntities (
    IN      RTM_ENTITY_HANDLE               RtmRegHandle,
    IN OUT  PUINT                           NumEntities,
    OUT     PRTM_ENTITY_HANDLE              EntityHandles,
    OUT     PRTM_ENTITY_INFO                EntityInfos OPTIONAL
    )

/*++

Routine Description:

    Retrieves information about all entities registered with an
    RTM instance.

Arguments:

    RtmRegHandle      - RTM registration handle for calling entity,

    NumEntities       - Number of entities that can be filled
                        is passed in, and number of entities
                        that exist in this address family is retd,

    RegdEntityHandles - Array to return the entity handles in,

    RegdEntityInfos   - Array to return the entity infos in

Return Value:

    Status of the operation

--*/

{
    PENTITY_INFO     Entity;
    PADDRFAM_INFO    AddrFamilyInfo;
    USHORT           RtmInstanceId;
    USHORT           AddressFamily;
    UINT             EntitiesCopied;
    UINT             i;
    PLIST_ENTRY      Entities;
    PLIST_ENTRY      p;
    DWORD            Status;

    TraceEnter("RtmGetRegisteredEntities");

    VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity);

    AddrFamilyInfo = Entity->OwningAddrFamily;

    //
    // Just cache the instance and address family
    // as it is identical for all entities infos.
    //

#if WRN
    RtmInstanceId = AddressFamily = 0;
#endif

    if (ARGUMENT_PRESENT(EntityInfos))
    {
        RtmInstanceId = AddrFamilyInfo->Instance->RtmInstanceId;
        AddressFamily = AddrFamilyInfo->AddressFamily;
    }

    //
    // Go over the entity table and copy out handles
    // If the OPTIONAL argument 'EntityInfos' is
    // given, copy out entity information as well.
    //

    EntitiesCopied = 0;

    ACQUIRE_INSTANCES_READ_LOCK();

    for (i = 0; (i < ENTITY_TABLE_SIZE) && (EntitiesCopied < *NumEntities);i++)
    {
        Entities = &AddrFamilyInfo->EntityTable[i];

        // 
        // Process the next bucket in the entities table
        //

        for (p = Entities->Flink; p != Entities; p = p->Flink)
        {
            Entity = CONTAINING_RECORD(p, ENTITY_INFO, EntityTableLE);

            //
            // Copy the next entity handle and info to output buffer
            //

            if (Entity->State != ENTITY_STATE_DEREGISTERED)
            {
                EntityHandles[EntitiesCopied]=MAKE_HANDLE_FROM_POINTER(Entity);

                REFERENCE_ENTITY(Entity, HANDLE_REF);

                if (ARGUMENT_PRESENT(EntityInfos))
                {
                    EntityInfos[EntitiesCopied].RtmInstanceId = RtmInstanceId;
                    EntityInfos[EntitiesCopied].AddressFamily = AddressFamily;
                    EntityInfos[EntitiesCopied].EntityId = Entity->EntityId;
                }

                if (++EntitiesCopied == *NumEntities)
                {
                    break;
                }
            }
        }
    }

    //
    // Set output to total entities present,
    // and also the appropriate return value
    //

    if (*NumEntities >= AddrFamilyInfo->NumEntities)
    {
        Status = NO_ERROR;
    }
    else
    {
        Status = ERROR_INSUFFICIENT_BUFFER;
    }

    *NumEntities = AddrFamilyInfo->NumEntities;

    RELEASE_INSTANCES_READ_LOCK();

    TraceLeave("RtmGetRegisteredEntities");

    return Status;
}


DWORD
WINAPI
RtmReleaseEntities (
    IN      RTM_ENTITY_HANDLE               RtmRegHandle,
    IN      UINT                            NumEntities,
    IN      PRTM_ENTITY_HANDLE              EntityHandles
    )

/*++

Routine Description:

    Release (also called de-reference) handles to entities
    obtained in other RTM calls.

Arguments:

    RtmRegHandle   - RTM registration handle for calling entity,

    NumEntities    - Number of handles that are being released,

    EntityHandles  - An array of handles that are being released.

Return Value:

    Status of the operation

--*/

{
    PENTITY_INFO     Entity;
    UINT             i;

    DBG_VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity);

    //
    // Dereference each entity handle in array
    //

    for (i = 0; i < NumEntities; i++)
    {
        Entity = ENTITY_FROM_HANDLE(EntityHandles[i]);

        DEREFERENCE_ENTITY(Entity, HANDLE_REF);
    }

    return NO_ERROR;
}


DWORD
WINAPI
RtmLockDestination(
    IN      RTM_ENTITY_HANDLE               RtmRegHandle,
    IN      RTM_DEST_HANDLE                 DestHandle,
    IN      BOOL                            Exclusive,
    IN      BOOL                            LockDest
    )

/*++

Routine Description:

    Locks/unlocks a destination in the route table. This function 
    is used to guard the dest while opaque ptrs are being changed.

Arguments:

    RtmRegHandle      - RTM registration handle for calling entity,

    DestHandle        - Handle to the destination to be locked,

    Exclusive         - TRUE to lock in write mode, else read mode,

    LockDest          - Flag that tells whether to lock or unlock.

Return Value:

    Status of the operation

--*/

{
    PENTITY_INFO     Entity;
    PDEST_INFO       Dest;

    DBG_VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity);

    VALIDATE_DEST_HANDLE(DestHandle, &Dest);

    // Lock or unlock the dest as the case may be

    if (LockDest)
    {
        if (Exclusive)
        {
            ACQUIRE_DEST_WRITE_LOCK(Dest);
        }
        else
        {
            ACQUIRE_DEST_READ_LOCK(Dest);
        }
    }
    else
    {
        if (Exclusive)
        {
            RELEASE_DEST_WRITE_LOCK(Dest);
        }
        else
        {
            RELEASE_DEST_READ_LOCK(Dest);
        }
    }

    return NO_ERROR;
}


DWORD
WINAPI
RtmGetOpaqueInformationPointer (
    IN      RTM_ENTITY_HANDLE               RtmRegHandle,
    IN      RTM_DEST_HANDLE                 DestHandle,
    OUT     PVOID                          *OpaqueInfoPtr
    )

/*++

Routine Description:

    Retrieves a pointer to the opaque info pointer field in a dest
    for this entity, or NULL if entity has not reserved such a ptr
    during registration.

Arguments:

    RtmRegHandle      - RTM registration handle for calling entity,

    DestHandle        - Handle to dest whose opaque info ptr we want,

    OpaqueInfoPtr     - Pointer to opaque info ptr is returned here 

Return Value:

    Status of the operation

--*/

{
    PENTITY_INFO     Entity;
    PDEST_INFO       Dest;
    DWORD            Status;

    TraceEnter("RtmGetOpaqueInformationPointer");

    *OpaqueInfoPtr = NULL;

    VALIDATE_ENTITY_HANDLE(RtmRegHandle, &Entity);

    Status = ERROR_NOT_FOUND;

    //
    // If dest is valid and we have an opaque slot
    // reserved, do ptr arithmetic to get the addr
    //

    if (Entity->OpaquePtrOffset != (-1))
    {
        //
        // We do not check if the dest in deleted
        // as the entity will need to access its
        // opaque info even after dest is deleted.
        //

        Dest = DEST_FROM_HANDLE(DestHandle);

        if (Dest)
        {
            *OpaqueInfoPtr = &Dest->OpaqueInfoPtrs[Entity->OpaquePtrOffset];

            Status = NO_ERROR;
        }
        else
        {
            Status = ERROR_INVALID_HANDLE;
        }
    }

    TraceLeave("RtmGetOpaqueInformationPointer");

    return Status;
}