Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2849 lines
69 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. ConMgr.c
  5. Abstract:
  6. Routines for managing channels in the sac.
  7. Author:
  8. Brian Guarraci (briangu) March, 2001.
  9. Revision History:
  10. --*/
  11. #include "sac.h"
  12. #include "concmd.h"
  13. #include "iomgr.h"
  14. //
  15. // Definitions for this file.
  16. //
  17. //
  18. // The maximum # of times we will try to write data via
  19. // headless dispatch
  20. //
  21. #define MAX_HEADLESS_DISPATCH_ATTEMPTS 32
  22. //
  23. // Spinlock macros
  24. //
  25. //
  26. // we need this lock to:
  27. //
  28. // 1. prevent asynchronous messages being put to the sac channel
  29. // 2. channels from dissapearing while they are the current channel
  30. //
  31. // we could get rid of this lock by:
  32. //
  33. // 1. providing some sort of event cue that gets processed when it's safe
  34. // 2. providing a means to notify channels when they are not the current channel
  35. // and have them stop outputting anymore
  36. // This goes back to the unresolved issue of having the Oecho and OFlush routines
  37. // managed by the conmgr - they could stop the outptu if the current channel changes,
  38. // rather than having the channel do the work. This is definitely a TODO since
  39. // having Headless dispatch calls in the channel I/O breaks the abstraction of the IoMgr.
  40. //
  41. #define INIT_CURRENT_CHANNEL_LOCK() \
  42. KeInitializeMutex( \
  43. &CurrentChannelLock, \
  44. 0 \
  45. ); \
  46. CurrentChannelRefCount = 0;
  47. #define LOCK_CURRENT_CHANNEL() \
  48. KeWaitForMutexObject( \
  49. &CurrentChannelLock, \
  50. Executive, \
  51. KernelMode, \
  52. FALSE, \
  53. NULL \
  54. ); \
  55. ASSERT(CurrentChannelRefCount == 0); \
  56. InterlockedIncrement((volatile long *)&CurrentChannelRefCount);
  57. #define UNLOCK_CURRENT_CHANNEL() \
  58. ASSERT(CurrentChannelRefCount == 1); \
  59. InterlockedDecrement((volatile long *)&CurrentChannelRefCount); \
  60. KeReleaseMutex( \
  61. &CurrentChannelLock, \
  62. FALSE \
  63. );
  64. #define ASSERT_LOCK_CURRENT_CHANNEL() \
  65. ASSERT(CurrentChannelRefCount == 1); \
  66. ASSERT(KeReadStateMutex(&CurrentChannelLock)==0);
  67. //
  68. // Serial Port Consumer globals
  69. //
  70. BOOLEAN ConMgrLastCharWasCR = FALSE;
  71. BOOLEAN InputInEscape = FALSE;
  72. BOOLEAN InputInEscTab = FALSE;
  73. UCHAR InputBuffer[SAC_VTUTF8_COL_WIDTH];
  74. //
  75. // Pointer to the SAC channel object
  76. //
  77. PSAC_CHANNEL SacChannel = NULL;
  78. //
  79. // The index the SAC in the channel array
  80. //
  81. #define SAC_CHANNEL_INDEX 0
  82. //
  83. // lock for r/w access on current channel globals
  84. //
  85. KMUTEX CurrentChannelLock;
  86. ULONG CurrentChannelRefCount;
  87. //
  88. //
  89. //
  90. EXECUTE_POST_CONSUMER_COMMAND_ENUM ExecutePostConsumerCommand = Nothing;
  91. PVOID ExecutePostConsumerCommandData = NULL;
  92. //
  93. // Channel Manager info for the current channel.
  94. // Depending on the application, the current channel
  95. // can be accessed using one of these references.
  96. //
  97. PSAC_CHANNEL CurrentChannel = NULL;
  98. //
  99. // prototypes
  100. //
  101. VOID
  102. ConMgrSerialPortConsumer(
  103. VOID
  104. );
  105. VOID
  106. ConMgrProcessInputLine(
  107. VOID
  108. );
  109. NTSTATUS
  110. ConMgrResetCurrentChannel(
  111. BOOLEAN SwitchDirectlyToChannel
  112. );
  113. NTSTATUS
  114. ConMgrInitialize(
  115. VOID
  116. )
  117. /*++
  118. Routine Description:
  119. This is the Console Manager's IoMgrInitialize implementation.
  120. Initialize the console manager
  121. Arguments:
  122. none
  123. Return Value:
  124. Status
  125. --*/
  126. {
  127. NTSTATUS Status;
  128. PSAC_CHANNEL TmpChannel;
  129. //
  130. // Initialize the current channel lock
  131. //
  132. INIT_CURRENT_CHANNEL_LOCK();
  133. //
  134. // Lock down the current channel globals
  135. //
  136. // Note: we need to do this here since many of the ConMgr support
  137. // routines do ASSERTs to ensure the current channel lock is held
  138. //
  139. LOCK_CURRENT_CHANNEL();
  140. //
  141. // Initialize
  142. //
  143. do {
  144. PCWSTR pcwch;
  145. SAC_CHANNEL_OPEN_ATTRIBUTES Attributes;
  146. //
  147. // Initialize the SAC channel attributes
  148. //
  149. RtlZeroMemory(&Attributes, sizeof(SAC_CHANNEL_OPEN_ATTRIBUTES));
  150. Attributes.Type = ChannelTypeVTUTF8;
  151. // attempt to copy the channel name
  152. pcwch = GetMessage(PRIMARY_SAC_CHANNEL_NAME);
  153. ASSERT(pcwch);
  154. if (!pcwch) {
  155. Status = STATUS_NO_MEMORY;
  156. break;
  157. }
  158. wcsncpy(Attributes.Name, pcwch, SAC_MAX_CHANNEL_NAME_LENGTH);
  159. Attributes.Name[SAC_MAX_CHANNEL_NAME_LENGTH] = UNICODE_NULL;
  160. // attempt to copy the channel description
  161. pcwch = GetMessage(PRIMARY_SAC_CHANNEL_DESCRIPTION);
  162. ASSERT(pcwch);
  163. if (!pcwch) {
  164. Status = STATUS_NO_MEMORY;
  165. break;
  166. }
  167. wcsncpy(Attributes.Description, pcwch, SAC_MAX_CHANNEL_DESCRIPTION_LENGTH);
  168. Attributes.Description[SAC_MAX_CHANNEL_DESCRIPTION_LENGTH] = UNICODE_NULL;
  169. Attributes.Flags = SAC_CHANNEL_FLAG_PRESERVE |
  170. SAC_CHANNEL_FLAG_APPLICATION_TYPE;
  171. Attributes.CloseEvent = NULL;
  172. Attributes.HasNewDataEvent = NULL;
  173. #if ENABLE_CHANNEL_LOCKING
  174. Attributes.LockEvent = NULL;
  175. #endif
  176. Attributes.RedrawEvent = NULL;
  177. Attributes.ApplicationType = PRIMARY_SAC_CHANNEL_APPLICATION_GUID;
  178. //
  179. // create the SAC channel
  180. //
  181. Status = ChanMgrCreateChannel(
  182. &SacChannel,
  183. &Attributes
  184. );
  185. if (! NT_SUCCESS(Status)) {
  186. break;
  187. }
  188. //
  189. // Get a reference to the SAC channel
  190. //
  191. // Note: this is the channel manager's policy
  192. // we need to get the reference of the channel
  193. // before we use it.
  194. //
  195. Status = ChanMgrGetByHandle(
  196. ChannelGetHandle(SacChannel),
  197. &TmpChannel
  198. );
  199. if (! NT_SUCCESS(Status)) {
  200. break;
  201. }
  202. SacChannel = TmpChannel;
  203. //
  204. // Assign the new current channel
  205. //
  206. CurrentChannel = SacChannel;
  207. //
  208. // Update the sent to screen status
  209. //
  210. ChannelSetSentToScreen(CurrentChannel, FALSE);
  211. //
  212. // Display the prompt
  213. //
  214. Status = HeadlessDispatch(
  215. HeadlessCmdClearDisplay,
  216. NULL,
  217. 0,
  218. NULL,
  219. NULL
  220. );
  221. if (! NT_SUCCESS(Status)) {
  222. IF_SAC_DEBUG(
  223. SAC_DEBUG_FAILS,
  224. KdPrint(("SAC ConMgrInitialize: Failed dispatch\n")));
  225. }
  226. //
  227. // Initialize the SAC display
  228. //
  229. SacPutSimpleMessage( SAC_ENTER );
  230. SacPutSimpleMessage( SAC_INITIALIZED );
  231. SacPutSimpleMessage( SAC_ENTER );
  232. SacPutSimpleMessage( SAC_PROMPT );
  233. //
  234. // Flush the channel data to the screen
  235. //
  236. Status = ConMgrDisplayCurrentChannel();
  237. if (! NT_SUCCESS(Status)) {
  238. break;
  239. }
  240. } while (FALSE);
  241. //
  242. // We are done with the current channel globals
  243. //
  244. UNLOCK_CURRENT_CHANNEL();
  245. return STATUS_SUCCESS;
  246. }
  247. NTSTATUS
  248. ConMgrShutdown(
  249. VOID
  250. )
  251. /*++
  252. Routine Description:
  253. This is the Console Manager's IoMgrShutdown implementation.
  254. Shutdown the console manager
  255. Arguments:
  256. none
  257. Return Value:
  258. Status
  259. --*/
  260. {
  261. NTSTATUS Status;
  262. //
  263. // close the sac channel
  264. //
  265. if (SacChannel) {
  266. Status = ChannelClose(SacChannel);
  267. if (!NT_SUCCESS(Status)) {
  268. IF_SAC_DEBUG(
  269. SAC_DEBUG_FAILS,
  270. KdPrint(("SAC ConMgrShutdown: failed closing SAC channel.\n"))
  271. );
  272. }
  273. SacChannel = NULL;
  274. }
  275. //
  276. // Release the current channel
  277. //
  278. if (CurrentChannel) {
  279. Status = ChanMgrReleaseChannel(CurrentChannel);
  280. if (!NT_SUCCESS(Status)) {
  281. IF_SAC_DEBUG(
  282. SAC_DEBUG_FAILS,
  283. KdPrint(("SAC ConMgrShutdown: failed releasing current channel\n"))
  284. );
  285. }
  286. CurrentChannel = NULL;
  287. }
  288. return STATUS_SUCCESS;
  289. }
  290. NTSTATUS
  291. ConMgrDisplayFastChannelSwitchingInterface(
  292. PSAC_CHANNEL Channel
  293. )
  294. /*++
  295. Routine Description:
  296. This routine displays the fast-channel-switching interface
  297. Note: caller must hold channel mutex
  298. Arguments:
  299. Channel - Channel to display
  300. Return Value:
  301. Status
  302. --*/
  303. {
  304. HEADLESS_CMD_POSITION_CURSOR SetCursor;
  305. HEADLESS_CMD_SET_COLOR SetColor;
  306. PCWSTR Message;
  307. NTSTATUS Status;
  308. ULONG OutputBufferSize;
  309. PWSTR OutputBuffer;
  310. PWSTR Name;
  311. PWSTR Description;
  312. PWSTR DescriptionWrapped;
  313. GUID ApplicationType;
  314. PWSTR ChannelTypeString;
  315. SAC_CHANNEL_HANDLE Handle;
  316. ASSERT_LOCK_CURRENT_CHANNEL();
  317. //
  318. // Initialize
  319. //
  320. OutputBufferSize = (11*SAC_VTUTF8_COL_WIDTH+1)*sizeof(WCHAR);
  321. OutputBuffer = ALLOCATE_POOL(OutputBufferSize, GENERAL_POOL_TAG);
  322. ASSERT_STATUS(OutputBuffer, STATUS_NO_MEMORY);
  323. Name = NULL;
  324. Description = NULL;
  325. DescriptionWrapped = NULL;
  326. //
  327. // Display the Fast-Channel-Switching interface
  328. //
  329. do {
  330. //
  331. // We cannot use the standard SacPutString() functions, because those write
  332. // over the channel screen buffer. We force directly onto the terminal here.
  333. //
  334. ASSERT(Utf8ConversionBuffer);
  335. if (!Utf8ConversionBuffer) {
  336. Status = STATUS_INSUFFICIENT_RESOURCES;
  337. break;
  338. }
  339. //
  340. // Clear the terminal screen.
  341. //
  342. Status = HeadlessDispatch(
  343. HeadlessCmdClearDisplay,
  344. NULL,
  345. 0,
  346. NULL,
  347. NULL
  348. );
  349. if (! NT_SUCCESS(Status)) {
  350. break;
  351. }
  352. SetCursor.Y = 0;
  353. SetCursor.X = 0;
  354. Status = HeadlessDispatch(
  355. HeadlessCmdPositionCursor,
  356. &SetCursor,
  357. sizeof(HEADLESS_CMD_POSITION_CURSOR),
  358. NULL,
  359. NULL
  360. );
  361. if (! NT_SUCCESS(Status)) {
  362. break;
  363. }
  364. //
  365. // Send starting colors.
  366. //
  367. SetColor.BkgColor = HEADLESS_TERM_DEFAULT_BKGD_COLOR;
  368. SetColor.FgColor = HEADLESS_TERM_DEFAULT_TEXT_COLOR;
  369. Status = HeadlessDispatch(
  370. HeadlessCmdSetColor,
  371. &SetColor,
  372. sizeof(HEADLESS_CMD_SET_COLOR),
  373. NULL,
  374. NULL
  375. );
  376. if (! NT_SUCCESS(Status)) {
  377. break;
  378. }
  379. Status = HeadlessDispatch(
  380. HeadlessCmdDisplayAttributesOff,
  381. NULL,
  382. 0,
  383. NULL,
  384. NULL
  385. );
  386. if (! NT_SUCCESS(Status)) {
  387. break;
  388. }
  389. //
  390. // Display xml bundle
  391. //
  392. Status = UTF8EncodeAndSend(L"<channel-switch>\r\n");
  393. if (! NT_SUCCESS(Status)) {
  394. break;
  395. }
  396. //
  397. // Get the channel's name
  398. //
  399. Status = ChannelGetName(
  400. Channel,
  401. &Name
  402. );
  403. if (! NT_SUCCESS(Status)) {
  404. break;
  405. }
  406. //
  407. // Get the channel's description
  408. //
  409. Status = ChannelGetDescription(
  410. Channel,
  411. &Description
  412. );
  413. if (! NT_SUCCESS(Status)) {
  414. break;
  415. }
  416. //
  417. // Get the channel handle
  418. //
  419. Handle = ChannelGetHandle(Channel);
  420. //
  421. // Get the channel's application type
  422. //
  423. ChannelGetApplicationType(
  424. Channel,
  425. &ApplicationType
  426. );
  427. //
  428. // Determine the channel type string
  429. //
  430. switch (ChannelGetType(Channel)) {
  431. case ChannelTypeVTUTF8:
  432. case ChannelTypeCmd:
  433. ChannelTypeString = L"VT-UTF8";
  434. break;
  435. case ChannelTypeRaw:
  436. ChannelTypeString = L"Raw";
  437. break;
  438. default:
  439. ChannelTypeString = L"UNKNOWN";
  440. ASSERT(0);
  441. break;
  442. }
  443. //
  444. // Assemble xml blob
  445. //
  446. SAFE_SWPRINTF(
  447. OutputBufferSize,
  448. (OutputBuffer,
  449. L"<name>%s</name>\r\n<description>%s</description>\r\n<type>%s</type>\r\n",
  450. Name,
  451. Description,
  452. ChannelTypeString
  453. ));
  454. Status = UTF8EncodeAndSend(OutputBuffer);
  455. if (! NT_SUCCESS(Status)) {
  456. break;
  457. }
  458. SAFE_SWPRINTF(
  459. OutputBufferSize,
  460. (OutputBuffer,
  461. L"<guid>%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x</guid>\r\n",
  462. Handle.ChannelHandle.Data1,
  463. Handle.ChannelHandle.Data2,
  464. Handle.ChannelHandle.Data3,
  465. Handle.ChannelHandle.Data4[0],
  466. Handle.ChannelHandle.Data4[1],
  467. Handle.ChannelHandle.Data4[2],
  468. Handle.ChannelHandle.Data4[3],
  469. Handle.ChannelHandle.Data4[4],
  470. Handle.ChannelHandle.Data4[5],
  471. Handle.ChannelHandle.Data4[6],
  472. Handle.ChannelHandle.Data4[7]
  473. ));
  474. Status = UTF8EncodeAndSend(OutputBuffer);
  475. if (! NT_SUCCESS(Status)) {
  476. break;
  477. }
  478. SAFE_SWPRINTF(
  479. OutputBufferSize,
  480. (OutputBuffer,
  481. L"<application-type>%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x</application-type>\r\n",
  482. ApplicationType.Data1,
  483. ApplicationType.Data2,
  484. ApplicationType.Data3,
  485. ApplicationType.Data4[0],
  486. ApplicationType.Data4[1],
  487. ApplicationType.Data4[2],
  488. ApplicationType.Data4[3],
  489. ApplicationType.Data4[4],
  490. ApplicationType.Data4[5],
  491. ApplicationType.Data4[6],
  492. ApplicationType.Data4[7]
  493. ));
  494. Status = UTF8EncodeAndSend(OutputBuffer);
  495. if (! NT_SUCCESS(Status)) {
  496. break;
  497. }
  498. Status = UTF8EncodeAndSend(L"</channel-switch>\r\n");
  499. if (! NT_SUCCESS(Status)) {
  500. break;
  501. }
  502. //
  503. // Clear the terminal screen.
  504. //
  505. Status = HeadlessDispatch(
  506. HeadlessCmdClearDisplay,
  507. NULL,
  508. 0,
  509. NULL,
  510. NULL
  511. );
  512. if (! NT_SUCCESS(Status)) {
  513. break;
  514. }
  515. SetCursor.Y = 0;
  516. SetCursor.X = 0;
  517. Status = HeadlessDispatch(
  518. HeadlessCmdPositionCursor,
  519. &SetCursor,
  520. sizeof(HEADLESS_CMD_POSITION_CURSOR),
  521. NULL,
  522. NULL
  523. );
  524. if (! NT_SUCCESS(Status)) {
  525. break;
  526. }
  527. //
  528. // Send starting colors.
  529. //
  530. SetColor.BkgColor = HEADLESS_TERM_DEFAULT_BKGD_COLOR;
  531. SetColor.FgColor = HEADLESS_TERM_DEFAULT_TEXT_COLOR;
  532. Status = HeadlessDispatch(
  533. HeadlessCmdSetColor,
  534. &SetColor,
  535. sizeof(HEADLESS_CMD_SET_COLOR),
  536. NULL,
  537. NULL
  538. );
  539. if (! NT_SUCCESS(Status)) {
  540. break;
  541. }
  542. Status = HeadlessDispatch(
  543. HeadlessCmdDisplayAttributesOff,
  544. NULL,
  545. 0,
  546. NULL,
  547. NULL
  548. );
  549. if (! NT_SUCCESS(Status)) {
  550. break;
  551. }
  552. //
  553. // Display the plaintext FCSwitching header
  554. //
  555. //
  556. // Modify the Descripto to wrap if necessary
  557. //
  558. Status = CopyAndInsertStringAtInterval(
  559. Description,
  560. 60,
  561. L"\r\n ",
  562. &DescriptionWrapped
  563. );
  564. if (! NT_SUCCESS(Status)) {
  565. break;
  566. }
  567. //
  568. // Get the Channel Switching Header
  569. //
  570. Message = GetMessage(SAC_CHANNEL_SWITCHING_HEADER);
  571. // Name: %%s
  572. // Description: %%s
  573. // Type: %%s
  574. // Channel GUID: %%08lx-%%04x-%%04x-%%02x%%02x-%%02x%%02x%%02x%%02x%%02x%%02x
  575. // Application Type: %%08lx-%%04x-%%04x-%%02x%%02x-%%02x%%02x%%02x%%02x%%02x%%02x
  576. //
  577. // Use <esc> then <tab> for next channel.
  578. // Use <esc> then 0 to return to the SAC channel.
  579. // Use any other key to view this channel.
  580. SAFE_SWPRINTF(
  581. OutputBufferSize,
  582. (OutputBuffer,
  583. Message,
  584. Name,
  585. DescriptionWrapped,
  586. ChannelTypeString,
  587. Handle.ChannelHandle.Data1,
  588. Handle.ChannelHandle.Data2,
  589. Handle.ChannelHandle.Data3,
  590. Handle.ChannelHandle.Data4[0],
  591. Handle.ChannelHandle.Data4[1],
  592. Handle.ChannelHandle.Data4[2],
  593. Handle.ChannelHandle.Data4[3],
  594. Handle.ChannelHandle.Data4[4],
  595. Handle.ChannelHandle.Data4[5],
  596. Handle.ChannelHandle.Data4[6],
  597. Handle.ChannelHandle.Data4[7],
  598. ApplicationType.Data1,
  599. ApplicationType.Data2,
  600. ApplicationType.Data3,
  601. ApplicationType.Data4[0],
  602. ApplicationType.Data4[1],
  603. ApplicationType.Data4[2],
  604. ApplicationType.Data4[3],
  605. ApplicationType.Data4[4],
  606. ApplicationType.Data4[5],
  607. ApplicationType.Data4[6],
  608. ApplicationType.Data4[7]
  609. ));
  610. Status = UTF8EncodeAndSend(OutputBuffer);
  611. if (! NT_SUCCESS(Status)) {
  612. break;
  613. }
  614. } while ( FALSE );
  615. SAFE_FREE_POOL(&Name);
  616. SAFE_FREE_POOL(&Description);
  617. SAFE_FREE_POOL(&DescriptionWrapped);
  618. SAFE_FREE_POOL(&OutputBuffer);
  619. return Status;
  620. }
  621. NTSTATUS
  622. ConMgrResetCurrentChannel(
  623. BOOLEAN SwitchDirectlyToChannel
  624. )
  625. /*++
  626. Routine Description:
  627. This routine makes the SAC the current channel
  628. Note: caller must hold channel mutex
  629. Arguments:
  630. SwitchDirectlyToChannel -
  631. if false,
  632. then show the switching interface,
  633. else switch directly to the channel
  634. Return Value:
  635. Status
  636. --*/
  637. {
  638. NTSTATUS Status;
  639. PSAC_CHANNEL TmpChannel;
  640. ASSERT_LOCK_CURRENT_CHANNEL();
  641. //
  642. // Get a reference to the SAC channel
  643. //
  644. // Note: this is the channel manager's policy
  645. // we need to get the reference of the channel
  646. // before we use it.
  647. //
  648. Status = ChanMgrGetByHandle(
  649. ChannelGetHandle(SacChannel),
  650. &TmpChannel
  651. );
  652. if (! NT_SUCCESS(Status)) {
  653. return Status;
  654. }
  655. SacChannel = TmpChannel;
  656. //
  657. // Make the SAC the current channel
  658. //
  659. Status = ConMgrSetCurrentChannel(SacChannel);
  660. if (! NT_SUCCESS(Status)) {
  661. return Status;
  662. }
  663. //
  664. //
  665. //
  666. if (SwitchDirectlyToChannel) {
  667. //
  668. // Flush the buffered channel data to the screen
  669. //
  670. // Note: we don't need to lock down the SAC, since we own it
  671. //
  672. Status = ConMgrDisplayCurrentChannel();
  673. } else {
  674. //
  675. // Let the user know we switched via the Channel switching interface
  676. //
  677. Status = ConMgrDisplayFastChannelSwitchingInterface(CurrentChannel);
  678. }
  679. return Status;
  680. }
  681. NTSTATUS
  682. ConMgrSetCurrentChannel(
  683. IN PSAC_CHANNEL Channel
  684. )
  685. /*++
  686. Routine Description:
  687. This routine release the current channel's ref count and sets
  688. the currently active channel to the one given. This routine
  689. assumes that the current channel was not release after it
  690. became the current channel. Hence, the typical use sequence
  691. for making a channel the current channel is:
  692. 1. ChanMgrGetByXXX --> Channel
  693. (This gets a channel and increments its ref count by 1)
  694. 2. ConMgrSetCurrentChannel(Channel)
  695. (This releases the current channel and makes the specified
  696. channel the current channel)
  697. 3. ...
  698. 4. goto 1.
  699. ( a new channel is made teh current channel)
  700. Arguments:
  701. NewChannel - the new current channel
  702. Return Value:
  703. Status
  704. --*/
  705. {
  706. NTSTATUS Status;
  707. BOOLEAN Present;
  708. ASSERT_LOCK_CURRENT_CHANNEL();
  709. //
  710. // Check to see if the channel has a redraw event
  711. //
  712. Status = ChannelHasRedrawEvent(
  713. CurrentChannel,
  714. &Present
  715. );
  716. if (!NT_SUCCESS(Status)) {
  717. return Status;
  718. }
  719. //
  720. // Tell the channel to start the drawing
  721. //
  722. if (Present) {
  723. ChannelClearRedrawEvent(CurrentChannel);
  724. }
  725. //
  726. // Update the sent to screen status
  727. //
  728. ChannelSetSentToScreen(CurrentChannel, FALSE);
  729. //
  730. // We are done with the current channel
  731. //
  732. Status = ChanMgrReleaseChannel(CurrentChannel);
  733. if (!NT_SUCCESS(Status)) {
  734. return Status;
  735. }
  736. //
  737. // Assign the new current channel
  738. //
  739. CurrentChannel = Channel;
  740. //
  741. // Update the sent to screen status
  742. //
  743. ChannelSetSentToScreen(CurrentChannel, FALSE);
  744. return STATUS_SUCCESS;
  745. }
  746. NTSTATUS
  747. ConMgrDisplayCurrentChannel(
  748. VOID
  749. )
  750. /*++
  751. Routine Description:
  752. This routine sets the currently active channel to the one given. It will transmit
  753. the channel buffer to the terminal if SendToScreen is TRUE.
  754. Note: caller must hold channel mutex
  755. Arguments:
  756. None
  757. Return Value:
  758. Status
  759. --*/
  760. {
  761. NTSTATUS Status;
  762. BOOLEAN Present;
  763. ASSERT_LOCK_CURRENT_CHANNEL();
  764. //
  765. // Check to see if the channel has a redraw event
  766. //
  767. Status = ChannelHasRedrawEvent(
  768. CurrentChannel,
  769. &Present
  770. );
  771. if (!NT_SUCCESS(Status)) {
  772. return Status;
  773. }
  774. //
  775. // The channel buffer has been sent to the screen
  776. //
  777. ChannelSetSentToScreen(CurrentChannel, TRUE);
  778. //
  779. // Tell the channel to start the drawing
  780. //
  781. if (Present) {
  782. ChannelSetRedrawEvent(CurrentChannel);
  783. }
  784. //
  785. // Flush the buffered data to the screen
  786. //
  787. Status = ChannelOFlush(CurrentChannel);
  788. return Status;
  789. }
  790. NTSTATUS
  791. ConMgrAdvanceCurrentChannel(
  792. VOID
  793. )
  794. /*++
  795. Routine Description:
  796. This routine queries the channel manager for the next available
  797. active channel and makes it the current channel.
  798. Note: The SAC channel is always active and cannot be deleted.
  799. Hence, we have a halting condition in that we will always
  800. stop at the SAC channel. For example, if the SAC is the
  801. only active channel, the current channel will remain the
  802. SAC channel.
  803. Arguments:
  804. None
  805. Return Value:
  806. Status
  807. --*/
  808. {
  809. NTSTATUS Status;
  810. ULONG NewIndex;
  811. PSAC_CHANNEL Channel;
  812. ASSERT_LOCK_CURRENT_CHANNEL();
  813. do {
  814. //
  815. // Query the channel manager for an array of currently active channels
  816. //
  817. Status = ChanMgrGetNextActiveChannel(
  818. CurrentChannel,
  819. &NewIndex,
  820. &Channel
  821. );
  822. if (! NT_SUCCESS(Status)) {
  823. break;
  824. }
  825. //
  826. // Change the current channel to the next active channel
  827. //
  828. Status = ConMgrSetCurrentChannel(Channel);
  829. if (! NT_SUCCESS(Status)) {
  830. break;
  831. }
  832. //
  833. // Let the user know we switched via the Channel switching interface
  834. //
  835. Status = ConMgrDisplayFastChannelSwitchingInterface(Channel);
  836. if (! NT_SUCCESS(Status)) {
  837. break;
  838. }
  839. } while ( FALSE );
  840. return Status;
  841. }
  842. BOOLEAN
  843. ConMgrIsWriteEnabled(
  844. PSAC_CHANNEL Channel
  845. )
  846. /*++
  847. Routine Description:
  848. This is the Console Manager's IoMgrIsWriteEnabled implementation.
  849. This routine determines if the channel in question is authorized
  850. to write to use the IoMgr's WriteData routine. In the console
  851. manager's case, this is TRUE if the channel is the current channel.
  852. From the channel's perspective, if the channel is not enabled to
  853. write, then it should buffer the data - to be released at a later
  854. time by the io manager.
  855. Arguments:
  856. ChannelHandle - channel handle to compare against
  857. Return Value:
  858. TRUE - the specified channel is the current channel
  859. --*/
  860. {
  861. SAC_CHANNEL_HANDLE Handle;
  862. //
  863. // Get the current channel's handle to compare against
  864. //
  865. Handle = ChannelGetHandle(CurrentChannel);
  866. //
  867. // Determine if the channel in question is the current channel
  868. //
  869. return ChannelIsEqual(
  870. Channel,
  871. &Handle
  872. );
  873. }
  874. VOID
  875. ConMgrWorkerProcessEvents(
  876. IN PSAC_DEVICE_CONTEXT DeviceContext
  877. )
  878. /*++
  879. Routine Description:
  880. This is the Console Manager's IoMgrWorkerProcessEvents implementation.
  881. This is the routine for the worker thread. It blocks on an event, when
  882. the event is signalled, then that indicates a request is ready to be processed.
  883. Arguments:
  884. DeviceContext - A pointer to this device.
  885. Return Value:
  886. None.
  887. --*/
  888. {
  889. NTSTATUS Status;
  890. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC WorkerProcessEvents: Entering.\n")));
  891. //
  892. // Loop forever.
  893. //
  894. while (1) {
  895. //
  896. // Block until there is work to do.
  897. //
  898. Status = KeWaitForSingleObject(
  899. (PVOID)&(DeviceContext->ProcessEvent),
  900. Executive,
  901. KernelMode,
  902. FALSE,
  903. NULL
  904. );
  905. //
  906. // Process the serial port buffer and return a processing state
  907. //
  908. ConMgrSerialPortConsumer();
  909. //
  910. // if there is work to do,
  911. // then something in the consumer wanted to perform
  912. // some action that would result in deadlock
  913. // contention for the Current channel lock.
  914. //
  915. switch(ExecutePostConsumerCommand) {
  916. case Reboot:
  917. DoRebootCommand(TRUE);
  918. //
  919. // we are done with this work
  920. //
  921. ExecutePostConsumerCommand = Nothing;
  922. break;
  923. case Shutdown:
  924. DoRebootCommand(FALSE);
  925. //
  926. // we are done with this work
  927. //
  928. ExecutePostConsumerCommand = Nothing;
  929. break;
  930. case CloseChannel: {
  931. PSAC_CHANNEL Channel;
  932. //
  933. // get the channel to close
  934. //
  935. Channel = (PSAC_CHANNEL)ExecutePostConsumerCommandData;
  936. //
  937. // attempt to close the channel
  938. //
  939. // Note: any error reporting necessary resulting
  940. // from this action will be carried out via
  941. // the IoMgrCloseChannel method
  942. //
  943. ChanMgrCloseChannel(Channel);
  944. //
  945. // We are done with the channel
  946. //
  947. ChanMgrReleaseChannel(Channel);
  948. //
  949. // we are done with this work
  950. //
  951. ExecutePostConsumerCommand = Nothing;
  952. ExecutePostConsumerCommandData = NULL;
  953. break;
  954. }
  955. case Nothing:
  956. default:
  957. break;
  958. }
  959. }
  960. ASSERT(0);
  961. }
  962. VOID
  963. ConMgrSerialPortConsumer(
  964. VOID
  965. )
  966. /*++
  967. Routine Description:
  968. This is a DPC routine that is queue'd by DriverEntry. It is used to check for any
  969. user input and then processes them.
  970. Arguments:
  971. None
  972. Return Value:
  973. None.
  974. --*/
  975. {
  976. NTSTATUS Status;
  977. UCHAR LocalTmpBuffer[4];
  978. ULONG i;
  979. UCHAR ch;
  980. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE_LOUD, KdPrint(("SAC TimerDpcRoutine: Entering.\n")));
  981. //
  982. // lock down the current channel globals
  983. //
  984. LOCK_CURRENT_CHANNEL();
  985. //
  986. // Make sure we have a current channel
  987. //
  988. // NOTE: we should at least have the SAC channel
  989. //
  990. ASSERT(CurrentChannel);
  991. if (CurrentChannel == NULL) {
  992. goto ConMgrSerialPortConsumerDone;
  993. }
  994. GetNextByte:
  995. //
  996. // Attempt to get a character from the serial port
  997. //
  998. Status = SerialBufferGetChar(&ch);
  999. //
  1000. // Bail if there are no new characters to read or if there was an error
  1001. //
  1002. if (!NT_SUCCESS(Status) || Status == STATUS_NO_DATA_DETECTED) {
  1003. goto ConMgrSerialPortConsumerDone;
  1004. }
  1005. //
  1006. // Possible states and actions:
  1007. //
  1008. // Note: <x> == <something else>
  1009. //
  1010. // <esc>
  1011. // <tab> --> advance channel
  1012. // <0> --> reset current channel to SAC
  1013. // <x> --> display current channel
  1014. // <x> --> if CurrentChannel != SacChannel, then write <esc> in channel->ibuffer
  1015. // and write <x> in channel->ibuffer
  1016. // <x> --> write <x> to current channel->ibuffer
  1017. //
  1018. //
  1019. // Check for <esc>
  1020. //
  1021. // Note: we can arrive at this routine from:
  1022. //
  1023. // <x>
  1024. // <esc><x>
  1025. // <esc><tab><x>
  1026. //
  1027. // So we need to clear the InputInEscTab flag
  1028. //
  1029. // If we are already in the <esc><x> sequence, then
  1030. // skip this block. This way we can receive <esc><esc>
  1031. // sequences.
  1032. //
  1033. if (ch == 0x1B && (InputInEscape == FALSE)) {
  1034. //
  1035. // We are no longer in an <esc><tab><x> sequence
  1036. //
  1037. InputInEscTab = FALSE;
  1038. //
  1039. // We are now in an <esc><x> sequence
  1040. //
  1041. InputInEscape = TRUE;
  1042. goto GetNextByte;
  1043. }
  1044. //
  1045. // Check for <esc><tab>
  1046. //
  1047. // Note: we can arrive at this routine from:
  1048. //
  1049. // <esc><x>
  1050. //
  1051. if ((ch == '\t') && InputInEscape) {
  1052. //
  1053. // We should not be in an <esc><tab><x> sequence already
  1054. //
  1055. ASSERT(InputInEscTab == FALSE);
  1056. //
  1057. // We are no longer in an <esc><x> sequence
  1058. //
  1059. InputInEscape = FALSE;
  1060. //
  1061. // Find the next active channel and make it the current
  1062. //
  1063. Status = ConMgrAdvanceCurrentChannel();
  1064. if (! NT_SUCCESS(Status)) {
  1065. goto ConMgrSerialPortConsumerDone;
  1066. }
  1067. //
  1068. // We are now in an <esc><tab><x> sequence
  1069. //
  1070. InputInEscTab = TRUE;
  1071. goto GetNextByte;
  1072. }
  1073. //
  1074. // If this screen has not yet been displayed and the user entered a 0,
  1075. // then switch to the SAC Channel
  1076. //
  1077. // Note: we can arrive at this routine from:
  1078. //
  1079. // <esc><tab><x>
  1080. //
  1081. if ((ch == '0') && InputInEscTab) {
  1082. //
  1083. // We should not be in an <esc><x> sequence at this point
  1084. //
  1085. ASSERT(InputInEscape == FALSE);
  1086. //
  1087. // We are no longer in an <esc><tab><x> sequence
  1088. //
  1089. InputInEscTab = FALSE;
  1090. //
  1091. // It is possible that the current channel has already been sent
  1092. // to the screen without having received the <x> of <esc><tab><x>
  1093. //
  1094. // For instance:
  1095. //
  1096. // 1. we received <esc><tab>
  1097. // a. InputInEscTab = TRUE
  1098. // b. the fast-channel-switching header is displayed
  1099. // c. sent to screen for current channel == false
  1100. // d. sent to screen for SAC channel == false
  1101. // 2. we leave the consumer since there is no new input
  1102. // 3. the current channel is closed by it's owner
  1103. // a. the current channel is removed
  1104. // b. the current channel becomes the SAC channel
  1105. // c. the current channel is displayed
  1106. // d. sent to screen for SAC channel == true
  1107. // 4. we receive <x> of <esc><tab><x> sequence
  1108. // 5. we end up here and are no longer in an EscTab sequence.
  1109. //
  1110. if (!ChannelSentToScreen(CurrentChannel)) {
  1111. //
  1112. // Make the current channel the SAC
  1113. //
  1114. // Note: There should not be anything modifying the SacChannel
  1115. // at this time, so this should be safe
  1116. //
  1117. Status = ConMgrResetCurrentChannel(FALSE);
  1118. if (! NT_SUCCESS(Status)) {
  1119. goto ConMgrSerialPortConsumerDone;
  1120. }
  1121. }
  1122. goto GetNextByte;
  1123. }
  1124. //
  1125. // If this screen has not yet been displayed,
  1126. // and the user entered a keystroke then display it.
  1127. //
  1128. // Note: we can arrive at this routine from:
  1129. //
  1130. // <x>
  1131. // <esc><x>
  1132. // <esc><tab><x>
  1133. //
  1134. // So we need to clear the esc sequence flags
  1135. //
  1136. if (!ChannelSentToScreen(CurrentChannel)) {
  1137. //
  1138. // We are no longer in an <esc><x> sequence
  1139. //
  1140. InputInEscape = FALSE;
  1141. //
  1142. // We are no longer in an <esc><tab><x> sequence
  1143. //
  1144. InputInEscTab = FALSE;
  1145. //
  1146. // Attempt to display the buffered contents of the current channel
  1147. //
  1148. Status = ConMgrDisplayCurrentChannel();
  1149. if (! NT_SUCCESS(Status)) {
  1150. goto ConMgrSerialPortConsumerDone;
  1151. }
  1152. goto GetNextByte;
  1153. } else {
  1154. //
  1155. // It is possible that the current channel has already been sent
  1156. // to the screen without having received the <x> of <esc><tab><x>
  1157. //
  1158. // For instance:
  1159. //
  1160. // 1. we received <esc><tab>
  1161. // a. InputInEscTab = TRUE
  1162. // b. the fast-channel-switching header is displayed
  1163. // c. sent to screen for current channel == false
  1164. // d. sent to screen for SAC channel == false
  1165. // 2. we leave the consumer since there is no new input
  1166. // 3. the current channel is closed by it's owner
  1167. // a. the current channel is removed
  1168. // b. the current channel becomes the SAC channel
  1169. // c. the current channel is displayed
  1170. // d. sent to screen for SAC channel == true
  1171. // 4. we receive <x> of <esc><tab><x> sequence
  1172. // 5. we skip the (!ChannelSentToScreen(CurrentChannel)) block
  1173. // 6. we end up here. Since the <x> != 0 and we have already
  1174. // sent the current data to the screen, we are no longer
  1175. // in an EscTab sequence.
  1176. //
  1177. InputInEscTab = FALSE;
  1178. }
  1179. //
  1180. // This is the beginning of the fall-through block.
  1181. // That is, if we get here, then the character is not a part
  1182. // of some special sequence that should have been processed
  1183. // above. Characters processed here are inserted into the
  1184. // current channel's input buffer.
  1185. //
  1186. // Note: we should not be in an <esc><tab><x> sequence here
  1187. //
  1188. ASSERT(InputInEscTab == FALSE);
  1189. //
  1190. // If the user was entering <esc><x> and the current channel
  1191. // is not the SAC, then store the <esc> in the current channel's
  1192. // ibuffer.
  1193. //
  1194. // Note: <esc><esc> buffers a single <esc>.
  1195. // This allows sending an real <esc><tab> to the channel.
  1196. //
  1197. if (InputInEscape && (CurrentChannel != SacChannel)) {
  1198. LocalTmpBuffer[0] = 0x1B;
  1199. Status = ChannelIWrite(
  1200. CurrentChannel,
  1201. LocalTmpBuffer,
  1202. sizeof(LocalTmpBuffer[0])
  1203. );
  1204. }
  1205. //
  1206. // If the current character is <esc>,
  1207. // then we still are in an escape sequence so
  1208. // don't change the InputInEscape.
  1209. // This allows <esc><esc> to be followed by <tab>
  1210. // and form a valid <esc><tab> sequence.
  1211. //
  1212. if (ch != 0x1B) {
  1213. //
  1214. // We are no longer in an <esc><x> sequence
  1215. //
  1216. InputInEscape = FALSE;
  1217. }
  1218. //
  1219. // Buffer this input to the current channel's IBuffer
  1220. //
  1221. ChannelIWrite(
  1222. CurrentChannel,
  1223. &ch,
  1224. sizeof(ch)
  1225. );
  1226. //
  1227. // If the current channel is not the SAC, then go and get more input.
  1228. // Otherwise, process the SAC's input buffer
  1229. //
  1230. if (CurrentChannel != SacChannel) {
  1231. goto GetNextByte;
  1232. } else {
  1233. ULONG ResponseLength;
  1234. WCHAR wch;
  1235. //
  1236. // Now do processing if the SAC is the active channel.
  1237. //
  1238. //
  1239. // Strip the LF if the last character was a CR
  1240. //
  1241. if (ConMgrLastCharWasCR && ch == (UCHAR)0x0A) {
  1242. ChannelIReadLast(CurrentChannel);
  1243. ConMgrLastCharWasCR = FALSE;
  1244. goto GetNextByte;
  1245. }
  1246. //
  1247. // Keep track of the of when we receive a CR so
  1248. // we can strip of the LF if it is next
  1249. //
  1250. ConMgrLastCharWasCR = (ch == 0x0D ? TRUE : FALSE);
  1251. //
  1252. // If this is a return, then we are done and need to return the line
  1253. //
  1254. if ((ch == '\n') || (ch == '\r')) {
  1255. SacPutString(L"\r\n");
  1256. ChannelIReadLast(CurrentChannel);
  1257. LocalTmpBuffer[0] = '\0';
  1258. ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
  1259. goto StripWhitespaceAndReturnLine;
  1260. }
  1261. //
  1262. // If this is a backspace or delete, then we need to do that.
  1263. //
  1264. if ((ch == 0x8) || (ch == 0x7F)) { // backspace (^H) or delete
  1265. //
  1266. // We want to:
  1267. // 1. remove the backspace or delete character
  1268. // 2. if the input buffer is non-empty, remove the last character
  1269. // (which is the character the user wanted to delete)
  1270. //
  1271. if (ChannelIBufferLength(CurrentChannel) > 0) {
  1272. ChannelIReadLast(CurrentChannel);
  1273. }
  1274. if (ChannelIBufferLength(CurrentChannel) > 0) {
  1275. SacPutString(L"\010 \010");
  1276. ChannelIReadLast(CurrentChannel);
  1277. }
  1278. } else if (ch == 0x3) { // Control-C
  1279. //
  1280. // Terminate the string and return it.
  1281. //
  1282. ChannelIReadLast(CurrentChannel);
  1283. LocalTmpBuffer[0] = '\0';
  1284. ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
  1285. goto StripWhitespaceAndReturnLine;
  1286. } else if (ch == 0x9) { // Tab
  1287. //
  1288. // Ignore tabs
  1289. //
  1290. ChannelIReadLast(CurrentChannel);
  1291. SacPutString(L"\007"); // send a BEL
  1292. goto GetNextByte;
  1293. } else if (ChannelIBufferLength(CurrentChannel) == SAC_VTUTF8_COL_WIDTH - 2) {
  1294. WCHAR Buffer[4];
  1295. //
  1296. // We are at the end of the screen - remove the last character from
  1297. // the terminal screen and replace it with this one.
  1298. //
  1299. swprintf(Buffer, L"\010%c", ch);
  1300. SacPutString(Buffer);
  1301. ChannelIReadLast(CurrentChannel);
  1302. ChannelIReadLast(CurrentChannel);
  1303. LocalTmpBuffer[0] = ch;
  1304. ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
  1305. } else {
  1306. WCHAR Buffer[4];
  1307. //
  1308. // Echo the character to the screen
  1309. //
  1310. swprintf(Buffer, L"%c", ch);
  1311. SacPutString(Buffer);
  1312. }
  1313. goto GetNextByte;
  1314. StripWhitespaceAndReturnLine:
  1315. //
  1316. // Before returning the input line, strip off all leading and trailing blanks
  1317. //
  1318. do {
  1319. LocalTmpBuffer[0] = (UCHAR)ChannelIReadLast(CurrentChannel);
  1320. } while (((LocalTmpBuffer[0] == '\0') ||
  1321. (LocalTmpBuffer[0] == ' ') ||
  1322. (LocalTmpBuffer[0] == '\t')) &&
  1323. (ChannelIBufferLength(CurrentChannel) > 0)
  1324. );
  1325. ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
  1326. LocalTmpBuffer[0] = '\0';
  1327. ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
  1328. do {
  1329. Status = ChannelIRead(
  1330. CurrentChannel,
  1331. (PUCHAR)&wch,
  1332. sizeof(WCHAR),
  1333. &ResponseLength
  1334. );
  1335. LocalTmpBuffer[0] = (UCHAR)wch;
  1336. } while ((ResponseLength != 0) &&
  1337. ((LocalTmpBuffer[0] == ' ') ||
  1338. (LocalTmpBuffer[0] == '\t')));
  1339. InputBuffer[0] = LocalTmpBuffer[0];
  1340. i = 1;
  1341. do {
  1342. Status = ChannelIRead(
  1343. CurrentChannel,
  1344. (PUCHAR)&wch,
  1345. sizeof(WCHAR),
  1346. &ResponseLength
  1347. );
  1348. ASSERT(i < SAC_VTUTF8_COL_WIDTH);
  1349. InputBuffer[i++] = (UCHAR)wch;
  1350. } while (ResponseLength != 0);
  1351. //
  1352. // Lower case all the characters. We do not use strlwr() or the like, so that
  1353. // the SAC (expecting ASCII always) doesn't accidently get DBCS or the like
  1354. // translation of the UCHAR stream.
  1355. //
  1356. for (i = 0; InputBuffer[i] != '\0'; i++) {
  1357. ASSERT(i < SAC_VTUTF8_COL_WIDTH);
  1358. if ((InputBuffer[i] >= 'A') && (InputBuffer[i] <= 'Z')) {
  1359. InputBuffer[i] = InputBuffer[i] - 'A' + 'a';
  1360. }
  1361. }
  1362. ASSERT(ExecutePostConsumerCommand == Nothing);
  1363. //
  1364. // Process the input line.
  1365. //
  1366. ConMgrProcessInputLine();
  1367. //
  1368. // Put the next command prompt
  1369. //
  1370. SacPutSimpleMessage(SAC_PROMPT);
  1371. //
  1372. // exit if we need to do some work
  1373. //
  1374. if (ExecutePostConsumerCommand != Nothing) {
  1375. goto ConMgrSerialPortConsumerDone;
  1376. }
  1377. //
  1378. // Keep on processing characters
  1379. //
  1380. goto GetNextByte;
  1381. }
  1382. ConMgrSerialPortConsumerDone:
  1383. //
  1384. // We are done with current channel globals
  1385. //
  1386. UNLOCK_CURRENT_CHANNEL();
  1387. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE_LOUD, KdPrint(("SAC TimerDpcRoutine: Exiting.\n")));
  1388. return;
  1389. }
  1390. VOID
  1391. ConMgrProcessInputLine(
  1392. VOID
  1393. )
  1394. /*++
  1395. Routine Description:
  1396. This routine is called to process an input line.
  1397. Arguments:
  1398. None.
  1399. Return Value:
  1400. None.
  1401. --*/
  1402. {
  1403. HEADLESS_CMD_DISPLAY_LOG Command;
  1404. PUCHAR InputLine;
  1405. BOOLEAN CommandFound = FALSE;
  1406. InputLine = &(InputBuffer[0]);
  1407. do {
  1408. if (!strcmp((LPSTR)InputLine, TLIST_COMMAND_STRING)) {
  1409. DoTlistCommand();
  1410. CommandFound = TRUE;
  1411. break;
  1412. }
  1413. if ((!strcmp((LPSTR)InputLine, HELP1_COMMAND_STRING)) ||
  1414. (!strcmp((LPSTR)InputLine, HELP2_COMMAND_STRING))) {
  1415. DoHelpCommand();
  1416. CommandFound = TRUE;
  1417. break;
  1418. }
  1419. if (!strcmp((LPSTR)InputLine, DUMP_COMMAND_STRING)) {
  1420. NTSTATUS Status;
  1421. Command.Paging = GlobalPagingNeeded;
  1422. Status = HeadlessDispatch(
  1423. HeadlessCmdDisplayLog,
  1424. &Command,
  1425. sizeof(HEADLESS_CMD_DISPLAY_LOG),
  1426. NULL,
  1427. NULL
  1428. );
  1429. if (! NT_SUCCESS(Status)) {
  1430. IF_SAC_DEBUG(
  1431. SAC_DEBUG_FAILS,
  1432. KdPrint(("SAC Display Log failed.\n"))
  1433. );
  1434. }
  1435. CommandFound = TRUE;
  1436. break;
  1437. }
  1438. if (!strcmp((LPSTR)InputLine, FULLINFO_COMMAND_STRING)) {
  1439. DoFullInfoCommand();
  1440. CommandFound = TRUE;
  1441. break;
  1442. }
  1443. if (!strcmp((LPSTR)InputLine, PAGING_COMMAND_STRING)) {
  1444. DoPagingCommand();
  1445. CommandFound = TRUE;
  1446. break;
  1447. }
  1448. if (!strncmp((LPSTR)InputLine,
  1449. CHANNEL_COMMAND_STRING,
  1450. strlen(CHANNEL_COMMAND_STRING))) {
  1451. ULONG Length;
  1452. Length = (ULONG)strlen(CHANNEL_COMMAND_STRING);
  1453. if (((strlen((LPSTR)InputLine) > 1) && (InputLine[Length] == ' ')) ||
  1454. (strlen((LPSTR)InputLine) == strlen(CHANNEL_COMMAND_STRING))) {
  1455. DoChannelCommand(InputLine);
  1456. CommandFound = TRUE;
  1457. break;
  1458. }
  1459. }
  1460. if (!strcmp((LPSTR)InputLine, CMD_COMMAND_STRING)) {
  1461. #if ENABLE_CMD_SESSION_PERMISSION_CHECKING
  1462. //
  1463. // If we are not able to launch cmd sessions,
  1464. // then notify that we cannot peform this action
  1465. //
  1466. if (IsCommandConsoleLaunchingEnabled()) {
  1467. DoCmdCommand(InputLine);
  1468. } else {
  1469. //
  1470. // Notify the user
  1471. //
  1472. SacPutSimpleMessage(SAC_CMD_LAUNCHING_DISABLED);
  1473. }
  1474. #else
  1475. DoCmdCommand(InputLine);
  1476. #endif
  1477. CommandFound = TRUE;
  1478. break;
  1479. }
  1480. if (!strcmp((LPSTR)InputLine, REBOOT_COMMAND_STRING)) {
  1481. //
  1482. // Set the reboot flag so that when we exit the serial consumer
  1483. // we know to reboot the computer. This way, the reboot
  1484. // command is executed when we dont have the Current Channel mutex
  1485. //
  1486. ExecutePostConsumerCommand = Reboot;
  1487. CommandFound = TRUE;
  1488. break;
  1489. }
  1490. if (!strcmp((LPSTR)InputLine, SHUTDOWN_COMMAND_STRING)) {
  1491. //
  1492. // Set the shutdown flag so that when we exit the serial consumer
  1493. // we know to shutdown the computer. This way, the shutdown
  1494. // command is executed when we dont have the Current Channel mutex
  1495. //
  1496. ExecutePostConsumerCommand = Shutdown;
  1497. CommandFound = TRUE;
  1498. break;
  1499. }
  1500. if (!strcmp((LPSTR)InputLine, CRASH_COMMAND_STRING)) {
  1501. CommandFound = TRUE;
  1502. DoCrashCommand(); // this call does not return
  1503. break;
  1504. }
  1505. if (!strncmp((LPSTR)InputLine,
  1506. KILL_COMMAND_STRING,
  1507. sizeof(KILL_COMMAND_STRING) - sizeof(UCHAR))) {
  1508. if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
  1509. (strlen((LPSTR)InputLine) == 1)
  1510. ) {
  1511. DoKillCommand(InputLine);
  1512. CommandFound = TRUE;
  1513. break;
  1514. }
  1515. }
  1516. #if ENABLE_CHANNEL_LOCKING
  1517. if (!strcmp((LPSTR)InputLine, LOCK_COMMAND_STRING)) {
  1518. DoLockCommand();
  1519. CommandFound = TRUE;
  1520. break;
  1521. }
  1522. #endif
  1523. if (!strncmp((LPSTR)InputLine,
  1524. LOWER_COMMAND_STRING,
  1525. sizeof(LOWER_COMMAND_STRING) - sizeof(UCHAR))) {
  1526. if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
  1527. (strlen((LPSTR)InputLine) == 1)
  1528. ) {
  1529. DoLowerPriorityCommand(InputLine);
  1530. CommandFound = TRUE;
  1531. break;
  1532. }
  1533. }
  1534. if (!strncmp((LPSTR)InputLine,
  1535. RAISE_COMMAND_STRING,
  1536. sizeof(RAISE_COMMAND_STRING) - sizeof(UCHAR))) {
  1537. if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
  1538. (strlen((LPSTR)InputLine) == 1)
  1539. ) {
  1540. DoRaisePriorityCommand(InputLine);
  1541. CommandFound = TRUE;
  1542. break;
  1543. }
  1544. }
  1545. if (!strncmp((LPSTR)InputLine,
  1546. LIMIT_COMMAND_STRING,
  1547. sizeof(LIMIT_COMMAND_STRING) - sizeof(UCHAR))) {
  1548. if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
  1549. (strlen((LPSTR)InputLine) == 1)
  1550. ) {
  1551. DoLimitMemoryCommand(InputLine);
  1552. CommandFound = TRUE;
  1553. break;
  1554. }
  1555. }
  1556. if (!strncmp((LPSTR)InputLine,
  1557. TIME_COMMAND_STRING,
  1558. sizeof(TIME_COMMAND_STRING) - sizeof(UCHAR))) {
  1559. if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
  1560. (strlen((LPSTR)InputLine) == 1)
  1561. ) {
  1562. DoSetTimeCommand(InputLine);
  1563. CommandFound = TRUE;
  1564. break;
  1565. }
  1566. }
  1567. if (!strcmp((LPSTR)InputLine, INFORMATION_COMMAND_STRING)) {
  1568. DoMachineInformationCommand();
  1569. CommandFound = TRUE;
  1570. break;
  1571. }
  1572. if (!strncmp((LPSTR)InputLine,
  1573. SETIP_COMMAND_STRING,
  1574. sizeof(SETIP_COMMAND_STRING) - sizeof(UCHAR))) {
  1575. if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
  1576. (strlen((LPSTR)InputLine) == 1)
  1577. ) {
  1578. DoSetIpAddressCommand(InputLine);
  1579. CommandFound = TRUE;
  1580. break;
  1581. }
  1582. }
  1583. if ((InputLine[0] == '\n') || (InputLine[0] == '\0')) {
  1584. CommandFound = TRUE;
  1585. }
  1586. } while ( FALSE );
  1587. if( !CommandFound ) {
  1588. //
  1589. // We don't know what this is.
  1590. //
  1591. SacPutSimpleMessage(SAC_UNKNOWN_COMMAND);
  1592. }
  1593. }
  1594. //
  1595. // Utility routines for writing to the SAC
  1596. //
  1597. VOID
  1598. ConMgrEventMessageHaveLock(
  1599. IN PCWSTR String
  1600. )
  1601. /*++
  1602. Routine Description:
  1603. This routine is for callers that want to deploy an event
  1604. message and already own the Current Channel Lock.
  1605. Arguments:
  1606. String - The string to display.
  1607. Return Value:
  1608. None.
  1609. --*/
  1610. {
  1611. //
  1612. // Currently, event messages are sent to the SAC channel
  1613. //
  1614. SacPutString(String);
  1615. }
  1616. VOID
  1617. ConMgrEventMessage(
  1618. IN PCWSTR String,
  1619. IN BOOLEAN HaveCurrentChannelLock
  1620. )
  1621. /*++
  1622. Routine Description:
  1623. This routine deploys an event message
  1624. Arguments:
  1625. String - The string to display.
  1626. HaveLock - Whether or not the caller currently owns the Current Channel Lock
  1627. Return Value:
  1628. None.
  1629. --*/
  1630. {
  1631. //
  1632. // Currently, event messages are sent to the SAC channel
  1633. //
  1634. if (! HaveCurrentChannelLock) {
  1635. LOCK_CURRENT_CHANNEL();
  1636. }
  1637. SacPutSimpleMessage(SAC_ENTER);
  1638. ConMgrEventMessageHaveLock(String);
  1639. SacPutSimpleMessage(SAC_PROMPT);
  1640. if (! HaveCurrentChannelLock) {
  1641. UNLOCK_CURRENT_CHANNEL();
  1642. }
  1643. }
  1644. BOOLEAN
  1645. ConMgrSimpleEventMessage(
  1646. IN ULONG MessageId,
  1647. IN BOOLEAN HaveCurrentChannelLock
  1648. )
  1649. /*++
  1650. Routine Description:
  1651. This routine retrieves a message resource and sends it as an event message
  1652. Arguments:
  1653. MessageId - The message id of the resource to send
  1654. Return Value:
  1655. TRUE - the message was found
  1656. otherwise, FALSE
  1657. --*/
  1658. {
  1659. PCWSTR p;
  1660. p = GetMessage(MessageId);
  1661. if (p) {
  1662. ConMgrEventMessage(
  1663. p,
  1664. HaveCurrentChannelLock
  1665. );
  1666. return(TRUE);
  1667. }
  1668. return(FALSE);
  1669. }
  1670. VOID
  1671. SacPutString(
  1672. PCWSTR String
  1673. )
  1674. /*++
  1675. Routine Description:
  1676. This routine takes a string and packages it into a command structure for the
  1677. HeadlessDispatch routine.
  1678. Arguments:
  1679. String - The string to display.
  1680. Return Value:
  1681. None.
  1682. --*/
  1683. {
  1684. NTSTATUS Status;
  1685. ASSERT(FIELD_OFFSET(HEADLESS_CMD_PUT_STRING, String) == 0); // ASSERT if anyone changes this structure.
  1686. //
  1687. // Write the to the sac channel
  1688. //
  1689. Status = ChannelOWrite(
  1690. SacChannel,
  1691. (PCUCHAR)String,
  1692. (ULONG)(wcslen(String)*sizeof(WCHAR))
  1693. );
  1694. if (! NT_SUCCESS(Status)) {
  1695. IF_SAC_DEBUG(
  1696. SAC_DEBUG_FAILS,
  1697. KdPrint(("SAC XmlMgrSacPutString: OWrite failed\n"))
  1698. );
  1699. }
  1700. }
  1701. BOOLEAN
  1702. SacPutSimpleMessage(
  1703. ULONG MessageId
  1704. )
  1705. /*++
  1706. Routine Description:
  1707. This routine retrieves a message resource and sends it to the SAC channel
  1708. Arguments:
  1709. MessageId - The message id of the resource to send
  1710. Return Value:
  1711. TRUE - the message was found
  1712. otherwise, FALSE
  1713. --*/
  1714. {
  1715. PCWSTR p;
  1716. p = GetMessage(MessageId);
  1717. if (p) {
  1718. SacPutString(p);
  1719. return(TRUE);
  1720. }
  1721. return(FALSE);
  1722. }
  1723. NTSTATUS
  1724. ConMgrChannelOWrite(
  1725. IN PSAC_CHANNEL Channel,
  1726. IN PSAC_CMD_WRITE_CHANNEL ChannelWriteCmd
  1727. )
  1728. /*++
  1729. Routine Description:
  1730. This routine attempts to write data to a channel
  1731. Arguments:
  1732. Channel - the channel to write to
  1733. ChannelWriteCmd - the write IOCTL command structure
  1734. Return Value:
  1735. Status
  1736. --*/
  1737. {
  1738. NTSTATUS Status;
  1739. //
  1740. //
  1741. //
  1742. LOCK_CURRENT_CHANNEL();
  1743. //
  1744. // Write the data to the channel's output buffer
  1745. //
  1746. Status = ChannelOWrite(
  1747. Channel,
  1748. &(ChannelWriteCmd->Buffer[0]),
  1749. ChannelWriteCmd->Size
  1750. );
  1751. //
  1752. //
  1753. //
  1754. UNLOCK_CURRENT_CHANNEL();
  1755. ASSERT(NT_SUCCESS(Status) || Status == STATUS_NOT_FOUND);
  1756. return Status;
  1757. }
  1758. NTSTATUS
  1759. ConMgrGetChannelCloseMessage(
  1760. IN PSAC_CHANNEL Channel,
  1761. IN NTSTATUS CloseStatus,
  1762. OUT PWSTR* OutputBuffer
  1763. )
  1764. /*++
  1765. Routine Description:
  1766. This routine constructs an event message based
  1767. on the status of attempting to close a channel
  1768. Arguments:
  1769. Channel - the channel being closed
  1770. CloseStatus - the resulting status
  1771. OutputBuffer - on exit, contains the message
  1772. Return Value:
  1773. Status
  1774. --*/
  1775. {
  1776. NTSTATUS Status;
  1777. ULONG Size;
  1778. PWSTR Name;
  1779. PCWSTR Message;
  1780. //
  1781. // default: we succeded
  1782. //
  1783. Status = STATUS_SUCCESS;
  1784. do {
  1785. //
  1786. // Get the channel's name
  1787. //
  1788. Status = ChannelGetName(
  1789. Channel,
  1790. &Name
  1791. );
  1792. if (! NT_SUCCESS(Status)) {
  1793. break;
  1794. }
  1795. //
  1796. // Allocate a local temp buffer for display
  1797. //
  1798. if (NT_SUCCESS(CloseStatus)) {
  1799. //
  1800. // get the string resource
  1801. //
  1802. Message = GetMessage(SAC_CHANNEL_CLOSED);
  1803. if (Message == NULL) {
  1804. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  1805. break;
  1806. }
  1807. //
  1808. // Allocate the buffer memory
  1809. //
  1810. Size = (ULONG)((wcslen(Message) + SAC_MAX_CHANNEL_NAME_LENGTH + 1) * sizeof(WCHAR));
  1811. *OutputBuffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
  1812. ASSERT_STATUS(*OutputBuffer, STATUS_NO_MEMORY);
  1813. //
  1814. // report the channel has been closed
  1815. //
  1816. SAFE_SWPRINTF(
  1817. Size,
  1818. (*OutputBuffer,
  1819. Message,
  1820. Name
  1821. ));
  1822. } else if (CloseStatus == STATUS_ALREADY_DISCONNECTED) {
  1823. //
  1824. // get the string resource
  1825. //
  1826. Message = GetMessage(SAC_CHANNEL_ALREADY_CLOSED);
  1827. if (Message == NULL) {
  1828. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  1829. break;
  1830. }
  1831. //
  1832. // Allocate the buffer memory
  1833. //
  1834. Size = (ULONG)((wcslen(Message) + SAC_MAX_CHANNEL_NAME_LENGTH + 1) * sizeof(WCHAR));
  1835. *OutputBuffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
  1836. ASSERT_STATUS(*OutputBuffer, STATUS_NO_MEMORY);
  1837. //
  1838. // report the channel was already closed
  1839. //
  1840. SAFE_SWPRINTF(
  1841. Size,
  1842. (*OutputBuffer,
  1843. Message,
  1844. Name
  1845. ));
  1846. } else {
  1847. //
  1848. // get the string resource
  1849. //
  1850. Message = GetMessage(SAC_CHANNEL_FAILED_CLOSE);
  1851. if (Message == NULL) {
  1852. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  1853. break;
  1854. }
  1855. //
  1856. // Allocate the buffer memory
  1857. //
  1858. Size = (ULONG)((wcslen(Message) + SAC_MAX_CHANNEL_NAME_LENGTH + 1) * sizeof(WCHAR));
  1859. *OutputBuffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
  1860. ASSERT_STATUS(*OutputBuffer, STATUS_NO_MEMORY);
  1861. //
  1862. // report that we failed to close the channel
  1863. //
  1864. SAFE_SWPRINTF(
  1865. Size,
  1866. (*OutputBuffer,
  1867. Message,
  1868. Name
  1869. ));
  1870. }
  1871. SAFE_FREE_POOL(&Name);
  1872. } while ( FALSE );
  1873. return Status;
  1874. }
  1875. NTSTATUS
  1876. ConMgrChannelClose(
  1877. PSAC_CHANNEL Channel
  1878. )
  1879. /*++
  1880. Routine Description:
  1881. This routine attempts to close a channel.
  1882. If we successfully close the channel and this channel was
  1883. the current channel, we reset the current channel to the SAC channel
  1884. Arguments:
  1885. Channel - the channel to close
  1886. Return Value:
  1887. STATUS_SUCCESS - the channel was closed
  1888. STATUS_ALREADY_DISCONNECTED - the channel was already closed
  1889. otherwise, error status
  1890. --*/
  1891. {
  1892. NTSTATUS Status;
  1893. //
  1894. // default
  1895. //
  1896. Status = STATUS_SUCCESS;
  1897. //
  1898. // Attempt to make the specified channel inactive
  1899. //
  1900. do {
  1901. //
  1902. // The current channel is being closed,
  1903. // so reset the current channel to the SAC
  1904. //
  1905. // Note: disable this check if you don't want
  1906. // the conmgr to switch to the SAC channel
  1907. // when the current chanenl is closed.
  1908. //
  1909. if (ConMgrIsWriteEnabled(Channel)) {
  1910. Status = ConMgrResetCurrentChannel(FALSE);
  1911. }
  1912. } while ( FALSE );
  1913. ASSERT(NT_SUCCESS(Status));
  1914. return Status;
  1915. }
  1916. NTSTATUS
  1917. ConMgrHandleEvent(
  1918. IN IO_MGR_EVENT Event,
  1919. IN PSAC_CHANNEL Channel, OPTIONAL
  1920. IN PVOID Data OPTIONAL
  1921. )
  1922. /*++
  1923. Routine Description:
  1924. This is the Console Manager's IoMgrHandleEvent implementation.
  1925. This routine handles asynchronous events that effect
  1926. the channels, the console manager and the SAC driver as a whole.
  1927. Note that this routine only handles events that are important for
  1928. the proper operation of the console manager. Hence, not all
  1929. possible events that can happen in the SAC driver are here.
  1930. Arguments:
  1931. ChannelWriteCmd - the write IOCTL command structure
  1932. Channel - Optional: the channel the event is targeted at
  1933. Data - Optional: data for the specified event
  1934. Return Value:
  1935. Status
  1936. --*/
  1937. {
  1938. NTSTATUS Status;
  1939. switch(Event) {
  1940. case IO_MGR_EVENT_CHANNEL_CREATE: {
  1941. PWCHAR OutputBuffer;
  1942. ULONG Size;
  1943. PWSTR Name;
  1944. PCWSTR Message;
  1945. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_2);
  1946. //
  1947. // get the string resource
  1948. //
  1949. Message = GetMessage(SAC_NEW_CHANNEL_CREATED);
  1950. if (Message == NULL) {
  1951. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  1952. break;
  1953. }
  1954. //
  1955. // Determine the size of the string buffer
  1956. //
  1957. Size = (ULONG)((wcslen(Message) + SAC_MAX_CHANNEL_NAME_LENGTH + 1) * sizeof(WCHAR));
  1958. //
  1959. // Allocate the buffer
  1960. //
  1961. OutputBuffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
  1962. ASSERT_STATUS(OutputBuffer, STATUS_NO_MEMORY);
  1963. do {
  1964. //
  1965. // Get the channel's name
  1966. //
  1967. Status = ChannelGetName(
  1968. Channel,
  1969. &Name
  1970. );
  1971. if (! NT_SUCCESS(Status)) {
  1972. break;
  1973. }
  1974. //
  1975. // Notify the SAC that a channel was created
  1976. //
  1977. SAFE_SWPRINTF(
  1978. Size,
  1979. (OutputBuffer,
  1980. Message,
  1981. Name
  1982. ));
  1983. FREE_POOL(&Name);
  1984. ConMgrEventMessage(OutputBuffer, FALSE);
  1985. } while ( FALSE );
  1986. FREE_POOL(&OutputBuffer);
  1987. break;
  1988. }
  1989. case IO_MGR_EVENT_CHANNEL_CLOSE: {
  1990. PWCHAR OutputBuffer;
  1991. OutputBuffer = NULL;
  1992. ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_2);
  1993. ASSERT_STATUS(Data, STATUS_INVALID_PARAMETER_3);
  1994. //
  1995. // We need to lock down current channel globals
  1996. // in case we need to close the current channel
  1997. // which will result in the resetting of the
  1998. // current channel to the SAC channel.
  1999. //
  2000. LOCK_CURRENT_CHANNEL();
  2001. //
  2002. // Perform the console mgrs close channel response
  2003. //
  2004. ConMgrChannelClose(Channel);
  2005. //
  2006. // get the channel close status message
  2007. // using the status sent in by the channel
  2008. // manager when it tried to close the channel.
  2009. //
  2010. Status = ConMgrGetChannelCloseMessage(
  2011. Channel,
  2012. *((NTSTATUS*)Data),
  2013. &OutputBuffer
  2014. );
  2015. if (NT_SUCCESS(Status)) {
  2016. //
  2017. // Display the message
  2018. //
  2019. ConMgrEventMessage(OutputBuffer, TRUE);
  2020. //
  2021. // cleanup
  2022. //
  2023. SAFE_FREE_POOL(&OutputBuffer);
  2024. }
  2025. //
  2026. // We are done with the current channel globals
  2027. //
  2028. UNLOCK_CURRENT_CHANNEL();
  2029. break;
  2030. }
  2031. case IO_MGR_EVENT_CHANNEL_WRITE:
  2032. Status = ConMgrChannelOWrite(
  2033. Channel,
  2034. (PSAC_CMD_WRITE_CHANNEL)Data
  2035. );
  2036. break;
  2037. case IO_MGR_EVENT_REGISTER_SAC_CMD_EVENT:
  2038. Status = ConMgrSimpleEventMessage(SAC_CMD_SERVICE_REGISTERED, FALSE) ?
  2039. STATUS_SUCCESS :
  2040. STATUS_UNSUCCESSFUL;
  2041. break;
  2042. case IO_MGR_EVENT_UNREGISTER_SAC_CMD_EVENT:
  2043. Status = ConMgrSimpleEventMessage(SAC_CMD_SERVICE_UNREGISTERED, FALSE) ?
  2044. STATUS_SUCCESS :
  2045. STATUS_UNSUCCESSFUL;
  2046. break;
  2047. case IO_MGR_EVENT_SHUTDOWN:
  2048. //
  2049. // We need to lock down current channel globals
  2050. // in case we need to close the current channel
  2051. // which will result in the resetting of the
  2052. // current channel to the SAC channel.
  2053. //
  2054. LOCK_CURRENT_CHANNEL();
  2055. //
  2056. // Send the event message to the SAC
  2057. //
  2058. Status = ConMgrSimpleEventMessage(SAC_SHUTDOWN, TRUE) ?
  2059. STATUS_SUCCESS :
  2060. STATUS_UNSUCCESSFUL;
  2061. //
  2062. // switch to the SAC channel if it is not the current channel
  2063. //
  2064. if (SacChannel != CurrentChannel) {
  2065. //
  2066. // switch directly to the SAC channel so the user
  2067. // can see that the system is shutting down
  2068. //
  2069. ConMgrResetCurrentChannel(TRUE);
  2070. }
  2071. //
  2072. // We are done with the current channel globals
  2073. //
  2074. UNLOCK_CURRENT_CHANNEL();
  2075. break;
  2076. default:
  2077. Status = STATUS_INVALID_PARAMETER_1;
  2078. break;
  2079. }
  2080. return Status;
  2081. }
  2082. NTSTATUS
  2083. ConMgrWriteData(
  2084. IN PSAC_CHANNEL Channel,
  2085. IN PCUCHAR Buffer,
  2086. IN ULONG BufferSize
  2087. )
  2088. /*++
  2089. Routine Description:
  2090. This is the Console Manager's IoMgrWriteData implementation.
  2091. This routine takes the channel's data buffer and
  2092. sends it to the headless port.
  2093. Note: The channel sending the data should only call this function
  2094. if they received a TRUE from the IoMgrIsWriteEnabled. In
  2095. the console manager's implementation, the channel only receives
  2096. TRUE if the current channel lock is held for this channel. This
  2097. is how the virtual terminal scheme works.
  2098. Arguments:
  2099. Channel - The channel sending the data
  2100. Buffer - The data to be written to the headless port
  2101. BufferSize - The size in bytes of the data to be written
  2102. Return Value:
  2103. Status
  2104. --*/
  2105. {
  2106. NTSTATUS Status;
  2107. ULONG Attempts;
  2108. //
  2109. // default: we were successful
  2110. //
  2111. Status = STATUS_SUCCESS;
  2112. //
  2113. // We don't use teh channel structure in this implementation
  2114. //
  2115. UNREFERENCED_PARAMETER(Channel);
  2116. //
  2117. // default: we have made 0 attempts
  2118. //
  2119. Attempts = 0;
  2120. do {
  2121. //
  2122. // We are making another attempt
  2123. //
  2124. Attempts++;
  2125. //
  2126. // Attempt to write
  2127. //
  2128. Status = HeadlessDispatch(
  2129. HeadlessCmdPutData,
  2130. (PUCHAR)Buffer,
  2131. BufferSize,
  2132. NULL,
  2133. NULL
  2134. );
  2135. //
  2136. // If we have made enough attempts to write,
  2137. // then don't attempt again, just return status.
  2138. //
  2139. if (Attempts > MAX_HEADLESS_DISPATCH_ATTEMPTS) {
  2140. break;
  2141. }
  2142. //
  2143. // If the HeadlessDispatch was unsuccessful,
  2144. // this means it was still processing another command,
  2145. // so delay for a short period and try again.
  2146. //
  2147. if (Status == STATUS_UNSUCCESSFUL) {
  2148. LARGE_INTEGER WaitTime;
  2149. //
  2150. // Define a delay of 10 ms
  2151. //
  2152. WaitTime.QuadPart = Int32x32To64((LONG)1, -100000);
  2153. //
  2154. // Wait...
  2155. //
  2156. KeDelayExecutionThread(KernelMode, FALSE, &WaitTime);
  2157. }
  2158. } while ( Status == STATUS_UNSUCCESSFUL );
  2159. //
  2160. // Catch any HeadlessDispatch failures
  2161. //
  2162. ASSERT(NT_SUCCESS(Status));
  2163. return Status;
  2164. }
  2165. NTSTATUS
  2166. ConMgrFlushData(
  2167. IN PSAC_CHANNEL Channel
  2168. )
  2169. /*++
  2170. Routine Description:
  2171. This is the Console Manager's IoMgrFlushData implementation.
  2172. This routine completes the write data operation for a channel's
  2173. previous write data calls. For instance, if they console manager
  2174. were packet based - that is, it formed packets when we wrote data,
  2175. this function would tell the console manager to complete the packet
  2176. and send it, rather than wait for more data.
  2177. Arguments:
  2178. Channel - The channel sending the data
  2179. Return Value:
  2180. Status
  2181. --*/
  2182. {
  2183. UNREFERENCED_PARAMETER(Channel);
  2184. NOTHING;
  2185. return STATUS_SUCCESS;
  2186. }
  2187. BOOLEAN
  2188. ConMgrIsSacChannel(
  2189. IN PSAC_CHANNEL Channel
  2190. )
  2191. /*++
  2192. Routine Description:
  2193. This routine determines if the specified channel is a SAC channel
  2194. Arguments:
  2195. Channel - The channel to compare
  2196. Return Value:
  2197. TRUE - the channel is a SAC channel
  2198. FALSE - otherwise
  2199. --*/
  2200. {
  2201. return (Channel == SacChannel) ? TRUE : FALSE;
  2202. }