656 lines
14 KiB
C
656 lines
14 KiB
C
/*++
|
|
|
|
Copyright (c) 1997 - 98, Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rtmmain.c
|
|
|
|
Abstract:
|
|
|
|
Contains routines that are invoked when
|
|
the RTMv2 DLL is loaded or unloaded.
|
|
|
|
Author:
|
|
|
|
Chaitanya Kodeboyina (chaitk) 17-Aug-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pchrtm.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
// All Global variables
|
|
RTMP_GLOBAL_INFO RtmGlobals;
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain(
|
|
IN HINSTANCE Instance,
|
|
IN DWORD Reason,
|
|
IN PVOID Unused
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the DLL's main entrypoint handler which
|
|
initializes RTMv1, RTMv2 and MGM components.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
static BOOL Rtmv1Initialized = FALSE;
|
|
static BOOL RtmInitialized = FALSE;
|
|
static BOOL MgmInitialized = FALSE;
|
|
BOOL Success;
|
|
|
|
UNREFERENCED_PARAMETER(Unused);
|
|
|
|
Success = FALSE;
|
|
|
|
switch(Reason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
DisableThreadLibraryCalls(Instance);
|
|
|
|
//
|
|
// Initialize the RTMv1, RTMv2 and MGM APIs
|
|
//
|
|
|
|
Rtmv1Initialized = Rtmv1DllStartup(Instance);
|
|
|
|
if (Rtmv1Initialized)
|
|
{
|
|
RtmInitialized = RtmDllStartup();
|
|
|
|
if (RtmInitialized)
|
|
{
|
|
MgmInitialized = MgmDllStartup();
|
|
}
|
|
}
|
|
|
|
return MgmInitialized;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
//
|
|
// Cleanup the MGM, RTMv2 and RTMv1 APIs
|
|
//
|
|
|
|
if (MgmInitialized)
|
|
{
|
|
MgmDllCleanup();
|
|
}
|
|
|
|
if (RtmInitialized)
|
|
{
|
|
Success = RtmDllCleanup();
|
|
}
|
|
|
|
if (Rtmv1Initialized)
|
|
{
|
|
Rtmv1DllCleanup();
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Success = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RtmDllStartup(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by DLL Main when the process is attached.
|
|
We do minimal initialization here like creating
|
|
a lock that protects all globals (including the
|
|
'ApiInitialized' -- see RtmRegisterEntity func).
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// One can safely assume that globals have been set to 0
|
|
//
|
|
|
|
// ZeroMemory(&RtmGlobals, sizeof(RTMP_GLOBAL_INFO));
|
|
|
|
//
|
|
// Initialize lock to guard the global table of instances
|
|
//
|
|
|
|
try
|
|
{
|
|
CREATE_READ_WRITE_LOCK(&RtmGlobals.InstancesLock);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
RtmApiStartup(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes most global data structures in RTMv2.
|
|
|
|
We initialize most variables here instead of in
|
|
RtmDllStartup as it might not be safe to perform
|
|
some operations in the context of DLL's DLLMain.
|
|
|
|
For example, if we find no config information, we
|
|
set up default config information in the registry.
|
|
|
|
This function is called when the first RTMv2 API
|
|
call, which is typically an entity registration,
|
|
is made. See the invocation in RtmRegisterEntity.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status of the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
RTM_INSTANCE_CONFIG InstanceConfig;
|
|
BOOL ListLockInited;
|
|
DWORD Status;
|
|
UINT i;
|
|
|
|
ListLockInited = FALSE;
|
|
|
|
Status = NO_ERROR;
|
|
|
|
ACQUIRE_INSTANCES_WRITE_LOCK();
|
|
|
|
do
|
|
{
|
|
//
|
|
// If API has already been initialized, work is done
|
|
//
|
|
|
|
if (RtmGlobals.ApiInitialized)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Enable logging and tracing for debugging purposes
|
|
//
|
|
|
|
START_TRACING();
|
|
START_LOGGING();
|
|
|
|
#if DBG_TRACE
|
|
RtmGlobals.TracingFlags = RTM_TRACE_ANY;
|
|
#endif
|
|
|
|
#if DBG_MEM
|
|
|
|
//
|
|
// Init a lock & list to hold mem allocs
|
|
//
|
|
|
|
try
|
|
{
|
|
InitializeCriticalSection(&RtmGlobals.AllocsLock);
|
|
|
|
ListLockInited = TRUE;
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
Trace1(ANY,
|
|
"RTMApiStartup : Failed to init a critical section %x",
|
|
Status);
|
|
|
|
LOGERR0(INIT_CRITSEC_FAILED, Status);
|
|
|
|
break;
|
|
}
|
|
|
|
InitializeListHead(&RtmGlobals.AllocsList);
|
|
#endif
|
|
|
|
//
|
|
// Create a private heap for RTM's use
|
|
//
|
|
|
|
RtmGlobals.GlobalHeap = HeapCreate(0, 0, 0);
|
|
|
|
if (RtmGlobals.GlobalHeap == NULL)
|
|
{
|
|
Status = GetLastError();
|
|
|
|
Trace1(ANY,
|
|
"RtmApiStartup: Failed to create a global private heap %x",
|
|
Status);
|
|
|
|
LOGERR0(HEAP_CREATE_FAILED, Status);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Initialize the root of RTM's registry information
|
|
//
|
|
|
|
RtmGlobals.RegistryPath = AllocNZeroMemory(MAX_CONFIG_KEY_SIZE);
|
|
|
|
if (RtmGlobals.RegistryPath == NULL)
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
CopyMemory(RtmGlobals.RegistryPath,
|
|
RTM_CONFIG_ROOT,
|
|
RTM_CONFIG_ROOT_SIZE);
|
|
|
|
//
|
|
// Initialize the global hash table of RTM instances
|
|
//
|
|
|
|
RtmGlobals.NumInstances = 0;
|
|
for (i = 0; i < INSTANCE_TABLE_SIZE; i++)
|
|
{
|
|
InitializeListHead(&RtmGlobals.InstanceTable[i]);
|
|
}
|
|
|
|
//
|
|
// You need to set this value to TRUE to avoid
|
|
// any more recursive calls into this function
|
|
//
|
|
|
|
RtmGlobals.ApiInitialized = TRUE;
|
|
|
|
//
|
|
// Read config info if present ; else pick default
|
|
//
|
|
|
|
Status = RtmReadInstanceConfig(DEFAULT_INSTANCE_ID, &InstanceConfig);
|
|
|
|
if (Status != NO_ERROR)
|
|
{
|
|
Status = RtmWriteDefaultConfig(DEFAULT_INSTANCE_ID);
|
|
|
|
if (Status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
if (Status != NO_ERROR)
|
|
{
|
|
//
|
|
// Some error occured - clean up and return the error code
|
|
//
|
|
|
|
if (RtmGlobals.RegistryPath != NULL)
|
|
{
|
|
FreeMemory(RtmGlobals.RegistryPath);
|
|
}
|
|
|
|
if (RtmGlobals.GlobalHeap != NULL)
|
|
{
|
|
HeapDestroy(RtmGlobals.GlobalHeap);
|
|
}
|
|
|
|
#if DBG_MEM
|
|
if (ListLockInited)
|
|
{
|
|
DeleteCriticalSection(&RtmGlobals.AllocsLock);
|
|
}
|
|
#endif
|
|
|
|
STOP_LOGGING();
|
|
STOP_TRACING();
|
|
|
|
//
|
|
// We had prematurely set the value to TRUE above, reset it
|
|
//
|
|
|
|
RtmGlobals.ApiInitialized = FALSE;
|
|
}
|
|
|
|
RELEASE_INSTANCES_WRITE_LOCK();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RtmDllCleanup(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up all global data structures at unload time.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
PINSTANCE_INFO Instance;
|
|
PLIST_ENTRY Instances, p, r;
|
|
UINT NumInstances;
|
|
PADDRFAM_INFO AddrFamilyInfo;
|
|
PLIST_ENTRY AddrFamilies, q;
|
|
PENTITY_INFO Entity;
|
|
PLIST_ENTRY Entities, s;
|
|
UINT NumEntities;
|
|
UINT i, j, k, l;
|
|
|
|
//
|
|
// Do we have any instances and associated ref counts left ?
|
|
//
|
|
|
|
if (RtmGlobals.NumInstances != 0)
|
|
{
|
|
//
|
|
// We need to stop all outstanding timers
|
|
// on every address family as the RTM DLL
|
|
// gets unloaded after this call returns.
|
|
// We also forcefully destroy entities &
|
|
// address families to reclaim resources.
|
|
//
|
|
|
|
ACQUIRE_INSTANCES_WRITE_LOCK();
|
|
|
|
NumInstances = RtmGlobals.NumInstances;
|
|
|
|
for (i = j = 0; i < INSTANCE_TABLE_SIZE; i++)
|
|
{
|
|
Instances = &RtmGlobals.InstanceTable[i];
|
|
|
|
for (p = Instances->Flink; p != Instances; p = r)
|
|
{
|
|
Instance = CONTAINING_RECORD(p, INSTANCE_INFO, InstTableLE);
|
|
|
|
AddrFamilies = &Instance->AddrFamilyTable;
|
|
#if WRN
|
|
r = p->Flink;
|
|
#endif
|
|
for (q = AddrFamilies->Flink; q != AddrFamilies; )
|
|
{
|
|
AddrFamilyInfo =
|
|
CONTAINING_RECORD(q, ADDRFAM_INFO, AFTableLE);
|
|
|
|
//
|
|
// Holding the instances lock while deleting
|
|
// timer queues (using blocking calls) can
|
|
// result in a deadlock, so just reference
|
|
// the address family and release the lock.
|
|
//
|
|
|
|
// Ref address family so that it does not disappear
|
|
REFERENCE_ADDR_FAMILY(AddrFamilyInfo, TEMP_USE_REF);
|
|
|
|
RELEASE_INSTANCES_WRITE_LOCK();
|
|
|
|
//
|
|
// Block until timers on address family are cleaned up
|
|
//
|
|
|
|
if (AddrFamilyInfo->RouteTimerQueue)
|
|
{
|
|
DeleteTimerQueueEx(AddrFamilyInfo->RouteTimerQueue,
|
|
(HANDLE) -1);
|
|
|
|
AddrFamilyInfo->RouteTimerQueue = NULL;
|
|
}
|
|
|
|
if (AddrFamilyInfo->NotifTimerQueue)
|
|
{
|
|
DeleteTimerQueueEx(AddrFamilyInfo->NotifTimerQueue,
|
|
(HANDLE) -1);
|
|
|
|
AddrFamilyInfo->NotifTimerQueue = NULL;
|
|
}
|
|
|
|
//
|
|
// We assume that we have no other code paths that
|
|
// access any data structures on this addr family
|
|
//
|
|
|
|
//
|
|
// Force destroy each entity on the address family
|
|
//
|
|
|
|
NumEntities = AddrFamilyInfo->NumEntities;
|
|
|
|
for (k = l = 0; k < ENTITY_TABLE_SIZE; k++)
|
|
{
|
|
Entities = &AddrFamilyInfo->EntityTable[k];
|
|
|
|
for (s = Entities->Flink; s != Entities; )
|
|
{
|
|
Entity =
|
|
CONTAINING_RECORD(s, ENTITY_INFO, EntityTableLE);
|
|
|
|
s = s->Flink;
|
|
|
|
// To satisfy the asserts in DestroyEntity
|
|
Entity->ObjectHeader.RefCount = 0;
|
|
|
|
DestroyEntity(Entity);
|
|
|
|
l++;
|
|
}
|
|
|
|
if (l == NumEntities)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Also destroy entities that have deregistered
|
|
// but haven't been destroyed due to ref counts
|
|
//
|
|
|
|
while (!IsListEmpty(&AddrFamilyInfo->DeregdEntities))
|
|
{
|
|
Entity =
|
|
CONTAINING_RECORD(AddrFamilyInfo->DeregdEntities.Flink,
|
|
ENTITY_INFO,
|
|
EntityTableLE);
|
|
|
|
// To satisfy the asserts in DestroyEntity
|
|
Entity->ObjectHeader.RefCount = 0;
|
|
|
|
DestroyEntity(Entity);
|
|
}
|
|
|
|
ACQUIRE_INSTANCES_WRITE_LOCK();
|
|
|
|
// Get next address family before de-ref-ing current
|
|
q = q->Flink;
|
|
|
|
// Get next instance also as it might be deleted too
|
|
r = p->Flink;
|
|
|
|
// Remove the temporary reference use added earlier
|
|
DEREFERENCE_ADDR_FAMILY(AddrFamilyInfo, TEMP_USE_REF);
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
if (j == NumInstances)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_INSTANCES_WRITE_LOCK();
|
|
}
|
|
|
|
// We have freed all instances to avoid any leaks
|
|
ASSERT(RtmGlobals.NumInstances == 0);
|
|
|
|
//
|
|
// Free resources allocated like locks and memory
|
|
//
|
|
|
|
if (RtmGlobals.ApiInitialized)
|
|
{
|
|
FreeMemory(RtmGlobals.RegistryPath);
|
|
|
|
//
|
|
// At this point we might have whole lots of dests,
|
|
// routes, nexthops etc. that are have not been
|
|
// freed because of outstanding ref counts; however
|
|
// none of these objects have any locks (except
|
|
// dest locks which are dynamic anyway and can
|
|
// be unlocked and freed after deregistration),
|
|
// so we can just blow the heap to reclaim memory.
|
|
//
|
|
|
|
HeapDestroy(RtmGlobals.GlobalHeap);
|
|
|
|
#if DBG_MEM
|
|
DeleteCriticalSection(&RtmGlobals.AllocsLock);
|
|
#endif
|
|
|
|
//
|
|
// Stop debugging aids like tracing and logging
|
|
//
|
|
|
|
STOP_LOGGING();
|
|
STOP_TRACING();
|
|
}
|
|
|
|
DELETE_READ_WRITE_LOCK(&RtmGlobals.InstancesLock);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#if DBG_MEM
|
|
|
|
VOID
|
|
DumpAllocs (VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug tool to dump all objects that are
|
|
allocated by RTMv2 at any instant.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
POBJECT_HEADER Object;
|
|
PLIST_ENTRY p;
|
|
UINT i;
|
|
|
|
printf("\n\n----------------Allocs Left Over------------------------\n");
|
|
|
|
ACQUIRE_ALLOCS_LIST_LOCK();
|
|
|
|
for (p = RtmGlobals.AllocsList.Flink;
|
|
p != &RtmGlobals.AllocsList;
|
|
p = p->Flink)
|
|
{
|
|
Object = CONTAINING_RECORD(p, OBJECT_HEADER, AllocLE);
|
|
|
|
printf("Object @ %p: \n", Object);
|
|
|
|
#if DBG_HDL
|
|
printf("Object Signature = %c%c%c%c\n",
|
|
Object->Type,
|
|
Object->Signature[0],
|
|
Object->Signature[1],
|
|
Object->Alloc);
|
|
#endif
|
|
|
|
#if DBG_REF
|
|
printf("Object RefCounts: \n");
|
|
|
|
for (i = 0; i < MAX_REFS; i++)
|
|
{
|
|
printf("%2lu", Object->RefTypes[i]);
|
|
}
|
|
#endif
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
RELEASE_ALLOCS_LIST_LOCK();
|
|
|
|
printf("\n--------------------------------------------------------\n\n");
|
|
}
|
|
|
|
#endif
|