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 ASSERT(ChannelGetReferenceCount((Index
)) == 1);
115 _InterlockedExchange(&ChannelRefCount
[Index
], 1);
116 ChannelSlotUnlock(Index
);
121 ChanMgrInitialize(VOID
)
125 /* Initialize the channel lock */
126 SacInitializeLock(&ChannelCreateLock
);
127 ChannelCreateEnabled
= TRUE
;
129 /* Loop through the channel arrays */
130 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
132 /* Clear and initialize their locks */
133 ChannelArray
[i
] = NULL
;
134 SacInitializeLock(&ChannelSlotLock
[i
]);
136 /* Clear their statuses and reference counts */
137 _InterlockedExchange(&ChannelRefCount
[i
], 0);
138 _InterlockedExchange(&ChannelReaped
[i
], 1);
142 return STATUS_SUCCESS
;
147 ChanMgrShutdown(VOID
)
150 return STATUS_NOT_IMPLEMENTED
;
155 ChanMgrGetChannelByName(IN PWCHAR Name
,
156 OUT PSAC_CHANNEL
* Channel
)
158 NTSTATUS Status
, Status1
;
160 PSAC_CHANNEL CurrentChannel
;
163 CHECK_PARAMETER1(Name
);
164 CHECK_PARAMETER2(Channel
);
168 Status
= STATUS_NOT_FOUND
;
170 /* Loop through all channels */
171 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
173 /* Reference this one and check if it's valid */
174 if (ChannelReferenceByIndexWithLock(i
) > 0)
176 /* All good, grab it */
177 CurrentChannel
= ChannelFromIndex(i
);
178 ASSERT(CurrentChannel
!= NULL
);
181 Status1
= ChannelGetName(CurrentChannel
, &ChannelName
);
182 ASSERT(NT_SUCCESS(Status1
));
184 /* Check if this is the name that was passed in */
185 Found
= wcsicmp(Name
, ChannelName
);
186 SacFreePool(ChannelName
);
189 /* We found it, return it (with a reference held) */
190 *Channel
= CurrentChannel
;
191 return STATUS_SUCCESS
;
194 /* Not the one we want, dereference this one and keep going */
195 ChannelDereferenceByIndexWithLock(i
);
199 /* No channels with this name were found */
205 ChanMgrGetByHandle(IN SAC_CHANNEL_ID ChannelId
,
206 OUT PSAC_CHANNEL
* TargetChannel
)
210 PSAC_CHANNEL Channel
;
211 CHECK_PARAMETER2(TargetChannel
);
214 *TargetChannel
= NULL
;
215 Status
= STATUS_NOT_FOUND
;
217 /* Loop through all channels */
218 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
220 /* Reference this one and check if it's valid */
221 if (ChannelReferenceByIndexWithLock(i
) > 0)
223 /* All good, grab it */
224 Channel
= ChannelFromIndex(i
);
225 ASSERT(Channel
!= NULL
);
227 /* Check if the channel ID matches */
228 if (ChannelIsEqual(Channel
, &ChannelId
))
230 /* We found it, return it (with a reference held) */
231 *TargetChannel
= Channel
;
232 return STATUS_SUCCESS
;
235 /* Not the one we want, dereference this one and keep going */
236 ChannelDereferenceByIndexWithLock(i
);
240 /* No channels with this ID were found */
246 ChanMgrReleaseChannel(IN PSAC_CHANNEL Channel
)
250 PSAC_CHANNEL ThisChannel
;
251 CHECK_PARAMETER(Channel
);
253 /* Get the index of the channel */
254 Index
= ChannelGetIndex(Channel
);
256 /* Drop a reference -- there should still be at least the keepalive left */
257 ChannelSlotLock(Index
);
258 RefCount
= ChannelDereferenceByIndex(Index
);
259 ASSERT(RefCount
> 0);
261 /* Do we only have the keep-alive left, and the channel is dead? */
262 if ((RefCount
== 1) && !(ChannelIsActive(Channel
)))
264 /* Check if the ??? flag is set, or if there's no output data */
265 ThisChannel
= ChannelFromIndex(Index
);
266 if (!(ThisChannel
->Flags
& 1))
268 /* Nope, we can wipe the references and get rid of it */
269 ChannelDereferenceToZeroByIndex(Index
);
271 else if (!ThisChannel
->ChannelHasNewOBufferData
)
273 /* No data, we can wipe the references and get rid of it */
274 ChannelDereferenceToZeroByIndex(Index
);
278 /* We're done, we can unlock the slot now */
279 ChannelSlotUnlock(Index
);
280 return STATUS_SUCCESS
;
285 ChanMgrIsUniqueName(IN PWCHAR ChannelName
)
288 BOOLEAN IsUnique
= FALSE
;
289 PSAC_CHANNEL Channel
;
291 /* Check if a channel with this name already exists */
292 Status
= ChanMgrGetChannelByName(ChannelName
, &Channel
);
293 if (Status
== STATUS_NOT_FOUND
) IsUnique
= TRUE
;
295 /* If one did, dereference it, all we wanted was to check uniqueness */
296 if (NT_SUCCESS(Status
)) ChanMgrReleaseChannel(Channel
);
298 /* Return if one was found or not */
304 ChanMgrReapChannel(IN ULONG ChannelIndex
)
307 return STATUS_NOT_IMPLEMENTED
;
312 ChanMgrReapChannels(VOID
)
315 NTSTATUS Status
= STATUS_SUCCESS
;
317 /* Loop all the channels */
318 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
320 /* Lock this index and see if the channel was reaped */
322 if (!ChannelReaped
[i
])
324 /* It was not reaped yet, so a channel should still be here */
325 ASSERT(ChannelFromIndex(i
) != NULL
);
326 if (ChannelGetReferenceCount(i
) <= 0)
328 /* The channel has no more references, so clear the buffer flags */
329 _InterlockedExchange(&ChannelArray
[i
]->ChannelHasNewIBufferData
, 0);
330 _InterlockedExchange(&ChannelArray
[i
]->ChannelHasNewOBufferData
, 0);
333 Status
= ChanMgrReapChannel(i
);
337 /* Release the lock, and move on unless reaping failed */
338 ChannelSlotUnlock(i
);
339 if (!NT_SUCCESS(Status
)) break;
342 /* Return reaping status */
348 ChanMgrCreateChannel(OUT PSAC_CHANNEL
*Channel
,
349 IN PSAC_CHANNEL_ATTRIBUTES Attributes
)
352 PSAC_CHANNEL NewChannel
;
353 SAC_CHANNEL_ID ChanId
;
355 CHECK_PARAMETER(Channel
);
356 CHECK_PARAMETER2(Attributes
);
358 /* No other channel create attempts can happen */
359 ChannelLockCreates();
361 /* Is the channel manager initialized? */
362 if (ChannelCreateEnabled
)
365 Status
= STATUS_UNSUCCESSFUL
;
369 /* Reap any zombie channels */
370 Status
= ChanMgrReapChannels();
371 if (!NT_SUCCESS(Status
))
373 /* Bail out on error */
374 Status
= STATUS_UNSUCCESSFUL
;
378 /* Check if we already have a channel with this name */
379 if (!ChanMgrIsUniqueName(Attributes
->NameBuffer
))
382 Status
= STATUS_DUPLICATE_NAME
;
386 /* Allocate this channel */
387 NewChannel
= SacAllocatePool(sizeof(SAC_CHANNEL
), CHANNEL_BLOCK_TAG
);
388 CHECK_PARAMETER_WITH_STATUS(NewChannel
, STATUS_NO_MEMORY
); // bug
389 RtlZeroMemory(NewChannel
, sizeof(SAC_CHANNEL
));
391 /* Loop channel slots */
392 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
394 /* Find a free spot for it */
395 if (ChannelReaped
[i
])
397 /* Free slot found, attempt to use it */
398 ASSERT(!CHANNEL_SLOT_IS_IN_USE(i
));
399 _InterlockedCompareExchangePointer((void* volatile*)&ChannelArray
[i
], NewChannel
, NULL
);
400 if (ChannelArray
[i
] == NewChannel
) break;
404 /* Did we not find a single free slot? */
405 if (i
== SAC_MAX_CHANNELS
)
408 Status
= STATUS_UNSUCCESSFUL
;
412 /* Create an ID for this channel */
413 RtlZeroMemory(&ChanId
, sizeof(ChanId
));
414 Status
= ExUuidCreate(&ChanId
.ChannelGuid
);
415 if (!NT_SUCCESS(Status
))
417 /* Bail out if we couldn't */
418 SAC_DBG(SAC_DBG_INIT
, "SAC Create Channel :: Failed to get GUID\n");
422 /* Now create the channel proper */
423 Status
= ChannelCreate(NewChannel
, Attributes
, ChanId
);
424 if (NT_SUCCESS(Status
))
426 /* Set the channel index */
427 _InterlockedExchange(&NewChannel
->Index
, i
);
429 /* Add the initial reference to the channel */
430 ChannelReferenceToOneByIndexWithLock(i
);
432 /* Return it to the caller */
433 *Channel
= NewChannel
;
435 /* This slot is now occupied */
436 ASSERT(ChannelReaped
[i
] == 1);
437 _InterlockedExchange(&ChannelReaped
[i
], 0);
441 /* We couldn't create it, free the buffer */
442 SacFreePool(NewChannel
);
446 /* Return whatever the operation status was */
447 ChannelUnlockCreates();
453 ChanMgrGetByHandleAndFileObject(IN SAC_CHANNEL_ID ChannelId
,
454 IN PFILE_OBJECT FileObject
,
455 OUT PSAC_CHANNEL
* TargetChannel
)
458 PSAC_CHANNEL FoundChannel
;
460 /* Lookup the channel by ID first */
461 Status
= ChanMgrGetByHandle(ChannelId
, &FoundChannel
);
462 if (NT_SUCCESS(Status
))
464 /* We found it, now check if the file object matches */
465 if (FoundChannel
->FileObject
== FileObject
)
467 /* Yep, return success */
468 *TargetChannel
= FoundChannel
;
472 /* Nope, drop the reference on the channel */
473 ChanMgrReleaseChannel(FoundChannel
);
475 /* And return failure */
476 *TargetChannel
= NULL
;
477 Status
= STATUS_NOT_FOUND
;
481 /* Return if we found it or not */
487 ChanMgrGetChannelIndex(IN PSAC_CHANNEL Channel
,
488 IN PLONG ChannelIndex
)
490 CHECK_PARAMETER1(Channel
);
491 CHECK_PARAMETER2(ChannelIndex
);
493 /* Just return the index of the channel */
494 *ChannelIndex
= ChannelGetIndex(Channel
);
495 return STATUS_SUCCESS
;
500 ChanMgrGetByIndex(IN LONG TargetIndex
,
501 IN PSAC_CHANNEL
* TargetChannel
)
504 CHECK_PARAMETER1(TargetIndex
< SAC_MAX_CHANNELS
);
505 CHECK_PARAMETER2(TargetChannel
);
508 *TargetChannel
= NULL
;
509 Status
= STATUS_NOT_FOUND
;
511 /* Reference this one and check if it's valid */
512 if (ChannelReferenceByIndexWithLock(TargetIndex
) > 0)
514 /* We found it, return it (with a reference held) */
515 *TargetChannel
= ChannelFromIndex(TargetIndex
);
516 return STATUS_SUCCESS
;
519 /* No channels with this ID were found */
525 ChanMgrGetNextActiveChannel(IN PSAC_CHANNEL CurrentChannel
,
526 IN PULONG TargetIndex
,
527 OUT PSAC_CHANNEL
*TargetChannel
)
531 LONG ChannelIndex
, StartIndex
;
532 PSAC_CHANNEL FoundChannel
;
533 BOOLEAN ChannelFound
;
534 CHECK_PARAMETER1(CurrentChannel
);
535 CHECK_PARAMETER2(TargetIndex
);
536 CHECK_PARAMETER3(TargetChannel
);
538 /* Get the current channel index */
539 Status
= ChanMgrGetChannelIndex(CurrentChannel
, &ChannelIndex
);
540 if (!NT_SUCCESS(Status
)) return Status
;
543 ChannelFound
= FALSE
;
545 /* Loop through all the possible active channels */
546 StartIndex
= (ChannelIndex
+ 1) % SAC_MAX_CHANNELS
;
547 for (i
= StartIndex
; i
!= StartIndex
; i
= (i
+ 1) % SAC_MAX_CHANNELS
)
549 /* Get the channel and see if it exists*/
550 Status
= ChanMgrGetByIndex(i
, &FoundChannel
);
551 if (Status
!= STATUS_NOT_FOUND
)
553 /* Bail out if we failed for some reason */
554 if (!NT_SUCCESS(Status
)) return Status
;
556 /* It exists -- is it active? Or, does it have output data? */
557 if ((ChannelIsActive(FoundChannel
)) ||
558 (!(ChannelIsActive(FoundChannel
)) &&
559 (FoundChannel
->ChannelHasNewOBufferData
)))
561 /* It's active or has output data, return with it */
566 /* Drop the reference on this channel and try the next one */
567 Status
= ChanMgrReleaseChannel(FoundChannel
);
568 if (!NT_SUCCESS(Status
)) return Status
;
572 /* Check if we successfully found a channel */
573 if ((NT_SUCCESS(Status
)) && (ChannelFound
))
575 /* Return it and its indexed. Remember we still hold the reference */
577 *TargetChannel
= FoundChannel
;
586 ChanMgrChannelDestroy(IN PSAC_CHANNEL Channel
)
588 CHECK_PARAMETER1(Channel
);
589 CHECK_PARAMETER(ChannelGetReferenceCount(Channel
->Index
) > 0);
591 /* Destroy the channel */
592 return Channel
->ChannelDestroy(Channel
);
597 ChanMgrCloseChannel(IN PSAC_CHANNEL Channel
)
600 CHECK_PARAMETER(Channel
);
602 /* Check if the channel is active */
603 if (ChannelIsActive(Channel
))
606 Status
= ChannelClose(Channel
);
611 Status
= STATUS_ALREADY_DISCONNECTED
;
614 /* Handle the channel close */
615 ConMgrHandleEvent(TRUE
, Channel
, &Status
);
621 ChanMgrGetChannelCount(OUT PULONG ChannelCount
)
624 PSAC_CHANNEL Channel
;
626 CHECK_PARAMETER(ChannelCount
);
628 /* Assume no channels */
631 /* Loop every channel */
632 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
634 /* See if this one exists */
635 Status
= ChanMgrGetByIndex(i
, &Channel
);
636 if (Status
!= STATUS_NOT_FOUND
)
639 ASSERT(NT_SUCCESS(Status
));
640 ASSERT(Channel
!= NULL
);
642 /* It exists -- is it active? Or, does it have output data? */
643 if ((ChannelIsActive(Channel
)) ||
644 (!(ChannelIsActive(Channel
)) &&
645 (Channel
->ChannelHasNewOBufferData
)))
647 /* It's active or has output data, increase the count */
652 /* Drop the reference on this channel and try the next one */
653 Status
= ChanMgrReleaseChannel(Channel
);
654 if (!NT_SUCCESS(Status
)) return Status
;
658 /* Channel doesn't exist, nothing wrong with that, keep going */
659 Status
= STATUS_SUCCESS
;
663 /* We should always succeed if we get here */
664 ASSERT(NT_SUCCESS(Status
));
670 ChanMgrIsFull(OUT PBOOLEAN IsFull
)
675 /* Count the channels */
676 Status
= ChanMgrGetChannelCount(&Count
);
677 CHECK_PARAMETER(Status
== STATUS_SUCCESS
);
679 /* Return if we hit the limit */
680 *IsFull
= (Count
== SAC_MAX_CHANNELS
);
686 ChanMgrCloseChannelsWithFileObject(IN PFILE_OBJECT FileObject
)
688 PSAC_CHANNEL Channel
;
691 CHECK_PARAMETER1(FileObject
);
693 /* Loop all channels */
694 for (i
= 0; i
< SAC_MAX_CHANNELS
; i
++)
696 /* Try to get this one */
697 Status
= ChanMgrGetByIndex(i
, &Channel
);
698 if (!NT_SUCCESS(Status
)) break;
700 /* Check if the FO matches, if so, close the channel */
701 if (Channel
->FileObject
== FileObject
) ChanMgrCloseChannel(Channel
);
703 /* Drop the reference and try the next channel(s) */
704 Status
= ChanMgrReleaseChannel(Channel
);
705 if (!NT_SUCCESS(Status
)) break;
714 ChanMgrGenerateUniqueCmdName(IN PWCHAR ChannelName
)
716 return STATUS_NOT_IMPLEMENTED
;