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

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name :
  4. rdpevlst.cpp
  5. Abstract:
  6. This manages user-mode RDP pending device management events. All
  7. functions are reentrant.
  8. Need to be more careful about holding on to spinlocks for
  9. two long, like when I am allocating memory, for instance.
  10. Revision History:
  11. --*/
  12. #include "precomp.hxx"
  13. #define TRC_FILE "rdpevlst"
  14. #include "trc.h"
  15. ////////////////////////////////////////////////////////////
  16. //
  17. // Defines
  18. //
  19. #define DEVLIST_POOLTAG 'DPDR'
  20. #if DBG
  21. #define MAGICNO 0x52530
  22. #define BOGUSMAGICNO 0xAAAAAAAA
  23. #endif
  24. //////////////////////////////////////////////////////////////////////
  25. //
  26. // Internal Prototypes
  27. //
  28. PSESSIONLISTNODE FetchSessionListNode(IN RDPEVNTLIST list,
  29. IN ULONG sessionID,
  30. BOOL createIfNotFound);
  31. void CleanupSessionListNodeRequestList(IN PSESSIONLISTNODE sessionListNode);
  32. void CleanupSessionListNodeEventList(
  33. IN PSESSIONLISTNODE sessionListNode
  34. );
  35. void ReleaseSessionListNode(IN RDPEVNTLIST list, IN ULONG sessionID);
  36. #if DBG
  37. void CheckListIntegrity(IN RDPEVNTLIST list);
  38. #endif
  39. //////////////////////////////////////////////////////////////////////
  40. //
  41. // Global Variables
  42. //
  43. #if DBG
  44. ULONG RDPEVNTLIST_LockCount = 0;
  45. #endif
  46. RDPEVNTLIST
  47. RDPEVNTLIST_CreateNewList()
  48. /*++
  49. Routine Description:
  50. Create a new pending device list.
  51. Arguments:
  52. Return Value:
  53. RDPEVNTLIST_INVALID_LIST on error. A new device list on success.
  54. --*/
  55. {
  56. PSESSIONLIST sessionList;
  57. BEGIN_FN("RDPEVNTLIST_CreateNewList");
  58. sessionList = new(NonPagedPool) SESSIONLIST;
  59. if (sessionList != NULL) {
  60. #if DBG
  61. sessionList->magicNo = MAGICNO;
  62. #endif
  63. KeInitializeSpinLock(&sessionList->spinlock);
  64. InitializeListHead(&sessionList->listHead);
  65. }
  66. return (RDPEVNTLIST)sessionList;
  67. }
  68. void RDPEVNTLIST_DestroyList(IN RDPEVNTLIST list)
  69. /*++
  70. Routine Description:
  71. Release a pending device list.
  72. Arguments:
  73. list
  74. Return Value:
  75. NULL on error. A new device list on success.
  76. --*/
  77. {
  78. #ifdef DBG
  79. PSESSIONLIST sessionList=NULL;
  80. #else
  81. PSESSIONLIST sessionList;
  82. #endif
  83. PSESSIONLISTNODE sessionListNode;
  84. PLIST_ENTRY sessionListEntry;
  85. BEGIN_FN("RDPEVNTLIST_DestroyList");
  86. sessionList = (PSESSIONLIST)list;
  87. #if DBG
  88. CheckListIntegrity(list);
  89. #endif
  90. //
  91. // Clean up each session node in LIFO fashion, as opposed to FIFO, for
  92. // efficiency.
  93. //
  94. while (!IsListEmpty(&sessionList->listHead)) {
  95. sessionListEntry = RemoveHeadList(&sessionList->listHead);
  96. sessionListNode = CONTAINING_RECORD(sessionListEntry, SESSIONLISTNODE, listEntry);
  97. TRC_ASSERT(sessionListNode->magicNo == MAGICNO,
  98. (TB, "Invalid magic number in list block entry."));
  99. // Clean up the request list for the current session node.
  100. CleanupSessionListNodeRequestList(sessionListNode);
  101. // Clean up the event list for the current session node.
  102. CleanupSessionListNodeEventList(sessionListNode);
  103. // Release the current session node.
  104. #if DBG
  105. sessionListNode->magicNo = BOGUSMAGICNO;
  106. #endif
  107. delete sessionListNode;
  108. }
  109. // Release the list.
  110. #if DBG
  111. sessionList->magicNo = BOGUSMAGICNO;
  112. #endif
  113. delete sessionList;
  114. }
  115. NTSTATUS
  116. RDPDEVNTLIST_EnqueueEventEx(
  117. IN RDPEVNTLIST list,
  118. IN ULONG sessionID,
  119. IN void *event,
  120. IN DrDevice *device,
  121. IN ULONG type,
  122. IN BOOL insertAtHead
  123. )
  124. /*++
  125. Routine Description:
  126. Queue a new pending event for the specified session. Note that this function simply
  127. stores the event pointer. It does not copy the data pointed to by
  128. the pointer.
  129. Arguments:
  130. list - Event management list allocated by RDPDDEVLIST_CreateNewList.
  131. sessionID - Identifier for session to associate with the device.
  132. devMgmtEvent - Pending device management event.
  133. type - Numeric identifier for event type. Valid values for this field
  134. are defined by the function caller.
  135. insertAtHead - If TRUE, then the element is queued at the head of the queue
  136. in standard FIFO fashion. Otherwise, the element is queued at
  137. the tail of the queue. This is convenient for requeueing.
  138. Return Value:
  139. NTSUCCESS on success. Alternative status, otherwise.
  140. --*/
  141. {
  142. PSESSIONLISTNODE sessionListNode;
  143. PLIST_ENTRY sessionListEntry;
  144. PLIST_ENTRY eventListEntry;
  145. PEVENTLISTNODE eventListNode;
  146. NTSTATUS ntStatus;
  147. #if DBG
  148. PSESSIONLIST sessionList=NULL;
  149. #else
  150. PSESSIONLIST sessionList;
  151. #endif
  152. BEGIN_FN("RDPDEVNTLIST_EnqueueEventEx");
  153. TRC_NRM((TB, "session %ld.", sessionID));
  154. sessionList = (PSESSIONLIST)list;
  155. #if DBG
  156. CheckListIntegrity(list);
  157. #endif
  158. // Fetch the session list node corresponding to the session ID.
  159. sessionListNode = FetchSessionListNode(list, sessionID, TRUE);
  160. if (sessionListNode == NULL) {
  161. ntStatus = STATUS_NO_MEMORY;
  162. goto ReturnWithStatus;
  163. }
  164. //
  165. // Add a new entry to the event list.
  166. //
  167. // Allocate a new event list node.
  168. eventListNode = new(NonPagedPool) EVENTLISTNODE;
  169. if (eventListNode != NULL) {
  170. // Initialize the new node.
  171. #if DBG
  172. eventListNode->magicNo = MAGICNO;
  173. #endif
  174. eventListNode->event = event;
  175. eventListNode->type = type;
  176. eventListNode->device = device;
  177. // Add it to the list head.
  178. if (insertAtHead) {
  179. InsertHeadList(&sessionListNode->eventListHead,
  180. &eventListNode->listEntry);
  181. }
  182. else {
  183. InsertTailList(&sessionListNode->eventListHead,
  184. &eventListNode->listEntry);
  185. }
  186. ntStatus = STATUS_SUCCESS;
  187. }
  188. else {
  189. ntStatus = STATUS_NO_MEMORY;
  190. }
  191. #if DBG
  192. CheckListIntegrity(list);
  193. #endif
  194. ReturnWithStatus:
  195. return ntStatus;
  196. }
  197. NTSTATUS
  198. RDPEVNTLIST_EnqueueEvent(
  199. IN RDPEVNTLIST list,
  200. IN ULONG sessionID,
  201. IN void *event,
  202. IN ULONG type,
  203. OPTIONAL IN DrDevice *device
  204. )
  205. /*++
  206. Routine Description:
  207. Queue a new pending event for the specified session. Note that this function simply
  208. stores the event pointer. It does not copy the data pointed to by
  209. the pointer.
  210. Arguments:
  211. list - Event management list allocated by RDPDDEVLIST_CreateNewList.
  212. sessionID - Identifier for session to associate with the device.
  213. devMgmtEvent - Pending device management event.
  214. type - Numeric identifier for event type. Valid values for this field
  215. are defined by the function caller.
  216. Return Value:
  217. NTSUCCESS on success. Alternative status, otherwise.
  218. --*/
  219. {
  220. BEGIN_FN("RDPEVNTLIST_EnqueueEvent");
  221. // Insert at the head of the queue.
  222. return RDPDEVNTLIST_EnqueueEventEx(list, sessionID, event, device, type, TRUE);
  223. }
  224. NTSTATUS
  225. RDPEVNTLIST_RequeueEvent(
  226. IN RDPEVNTLIST list,
  227. IN ULONG sessionID,
  228. IN void *event,
  229. IN ULONG type,
  230. OPTIONAL IN DrDevice *device
  231. )
  232. /*++
  233. Routine Description:
  234. Requeue a pending event for the specified session at the tail of the queue.
  235. Note that this function simply stores the event pointer. It does not copy the
  236. data pointed to by the pointer.
  237. Arguments:
  238. list - Event management list allocated by RDPDDEVLIST_CreateNewList.
  239. sessionID - Identifier for session to associate with the device.
  240. devMgmtEvent - Pending device management event.
  241. type - Numeric identifier for event type. Valid values for this field
  242. are defined by the function caller.
  243. Return Value:
  244. NTSUCCESS on success. Alternative status, otherwise.
  245. --*/
  246. {
  247. BEGIN_FN("RDPEVNTLIST_RequeueEvent");
  248. // Insert at the head of the queue.
  249. return RDPDEVNTLIST_EnqueueEventEx(list, sessionID, event, device, type, FALSE);
  250. }
  251. BOOL RDPEVNTLIST_PeekNextEvent(
  252. IN RDPEVNTLIST list,
  253. IN ULONG sessionID,
  254. PVOID *eventPtr,
  255. OPTIONAL IN OUT ULONG *type,
  256. OPTIONAL IN OUT DrDevice **devicePtr
  257. )
  258. /*++
  259. Routine Description:
  260. Peek at the next pending event for the specified session, without dequeueing
  261. it. NULL is returned if there are no more pending events for the specified
  262. session. Note that, if non-NULL is returned, the pointer returned is the
  263. pointer that was passed in to RDPEVNTLIST_EnqueueEvent.
  264. Arguments:
  265. list - Event management list allocated by RDPDDEVLIST_CreateNewList.
  266. sessionID - Identifier for session to associate with the device.
  267. type - Can be used to identify the type of event.
  268. eventPtr - The returned event.
  269. Return Value:
  270. TRUE if pending event exists. FALSE, otherwise.
  271. --*/
  272. {
  273. PSESSIONLISTNODE sessionListNode;
  274. PEVENTLISTNODE eventListNode;
  275. PLIST_ENTRY tail;
  276. BOOL result;
  277. PSESSIONLIST sessionList;
  278. BEGIN_FN("RDPEVNTLIST_PeekNextEvent");
  279. TRC_NRM((TB, "session %ld.", sessionID));
  280. sessionList = (PSESSIONLIST)list;
  281. #if DBG
  282. CheckListIntegrity(list);
  283. #endif
  284. //
  285. // Fetch the session list node corresponding to the session ID.
  286. //
  287. sessionListNode = FetchSessionListNode(list, sessionID, FALSE);
  288. //
  289. // If we have a non-empty session list node.
  290. //
  291. if ((sessionListNode != NULL) &&
  292. !IsListEmpty(&sessionListNode->eventListHead)) {
  293. //
  294. // Get the event at the tail of the session's event list.
  295. //
  296. tail = sessionListNode->eventListHead.Blink;
  297. eventListNode = CONTAINING_RECORD(tail, EVENTLISTNODE, listEntry);
  298. TRC_ASSERT(eventListNode->magicNo == MAGICNO,
  299. (TB, "Invalid event list node."));
  300. //
  301. // Grab the fields to return.
  302. //
  303. *eventPtr = eventListNode->event;
  304. if (type != NULL) *type = eventListNode->type;
  305. if (devicePtr != NULL) *devicePtr = eventListNode->device;
  306. result = TRUE;
  307. }
  308. else {
  309. *eventPtr = NULL;
  310. result = FALSE;
  311. }
  312. return result;
  313. }
  314. BOOL RDPEVNTLIST_DequeueEvent(
  315. IN RDPEVNTLIST list,
  316. IN ULONG sessionID,
  317. OPTIONAL IN OUT ULONG *type,
  318. PVOID *eventPtr,
  319. OPTIONAL IN OUT DrDevice **devicePtr
  320. )
  321. /*++
  322. Routine Description:
  323. Returns and removes the next pending event for the specified session.
  324. NULL is returned if there are no more pending events for the specified session.
  325. Note that, if non-NULL is returned, the pointer returned is the pointer that was
  326. passed in to RDPEVNTLIST_EnqueueEvent.
  327. Arguments:
  328. list - Event management list allocated by RDPDDEVLIST_CreateNewList.
  329. sessionID - Identifier for session to associate with the device.
  330. type - Can be used to identify the type of event.
  331. eventPtr - Returned event.
  332. Return Value:
  333. TRUE if Pending event if one exists. FALSE, otherwise.
  334. --*/
  335. {
  336. PSESSIONLISTNODE sessionListNode;
  337. PLIST_ENTRY eventListEntry;
  338. PEVENTLISTNODE eventListNode;
  339. #ifdef DBG
  340. PSESSIONLIST sessionList=NULL;
  341. #else
  342. PSESSIONLIST sessionList;
  343. #endif
  344. BOOL result;
  345. BEGIN_FN("RDPEVNTLIST_DequeueEvent");
  346. TRC_NRM((TB, "session %ld.", sessionID));
  347. sessionList = (PSESSIONLIST)list;
  348. #if DBG
  349. CheckListIntegrity(list);
  350. #endif
  351. //
  352. // Fetch the session list node corresponding to the session ID.
  353. //
  354. sessionListNode = FetchSessionListNode(list, sessionID, FALSE);
  355. if (sessionListNode != NULL) {
  356. //
  357. // Get the next session list node in FIFO fashion.
  358. //
  359. if (!IsListEmpty(&sessionListNode->eventListHead)) {
  360. eventListEntry = RemoveTailList(&sessionListNode->eventListHead);
  361. eventListNode = CONTAINING_RECORD(eventListEntry, EVENTLISTNODE, listEntry);
  362. TRC_ASSERT(eventListNode->magicNo == MAGICNO,
  363. (TB, "Invalid event list node."));
  364. *eventPtr = eventListNode->event;
  365. if (type != NULL) {
  366. *type = eventListNode->type;
  367. }
  368. if (devicePtr != NULL) {
  369. *devicePtr = eventListNode->device;
  370. }
  371. result = TRUE;
  372. #if DBG
  373. eventListNode->magicNo = BOGUSMAGICNO;
  374. #endif
  375. // Release the event list node.
  376. delete eventListNode;
  377. TRC_NRM((TB, "returning session %ld entry.", sessionID));
  378. }
  379. else {
  380. TRC_NRM((TB, "session %ld empty.", sessionID));
  381. *eventPtr = NULL;
  382. result = FALSE;
  383. }
  384. //
  385. // If the request list is empty and the event list is empty, then
  386. // delete the session node.
  387. //
  388. if (IsListEmpty(&sessionListNode->requestListHead) &&
  389. IsListEmpty(&sessionListNode->eventListHead)) {
  390. ReleaseSessionListNode(list, sessionListNode->sessionID);
  391. }
  392. }
  393. else {
  394. TRC_NRM((TB, "session %ld not found.", sessionID));
  395. *eventPtr = NULL;
  396. result = FALSE;
  397. }
  398. #if DBG
  399. CheckListIntegrity(list);
  400. #endif
  401. return result;
  402. }
  403. NTSTATUS RDPEVNTLIST_EnqueueRequest(IN RDPEVNTLIST list,
  404. IN ULONG sessionID, IN PVOID request)
  405. /*++
  406. Routine Description:
  407. Add a new pending request. Note that this function simply stores the request
  408. pointer. It does not copy the data pointed to by the pointer.
  409. Arguments:
  410. list - Device management list allocated by RDPDDEVLIST_CreateNewList.
  411. sessionID - Identifier for session to associate with the device.
  412. request - Pending request.
  413. Return Value:
  414. Pending event if one exists. NULL, otherwise.
  415. --*/
  416. {
  417. PSESSIONLISTNODE sessionListNode;
  418. PLIST_ENTRY sessionListEntry;
  419. PLIST_ENTRY requestListEntry;
  420. PREQUESTLISTNODE requestListNode;
  421. NTSTATUS ntStatus;
  422. #ifdef DBG
  423. PSESSIONLIST sessionList=NULL;
  424. #else
  425. PSESSIONLIST sessionList;
  426. #endif
  427. BEGIN_FN("RDPEVNTLIST_EnqueueRequest");
  428. TRC_NRM((TB, "session %ld.", sessionID));
  429. sessionList = (PSESSIONLIST)list;
  430. #if DBG
  431. CheckListIntegrity(list);
  432. #endif
  433. // Fetch the session list node corresponding to the session ID.
  434. sessionListNode = FetchSessionListNode(list, sessionID, TRUE);
  435. if (sessionListNode == NULL) {
  436. ntStatus = STATUS_NO_MEMORY;
  437. goto ReturnWithStatus;
  438. }
  439. //
  440. // Add a new entry to the event list.
  441. //
  442. // Allocate a new request list node.
  443. requestListNode = new(NonPagedPool) REQUESTLISTNODE;
  444. if (requestListNode != NULL) {
  445. // Add it to the list head.
  446. #if DBG
  447. requestListNode->magicNo = MAGICNO;
  448. #endif
  449. requestListNode->request = request;
  450. InsertHeadList(&sessionListNode->requestListHead, &requestListNode->listEntry);
  451. ntStatus = STATUS_SUCCESS;
  452. }
  453. else {
  454. ntStatus = STATUS_NO_MEMORY;
  455. }
  456. #if DBG
  457. CheckListIntegrity(list);
  458. #endif
  459. ReturnWithStatus:
  460. return ntStatus;
  461. }
  462. PVOID RDPEVNTLIST_DequeueRequest(IN RDPEVNTLIST list,
  463. IN ULONG sessionID)
  464. /*++
  465. Routine Description:
  466. Returns and removes the next pending request for the specified session.
  467. NULL is returned if there are no more pending devices for the specified session.
  468. Note that, if non-NULL is returned, the pointer returned is the pointer that was
  469. passed in to RDPEVNTLIST_EnqueueRequest.
  470. Arguments:
  471. list - Device management list allocated by RDPDDEVLIST_CreateNewList.
  472. sessionID - Identifier for session to associate with the device.
  473. Return Value:
  474. Pending request if one exists. NULL, otherwise.
  475. --*/
  476. {
  477. PSESSIONLISTNODE sessionListNode;
  478. PLIST_ENTRY requestListEntry;
  479. PREQUESTLISTNODE requestListNode;
  480. PVOID requestPtr;
  481. #ifdef DBG
  482. PSESSIONLIST sessionList=NULL;
  483. #else
  484. PSESSIONLIST sessionList;
  485. #endif
  486. BEGIN_FN("RDPEVNTLIST_DequeueRequest");
  487. TRC_NRM((TB, "session %ld.", sessionID));
  488. sessionList = (PSESSIONLIST)list;
  489. #if DBG
  490. CheckListIntegrity(list);
  491. #endif
  492. //
  493. // Fetch the session list node corresponding to the session ID.
  494. //
  495. sessionListNode = FetchSessionListNode(list, sessionID, FALSE);
  496. if (sessionListNode != NULL) {
  497. //
  498. // Get the next session list node in FIFO fashion.
  499. //
  500. if (!IsListEmpty(&sessionListNode->requestListHead)) {
  501. requestListEntry = RemoveTailList(&sessionListNode->requestListHead);
  502. requestListNode = CONTAINING_RECORD(requestListEntry, REQUESTLISTNODE, listEntry);
  503. TRC_ASSERT(requestListNode->magicNo == MAGICNO, (TB, "Invalid request list node."));
  504. requestPtr = requestListNode->request;
  505. #if DBG
  506. requestListNode->magicNo = BOGUSMAGICNO;
  507. #endif
  508. // Release the event list node.
  509. delete requestListNode;
  510. }
  511. else {
  512. requestPtr = NULL;
  513. }
  514. //
  515. // If the request list is empty and the event list is empty, then
  516. // delete the session node.
  517. //
  518. if (IsListEmpty(&sessionListNode->requestListHead) &&
  519. IsListEmpty(&sessionListNode->eventListHead)) {
  520. ReleaseSessionListNode(list, sessionListNode->sessionID);
  521. #if DBG
  522. sessionListNode = NULL;
  523. #endif
  524. }
  525. }
  526. else {
  527. requestPtr = NULL;
  528. }
  529. #if DBG
  530. CheckListIntegrity(list);
  531. #endif
  532. return requestPtr;
  533. }
  534. PVOID
  535. RDPEVNTLIST_DequeueSpecificRequest(
  536. IN RDPEVNTLIST list,
  537. IN ULONG sessionID,
  538. IN PVOID request
  539. )
  540. /*++
  541. Routine Description:
  542. Dequeues a specific request from a session's request list. The dequeued request
  543. is returned if it is found. Otherwise, NULL is returned.
  544. Arguments:
  545. list - Device management list allocated by RDPDDEVLIST_CreateNewList.
  546. request - A request that was queued for the specified session via
  547. RDPEVNTLIST_EnqueueRequest.
  548. sessionID - Session from which the request should be dequeued.
  549. Return Value:
  550. Pending request if one exists. NULL, otherwise.
  551. --*/
  552. {
  553. PSESSIONLISTNODE sessionListNode;
  554. PLIST_ENTRY requestListEntry;
  555. PREQUESTLISTNODE requestListNode;
  556. PVOID requestPtr = NULL;
  557. #ifdef DBG
  558. PSESSIONLIST sessionList=NULL;
  559. #else
  560. PSESSIONLIST sessionList;
  561. #endif
  562. BEGIN_FN("RDPEVNTLIST_DequeueSpecificRequest");
  563. TRC_NRM((TB, "session %ld.", sessionID));
  564. sessionList = (PSESSIONLIST)list;
  565. #if DBG
  566. CheckListIntegrity(list);
  567. #endif
  568. //
  569. // Fetch the session list node corresponding to the session ID.
  570. //
  571. sessionListNode = FetchSessionListNode(list, sessionID, FALSE);
  572. if (sessionListNode != NULL) {
  573. //
  574. // Perform a linear search for the specified request.
  575. //
  576. requestListEntry = sessionListNode->requestListHead.Flink;
  577. while(requestListEntry != &sessionListNode->requestListHead) {
  578. requestListNode = CONTAINING_RECORD(requestListEntry, REQUESTLISTNODE, listEntry);
  579. TRC_ASSERT(requestListNode->magicNo == MAGICNO,
  580. (TB, "Invalid magic number in list block entry."));
  581. if (requestListNode->request == request) {
  582. requestPtr = requestListNode->request;
  583. break;
  584. }
  585. requestListEntry = requestListEntry->Flink;
  586. }
  587. //
  588. // If we found the entry, then remove it.
  589. //
  590. if (requestPtr != NULL) {
  591. #if DBG
  592. requestListNode->magicNo = BOGUSMAGICNO;
  593. #endif
  594. RemoveEntryList(requestListEntry);
  595. // Release the request list node.
  596. delete requestListNode;
  597. }
  598. else {
  599. TRC_ALT((TB, "no req. for session %ld.", sessionID));
  600. }
  601. //
  602. // If the request list is empty and the event list is empty, then
  603. // delete the session node.
  604. //
  605. if (IsListEmpty(&sessionListNode->requestListHead) &&
  606. IsListEmpty(&sessionListNode->eventListHead)) {
  607. ReleaseSessionListNode(list, sessionListNode->sessionID);
  608. #if DBG
  609. sessionListNode = NULL;
  610. #endif
  611. }
  612. }
  613. else {
  614. TRC_ALT((TB, "did not find session %ld.", sessionID));
  615. }
  616. #if DBG
  617. CheckListIntegrity(list);
  618. #endif
  619. return requestPtr;
  620. }
  621. void CleanupSessionListNodeEventList(
  622. IN PSESSIONLISTNODE sessionListNode
  623. )
  624. /*++
  625. Routine Description:
  626. Clean up the event list for the specified session node. The list must
  627. be locked before this function is called.
  628. Arguments:
  629. sessionListNode - Session list node to clean up.
  630. Return Value:
  631. None.
  632. --*/
  633. {
  634. PLIST_ENTRY eventListEntry;
  635. PEVENTLISTNODE eventListNode;
  636. BEGIN_FN("CleanupSessionListNodeEventList");
  637. //
  638. // Clean up the event list for the current session node in LIFO, as
  639. // opposed to FIFO fashion, for efficiency.
  640. //
  641. while (!IsListEmpty(&sessionListNode->eventListHead)) {
  642. eventListEntry = RemoveHeadList(&sessionListNode->eventListHead);
  643. eventListNode = CONTAINING_RECORD(eventListEntry, EVENTLISTNODE, listEntry);
  644. TRC_ASSERT(eventListNode->magicNo == MAGICNO,
  645. (TB, "Invalid magic number in list block entry."));
  646. // Release the current request node.
  647. #if DBG
  648. eventListNode->magicNo = BOGUSMAGICNO;
  649. #endif
  650. delete eventListNode;
  651. }
  652. }
  653. void CleanupSessionListNodeRequestList(
  654. IN PSESSIONLISTNODE sessionListNode
  655. )
  656. /*++
  657. Routine Description:
  658. Clean up the request list for the specified session node. The list must
  659. be locked before this function is called.
  660. Arguments:
  661. sessionListNode - Session list node to clean up.
  662. Return Value:
  663. None.
  664. --*/
  665. {
  666. PLIST_ENTRY requestListEntry;
  667. PREQUESTLISTNODE requestListNode;
  668. PVOID requestPtr;
  669. BEGIN_FN("CleanupSessionListNodeRequestList");
  670. //
  671. // Clean up the request list for the current session node in LIFO
  672. // fashion, as opposed to FIFO fashion, for efficiency.
  673. //
  674. while (!IsListEmpty(&sessionListNode->requestListHead)) {
  675. requestListEntry = RemoveHeadList(&sessionListNode->requestListHead);
  676. requestListNode = CONTAINING_RECORD(requestListEntry, REQUESTLISTNODE, listEntry);
  677. TRC_ASSERT(requestListNode->magicNo == MAGICNO,
  678. (TB, "Invalid magic number in list block entry."));
  679. // Release the current request node.
  680. #if DBG
  681. requestListNode->magicNo = BOGUSMAGICNO;
  682. #endif
  683. delete requestListNode;
  684. }
  685. }
  686. void
  687. ReleaseSessionListNode(
  688. IN RDPEVNTLIST list,
  689. IN ULONG sessionID
  690. )
  691. /*++
  692. Routine Description:
  693. Remove the session list node from the list if it exists.
  694. Arguments:
  695. list - Device management list allocated by
  696. RDPDDEVLIST_CreateNewList.
  697. sessionID - Identifier for session list node to fetch.
  698. Return Value:
  699. The matching session node or NULL on error.
  700. --*/
  701. {
  702. PSESSIONLIST sessionList;
  703. PSESSIONLISTNODE sessionListNode;
  704. PLIST_ENTRY current;
  705. BEGIN_FN("ReleaseSessionListNode");
  706. #if DBG
  707. CheckListIntegrity(list);
  708. #endif
  709. // Cast the list to the correct type.
  710. sessionList = (PSESSIONLIST)list;
  711. TRC_ASSERT(sessionList->magicNo == MAGICNO,
  712. (TB, "Invalid magic number in session list."));
  713. //
  714. // Scan through the session node list, looking for a matching session.
  715. //
  716. current = sessionList->listHead.Flink;
  717. while (current != &sessionList->listHead) {
  718. sessionListNode = CONTAINING_RECORD(current, SESSIONLISTNODE, listEntry);
  719. TRC_ASSERT(sessionListNode->magicNo == MAGICNO,
  720. (TB, "Invalid magic number in list block entry."));
  721. if (sessionListNode->sessionID == sessionID) {
  722. break;
  723. }
  724. current = current->Flink;
  725. }
  726. //
  727. // Clean up the found entry.
  728. //
  729. if (current != &sessionList->listHead) {
  730. // Remove the entry from the linked list.
  731. RemoveEntryList(current);
  732. // Clean up the found node's request list.
  733. CleanupSessionListNodeRequestList(sessionListNode);
  734. // Clean up the event list for the found node.
  735. CleanupSessionListNodeEventList(sessionListNode);
  736. // Release the found session node.
  737. #if DBG
  738. sessionListNode->magicNo = BOGUSMAGICNO;
  739. #endif
  740. delete sessionListNode;
  741. }
  742. #if DBG
  743. CheckListIntegrity(list);
  744. #endif
  745. }
  746. PSESSIONLISTNODE
  747. FetchSessionListNode(
  748. IN RDPEVNTLIST list,
  749. IN ULONG sessionID,
  750. IN BOOL createIfNotFound
  751. )
  752. /*++
  753. Routine Description:
  754. This is a convenience function that fetches the session list node with the
  755. specified session ID.
  756. Arguments:
  757. list - Device management list allocated by
  758. RDPDDEVLIST_CreateNewList.
  759. sessionID - Identifier for session list node to fetch.
  760. createIfNotFound - Flag that indicates whether the function should create
  761. a session list node if one is not found.
  762. Return Value:
  763. The matching session node or NULL on error.
  764. --*/
  765. {
  766. PSESSIONLIST sessionList;
  767. PSESSIONLISTNODE sessionListNode;
  768. PLIST_ENTRY sessionListEntry;
  769. BEGIN_FN("FetchSessionListNode");
  770. // Cast the list to the correct type.
  771. sessionList = (PSESSIONLIST)list;
  772. TRC_ASSERT(sessionList->magicNo == MAGICNO,
  773. (TB, "Invalid magic number in session list."));
  774. #if DBG
  775. CheckListIntegrity(list);
  776. #endif
  777. //
  778. // Scan through the session node list, looking for a matching session.
  779. //
  780. sessionListEntry = sessionList->listHead.Flink;
  781. while(sessionListEntry != &sessionList->listHead) {
  782. sessionListNode = CONTAINING_RECORD(sessionListEntry, SESSIONLISTNODE, listEntry);
  783. TRC_ASSERT(sessionListNode->magicNo == MAGICNO,
  784. (TB, "Invalid magic number in list block entry."));
  785. if (sessionListNode->sessionID == sessionID) {
  786. break;
  787. }
  788. sessionListEntry = sessionListEntry->Flink;
  789. }
  790. // If we didn't find a match.
  791. if (sessionListEntry == &sessionList->listHead) {
  792. // If we are supposed to create a missing node.
  793. if (createIfNotFound) {
  794. // Allocate a new session list node.
  795. sessionListNode = new(NonPagedPool) SESSIONLISTNODE;
  796. if (sessionListNode != NULL) {
  797. #if DBG
  798. sessionListNode->magicNo = MAGICNO;
  799. #endif
  800. InitializeListHead(&sessionListNode->requestListHead);
  801. InitializeListHead(&sessionListNode->eventListHead);
  802. sessionListNode->sessionID = sessionID;
  803. // Add it to the head list.
  804. InsertHeadList(&sessionList->listHead, &sessionListNode->listEntry);
  805. }
  806. }
  807. // Otherwise, just return NULL for the session list node.
  808. else {
  809. sessionListNode = NULL;
  810. }
  811. }
  812. #if DBG
  813. CheckListIntegrity(list);
  814. #endif
  815. return sessionListNode;
  816. }
  817. BOOL
  818. RDPEVNTLLIST_GetFirstSessionID(
  819. IN RDPEVNTLIST list,
  820. IN ULONG *pSessionID
  821. )
  822. /*++
  823. Routine Description:
  824. Get the first session ID in the set of currently managed sessions. A
  825. session is managed if there are any pending request's or events.
  826. A session is no longer managed when there are no longer any
  827. pending request's or events.
  828. This session is useful for cleaning up pending request's and pending events.
  829. Arguments:
  830. list - Device management list allocated by RDPDDEVLIST_CreateNewList.
  831. pSessionID - Pointer for storing returned first session ID.
  832. Return Value:
  833. TRUE if a session ID is returned. FALSE, if there are no more sessions being
  834. managed by the list.
  835. --*/
  836. {
  837. PSESSIONLIST sessionList;
  838. PLIST_ENTRY sessionListEntry;
  839. PSESSIONLISTNODE sessionListNode;
  840. BEGIN_FN("RDPEVNTLLIST_GetFirstSessionID");
  841. sessionList = (PSESSIONLIST)list;
  842. #if DBG
  843. CheckListIntegrity(list);
  844. #endif
  845. // If the list is empty.
  846. if (IsListEmpty(&sessionList->listHead)) {
  847. return FALSE;
  848. }
  849. else {
  850. sessionListNode = CONTAINING_RECORD(sessionList->listHead.Flink,
  851. SESSIONLISTNODE, listEntry);
  852. TRC_ASSERT(sessionListNode->magicNo == MAGICNO,
  853. (TB, "Invalid magic number in list block entry."));
  854. *pSessionID = sessionListNode->sessionID;
  855. return TRUE;
  856. }
  857. }
  858. #if DBG
  859. void CheckListIntegrity(
  860. IN RDPEVNTLIST list
  861. )
  862. /*++
  863. Routine Description:
  864. Check the integrity of the event and request list.
  865. Arguments:
  866. Return Value:
  867. None
  868. --*/
  869. {
  870. PLIST_ENTRY currentSessionEntry;
  871. PLIST_ENTRY listEntry;
  872. PREQUESTLISTNODE requestListNode;
  873. PEVENTLISTNODE eventListNode;
  874. PSESSIONLISTNODE sessionListNode;
  875. PSESSIONLIST sessionList;
  876. BEGIN_FN("CheckListIntegrity");
  877. sessionList = (PSESSIONLIST)list;
  878. currentSessionEntry = sessionList->listHead.Flink;
  879. while (currentSessionEntry != &sessionList->listHead) {
  880. // Check the current session node.
  881. sessionListNode = CONTAINING_RECORD(currentSessionEntry,
  882. SESSIONLISTNODE, listEntry);
  883. if (sessionListNode->magicNo == BOGUSMAGICNO) {
  884. TRC_ASSERT(FALSE,
  885. (TB, "Stale link in event list in session list entry in integrity check."));
  886. }
  887. else if (sessionListNode->magicNo != MAGICNO) {
  888. TRC_ASSERT(FALSE,
  889. (TB, "Invalid magic number in session list entry in integrity check."));
  890. }
  891. // Check the current session's request list.
  892. listEntry = sessionListNode->requestListHead.Flink;
  893. while (listEntry != &sessionListNode->requestListHead) {
  894. requestListNode = CONTAINING_RECORD(listEntry,
  895. REQUESTLISTNODE, listEntry);
  896. if (requestListNode->magicNo == BOGUSMAGICNO) {
  897. TRC_ASSERT(FALSE,
  898. (TB, "Stale link in event list entry in integrity check."));
  899. }
  900. else if (requestListNode->magicNo != MAGICNO) {
  901. TRC_ASSERT(FALSE,
  902. (TB, "Invalid magic number in request list entry in integrity check."));
  903. }
  904. listEntry = listEntry->Flink;
  905. }
  906. // Check the current session's event list.
  907. listEntry = sessionListNode->eventListHead.Flink;
  908. while (listEntry != &sessionListNode->eventListHead) {
  909. eventListNode = CONTAINING_RECORD(listEntry,
  910. EVENTLISTNODE, listEntry);
  911. if (eventListNode->magicNo == BOGUSMAGICNO) {
  912. TRC_ASSERT(FALSE,
  913. (TB, "Stale link in event list entry in integrity check."));
  914. }
  915. else if (eventListNode->magicNo != MAGICNO) {
  916. TRC_ASSERT(FALSE,
  917. (TB, "Corrupted magic number in event list entry in integrity check."));
  918. }
  919. listEntry = listEntry->Flink;
  920. }
  921. // Next session entry.
  922. currentSessionEntry = currentSessionEntry->Flink;
  923. }
  924. }
  925. #endif
  926. #if DBG
  927. void RDPEVNTLIST_UnitTest()
  928. /*++
  929. Routine Description:
  930. Unit-Test function that can be called from a kernel-mode driver to
  931. cover all functions implemented by this module.
  932. Arguments:
  933. Return Value:
  934. none.
  935. --*/
  936. #define MAXSESSIONS 2
  937. #define MAXREQUESTS 2
  938. #define MAXEVENT 2
  939. #define REQUESTADDRESS (PVOID)0x55000055
  940. #define DEVEVTADDRESS (PVOID)0x66000066
  941. #define TESTDEVTYPE (ULONG)0
  942. {
  943. RDPEVNTLIST devList;
  944. ULONG i,j;
  945. PVOID address;
  946. ULONG sessionID;
  947. BOOL result;
  948. BEGIN_FN("RDPEVNTLIST_UnitTest");
  949. devList = RDPEVNTLIST_CreateNewList();
  950. TRC_ASSERT(devList != NULL,
  951. (TB, "Unit test failed because list did not initialize properly."));
  952. // Add request's and event pointers for each session.
  953. for (i=0; i<MAXSESSIONS; i++) {
  954. for (j=0; j<MAXREQUESTS; j++) {
  955. if (!(j%5)) {
  956. TRC_NRM((TB, "Adding test requests"));
  957. }
  958. RDPEVNTLIST_EnqueueRequest(devList, i, (PVOID)REQUESTADDRESS);
  959. }
  960. for (j=0; j<MAXEVENT; j++) {
  961. if (!(j%5)) {
  962. TRC_NRM((TB, "Adding test device events."));
  963. }
  964. RDPEVNTLIST_EnqueueEvent(devList, i,
  965. DEVEVTADDRESS,
  966. TESTDEVTYPE,
  967. NULL);
  968. }
  969. }
  970. // Remove them.
  971. for (i=0; i<MAXSESSIONS; i++) {
  972. for (j=0; j<MAXREQUESTS; j++) {
  973. address = RDPEVNTLIST_DequeueRequest(devList, i);
  974. TRC_ASSERT(address == REQUESTADDRESS,
  975. (TB, "Unit test failed because invalid request address."));
  976. if (!(j%5)) {
  977. TRC_NRM((TB, "Removing test requests"));
  978. }
  979. }
  980. TRC_ASSERT(RDPEVNTLIST_DequeueRequest(devList, i) == NULL, (TB, ""));
  981. for (j=0; j<MAXEVENT; j++) {
  982. if (!(j%5)) {
  983. TRC_NRM((TB, "Removing test events"));
  984. }
  985. result = RDPEVNTLIST_DequeueEvent(devList, i, NULL, &address, NULL);
  986. TRC_ASSERT(result, (TB, "Unit test failed because missing event."));
  987. TRC_ASSERT(address == DEVEVTADDRESS,
  988. (TB, "Unit test failed because invalid event address."));
  989. }
  990. TRC_ASSERT(!RDPEVNTLIST_DequeueEvent(devList, i, NULL, &address, NULL),
  991. (TB, "Unit test failed because pending session exists."));
  992. }
  993. // All sessions should now be removed.
  994. TRC_ASSERT(!RDPEVNTLLIST_GetFirstSessionID(devList, &sessionID),
  995. (TB, "Unit test failed because session exists."));
  996. // Destroy the list.
  997. RDPEVNTLIST_DestroyList(devList);
  998. }
  999. #endif