Synchronize with trunk revision 59781.
[reactos.git] / drivers / sac / driver / chanmgr.c
1 /*
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
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "sacdrv.h"
12
13 /* GLOBALS *******************************************************************/
14
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;
23
24 /* FUNCTIONS *****************************************************************/
25
26 #define MAX_REF_COUNT 100
27
28 #define CHANNEL_SLOT_IS_IN_USE(x) (ChannelRefCount[(x)] > 0)
29
30 FORCEINLINE
31 PSAC_CHANNEL
32 ChannelFromIndex(IN ULONG Index)
33 {
34 return ChannelArray[Index];
35 }
36
37 FORCEINLINE
38 LONG
39 ChannelGetReferenceCount(IN LONG Index)
40 {
41 return ChannelRefCount[Index];
42 }
43
44 FORCEINLINE
45 LONG
46 ChannelReferenceByIndex(IN LONG Index)
47 {
48 if (ChannelGetReferenceCount(Index) > 0)
49 {
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);
55 }
56
57 return ChannelGetReferenceCount(Index);
58 }
59
60 FORCEINLINE
61 LONG
62 ChannelReferenceByIndexWithLock(IN LONG Index)
63 {
64 LONG RefCount;
65
66 ChannelSlotLock(Index);
67 RefCount = ChannelReferenceByIndex(Index);
68 ChannelSlotUnlock(Index);
69 return RefCount;
70 }
71
72 FORCEINLINE
73 LONG
74 ChannelDereferenceByIndex(IN LONG Index)
75 {
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);
81 }
82
83 FORCEINLINE
84 VOID
85 ChannelDereferenceByIndexWithLock(IN LONG Index)
86 {
87 ChannelSlotLock(Index);
88 ChannelDereferenceByIndex(Index);
89 ChannelSlotUnlock(Index);
90 }
91
92 FORCEINLINE
93 VOID
94 ChannelDereferenceToZeroByIndex(IN LONG Index)
95 {
96 ASSERT(ChannelGetReferenceCount(Index) == 1);
97 ASSERT(ChannelIsActive(ChannelFromIndex(Index)) == FALSE);
98 _InterlockedExchange(&ChannelRefCount[Index], 0);
99 }
100
101 FORCEINLINE
102 VOID
103 ChannelReferenceToOneByIndex(IN LONG Index)
104 {
105 ASSERT(ChannelGetReferenceCount(Index) == 0);
106 _InterlockedExchange(&ChannelRefCount[Index], 1);
107 }
108
109 FORCEINLINE
110 VOID
111 ChannelReferenceToOneByIndexWithLock(IN LONG Index)
112 {
113 ChannelSlotLock(Index);
114 ChannelReferenceToOneByIndex(Index);
115 ChannelSlotUnlock(Index);
116 }
117
118 NTSTATUS
119 NTAPI
120 ChanMgrInitialize(VOID)
121 {
122 ULONG i;
123
124 /* Initialize the channel lock */
125 SacInitializeLock(&ChannelCreateLock);
126 ChannelCreateEnabled = TRUE;
127
128 /* Loop through the channel arrays */
129 for (i = 0; i < SAC_MAX_CHANNELS; i++)
130 {
131 /* Clear and initialize their locks */
132 ChannelArray[i] = NULL;
133 SacInitializeLock(&ChannelSlotLock[i]);
134
135 /* Clear their statuses and reference counts */
136 _InterlockedExchange(&ChannelRefCount[i], 0);
137 _InterlockedExchange(&ChannelReaped[i], 1);
138 }
139
140 /* All good */
141 return STATUS_SUCCESS;
142 }
143
144 NTSTATUS
145 NTAPI
146 ChanMgrShutdown(VOID)
147 {
148 /* FIXME: TODO */
149 return STATUS_NOT_IMPLEMENTED;
150 }
151
152 NTSTATUS
153 NTAPI
154 ChanMgrGetChannelByName(IN PWCHAR Name,
155 OUT PSAC_CHANNEL* Channel)
156 {
157 NTSTATUS Status, Status1;
158 ULONG i;
159 PSAC_CHANNEL CurrentChannel;
160 PWCHAR ChannelName;
161 BOOLEAN Found;
162 CHECK_PARAMETER1(Name);
163 CHECK_PARAMETER2(Channel);
164
165 /* Assume failure */
166 *Channel = NULL;
167 Status = STATUS_NOT_FOUND;
168
169 /* Loop through all channels */
170 for (i = 0; i < SAC_MAX_CHANNELS; i++)
171 {
172 /* Reference this one and check if it's valid */
173 if (ChannelReferenceByIndexWithLock(i) > 0)
174 {
175 /* All good, grab it */
176 CurrentChannel = ChannelFromIndex(i);
177 ASSERT(CurrentChannel != NULL);
178
179 /* Get its name */
180 Status1 = ChannelGetName(CurrentChannel, &ChannelName);
181 ASSERT(NT_SUCCESS(Status1));
182
183 /* Check if this is the name that was passed in */
184 Found = wcsicmp(Name, ChannelName);
185 SacFreePool(ChannelName);
186 if (Found)
187 {
188 /* We found it, return it (with a reference held) */
189 *Channel = CurrentChannel;
190 return STATUS_SUCCESS;
191 }
192
193 /* Not the one we want, dereference this one and keep going */
194 ChannelDereferenceByIndexWithLock(i);
195 }
196 }
197
198 /* No channels with this name were found */
199 return Status;
200 }
201
202 NTSTATUS
203 NTAPI
204 ChanMgrGetByHandle(IN SAC_CHANNEL_ID ChannelId,
205 OUT PSAC_CHANNEL* TargetChannel)
206 {
207 NTSTATUS Status;
208 ULONG i;
209 PSAC_CHANNEL Channel;
210 CHECK_PARAMETER2(TargetChannel);
211
212 /* Assume failure */
213 *TargetChannel = NULL;
214 Status = STATUS_NOT_FOUND;
215
216 /* Loop through all channels */
217 for (i = 0; i < SAC_MAX_CHANNELS; i++)
218 {
219 /* Reference this one and check if it's valid */
220 if (ChannelReferenceByIndexWithLock(i) > 0)
221 {
222 /* All good, grab it */
223 Channel = ChannelFromIndex(i);
224 ASSERT(Channel != NULL);
225
226 /* Check if the channel ID matches */
227 if (ChannelIsEqual(Channel, &ChannelId))
228 {
229 /* We found it, return it (with a reference held) */
230 *TargetChannel = Channel;
231 return STATUS_SUCCESS;
232 }
233
234 /* Not the one we want, dereference this one and keep going */
235 ChannelDereferenceByIndexWithLock(i);
236 }
237 }
238
239 /* No channels with this ID were found */
240 return Status;
241 }
242
243 NTSTATUS
244 NTAPI
245 ChanMgrReleaseChannel(IN PSAC_CHANNEL Channel)
246 {
247 LONG Index;
248 ULONG RefCount;
249 PSAC_CHANNEL ThisChannel;
250 CHECK_PARAMETER(Channel);
251
252 /* Get the index of the channel */
253 Index = ChannelGetIndex(Channel);
254
255 /* Drop a reference -- there should still be at least the keepalive left */
256 ChannelSlotLock(Index);
257 RefCount = ChannelDereferenceByIndex(Index);
258 ASSERT(RefCount > 0);
259
260 /* Do we only have the keep-alive left, and the channel is dead? */
261 if ((RefCount == 1) && !(ChannelIsActive(Channel)))
262 {
263 /* Check if the ??? flag is set, or if there's no output data */
264 ThisChannel = ChannelFromIndex(Index);
265 if (!(ThisChannel->Flags & 1))
266 {
267 /* Nope, we can wipe the references and get rid of it */
268 ChannelDereferenceToZeroByIndex(Index);
269 }
270 else if (!ThisChannel->ChannelHasNewOBufferData)
271 {
272 /* No data, we can wipe the references and get rid of it */
273 ChannelDereferenceToZeroByIndex(Index);
274 }
275 }
276
277 /* We're done, we can unlock the slot now */
278 ChannelSlotUnlock(Index);
279 return STATUS_SUCCESS;
280 }
281
282 BOOLEAN
283 NTAPI
284 ChanMgrIsUniqueName(IN PWCHAR ChannelName)
285 {
286 NTSTATUS Status;
287 BOOLEAN IsUnique = FALSE;
288 PSAC_CHANNEL Channel;
289
290 /* Check if a channel with this name already exists */
291 Status = ChanMgrGetChannelByName(ChannelName, &Channel);
292 if (Status == STATUS_NOT_FOUND) IsUnique = TRUE;
293
294 /* If one did, dereference it, all we wanted was to check uniqueness */
295 if (NT_SUCCESS(Status)) ChanMgrReleaseChannel(Channel);
296
297 /* Return if one was found or not */
298 return IsUnique;
299 }
300
301 NTSTATUS
302 NTAPI
303 ChanMgrReapChannel(IN ULONG ChannelIndex)
304 {
305 /* FIXME: TODO */
306 return STATUS_NOT_IMPLEMENTED;
307 }
308
309 NTSTATUS
310 NTAPI
311 ChanMgrReapChannels(VOID)
312 {
313 ULONG i;
314 NTSTATUS Status = STATUS_SUCCESS;
315
316 /* Loop all the channels */
317 for (i = 0; i < SAC_MAX_CHANNELS; i++)
318 {
319 /* Lock this index and see if the channel was reaped */
320 ChannelSlotLock(i);
321 if (!ChannelReaped[i])
322 {
323 /* It was not reaped yet, so a channel should still be here */
324 ASSERT(ChannelFromIndex(i) != NULL);
325 if (ChannelGetReferenceCount(i) <= 0)
326 {
327 /* The channel has no more references, so clear the buffer flags */
328 _InterlockedExchange(&ChannelArray[i]->ChannelHasNewIBufferData, 0);
329 _InterlockedExchange(&ChannelArray[i]->ChannelHasNewOBufferData, 0);
330
331 /* And reap it */
332 Status = ChanMgrReapChannel(i);
333 }
334 }
335
336 /* Release the lock, and move on unless reaping failed */
337 ChannelSlotUnlock(i);
338 if (!NT_SUCCESS(Status)) break;
339 }
340
341 /* Return reaping status */
342 return Status;
343 }
344
345 NTSTATUS
346 NTAPI
347 ChanMgrCreateChannel(OUT PSAC_CHANNEL *Channel,
348 IN PSAC_CHANNEL_ATTRIBUTES Attributes)
349 {
350 NTSTATUS Status;
351 PSAC_CHANNEL NewChannel;
352 SAC_CHANNEL_ID ChanId;
353 ULONG i;
354 CHECK_PARAMETER(Channel);
355 CHECK_PARAMETER2(Attributes);
356
357 /* No other channel create attempts can happen */
358 ChannelLockCreates();
359
360 /* Is the channel manager initialized? */
361 if (!ChannelCreateEnabled)
362 {
363 /* Nope, bail out */
364 Status = STATUS_UNSUCCESSFUL;
365 goto ReturnStatus;
366 }
367
368 /* Reap any zombie channels */
369 Status = ChanMgrReapChannels();
370 if (!NT_SUCCESS(Status))
371 {
372 /* Bail out on error */
373 Status = STATUS_UNSUCCESSFUL;
374 goto ReturnStatus;
375 }
376
377 /* Check if we already have a channel with this name */
378 if (!ChanMgrIsUniqueName(Attributes->NameBuffer))
379 {
380 /* We do, fail */
381 Status = STATUS_DUPLICATE_NAME;
382 goto ReturnStatus;
383 }
384
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));
389
390 /* Loop channel slots */
391 for (i = 0; i < SAC_MAX_CHANNELS; i++)
392 {
393 /* Find a free spot for it */
394 if (ChannelReaped[i])
395 {
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;
400 }
401 }
402
403 /* Did we not find a single free slot? */
404 if (i == SAC_MAX_CHANNELS)
405 {
406 /* Bail out */
407 goto ReturnStatus;
408 }
409
410 /* Create an ID for this channel */
411 RtlZeroMemory(&ChanId, sizeof(ChanId));
412 Status = ExUuidCreate(&ChanId.ChannelGuid);
413 if (!NT_SUCCESS(Status))
414 {
415 /* Bail out if we couldn't */
416 SAC_DBG(SAC_DBG_INIT, "SAC Create Channel :: Failed to get GUID\n");
417 goto ReturnStatus;
418 }
419
420 /* Now create the channel proper */
421 Status = ChannelCreate(NewChannel, Attributes, ChanId);
422 if (NT_SUCCESS(Status))
423 {
424 /* Set the channel index */
425 _InterlockedExchange(&NewChannel->Index, i);
426
427 /* Add the initial reference to the channel */
428 ChannelReferenceToOneByIndexWithLock(i);
429
430 /* Return it to the caller */
431 *Channel = NewChannel;
432
433 /* This slot is now occupied */
434 ASSERT(ChannelReaped[i] == 1);
435 _InterlockedExchange(&ChannelReaped[i], 0);
436 }
437 else
438 {
439 /* We couldn't create it, free the buffer */
440 SacFreePool(NewChannel);
441 }
442
443 ReturnStatus:
444 /* Return whatever the operation status was */
445 ChannelUnlockCreates();
446 return Status;
447 }
448
449 NTSTATUS
450 NTAPI
451 ChanMgrGetByHandleAndFileObject(IN SAC_CHANNEL_ID ChannelId,
452 IN PFILE_OBJECT FileObject,
453 OUT PSAC_CHANNEL* TargetChannel)
454 {
455 NTSTATUS Status;
456 PSAC_CHANNEL FoundChannel;
457
458 /* Lookup the channel by ID first */
459 Status = ChanMgrGetByHandle(ChannelId, &FoundChannel);
460 if (NT_SUCCESS(Status))
461 {
462 /* We found it, now check if the file object matches */
463 if (FoundChannel->FileObject == FileObject)
464 {
465 /* Yep, return success */
466 *TargetChannel = FoundChannel;
467 }
468 else
469 {
470 /* Nope, drop the reference on the channel */
471 ChanMgrReleaseChannel(FoundChannel);
472
473 /* And return failure */
474 *TargetChannel = NULL;
475 Status = STATUS_NOT_FOUND;
476 }
477 }
478
479 /* Return if we found it or not */
480 return Status;
481 }
482
483 NTSTATUS
484 NTAPI
485 ChanMgrGetChannelIndex(IN PSAC_CHANNEL Channel,
486 IN PLONG ChannelIndex)
487 {
488 CHECK_PARAMETER1(Channel);
489 CHECK_PARAMETER2(ChannelIndex);
490
491 /* Just return the index of the channel */
492 *ChannelIndex = ChannelGetIndex(Channel);
493 return STATUS_SUCCESS;
494 }
495
496 NTSTATUS
497 NTAPI
498 ChanMgrGetByIndex(IN LONG TargetIndex,
499 IN PSAC_CHANNEL* TargetChannel)
500 {
501 NTSTATUS Status;
502 CHECK_PARAMETER1(TargetIndex < SAC_MAX_CHANNELS);
503 CHECK_PARAMETER2(TargetChannel);
504
505 /* Assume failure */
506 *TargetChannel = NULL;
507 Status = STATUS_NOT_FOUND;
508
509 /* Reference this one and check if it's valid */
510 if (ChannelReferenceByIndexWithLock(TargetIndex) > 0)
511 {
512 /* We found it, return it (with a reference held) */
513 *TargetChannel = ChannelFromIndex(TargetIndex);
514 return STATUS_SUCCESS;
515 }
516
517 /* No channels with this ID were found */
518 return Status;
519 }
520
521 NTSTATUS
522 NTAPI
523 ChanMgrGetNextActiveChannel(IN PSAC_CHANNEL CurrentChannel,
524 IN PULONG TargetIndex,
525 OUT PSAC_CHANNEL *TargetChannel)
526 {
527 NTSTATUS Status;
528 ULONG i;
529 LONG ChannelIndex, StartIndex;
530 PSAC_CHANNEL FoundChannel;
531 BOOLEAN ChannelFound;
532 CHECK_PARAMETER1(CurrentChannel);
533 CHECK_PARAMETER2(TargetIndex);
534 CHECK_PARAMETER3(TargetChannel);
535
536 /* Get the current channel index */
537 Status = ChanMgrGetChannelIndex(CurrentChannel, &ChannelIndex);
538 if (!NT_SUCCESS(Status)) return Status;
539
540 /* Assume failure */
541 ChannelFound = FALSE;
542
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)
546 {
547 /* Get the channel and see if it exists*/
548 Status = ChanMgrGetByIndex(i, &FoundChannel);
549 if (Status != STATUS_NOT_FOUND)
550 {
551 /* Bail out if we failed for some reason */
552 if (!NT_SUCCESS(Status)) return Status;
553
554 /* It exists -- is it active? Or, does it have output data? */
555 if ((ChannelIsActive(FoundChannel)) ||
556 (!(ChannelIsActive(FoundChannel)) &&
557 (FoundChannel->ChannelHasNewOBufferData)))
558 {
559 /* It's active or has output data, return with it */
560 ChannelFound = TRUE;
561 break;
562 }
563
564 /* Drop the reference on this channel and try the next one */
565 Status = ChanMgrReleaseChannel(FoundChannel);
566 if (!NT_SUCCESS(Status)) return Status;
567 }
568 }
569
570 /* Check if we successfully found a channel */
571 if ((NT_SUCCESS(Status)) && (ChannelFound))
572 {
573 /* Return it and its indexed. Remember we still hold the reference */
574 *TargetIndex = i;
575 *TargetChannel = FoundChannel;
576 }
577
578 /* All done */
579 return Status;
580 }
581
582 NTSTATUS
583 NTAPI
584 ChanMgrChannelDestroy(IN PSAC_CHANNEL Channel)
585 {
586 CHECK_PARAMETER1(Channel);
587 CHECK_PARAMETER(ChannelGetReferenceCount(Channel->Index) > 0);
588
589 /* Destroy the channel */
590 return Channel->ChannelDestroy(Channel);
591 }
592
593 NTSTATUS
594 NTAPI
595 ChanMgrCloseChannel(IN PSAC_CHANNEL Channel)
596 {
597 NTSTATUS Status;
598 CHECK_PARAMETER(Channel);
599
600 /* Check if the channel is active */
601 if (ChannelIsActive(Channel))
602 {
603 /* Yep, close it */
604 Status = ChannelClose(Channel);
605 }
606 else
607 {
608 /* Nothing to do */
609 Status = STATUS_ALREADY_DISCONNECTED;
610 }
611
612 /* Handle the channel close */
613 ConMgrHandleEvent(TRUE, Channel, &Status);
614 return Status;
615 }
616
617 NTSTATUS
618 NTAPI
619 ChanMgrGetChannelCount(OUT PULONG ChannelCount)
620 {
621 ULONG i;
622 PSAC_CHANNEL Channel;
623 NTSTATUS Status;
624 CHECK_PARAMETER(ChannelCount);
625
626 /* Assume no channels */
627 *ChannelCount = 0;
628
629 /* Loop every channel */
630 for (i = 0; i < SAC_MAX_CHANNELS; i++)
631 {
632 /* See if this one exists */
633 Status = ChanMgrGetByIndex(i, &Channel);
634 if (Status != STATUS_NOT_FOUND)
635 {
636 /* Sanity checks*/
637 ASSERT(NT_SUCCESS(Status));
638 ASSERT(Channel != NULL);
639
640 /* It exists -- is it active? Or, does it have output data? */
641 if ((ChannelIsActive(Channel)) ||
642 (!(ChannelIsActive(Channel)) &&
643 (Channel->ChannelHasNewOBufferData)))
644 {
645 /* It's active or has output data, increase the count */
646 ++*ChannelCount;
647 break;
648 }
649
650 /* Drop the reference on this channel and try the next one */
651 Status = ChanMgrReleaseChannel(Channel);
652 if (!NT_SUCCESS(Status)) return Status;
653 }
654 else
655 {
656 /* Channel doesn't exist, nothing wrong with that, keep going */
657 Status = STATUS_SUCCESS;
658 }
659 }
660
661 /* We should always succeed if we get here */
662 ASSERT(NT_SUCCESS(Status));
663 return Status;
664 }
665
666 NTSTATUS
667 NTAPI
668 ChanMgrIsFull(OUT PBOOLEAN IsFull)
669 {
670 NTSTATUS Status;
671 ULONG Count;
672
673 /* Count the channels */
674 Status = ChanMgrGetChannelCount(&Count);
675 CHECK_PARAMETER(Status == STATUS_SUCCESS);
676
677 /* Return if we hit the limit */
678 *IsFull = (Count == SAC_MAX_CHANNELS);
679 return Status;
680 }
681
682 NTSTATUS
683 NTAPI
684 ChanMgrCloseChannelsWithFileObject(IN PFILE_OBJECT FileObject)
685 {
686 PSAC_CHANNEL Channel;
687 ULONG i;
688 NTSTATUS Status;
689 CHECK_PARAMETER1(FileObject);
690
691 /* Loop all channels */
692 for (i = 0; i < SAC_MAX_CHANNELS; i++)
693 {
694 /* Try to get this one */
695 Status = ChanMgrGetByIndex(i, &Channel);
696 if (!NT_SUCCESS(Status)) break;
697
698 /* Check if the FO matches, if so, close the channel */
699 if (Channel->FileObject == FileObject) ChanMgrCloseChannel(Channel);
700
701 /* Drop the reference and try the next channel(s) */
702 Status = ChanMgrReleaseChannel(Channel);
703 if (!NT_SUCCESS(Status)) break;
704 }
705
706 /* All done */
707 return Status;
708 }
709
710 NTSTATUS
711 NTAPI
712 ChanMgrGenerateUniqueCmdName(IN PWCHAR ChannelName)
713 {
714 return STATUS_NOT_IMPLEMENTED;
715 }