2 * PROJECT: ReactOS Drivers
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/sac/driver/chanmgr.c
5 * PURPOSE: Driver for the Server Administration Console (SAC) for EMS
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES ******************************************************************/
13 /* GLOBALS *******************************************************************/
15 SAC_CHANNEL_LOCK ChannelCreateLock
;
16 BOOLEAN ChannelCreateEnabled
;
17 PSAC_CHANNEL ChannelArray
[SAC_MAX_CHANNELS
];
18 LONG ChannelRefCount
[SAC_MAX_CHANNELS
];
19 LONG ChannelReaped
[SAC_MAX_CHANNELS
];
20 SAC_CHANNEL_LOCK ChannelSlotLock
[SAC_MAX_CHANNELS
];
21 LONG CurrentChannelRefCount
;
22 KMUTEX CurrentChannelLock
;
24 /* FUNCTIONS *****************************************************************/
26 #define MAX_REF_COUNT 100
28 #define CHANNEL_SLOT_IS_IN_USE(x) (ChannelRefCount[(x)] > 0)
32 ChannelFromIndex(IN ULONG Index
)
34 return ChannelArray
[Index
];
39 ChannelGetReferenceCount(IN LONG Index
)
41 return ChannelRefCount
[Index
];
46 ChannelReferenceByIndex(IN LONG Index
)
48 if (ChannelGetReferenceCount(Index
) > 0)
50 ASSERT(ChannelRefCount
[Index
] <= MAX_REF_COUNT
);
51 ASSERT(ChannelRefCount
[Index
] >= 1);
52 _InterlockedIncrement(&ChannelRefCount
[Index
]);
53 ASSERT(ChannelRefCount
[Index
] <= MAX_REF_COUNT
);
54 ASSERT(ChannelRefCount
[Index
] >= 2);
57 return ChannelGetReferenceCount(Index
);
62 ChannelReferenceByIndexWithLock(IN LONG Index
)
66 ChannelSlotLock(Index
);
67 RefCount
= ChannelReferenceByIndex(Index
);
68 ChannelSlotUnlock(Index
);
74 ChannelDereferenceByIndex(IN LONG Index
)
76 ASSERT(ChannelGetReferenceCount(Index
) <= MAX_REF_COUNT
);
77 ASSERT(ChannelGetReferenceCount(Index
) > 1);
78 _InterlockedDecrement(&ChannelRefCount
[Index
]);
79 ASSERT(ChannelGetReferenceCount(Index
) >= 1);
80 return ChannelGetReferenceCount(Index
);
85 ChannelDereferenceByIndexWithLock(IN LONG Index
)
87 ChannelSlotLock(Index
);
88 ChannelDereferenceByIndex(Index
);
89 ChannelSlotUnlock(Index
);
94 ChannelDereferenceToZeroByIndex(IN LONG Index
)
96 ASSERT(ChannelGetReferenceCount(Index
) == 1);
97 ASSERT(ChannelIsActive(ChannelFromIndex(Index
)) == FALSE
);
98 _InterlockedExchange(&ChannelRefCount
[Index
], 0);
103 ChannelReferenceToOneByIndex(IN LONG Index
)
105 ASSERT(ChannelGetReferenceCount(Index
) == 0);
106 _InterlockedExchange(&ChannelRefCount
[Index
], 1);
111 ChannelReferenceToOneByIndexWithLock(IN LONG Index
)
113 ChannelSlotLock(Index
);
114 ChannelReferenceToOneByIndex(Index
);
115 ChannelSlotUnlock(Index
);
120 ChanMgrInitialize(VOID
)
124 /* Initialize the channel lock */
125 SacInitializeLock(&ChannelCreateLock
);
126 ChannelCreateEnabled
= TRUE
;
128 /* Loop through the channel arrays */
129 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
131 /* Clear and initialize their locks */
132 ChannelArray
[i
] = NULL
;
133 SacInitializeLock(&ChannelSlotLock
[i
]);
135 /* Clear their statuses and reference counts */
136 _InterlockedExchange(&ChannelRefCount
[i
], 0);
137 _InterlockedExchange(&ChannelReaped
[i
], 1);
141 return STATUS_SUCCESS
;
146 ChanMgrShutdown(VOID
)
149 return STATUS_NOT_IMPLEMENTED
;
154 ChanMgrGetChannelByName(IN PWCHAR Name
,
155 OUT PSAC_CHANNEL
* Channel
)
157 NTSTATUS Status
, Status1
;
159 PSAC_CHANNEL CurrentChannel
;
162 CHECK_PARAMETER1(Name
);
163 CHECK_PARAMETER2(Channel
);
167 Status
= STATUS_NOT_FOUND
;
169 /* Loop through all channels */
170 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
172 /* Reference this one and check if it's valid */
173 if (ChannelReferenceByIndexWithLock(i
) > 0)
175 /* All good, grab it */
176 CurrentChannel
= ChannelFromIndex(i
);
177 ASSERT(CurrentChannel
!= NULL
);
180 Status1
= ChannelGetName(CurrentChannel
, &ChannelName
);
181 ASSERT(NT_SUCCESS(Status1
));
183 /* Check if this is the name that was passed in */
184 Found
= wcsicmp(Name
, ChannelName
);
185 SacFreePool(ChannelName
);
188 /* We found it, return it (with a reference held) */
189 *Channel
= CurrentChannel
;
190 return STATUS_SUCCESS
;
193 /* Not the one we want, dereference this one and keep going */
194 ChannelDereferenceByIndexWithLock(i
);
198 /* No channels with this name were found */
204 ChanMgrGetByHandle(IN SAC_CHANNEL_ID ChannelId
,
205 OUT PSAC_CHANNEL
* TargetChannel
)
209 PSAC_CHANNEL Channel
;
210 CHECK_PARAMETER2(TargetChannel
);
213 *TargetChannel
= NULL
;
214 Status
= STATUS_NOT_FOUND
;
216 /* Loop through all channels */
217 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
219 /* Reference this one and check if it's valid */
220 if (ChannelReferenceByIndexWithLock(i
) > 0)
222 /* All good, grab it */
223 Channel
= ChannelFromIndex(i
);
224 ASSERT(Channel
!= NULL
);
226 /* Check if the channel ID matches */
227 if (ChannelIsEqual(Channel
, &ChannelId
))
229 /* We found it, return it (with a reference held) */
230 *TargetChannel
= Channel
;
231 return STATUS_SUCCESS
;
234 /* Not the one we want, dereference this one and keep going */
235 ChannelDereferenceByIndexWithLock(i
);
239 /* No channels with this ID were found */
245 ChanMgrReleaseChannel(IN PSAC_CHANNEL Channel
)
249 PSAC_CHANNEL ThisChannel
;
250 CHECK_PARAMETER(Channel
);
252 /* Get the index of the channel */
253 Index
= ChannelGetIndex(Channel
);
255 /* Drop a reference -- there should still be at least the keepalive left */
256 ChannelSlotLock(Index
);
257 RefCount
= ChannelDereferenceByIndex(Index
);
258 ASSERT(RefCount
> 0);
260 /* Do we only have the keep-alive left, and the channel is dead? */
261 if ((RefCount
== 1) && !(ChannelIsActive(Channel
)))
263 /* Check if the ??? flag is set, or if there's no output data */
264 ThisChannel
= ChannelFromIndex(Index
);
265 if (!(ThisChannel
->Flags
& 1))
267 /* Nope, we can wipe the references and get rid of it */
268 ChannelDereferenceToZeroByIndex(Index
);
270 else if (!ThisChannel
->ChannelHasNewOBufferData
)
272 /* No data, we can wipe the references and get rid of it */
273 ChannelDereferenceToZeroByIndex(Index
);
277 /* We're done, we can unlock the slot now */
278 ChannelSlotUnlock(Index
);
279 return STATUS_SUCCESS
;
284 ChanMgrIsUniqueName(IN PWCHAR ChannelName
)
287 BOOLEAN IsUnique
= FALSE
;
288 PSAC_CHANNEL Channel
;
290 /* Check if a channel with this name already exists */
291 Status
= ChanMgrGetChannelByName(ChannelName
, &Channel
);
292 if (Status
== STATUS_NOT_FOUND
) IsUnique
= TRUE
;
294 /* If one did, dereference it, all we wanted was to check uniqueness */
295 if (NT_SUCCESS(Status
)) ChanMgrReleaseChannel(Channel
);
297 /* Return if one was found or not */
303 ChanMgrReapChannel(IN ULONG ChannelIndex
)
306 return STATUS_NOT_IMPLEMENTED
;
311 ChanMgrReapChannels(VOID
)
314 NTSTATUS Status
= STATUS_SUCCESS
;
316 /* Loop all the channels */
317 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
319 /* Lock this index and see if the channel was reaped */
321 if (!ChannelReaped
[i
])
323 /* It was not reaped yet, so a channel should still be here */
324 ASSERT(ChannelFromIndex(i
) != NULL
);
325 if (ChannelGetReferenceCount(i
) <= 0)
327 /* The channel has no more references, so clear the buffer flags */
328 _InterlockedExchange(&ChannelArray
[i
]->ChannelHasNewIBufferData
, 0);
329 _InterlockedExchange(&ChannelArray
[i
]->ChannelHasNewOBufferData
, 0);
332 Status
= ChanMgrReapChannel(i
);
336 /* Release the lock, and move on unless reaping failed */
337 ChannelSlotUnlock(i
);
338 if (!NT_SUCCESS(Status
)) break;
341 /* Return reaping status */
347 ChanMgrCreateChannel(OUT PSAC_CHANNEL
*Channel
,
348 IN PSAC_CHANNEL_ATTRIBUTES Attributes
)
351 PSAC_CHANNEL NewChannel
;
352 SAC_CHANNEL_ID ChanId
;
354 CHECK_PARAMETER(Channel
);
355 CHECK_PARAMETER2(Attributes
);
357 /* No other channel create attempts can happen */
358 ChannelLockCreates();
360 /* Is the channel manager initialized? */
361 if (!ChannelCreateEnabled
)
364 Status
= STATUS_UNSUCCESSFUL
;
368 /* Reap any zombie channels */
369 Status
= ChanMgrReapChannels();
370 if (!NT_SUCCESS(Status
))
372 /* Bail out on error */
373 Status
= STATUS_UNSUCCESSFUL
;
377 /* Check if we already have a channel with this name */
378 if (!ChanMgrIsUniqueName(Attributes
->NameBuffer
))
381 Status
= STATUS_DUPLICATE_NAME
;
385 /* Allocate this channel */
386 NewChannel
= SacAllocatePool(sizeof(SAC_CHANNEL
), CHANNEL_BLOCK_TAG
);
387 CHECK_PARAMETER_WITH_STATUS(NewChannel
, STATUS_NO_MEMORY
); // bug
388 RtlZeroMemory(NewChannel
, sizeof(SAC_CHANNEL
));
390 /* Loop channel slots */
391 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
393 /* Find a free spot for it */
394 if (ChannelReaped
[i
])
396 /* Free slot found, attempt to use it */
397 ASSERT(!CHANNEL_SLOT_IS_IN_USE(i
));
398 _InterlockedCompareExchange((PLONG
)&ChannelArray
[i
], (LONG
)NewChannel
, 0);
399 if (ChannelArray
[i
] == NewChannel
) break;
403 /* Did we not find a single free slot? */
404 if (i
== SAC_MAX_CHANNELS
)
410 /* Create an ID for this channel */
411 RtlZeroMemory(&ChanId
, sizeof(ChanId
));
412 Status
= ExUuidCreate(&ChanId
.ChannelGuid
);
413 if (!NT_SUCCESS(Status
))
415 /* Bail out if we couldn't */
416 SAC_DBG(SAC_DBG_INIT
, "SAC Create Channel :: Failed to get GUID\n");
420 /* Now create the channel proper */
421 Status
= ChannelCreate(NewChannel
, Attributes
, ChanId
);
422 if (NT_SUCCESS(Status
))
424 /* Set the channel index */
425 _InterlockedExchange(&NewChannel
->Index
, i
);
427 /* Add the initial reference to the channel */
428 ChannelReferenceToOneByIndexWithLock(i
);
430 /* Return it to the caller */
431 *Channel
= NewChannel
;
433 /* This slot is now occupied */
434 ASSERT(ChannelReaped
[i
] == 1);
435 _InterlockedExchange(&ChannelReaped
[i
], 0);
439 /* We couldn't create it, free the buffer */
440 SacFreePool(NewChannel
);
444 /* Return whatever the operation status was */
445 ChannelUnlockCreates();
451 ChanMgrGetByHandleAndFileObject(IN SAC_CHANNEL_ID ChannelId
,
452 IN PFILE_OBJECT FileObject
,
453 OUT PSAC_CHANNEL
* TargetChannel
)
456 PSAC_CHANNEL FoundChannel
;
458 /* Lookup the channel by ID first */
459 Status
= ChanMgrGetByHandle(ChannelId
, &FoundChannel
);
460 if (NT_SUCCESS(Status
))
462 /* We found it, now check if the file object matches */
463 if (FoundChannel
->FileObject
== FileObject
)
465 /* Yep, return success */
466 *TargetChannel
= FoundChannel
;
470 /* Nope, drop the reference on the channel */
471 ChanMgrReleaseChannel(FoundChannel
);
473 /* And return failure */
474 *TargetChannel
= NULL
;
475 Status
= STATUS_NOT_FOUND
;
479 /* Return if we found it or not */
485 ChanMgrGetChannelIndex(IN PSAC_CHANNEL Channel
,
486 IN PLONG ChannelIndex
)
488 CHECK_PARAMETER1(Channel
);
489 CHECK_PARAMETER2(ChannelIndex
);
491 /* Just return the index of the channel */
492 *ChannelIndex
= ChannelGetIndex(Channel
);
493 return STATUS_SUCCESS
;
498 ChanMgrGetByIndex(IN LONG TargetIndex
,
499 IN PSAC_CHANNEL
* TargetChannel
)
502 CHECK_PARAMETER1(TargetIndex
< SAC_MAX_CHANNELS
);
503 CHECK_PARAMETER2(TargetChannel
);
506 *TargetChannel
= NULL
;
507 Status
= STATUS_NOT_FOUND
;
509 /* Reference this one and check if it's valid */
510 if (ChannelReferenceByIndexWithLock(TargetIndex
) > 0)
512 /* We found it, return it (with a reference held) */
513 *TargetChannel
= ChannelFromIndex(TargetIndex
);
514 return STATUS_SUCCESS
;
517 /* No channels with this ID were found */
523 ChanMgrGetNextActiveChannel(IN PSAC_CHANNEL CurrentChannel
,
524 IN PULONG TargetIndex
,
525 OUT PSAC_CHANNEL
*TargetChannel
)
529 LONG ChannelIndex
, StartIndex
;
530 PSAC_CHANNEL FoundChannel
;
531 BOOLEAN ChannelFound
;
532 CHECK_PARAMETER1(CurrentChannel
);
533 CHECK_PARAMETER2(TargetIndex
);
534 CHECK_PARAMETER3(TargetChannel
);
536 /* Get the current channel index */
537 Status
= ChanMgrGetChannelIndex(CurrentChannel
, &ChannelIndex
);
538 if (!NT_SUCCESS(Status
)) return Status
;
541 ChannelFound
= FALSE
;
543 /* Loop through all the possible active channels */
544 StartIndex
= (ChannelIndex
+ 1) % SAC_MAX_CHANNELS
;
545 for (i
= StartIndex
; i
!= StartIndex
; i
= (i
+ 1) % SAC_MAX_CHANNELS
)
547 /* Get the channel and see if it exists*/
548 Status
= ChanMgrGetByIndex(i
, &FoundChannel
);
549 if (Status
!= STATUS_NOT_FOUND
)
551 /* Bail out if we failed for some reason */
552 if (!NT_SUCCESS(Status
)) return Status
;
554 /* It exists -- is it active? Or, does it have output data? */
555 if ((ChannelIsActive(FoundChannel
)) ||
556 (!(ChannelIsActive(FoundChannel
)) &&
557 (FoundChannel
->ChannelHasNewOBufferData
)))
559 /* It's active or has output data, return with it */
564 /* Drop the reference on this channel and try the next one */
565 Status
= ChanMgrReleaseChannel(FoundChannel
);
566 if (!NT_SUCCESS(Status
)) return Status
;
570 /* Check if we successfully found a channel */
571 if ((NT_SUCCESS(Status
)) && (ChannelFound
))
573 /* Return it and its indexed. Remember we still hold the reference */
575 *TargetChannel
= FoundChannel
;
584 ChanMgrChannelDestroy(IN PSAC_CHANNEL Channel
)
586 CHECK_PARAMETER1(Channel
);
587 CHECK_PARAMETER(ChannelGetReferenceCount(Channel
->Index
) > 0);
589 /* Destroy the channel */
590 return Channel
->ChannelDestroy(Channel
);
595 ChanMgrCloseChannel(IN PSAC_CHANNEL Channel
)
598 CHECK_PARAMETER(Channel
);
600 /* Check if the channel is active */
601 if (ChannelIsActive(Channel
))
604 Status
= ChannelClose(Channel
);
609 Status
= STATUS_ALREADY_DISCONNECTED
;
612 /* Handle the channel close */
613 ConMgrHandleEvent(TRUE
, Channel
, &Status
);
619 ChanMgrGetChannelCount(OUT PULONG ChannelCount
)
622 PSAC_CHANNEL Channel
;
624 CHECK_PARAMETER(ChannelCount
);
626 /* Assume no channels */
629 /* Loop every channel */
630 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
632 /* See if this one exists */
633 Status
= ChanMgrGetByIndex(i
, &Channel
);
634 if (Status
!= STATUS_NOT_FOUND
)
637 ASSERT(NT_SUCCESS(Status
));
638 ASSERT(Channel
!= NULL
);
640 /* It exists -- is it active? Or, does it have output data? */
641 if ((ChannelIsActive(Channel
)) ||
642 (!(ChannelIsActive(Channel
)) &&
643 (Channel
->ChannelHasNewOBufferData
)))
645 /* It's active or has output data, increase the count */
650 /* Drop the reference on this channel and try the next one */
651 Status
= ChanMgrReleaseChannel(Channel
);
652 if (!NT_SUCCESS(Status
)) return Status
;
656 /* Channel doesn't exist, nothing wrong with that, keep going */
657 Status
= STATUS_SUCCESS
;
661 /* We should always succeed if we get here */
662 ASSERT(NT_SUCCESS(Status
));
668 ChanMgrIsFull(OUT PBOOLEAN IsFull
)
673 /* Count the channels */
674 Status
= ChanMgrGetChannelCount(&Count
);
675 CHECK_PARAMETER(Status
== STATUS_SUCCESS
);
677 /* Return if we hit the limit */
678 *IsFull
= (Count
== SAC_MAX_CHANNELS
);
684 ChanMgrCloseChannelsWithFileObject(IN PFILE_OBJECT FileObject
)
686 PSAC_CHANNEL Channel
;
689 CHECK_PARAMETER1(FileObject
);
691 /* Loop all channels */
692 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
694 /* Try to get this one */
695 Status
= ChanMgrGetByIndex(i
, &Channel
);
696 if (!NT_SUCCESS(Status
)) break;
698 /* Check if the FO matches, if so, close the channel */
699 if (Channel
->FileObject
== FileObject
) ChanMgrCloseChannel(Channel
);
701 /* Drop the reference and try the next channel(s) */
702 Status
= ChanMgrReleaseChannel(Channel
);
703 if (!NT_SUCCESS(Status
)) break;
712 ChanMgrGenerateUniqueCmdName(IN PWCHAR ChannelName
)
714 return STATUS_NOT_IMPLEMENTED
;