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