Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1285 lines
35 KiB

/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name :
rdpevlst.cpp
Abstract:
This manages user-mode RDP pending device management events. All
functions are reentrant.
Need to be more careful about holding on to spinlocks for
two long, like when I am allocating memory, for instance.
Revision History:
--*/
#include "precomp.hxx"
#define TRC_FILE "rdpevlst"
#include "trc.h"
////////////////////////////////////////////////////////////
//
// Defines
//
#define DEVLIST_POOLTAG 'DPDR'
#if DBG
#define MAGICNO 0x52530
#define BOGUSMAGICNO 0xAAAAAAAA
#endif
//////////////////////////////////////////////////////////////////////
//
// Internal Prototypes
//
PSESSIONLISTNODE FetchSessionListNode(IN RDPEVNTLIST list,
IN ULONG sessionID,
BOOL createIfNotFound);
void CleanupSessionListNodeRequestList(IN PSESSIONLISTNODE sessionListNode);
void CleanupSessionListNodeEventList(
IN PSESSIONLISTNODE sessionListNode
);
void ReleaseSessionListNode(IN RDPEVNTLIST list, IN ULONG sessionID);
#if DBG
void CheckListIntegrity(IN RDPEVNTLIST list);
#endif
//////////////////////////////////////////////////////////////////////
//
// Global Variables
//
#if DBG
ULONG RDPEVNTLIST_LockCount = 0;
#endif
RDPEVNTLIST
RDPEVNTLIST_CreateNewList()
/*++
Routine Description:
Create a new pending device list.
Arguments:
Return Value:
RDPEVNTLIST_INVALID_LIST on error. A new device list on success.
--*/
{
PSESSIONLIST sessionList;
BEGIN_FN("RDPEVNTLIST_CreateNewList");
sessionList = new(NonPagedPool) SESSIONLIST;
if (sessionList != NULL) {
#if DBG
sessionList->magicNo = MAGICNO;
#endif
KeInitializeSpinLock(&sessionList->spinlock);
InitializeListHead(&sessionList->listHead);
}
return (RDPEVNTLIST)sessionList;
}
void RDPEVNTLIST_DestroyList(IN RDPEVNTLIST list)
/*++
Routine Description:
Release a pending device list.
Arguments:
list
Return Value:
NULL on error. A new device list on success.
--*/
{
#ifdef DBG
PSESSIONLIST sessionList=NULL;
#else
PSESSIONLIST sessionList;
#endif
PSESSIONLISTNODE sessionListNode;
PLIST_ENTRY sessionListEntry;
BEGIN_FN("RDPEVNTLIST_DestroyList");
sessionList = (PSESSIONLIST)list;
#if DBG
CheckListIntegrity(list);
#endif
//
// Clean up each session node in LIFO fashion, as opposed to FIFO, for
// efficiency.
//
while (!IsListEmpty(&sessionList->listHead)) {
sessionListEntry = RemoveHeadList(&sessionList->listHead);
sessionListNode = CONTAINING_RECORD(sessionListEntry, SESSIONLISTNODE, listEntry);
TRC_ASSERT(sessionListNode->magicNo == MAGICNO,
(TB, "Invalid magic number in list block entry."));
// Clean up the request list for the current session node.
CleanupSessionListNodeRequestList(sessionListNode);
// Clean up the event list for the current session node.
CleanupSessionListNodeEventList(sessionListNode);
// Release the current session node.
#if DBG
sessionListNode->magicNo = BOGUSMAGICNO;
#endif
delete sessionListNode;
}
// Release the list.
#if DBG
sessionList->magicNo = BOGUSMAGICNO;
#endif
delete sessionList;
}
NTSTATUS
RDPDEVNTLIST_EnqueueEventEx(
IN RDPEVNTLIST list,
IN ULONG sessionID,
IN void *event,
IN DrDevice *device,
IN ULONG type,
IN BOOL insertAtHead
)
/*++
Routine Description:
Queue a new pending event for the specified session. Note that this function simply
stores the event pointer. It does not copy the data pointed to by
the pointer.
Arguments:
list - Event management list allocated by RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session to associate with the device.
devMgmtEvent - Pending device management event.
type - Numeric identifier for event type. Valid values for this field
are defined by the function caller.
insertAtHead - If TRUE, then the element is queued at the head of the queue
in standard FIFO fashion. Otherwise, the element is queued at
the tail of the queue. This is convenient for requeueing.
Return Value:
NTSUCCESS on success. Alternative status, otherwise.
--*/
{
PSESSIONLISTNODE sessionListNode;
PLIST_ENTRY sessionListEntry;
PLIST_ENTRY eventListEntry;
PEVENTLISTNODE eventListNode;
NTSTATUS ntStatus;
#if DBG
PSESSIONLIST sessionList=NULL;
#else
PSESSIONLIST sessionList;
#endif
BEGIN_FN("RDPDEVNTLIST_EnqueueEventEx");
TRC_NRM((TB, "session %ld.", sessionID));
sessionList = (PSESSIONLIST)list;
#if DBG
CheckListIntegrity(list);
#endif
// Fetch the session list node corresponding to the session ID.
sessionListNode = FetchSessionListNode(list, sessionID, TRUE);
if (sessionListNode == NULL) {
ntStatus = STATUS_NO_MEMORY;
goto ReturnWithStatus;
}
//
// Add a new entry to the event list.
//
// Allocate a new event list node.
eventListNode = new(NonPagedPool) EVENTLISTNODE;
if (eventListNode != NULL) {
// Initialize the new node.
#if DBG
eventListNode->magicNo = MAGICNO;
#endif
eventListNode->event = event;
eventListNode->type = type;
eventListNode->device = device;
// Add it to the list head.
if (insertAtHead) {
InsertHeadList(&sessionListNode->eventListHead,
&eventListNode->listEntry);
}
else {
InsertTailList(&sessionListNode->eventListHead,
&eventListNode->listEntry);
}
ntStatus = STATUS_SUCCESS;
}
else {
ntStatus = STATUS_NO_MEMORY;
}
#if DBG
CheckListIntegrity(list);
#endif
ReturnWithStatus:
return ntStatus;
}
NTSTATUS
RDPEVNTLIST_EnqueueEvent(
IN RDPEVNTLIST list,
IN ULONG sessionID,
IN void *event,
IN ULONG type,
OPTIONAL IN DrDevice *device
)
/*++
Routine Description:
Queue a new pending event for the specified session. Note that this function simply
stores the event pointer. It does not copy the data pointed to by
the pointer.
Arguments:
list - Event management list allocated by RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session to associate with the device.
devMgmtEvent - Pending device management event.
type - Numeric identifier for event type. Valid values for this field
are defined by the function caller.
Return Value:
NTSUCCESS on success. Alternative status, otherwise.
--*/
{
BEGIN_FN("RDPEVNTLIST_EnqueueEvent");
// Insert at the head of the queue.
return RDPDEVNTLIST_EnqueueEventEx(list, sessionID, event, device, type, TRUE);
}
NTSTATUS
RDPEVNTLIST_RequeueEvent(
IN RDPEVNTLIST list,
IN ULONG sessionID,
IN void *event,
IN ULONG type,
OPTIONAL IN DrDevice *device
)
/*++
Routine Description:
Requeue a pending event for the specified session at the tail of the queue.
Note that this function simply stores the event pointer. It does not copy the
data pointed to by the pointer.
Arguments:
list - Event management list allocated by RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session to associate with the device.
devMgmtEvent - Pending device management event.
type - Numeric identifier for event type. Valid values for this field
are defined by the function caller.
Return Value:
NTSUCCESS on success. Alternative status, otherwise.
--*/
{
BEGIN_FN("RDPEVNTLIST_RequeueEvent");
// Insert at the head of the queue.
return RDPDEVNTLIST_EnqueueEventEx(list, sessionID, event, device, type, FALSE);
}
BOOL RDPEVNTLIST_PeekNextEvent(
IN RDPEVNTLIST list,
IN ULONG sessionID,
PVOID *eventPtr,
OPTIONAL IN OUT ULONG *type,
OPTIONAL IN OUT DrDevice **devicePtr
)
/*++
Routine Description:
Peek at the next pending event for the specified session, without dequeueing
it. NULL is returned if there are no more pending events for the specified
session. Note that, if non-NULL is returned, the pointer returned is the
pointer that was passed in to RDPEVNTLIST_EnqueueEvent.
Arguments:
list - Event management list allocated by RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session to associate with the device.
type - Can be used to identify the type of event.
eventPtr - The returned event.
Return Value:
TRUE if pending event exists. FALSE, otherwise.
--*/
{
PSESSIONLISTNODE sessionListNode;
PEVENTLISTNODE eventListNode;
PLIST_ENTRY tail;
BOOL result;
PSESSIONLIST sessionList;
BEGIN_FN("RDPEVNTLIST_PeekNextEvent");
TRC_NRM((TB, "session %ld.", sessionID));
sessionList = (PSESSIONLIST)list;
#if DBG
CheckListIntegrity(list);
#endif
//
// Fetch the session list node corresponding to the session ID.
//
sessionListNode = FetchSessionListNode(list, sessionID, FALSE);
//
// If we have a non-empty session list node.
//
if ((sessionListNode != NULL) &&
!IsListEmpty(&sessionListNode->eventListHead)) {
//
// Get the event at the tail of the session's event list.
//
tail = sessionListNode->eventListHead.Blink;
eventListNode = CONTAINING_RECORD(tail, EVENTLISTNODE, listEntry);
TRC_ASSERT(eventListNode->magicNo == MAGICNO,
(TB, "Invalid event list node."));
//
// Grab the fields to return.
//
*eventPtr = eventListNode->event;
if (type != NULL) *type = eventListNode->type;
if (devicePtr != NULL) *devicePtr = eventListNode->device;
result = TRUE;
}
else {
*eventPtr = NULL;
result = FALSE;
}
return result;
}
BOOL RDPEVNTLIST_DequeueEvent(
IN RDPEVNTLIST list,
IN ULONG sessionID,
OPTIONAL IN OUT ULONG *type,
PVOID *eventPtr,
OPTIONAL IN OUT DrDevice **devicePtr
)
/*++
Routine Description:
Returns and removes the next pending event for the specified session.
NULL is returned if there are no more pending events for the specified session.
Note that, if non-NULL is returned, the pointer returned is the pointer that was
passed in to RDPEVNTLIST_EnqueueEvent.
Arguments:
list - Event management list allocated by RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session to associate with the device.
type - Can be used to identify the type of event.
eventPtr - Returned event.
Return Value:
TRUE if Pending event if one exists. FALSE, otherwise.
--*/
{
PSESSIONLISTNODE sessionListNode;
PLIST_ENTRY eventListEntry;
PEVENTLISTNODE eventListNode;
#ifdef DBG
PSESSIONLIST sessionList=NULL;
#else
PSESSIONLIST sessionList;
#endif
BOOL result;
BEGIN_FN("RDPEVNTLIST_DequeueEvent");
TRC_NRM((TB, "session %ld.", sessionID));
sessionList = (PSESSIONLIST)list;
#if DBG
CheckListIntegrity(list);
#endif
//
// Fetch the session list node corresponding to the session ID.
//
sessionListNode = FetchSessionListNode(list, sessionID, FALSE);
if (sessionListNode != NULL) {
//
// Get the next session list node in FIFO fashion.
//
if (!IsListEmpty(&sessionListNode->eventListHead)) {
eventListEntry = RemoveTailList(&sessionListNode->eventListHead);
eventListNode = CONTAINING_RECORD(eventListEntry, EVENTLISTNODE, listEntry);
TRC_ASSERT(eventListNode->magicNo == MAGICNO,
(TB, "Invalid event list node."));
*eventPtr = eventListNode->event;
if (type != NULL) {
*type = eventListNode->type;
}
if (devicePtr != NULL) {
*devicePtr = eventListNode->device;
}
result = TRUE;
#if DBG
eventListNode->magicNo = BOGUSMAGICNO;
#endif
// Release the event list node.
delete eventListNode;
TRC_NRM((TB, "returning session %ld entry.", sessionID));
}
else {
TRC_NRM((TB, "session %ld empty.", sessionID));
*eventPtr = NULL;
result = FALSE;
}
//
// If the request list is empty and the event list is empty, then
// delete the session node.
//
if (IsListEmpty(&sessionListNode->requestListHead) &&
IsListEmpty(&sessionListNode->eventListHead)) {
ReleaseSessionListNode(list, sessionListNode->sessionID);
}
}
else {
TRC_NRM((TB, "session %ld not found.", sessionID));
*eventPtr = NULL;
result = FALSE;
}
#if DBG
CheckListIntegrity(list);
#endif
return result;
}
NTSTATUS RDPEVNTLIST_EnqueueRequest(IN RDPEVNTLIST list,
IN ULONG sessionID, IN PVOID request)
/*++
Routine Description:
Add a new pending request. Note that this function simply stores the request
pointer. It does not copy the data pointed to by the pointer.
Arguments:
list - Device management list allocated by RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session to associate with the device.
request - Pending request.
Return Value:
Pending event if one exists. NULL, otherwise.
--*/
{
PSESSIONLISTNODE sessionListNode;
PLIST_ENTRY sessionListEntry;
PLIST_ENTRY requestListEntry;
PREQUESTLISTNODE requestListNode;
NTSTATUS ntStatus;
#ifdef DBG
PSESSIONLIST sessionList=NULL;
#else
PSESSIONLIST sessionList;
#endif
BEGIN_FN("RDPEVNTLIST_EnqueueRequest");
TRC_NRM((TB, "session %ld.", sessionID));
sessionList = (PSESSIONLIST)list;
#if DBG
CheckListIntegrity(list);
#endif
// Fetch the session list node corresponding to the session ID.
sessionListNode = FetchSessionListNode(list, sessionID, TRUE);
if (sessionListNode == NULL) {
ntStatus = STATUS_NO_MEMORY;
goto ReturnWithStatus;
}
//
// Add a new entry to the event list.
//
// Allocate a new request list node.
requestListNode = new(NonPagedPool) REQUESTLISTNODE;
if (requestListNode != NULL) {
// Add it to the list head.
#if DBG
requestListNode->magicNo = MAGICNO;
#endif
requestListNode->request = request;
InsertHeadList(&sessionListNode->requestListHead, &requestListNode->listEntry);
ntStatus = STATUS_SUCCESS;
}
else {
ntStatus = STATUS_NO_MEMORY;
}
#if DBG
CheckListIntegrity(list);
#endif
ReturnWithStatus:
return ntStatus;
}
PVOID RDPEVNTLIST_DequeueRequest(IN RDPEVNTLIST list,
IN ULONG sessionID)
/*++
Routine Description:
Returns and removes the next pending request for the specified session.
NULL is returned if there are no more pending devices for the specified session.
Note that, if non-NULL is returned, the pointer returned is the pointer that was
passed in to RDPEVNTLIST_EnqueueRequest.
Arguments:
list - Device management list allocated by RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session to associate with the device.
Return Value:
Pending request if one exists. NULL, otherwise.
--*/
{
PSESSIONLISTNODE sessionListNode;
PLIST_ENTRY requestListEntry;
PREQUESTLISTNODE requestListNode;
PVOID requestPtr;
#ifdef DBG
PSESSIONLIST sessionList=NULL;
#else
PSESSIONLIST sessionList;
#endif
BEGIN_FN("RDPEVNTLIST_DequeueRequest");
TRC_NRM((TB, "session %ld.", sessionID));
sessionList = (PSESSIONLIST)list;
#if DBG
CheckListIntegrity(list);
#endif
//
// Fetch the session list node corresponding to the session ID.
//
sessionListNode = FetchSessionListNode(list, sessionID, FALSE);
if (sessionListNode != NULL) {
//
// Get the next session list node in FIFO fashion.
//
if (!IsListEmpty(&sessionListNode->requestListHead)) {
requestListEntry = RemoveTailList(&sessionListNode->requestListHead);
requestListNode = CONTAINING_RECORD(requestListEntry, REQUESTLISTNODE, listEntry);
TRC_ASSERT(requestListNode->magicNo == MAGICNO, (TB, "Invalid request list node."));
requestPtr = requestListNode->request;
#if DBG
requestListNode->magicNo = BOGUSMAGICNO;
#endif
// Release the event list node.
delete requestListNode;
}
else {
requestPtr = NULL;
}
//
// If the request list is empty and the event list is empty, then
// delete the session node.
//
if (IsListEmpty(&sessionListNode->requestListHead) &&
IsListEmpty(&sessionListNode->eventListHead)) {
ReleaseSessionListNode(list, sessionListNode->sessionID);
#if DBG
sessionListNode = NULL;
#endif
}
}
else {
requestPtr = NULL;
}
#if DBG
CheckListIntegrity(list);
#endif
return requestPtr;
}
PVOID
RDPEVNTLIST_DequeueSpecificRequest(
IN RDPEVNTLIST list,
IN ULONG sessionID,
IN PVOID request
)
/*++
Routine Description:
Dequeues a specific request from a session's request list. The dequeued request
is returned if it is found. Otherwise, NULL is returned.
Arguments:
list - Device management list allocated by RDPDDEVLIST_CreateNewList.
request - A request that was queued for the specified session via
RDPEVNTLIST_EnqueueRequest.
sessionID - Session from which the request should be dequeued.
Return Value:
Pending request if one exists. NULL, otherwise.
--*/
{
PSESSIONLISTNODE sessionListNode;
PLIST_ENTRY requestListEntry;
PREQUESTLISTNODE requestListNode;
PVOID requestPtr = NULL;
#ifdef DBG
PSESSIONLIST sessionList=NULL;
#else
PSESSIONLIST sessionList;
#endif
BEGIN_FN("RDPEVNTLIST_DequeueSpecificRequest");
TRC_NRM((TB, "session %ld.", sessionID));
sessionList = (PSESSIONLIST)list;
#if DBG
CheckListIntegrity(list);
#endif
//
// Fetch the session list node corresponding to the session ID.
//
sessionListNode = FetchSessionListNode(list, sessionID, FALSE);
if (sessionListNode != NULL) {
//
// Perform a linear search for the specified request.
//
requestListEntry = sessionListNode->requestListHead.Flink;
while(requestListEntry != &sessionListNode->requestListHead) {
requestListNode = CONTAINING_RECORD(requestListEntry, REQUESTLISTNODE, listEntry);
TRC_ASSERT(requestListNode->magicNo == MAGICNO,
(TB, "Invalid magic number in list block entry."));
if (requestListNode->request == request) {
requestPtr = requestListNode->request;
break;
}
requestListEntry = requestListEntry->Flink;
}
//
// If we found the entry, then remove it.
//
if (requestPtr != NULL) {
#if DBG
requestListNode->magicNo = BOGUSMAGICNO;
#endif
RemoveEntryList(requestListEntry);
// Release the request list node.
delete requestListNode;
}
else {
TRC_ALT((TB, "no req. for session %ld.", sessionID));
}
//
// If the request list is empty and the event list is empty, then
// delete the session node.
//
if (IsListEmpty(&sessionListNode->requestListHead) &&
IsListEmpty(&sessionListNode->eventListHead)) {
ReleaseSessionListNode(list, sessionListNode->sessionID);
#if DBG
sessionListNode = NULL;
#endif
}
}
else {
TRC_ALT((TB, "did not find session %ld.", sessionID));
}
#if DBG
CheckListIntegrity(list);
#endif
return requestPtr;
}
void CleanupSessionListNodeEventList(
IN PSESSIONLISTNODE sessionListNode
)
/*++
Routine Description:
Clean up the event list for the specified session node. The list must
be locked before this function is called.
Arguments:
sessionListNode - Session list node to clean up.
Return Value:
None.
--*/
{
PLIST_ENTRY eventListEntry;
PEVENTLISTNODE eventListNode;
BEGIN_FN("CleanupSessionListNodeEventList");
//
// Clean up the event list for the current session node in LIFO, as
// opposed to FIFO fashion, for efficiency.
//
while (!IsListEmpty(&sessionListNode->eventListHead)) {
eventListEntry = RemoveHeadList(&sessionListNode->eventListHead);
eventListNode = CONTAINING_RECORD(eventListEntry, EVENTLISTNODE, listEntry);
TRC_ASSERT(eventListNode->magicNo == MAGICNO,
(TB, "Invalid magic number in list block entry."));
// Release the current request node.
#if DBG
eventListNode->magicNo = BOGUSMAGICNO;
#endif
delete eventListNode;
}
}
void CleanupSessionListNodeRequestList(
IN PSESSIONLISTNODE sessionListNode
)
/*++
Routine Description:
Clean up the request list for the specified session node. The list must
be locked before this function is called.
Arguments:
sessionListNode - Session list node to clean up.
Return Value:
None.
--*/
{
PLIST_ENTRY requestListEntry;
PREQUESTLISTNODE requestListNode;
PVOID requestPtr;
BEGIN_FN("CleanupSessionListNodeRequestList");
//
// Clean up the request list for the current session node in LIFO
// fashion, as opposed to FIFO fashion, for efficiency.
//
while (!IsListEmpty(&sessionListNode->requestListHead)) {
requestListEntry = RemoveHeadList(&sessionListNode->requestListHead);
requestListNode = CONTAINING_RECORD(requestListEntry, REQUESTLISTNODE, listEntry);
TRC_ASSERT(requestListNode->magicNo == MAGICNO,
(TB, "Invalid magic number in list block entry."));
// Release the current request node.
#if DBG
requestListNode->magicNo = BOGUSMAGICNO;
#endif
delete requestListNode;
}
}
void
ReleaseSessionListNode(
IN RDPEVNTLIST list,
IN ULONG sessionID
)
/*++
Routine Description:
Remove the session list node from the list if it exists.
Arguments:
list - Device management list allocated by
RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session list node to fetch.
Return Value:
The matching session node or NULL on error.
--*/
{
PSESSIONLIST sessionList;
PSESSIONLISTNODE sessionListNode;
PLIST_ENTRY current;
BEGIN_FN("ReleaseSessionListNode");
#if DBG
CheckListIntegrity(list);
#endif
// Cast the list to the correct type.
sessionList = (PSESSIONLIST)list;
TRC_ASSERT(sessionList->magicNo == MAGICNO,
(TB, "Invalid magic number in session list."));
//
// Scan through the session node list, looking for a matching session.
//
current = sessionList->listHead.Flink;
while (current != &sessionList->listHead) {
sessionListNode = CONTAINING_RECORD(current, SESSIONLISTNODE, listEntry);
TRC_ASSERT(sessionListNode->magicNo == MAGICNO,
(TB, "Invalid magic number in list block entry."));
if (sessionListNode->sessionID == sessionID) {
break;
}
current = current->Flink;
}
//
// Clean up the found entry.
//
if (current != &sessionList->listHead) {
// Remove the entry from the linked list.
RemoveEntryList(current);
// Clean up the found node's request list.
CleanupSessionListNodeRequestList(sessionListNode);
// Clean up the event list for the found node.
CleanupSessionListNodeEventList(sessionListNode);
// Release the found session node.
#if DBG
sessionListNode->magicNo = BOGUSMAGICNO;
#endif
delete sessionListNode;
}
#if DBG
CheckListIntegrity(list);
#endif
}
PSESSIONLISTNODE
FetchSessionListNode(
IN RDPEVNTLIST list,
IN ULONG sessionID,
IN BOOL createIfNotFound
)
/*++
Routine Description:
This is a convenience function that fetches the session list node with the
specified session ID.
Arguments:
list - Device management list allocated by
RDPDDEVLIST_CreateNewList.
sessionID - Identifier for session list node to fetch.
createIfNotFound - Flag that indicates whether the function should create
a session list node if one is not found.
Return Value:
The matching session node or NULL on error.
--*/
{
PSESSIONLIST sessionList;
PSESSIONLISTNODE sessionListNode;
PLIST_ENTRY sessionListEntry;
BEGIN_FN("FetchSessionListNode");
// Cast the list to the correct type.
sessionList = (PSESSIONLIST)list;
TRC_ASSERT(sessionList->magicNo == MAGICNO,
(TB, "Invalid magic number in session list."));
#if DBG
CheckListIntegrity(list);
#endif
//
// Scan through the session node list, looking for a matching session.
//
sessionListEntry = sessionList->listHead.Flink;
while(sessionListEntry != &sessionList->listHead) {
sessionListNode = CONTAINING_RECORD(sessionListEntry, SESSIONLISTNODE, listEntry);
TRC_ASSERT(sessionListNode->magicNo == MAGICNO,
(TB, "Invalid magic number in list block entry."));
if (sessionListNode->sessionID == sessionID) {
break;
}
sessionListEntry = sessionListEntry->Flink;
}
// If we didn't find a match.
if (sessionListEntry == &sessionList->listHead) {
// If we are supposed to create a missing node.
if (createIfNotFound) {
// Allocate a new session list node.
sessionListNode = new(NonPagedPool) SESSIONLISTNODE;
if (sessionListNode != NULL) {
#if DBG
sessionListNode->magicNo = MAGICNO;
#endif
InitializeListHead(&sessionListNode->requestListHead);
InitializeListHead(&sessionListNode->eventListHead);
sessionListNode->sessionID = sessionID;
// Add it to the head list.
InsertHeadList(&sessionList->listHead, &sessionListNode->listEntry);
}
}
// Otherwise, just return NULL for the session list node.
else {
sessionListNode = NULL;
}
}
#if DBG
CheckListIntegrity(list);
#endif
return sessionListNode;
}
BOOL
RDPEVNTLLIST_GetFirstSessionID(
IN RDPEVNTLIST list,
IN ULONG *pSessionID
)
/*++
Routine Description:
Get the first session ID in the set of currently managed sessions. A
session is managed if there are any pending request's or events.
A session is no longer managed when there are no longer any
pending request's or events.
This session is useful for cleaning up pending request's and pending events.
Arguments:
list - Device management list allocated by RDPDDEVLIST_CreateNewList.
pSessionID - Pointer for storing returned first session ID.
Return Value:
TRUE if a session ID is returned. FALSE, if there are no more sessions being
managed by the list.
--*/
{
PSESSIONLIST sessionList;
PLIST_ENTRY sessionListEntry;
PSESSIONLISTNODE sessionListNode;
BEGIN_FN("RDPEVNTLLIST_GetFirstSessionID");
sessionList = (PSESSIONLIST)list;
#if DBG
CheckListIntegrity(list);
#endif
// If the list is empty.
if (IsListEmpty(&sessionList->listHead)) {
return FALSE;
}
else {
sessionListNode = CONTAINING_RECORD(sessionList->listHead.Flink,
SESSIONLISTNODE, listEntry);
TRC_ASSERT(sessionListNode->magicNo == MAGICNO,
(TB, "Invalid magic number in list block entry."));
*pSessionID = sessionListNode->sessionID;
return TRUE;
}
}
#if DBG
void CheckListIntegrity(
IN RDPEVNTLIST list
)
/*++
Routine Description:
Check the integrity of the event and request list.
Arguments:
Return Value:
None
--*/
{
PLIST_ENTRY currentSessionEntry;
PLIST_ENTRY listEntry;
PREQUESTLISTNODE requestListNode;
PEVENTLISTNODE eventListNode;
PSESSIONLISTNODE sessionListNode;
PSESSIONLIST sessionList;
BEGIN_FN("CheckListIntegrity");
sessionList = (PSESSIONLIST)list;
currentSessionEntry = sessionList->listHead.Flink;
while (currentSessionEntry != &sessionList->listHead) {
// Check the current session node.
sessionListNode = CONTAINING_RECORD(currentSessionEntry,
SESSIONLISTNODE, listEntry);
if (sessionListNode->magicNo == BOGUSMAGICNO) {
TRC_ASSERT(FALSE,
(TB, "Stale link in event list in session list entry in integrity check."));
}
else if (sessionListNode->magicNo != MAGICNO) {
TRC_ASSERT(FALSE,
(TB, "Invalid magic number in session list entry in integrity check."));
}
// Check the current session's request list.
listEntry = sessionListNode->requestListHead.Flink;
while (listEntry != &sessionListNode->requestListHead) {
requestListNode = CONTAINING_RECORD(listEntry,
REQUESTLISTNODE, listEntry);
if (requestListNode->magicNo == BOGUSMAGICNO) {
TRC_ASSERT(FALSE,
(TB, "Stale link in event list entry in integrity check."));
}
else if (requestListNode->magicNo != MAGICNO) {
TRC_ASSERT(FALSE,
(TB, "Invalid magic number in request list entry in integrity check."));
}
listEntry = listEntry->Flink;
}
// Check the current session's event list.
listEntry = sessionListNode->eventListHead.Flink;
while (listEntry != &sessionListNode->eventListHead) {
eventListNode = CONTAINING_RECORD(listEntry,
EVENTLISTNODE, listEntry);
if (eventListNode->magicNo == BOGUSMAGICNO) {
TRC_ASSERT(FALSE,
(TB, "Stale link in event list entry in integrity check."));
}
else if (eventListNode->magicNo != MAGICNO) {
TRC_ASSERT(FALSE,
(TB, "Corrupted magic number in event list entry in integrity check."));
}
listEntry = listEntry->Flink;
}
// Next session entry.
currentSessionEntry = currentSessionEntry->Flink;
}
}
#endif
#if DBG
void RDPEVNTLIST_UnitTest()
/*++
Routine Description:
Unit-Test function that can be called from a kernel-mode driver to
cover all functions implemented by this module.
Arguments:
Return Value:
none.
--*/
#define MAXSESSIONS 2
#define MAXREQUESTS 2
#define MAXEVENT 2
#define REQUESTADDRESS (PVOID)0x55000055
#define DEVEVTADDRESS (PVOID)0x66000066
#define TESTDEVTYPE (ULONG)0
{
RDPEVNTLIST devList;
ULONG i,j;
PVOID address;
ULONG sessionID;
BOOL result;
BEGIN_FN("RDPEVNTLIST_UnitTest");
devList = RDPEVNTLIST_CreateNewList();
TRC_ASSERT(devList != NULL,
(TB, "Unit test failed because list did not initialize properly."));
// Add request's and event pointers for each session.
for (i=0; i<MAXSESSIONS; i++) {
for (j=0; j<MAXREQUESTS; j++) {
if (!(j%5)) {
TRC_NRM((TB, "Adding test requests"));
}
RDPEVNTLIST_EnqueueRequest(devList, i, (PVOID)REQUESTADDRESS);
}
for (j=0; j<MAXEVENT; j++) {
if (!(j%5)) {
TRC_NRM((TB, "Adding test device events."));
}
RDPEVNTLIST_EnqueueEvent(devList, i,
DEVEVTADDRESS,
TESTDEVTYPE,
NULL);
}
}
// Remove them.
for (i=0; i<MAXSESSIONS; i++) {
for (j=0; j<MAXREQUESTS; j++) {
address = RDPEVNTLIST_DequeueRequest(devList, i);
TRC_ASSERT(address == REQUESTADDRESS,
(TB, "Unit test failed because invalid request address."));
if (!(j%5)) {
TRC_NRM((TB, "Removing test requests"));
}
}
TRC_ASSERT(RDPEVNTLIST_DequeueRequest(devList, i) == NULL, (TB, ""));
for (j=0; j<MAXEVENT; j++) {
if (!(j%5)) {
TRC_NRM((TB, "Removing test events"));
}
result = RDPEVNTLIST_DequeueEvent(devList, i, NULL, &address, NULL);
TRC_ASSERT(result, (TB, "Unit test failed because missing event."));
TRC_ASSERT(address == DEVEVTADDRESS,
(TB, "Unit test failed because invalid event address."));
}
TRC_ASSERT(!RDPEVNTLIST_DequeueEvent(devList, i, NULL, &address, NULL),
(TB, "Unit test failed because pending session exists."));
}
// All sessions should now be removed.
TRC_ASSERT(!RDPEVNTLLIST_GetFirstSessionID(devList, &sessionID),
(TB, "Unit test failed because session exists."));
// Destroy the list.
RDPEVNTLIST_DestroyList(devList);
}
#endif