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
];
22 /* FUNCTIONS ******************************************************************/
24 #define MAX_REF_COUNT 100
26 #define CHANNEL_SLOT_IS_IN_USE(x) (ChannelRefCount[(x)] > 0)
30 ChannelFromIndex(IN ULONG Index
)
32 return ChannelArray
[Index
];
37 ChannelGetReferenceCount(IN LONG Index
)
39 return ChannelRefCount
[Index
];
44 ChannelReferenceByIndex(IN LONG Index
)
46 if (ChannelGetReferenceCount(Index
) > 0)
48 ASSERT(ChannelRefCount
[Index
] <= MAX_REF_COUNT
);
49 ASSERT(ChannelRefCount
[Index
] >= 1);
50 _InterlockedIncrement(&ChannelRefCount
[Index
]);
51 ASSERT(ChannelRefCount
[Index
] <= MAX_REF_COUNT
);
52 ASSERT(ChannelRefCount
[Index
] >= 2);
55 return ChannelGetReferenceCount(Index
);
60 ChannelReferenceByIndexWithLock(IN LONG Index
)
64 ChannelSlotLock(Index
);
65 RefCount
= ChannelReferenceByIndex(Index
);
66 ChannelSlotUnlock(Index
);
72 ChannelDereferenceByIndex(IN LONG Index
)
74 ASSERT(ChannelGetReferenceCount(Index
) <= MAX_REF_COUNT
);
75 ASSERT(ChannelGetReferenceCount(Index
) > 1);
76 _InterlockedDecrement(&ChannelRefCount
[Index
]);
77 ASSERT(ChannelGetReferenceCount(Index
) >= 1);
78 return ChannelGetReferenceCount(Index
);
83 ChannelDereferenceByIndexWithLock(IN LONG Index
)
85 ChannelSlotLock(Index
);
86 ChannelDereferenceByIndex(Index
);
87 ChannelSlotUnlock(Index
);
92 ChannelDereferenceToZeroByIndex(IN LONG Index
)
94 ASSERT(ChannelGetReferenceCount(Index
) == 1);
95 ASSERT(ChannelIsActive(ChannelFromIndex(Index
)) == FALSE
);
96 _InterlockedExchange(&ChannelRefCount
[Index
], 0);
101 ChannelReferenceToOneByIndex(IN LONG Index
)
103 ASSERT(ChannelGetReferenceCount(Index
) == 0);
104 _InterlockedExchange(&ChannelRefCount
[Index
], 1);
109 ChannelReferenceToOneByIndexWithLock(IN LONG Index
)
111 ChannelSlotLock(Index
);
112 ChannelReferenceToOneByIndex(Index
);
113 ChannelSlotUnlock(Index
);
118 ChanMgrInitialize(VOID
)
122 /* Initialize the channel lock */
123 SacInitializeLock(&ChannelCreateLock
);
124 ChannelCreateEnabled
= TRUE
;
126 /* Loop through the channel arrays */
127 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
129 /* Clear and initialize their locks */
130 ChannelArray
[i
] = NULL
;
131 SacInitializeLock(&ChannelSlotLock
[i
]);
133 /* Clear their statuses and reference counts */
134 _InterlockedExchange(&ChannelRefCount
[i
], 0);
135 _InterlockedExchange(&ChannelReaped
[i
], 1);
139 return STATUS_SUCCESS
;
144 ChanMgrShutdown(VOID
)
147 return STATUS_NOT_IMPLEMENTED
;
152 ChanMgrGetChannelByName(IN PWCHAR Name
,
153 OUT PSAC_CHANNEL
* Channel
)
155 NTSTATUS Status
, Status1
;
157 PSAC_CHANNEL CurrentChannel
;
160 CHECK_PARAMETER1(Name
);
161 CHECK_PARAMETER2(Channel
);
165 Status
= STATUS_NOT_FOUND
;
167 /* Loop through all channels */
168 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
170 /* Reference this one and check if it's valid */
171 if (ChannelReferenceByIndexWithLock(i
) > 0)
173 /* All good, grab it */
174 CurrentChannel
= ChannelFromIndex(i
);
175 ASSERT(CurrentChannel
!= NULL
);
178 Status1
= ChannelGetName(CurrentChannel
, &ChannelName
);
179 ASSERT(NT_SUCCESS(Status1
));
181 /* Check if this is the name that was passed in */
182 Found
= wcsicmp(Name
, ChannelName
);
183 SacFreePool(ChannelName
);
186 /* We found it, return it (with a reference held) */
187 *Channel
= CurrentChannel
;
188 return STATUS_SUCCESS
;
191 /* Not the one we want, dereference this one and keep going */
192 ChannelDereferenceByIndexWithLock(i
);
196 /* No channels with this name were found */
202 ChanMgrGetByHandle(IN SAC_CHANNEL_ID ChannelId
,
203 OUT PSAC_CHANNEL
* TargetChannel
)
207 PSAC_CHANNEL Channel
;
208 CHECK_PARAMETER2(TargetChannel
);
211 *TargetChannel
= NULL
;
212 Status
= STATUS_NOT_FOUND
;
214 /* Loop through all channels */
215 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
217 /* Reference this one and check if it's valid */
218 if (ChannelReferenceByIndexWithLock(i
) > 0)
220 /* All good, grab it */
221 Channel
= ChannelFromIndex(i
);
222 ASSERT(Channel
!= NULL
);
224 /* Check if the channel ID matches */
225 if (ChannelIsEqual(Channel
, &ChannelId
))
227 /* We found it, return it (with a reference held) */
228 *TargetChannel
= Channel
;
229 return STATUS_SUCCESS
;
232 /* Not the one we want, dereference this one and keep going */
233 ChannelDereferenceByIndexWithLock(i
);
237 /* No channels with this ID were found */
243 ChanMgrReleaseChannel(IN PSAC_CHANNEL Channel
)
247 PSAC_CHANNEL ThisChannel
;
248 CHECK_PARAMETER(Channel
);
250 /* Get the index of the channel */
251 Index
= ChannelGetIndex(Channel
);
253 /* Drop a reference -- there should still be at least the keepalive left */
254 ChannelSlotLock(Index
);
255 RefCount
= ChannelDereferenceByIndex(Index
);
256 ASSERT(RefCount
> 0);
258 /* Do we only have the keep-alive left, and the channel is dead? */
259 if ((RefCount
== 1) && !(ChannelIsActive(Channel
)))
261 /* Check if the ??? flag is set, or if there's no output data */
262 ThisChannel
= ChannelFromIndex(Index
);
263 if (!(ThisChannel
->Flags
& 1))
265 /* Nope, we can wipe the references and get rid of it */
266 ChannelDereferenceToZeroByIndex(Index
);
268 else if (!ThisChannel
->ChannelHasNewOBufferData
)
270 /* No data, we can wipe the references and get rid of it */
271 ChannelDereferenceToZeroByIndex(Index
);
275 /* We're done, we can unlock the slot now */
276 ChannelSlotUnlock(Index
);
277 return STATUS_SUCCESS
;
282 ChanMgrIsUniqueName(IN PWCHAR ChannelName
)
285 BOOLEAN IsUnique
= FALSE
;
286 PSAC_CHANNEL Channel
;
288 /* Check if a channel with this name already exists */
289 Status
= ChanMgrGetChannelByName(ChannelName
, &Channel
);
290 if (Status
== STATUS_NOT_FOUND
) IsUnique
= TRUE
;
292 /* If one did, dereference it, all we wanted was to check uniqueness */
293 if (NT_SUCCESS(Status
)) ChanMgrReleaseChannel(Channel
);
295 /* Return if one was found or not */
301 ChanMgrReapChannel(IN ULONG ChannelIndex
)
304 return STATUS_NOT_IMPLEMENTED
;
309 ChanMgrReapChannels(VOID
)
312 NTSTATUS Status
= STATUS_SUCCESS
;
314 /* Loop all the channels */
315 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
317 /* Lock this index and see if the channel was reaped */
319 if (!ChannelReaped
[i
])
321 /* It was not reaped yet, so a channel should still be here */
322 ASSERT(ChannelFromIndex(i
) != NULL
);
323 if (ChannelGetReferenceCount(i
) <= 0)
325 /* The channel has no more references, so clear the buffer flags */
326 _InterlockedExchange(&ChannelArray
[i
]->ChannelHasNewIBufferData
, 0);
327 _InterlockedExchange(&ChannelArray
[i
]->ChannelHasNewOBufferData
, 0);
330 Status
= ChanMgrReapChannel(i
);
334 /* Release the lock, and move on unless reaping failed */
335 ChannelSlotUnlock(i
);
336 if (!NT_SUCCESS(Status
)) break;
339 /* Return reaping status */
345 ChanMgrCreateChannel(OUT PSAC_CHANNEL
*Channel
,
346 IN PSAC_CHANNEL_ATTRIBUTES Attributes
)
349 PSAC_CHANNEL NewChannel
;
350 SAC_CHANNEL_ID ChanId
;
352 CHECK_PARAMETER(Channel
);
353 CHECK_PARAMETER2(Attributes
);
355 /* No other channel create attempts can happen */
356 ChannelLockCreates();
358 /* Is the channel manager initialized? */
359 if (!ChannelCreateEnabled
)
362 Status
= STATUS_UNSUCCESSFUL
;
366 /* Reap any zombie channels */
367 Status
= ChanMgrReapChannels();
368 if (!NT_SUCCESS(Status
))
370 /* Bail out on error */
371 Status
= STATUS_UNSUCCESSFUL
;
375 /* Check if we already have a channel with this name */
376 if (!ChanMgrIsUniqueName(Attributes
->NameBuffer
))
379 Status
= STATUS_DUPLICATE_NAME
;
383 /* Allocate this channel */
384 NewChannel
= SacAllocatePool(sizeof(SAC_CHANNEL
), CHANNEL_BLOCK_TAG
);
385 CHECK_PARAMETER_WITH_STATUS(NewChannel
, STATUS_NO_MEMORY
); // bug
386 RtlZeroMemory(NewChannel
, sizeof(SAC_CHANNEL
));
388 /* Loop channel slots */
389 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
391 /* Find a free spot for it */
392 if (ChannelReaped
[i
])
394 /* Free slot found, attempt to use it */
395 ASSERT(!CHANNEL_SLOT_IS_IN_USE(i
));
396 _InterlockedCompareExchange((PLONG
)&ChannelArray
[i
], (LONG
)NewChannel
, 0);
397 if (ChannelArray
[i
] == NewChannel
) break;
401 /* Did we not find a single free slot? */
402 if (i
== SAC_MAX_CHANNELS
)
408 /* Create an ID for this channel */
409 RtlZeroMemory(&ChanId
, sizeof(ChanId
));
410 Status
= ExUuidCreate(&ChanId
.ChannelGuid
);
411 if (!NT_SUCCESS(Status
))
413 /* Bail out if we couldn't */
414 SAC_DBG(SAC_DBG_INIT
, "SAC Create Channel :: Failed to get GUID\n");
418 /* Now create the channel proper */
419 Status
= ChannelCreate(NewChannel
, Attributes
, ChanId
);
420 if (NT_SUCCESS(Status
))
422 /* Set the channel index */
423 _InterlockedExchange(&NewChannel
->Index
, i
);
425 /* Add the initial reference to the channel */
426 ChannelReferenceToOneByIndexWithLock(i
);
428 /* Return it to the caller */
429 *Channel
= NewChannel
;
431 /* This slot is now occupied */
432 ASSERT(ChannelReaped
[i
] == 1);
433 _InterlockedExchange(&ChannelReaped
[i
], 0);
437 /* We couldn't create it, free the buffer */
438 SacFreePool(NewChannel
);
442 /* Return whatever the operation status was */
443 ChannelUnlockCreates();
449 ChanMgrGetByHandleAndFileObject(IN SAC_CHANNEL_ID ChannelId
,
450 IN PFILE_OBJECT FileObject
,
451 OUT PSAC_CHANNEL
* TargetChannel
)
454 PSAC_CHANNEL FoundChannel
;
456 /* Lookup the channel by ID first */
457 Status
= ChanMgrGetByHandle(ChannelId
, &FoundChannel
);
458 if (NT_SUCCESS(Status
))
460 /* We found it, now check if the file object matches */
461 if (FoundChannel
->FileObject
== FileObject
)
463 /* Yep, return success */
464 *TargetChannel
= FoundChannel
;
468 /* Nope, drop the reference on the channel */
469 ChanMgrReleaseChannel(FoundChannel
);
471 /* And return failure */
472 *TargetChannel
= NULL
;
473 Status
= STATUS_NOT_FOUND
;
477 /* Return if we found it or not */
483 ChanMgrGetChannelIndex(IN PSAC_CHANNEL Channel
,
484 IN PLONG ChannelIndex
)
486 CHECK_PARAMETER1(Channel
);
487 CHECK_PARAMETER2(ChannelIndex
);
489 /* Just return the index of the channel */
490 *ChannelIndex
= ChannelGetIndex(Channel
);
491 return STATUS_SUCCESS
;
496 ChanMgrGetByIndex(IN LONG TargetIndex
,
497 IN PSAC_CHANNEL
* TargetChannel
)
500 CHECK_PARAMETER1(TargetIndex
< SAC_MAX_CHANNELS
);
501 CHECK_PARAMETER2(TargetChannel
);
504 *TargetChannel
= NULL
;
505 Status
= STATUS_NOT_FOUND
;
507 /* Reference this one and check if it's valid */
508 if (ChannelReferenceByIndexWithLock(TargetIndex
) > 0)
510 /* We found it, return it (with a reference held) */
511 *TargetChannel
= ChannelFromIndex(TargetIndex
);
512 return STATUS_SUCCESS
;
515 /* No channels with this ID were found */
521 ChanMgrGetNextActiveChannel(IN PSAC_CHANNEL CurrentChannel
,
522 IN PULONG TargetIndex
,
523 OUT PSAC_CHANNEL
*TargetChannel
)
527 LONG ChannelIndex
, StartIndex
;
528 PSAC_CHANNEL FoundChannel
;
529 BOOLEAN ChannelFound
;
530 CHECK_PARAMETER1(CurrentChannel
);
531 CHECK_PARAMETER2(TargetIndex
);
532 CHECK_PARAMETER3(TargetChannel
);
534 /* Get the current channel index */
535 Status
= ChanMgrGetChannelIndex(CurrentChannel
, &ChannelIndex
);
536 if (!NT_SUCCESS(Status
)) return Status
;
539 ChannelFound
= FALSE
;
541 /* Loop through all the possible active channels */
542 StartIndex
= (ChannelIndex
+ 1) % SAC_MAX_CHANNELS
;
543 for (i
= StartIndex
; i
!= StartIndex
; i
= (i
+ 1) % SAC_MAX_CHANNELS
)
545 /* Get the channel and see if it exists*/
546 Status
= ChanMgrGetByIndex(i
, &FoundChannel
);
547 if (Status
!= STATUS_NOT_FOUND
)
549 /* Bail out if we failed for some reason */
550 if (!NT_SUCCESS(Status
)) return Status
;
552 /* It exists -- is it active? Or, does it have output data? */
553 if ((ChannelIsActive(FoundChannel
)) ||
554 (!(ChannelIsActive(FoundChannel
)) &&
555 (FoundChannel
->ChannelHasNewOBufferData
)))
557 /* It's active or has output data, return with it */
562 /* Drop the reference on this channel and try the next one */
563 Status
= ChanMgrReleaseChannel(FoundChannel
);
564 if (!NT_SUCCESS(Status
)) return Status
;
568 /* Check if we successfully found a channel */
569 if ((NT_SUCCESS(Status
)) && (ChannelFound
))
571 /* Return it and its indexed. Remember we still hold the reference */
573 *TargetChannel
= FoundChannel
;
582 ChanMgrChannelDestroy(IN PSAC_CHANNEL Channel
)
584 CHECK_PARAMETER1(Channel
);
585 CHECK_PARAMETER(ChannelGetReferenceCount(Channel
->Index
) > 0);
587 /* Destroy the channel */
588 return Channel
->ChannelDestroy(Channel
);
593 ChanMgrCloseChannel(IN PSAC_CHANNEL Channel
)
596 CHECK_PARAMETER(Channel
);
598 /* Check if the channel is active */
599 if (ChannelIsActive(Channel
))
602 Status
= ChannelClose(Channel
);
607 Status
= STATUS_ALREADY_DISCONNECTED
;
610 /* Handle the channel close */
611 ConMgrHandleEvent(TRUE
, Channel
, &Status
);
617 ChanMgrGetChannelCount(OUT PULONG ChannelCount
)
620 PSAC_CHANNEL Channel
;
622 CHECK_PARAMETER(ChannelCount
);
624 /* Assume no channels */
627 /* Loop every channel */
628 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
630 /* See if this one exists */
631 Status
= ChanMgrGetByIndex(i
, &Channel
);
632 if (Status
!= STATUS_NOT_FOUND
)
635 ASSERT(NT_SUCCESS(Status
));
636 ASSERT(Channel
!= NULL
);
638 /* It exists -- is it active? Or, does it have output data? */
639 if ((ChannelIsActive(Channel
)) ||
640 (!(ChannelIsActive(Channel
)) &&
641 (Channel
->ChannelHasNewOBufferData
)))
643 /* It's active or has output data, increase the count */
648 /* Drop the reference on this channel and try the next one */
649 Status
= ChanMgrReleaseChannel(Channel
);
650 if (!NT_SUCCESS(Status
)) return Status
;
654 /* Channel doesn't exist, nothing wrong with that, keep going */
655 Status
= STATUS_SUCCESS
;
659 /* We should always succeed if we get here */
660 ASSERT(NT_SUCCESS(Status
));
666 ChanMgrIsFull(OUT PBOOLEAN IsFull
)
671 /* Count the channels */
672 Status
= ChanMgrGetChannelCount(&Count
);
673 CHECK_PARAMETER(Status
== STATUS_SUCCESS
);
675 /* Return if we hit the limit */
676 *IsFull
= (Count
== SAC_MAX_CHANNELS
);
682 ChanMgrCloseChannelsWithFileObject(IN PFILE_OBJECT FileObject
)
684 PSAC_CHANNEL Channel
;
687 CHECK_PARAMETER1(FileObject
);
689 /* Loop all channels */
690 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
692 /* Try to get this one */
693 Status
= ChanMgrGetByIndex(i
, &Channel
);
694 if (!NT_SUCCESS(Status
)) break;
696 /* Check if the FO matches, if so, close the channel */
697 if (Channel
->FileObject
== FileObject
) ChanMgrCloseChannel(Channel
);
699 /* Drop the reference and try the next channel(s) */
700 Status
= ChanMgrReleaseChannel(Channel
);
701 if (!NT_SUCCESS(Status
)) break;
710 ChanMgrGenerateUniqueCmdName(IN PWCHAR ChannelName
)
712 return STATUS_NOT_IMPLEMENTED
;