xref: /reactos/drivers/sac/driver/conmgr.c (revision 1734f297)
1 /*
2  * PROJECT:     ReactOS Drivers
3  * LICENSE:     BSD - See COPYING.ARM in the top level directory
4  * FILE:        drivers/sac/driver/conmgr.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 #include <initguid.h>
14 
15 /* GLOBALS ********************************************************************/
16 
17 DEFINE_GUID(PRIMARY_SAC_CHANNEL_APPLICATION_GUID,
18             0x63D02270,
19             0x8AA4,
20             0x11D5,
21             0xBC, 0xCF, 0x80, 0x6D, 0x61, 0x72, 0x69, 0x6F);
22 
23 LONG CurrentChannelRefCount;
24 KMUTEX CurrentChannelLock;
25 
26 PSAC_CHANNEL CurrentChannel;
27 PSAC_CHANNEL SacChannel;
28 
29 ULONG ExecutePostConsumerCommand;
30 PSAC_CHANNEL ExecutePostConsumerCommandData;
31 
32 BOOLEAN InputInEscape, InputInEscTab, ConMgrLastCharWasCR;
33 CHAR InputBuffer[80];
34 
35 BOOLEAN GlobalPagingNeeded, GlobalDoThreads;
36 
37 /* FUNCTIONS ******************************************************************/
38 
39 VOID
40 NTAPI
41 SacPutString(IN PWCHAR String)
42 {
43     NTSTATUS Status;
44 
45     /* Write the string on the main SAC channel */
46     Status = ChannelOWrite(SacChannel,
47                            (PCHAR)String,
48                            wcslen(String) * sizeof(WCHAR));
49     if (!NT_SUCCESS(Status))
50     {
51         SAC_DBG(SAC_DBG_INIT, "SAC XmlMgrSacPutString: OWrite failed\n");
52     }
53 }
54 
55 BOOLEAN
56 NTAPI
57 SacPutSimpleMessage(IN ULONG MessageIndex)
58 {
59     PWCHAR MessageBuffer;
60     BOOLEAN Result;
61 
62     /* Get the message */
63     MessageBuffer = GetMessage(MessageIndex);
64     if (MessageBuffer)
65     {
66         /* Output it */
67         SacPutString(MessageBuffer);
68         Result = TRUE;
69     }
70     else
71     {
72         Result = FALSE;
73     }
74 
75     /* All done */
76     return Result;
77 }
78 
79 NTSTATUS
80 NTAPI
81 ConMgrDisplayCurrentChannel(VOID)
82 {
83     NTSTATUS Status;
84     BOOLEAN HasRedraw;
85 
86     /* Make sure the lock is held */
87     SacAssertMutexLockHeld();
88 
89     /* Check if we can redraw */
90     Status = ChannelHasRedrawEvent(CurrentChannel, &HasRedraw);
91     if (NT_SUCCESS(Status))
92     {
93         /* Enable writes */
94         _InterlockedExchange(&CurrentChannel->WriteEnabled, 1);
95         if (HasRedraw)
96         {
97             /* If we can redraw, set the event */
98             ChannelSetRedrawEvent(CurrentChannel);
99         }
100 
101         /* Flush the output */
102         Status = ChannelOFlush(CurrentChannel);
103     }
104 
105     /* All done, return the status */
106     return Status;
107 }
108 
109 NTSTATUS
110 NTAPI
111 ConMgrWriteData(IN PSAC_CHANNEL Channel,
112                 IN PVOID Buffer,
113                 IN ULONG BufferLength)
114 {
115     ULONG i;
116     NTSTATUS Status;
117     LARGE_INTEGER Interval;
118 
119     /* Loop up to 32 times */
120     for (i = 0; i < 32; i++)
121     {
122         /* Attempt sending the data */
123         Status = HeadlessDispatch(HeadlessCmdPutData, Buffer, BufferLength, NULL, NULL);
124         if (Status != STATUS_UNSUCCESSFUL) break;
125 
126         /* Sending the data on the port failed, wait a second... */
127         Interval.HighPart = -1;
128         Interval.LowPart = -100000;
129         KeDelayExecutionThread(KernelMode, FALSE, &Interval);
130     }
131 
132     /* After 32 attempts it should really have worked... */
133     ASSERT(NT_SUCCESS(Status));
134     return Status;
135 }
136 
137 NTSTATUS
138 NTAPI
139 ConMgrFlushData(IN PSAC_CHANNEL Channel)
140 {
141     /* Nothing to do */
142     return STATUS_SUCCESS;
143 }
144 
145 BOOLEAN
146 NTAPI
147 ConMgrIsSacChannel(IN PSAC_CHANNEL Channel)
148 {
149     /* Check which channel is active */
150     return Channel == SacChannel;
151 }
152 
153 BOOLEAN
154 NTAPI
155 ConMgrIsWriteEnabled(IN PSAC_CHANNEL Channel)
156 {
157     /* If the current channel is active, allow writes */
158     return ChannelIsEqual(Channel, &CurrentChannel->ChannelId);
159 }
160 
161 NTSTATUS
162 NTAPI
163 ConMgrInitialize(VOID)
164 {
165     PWCHAR pcwch;
166     PSAC_CHANNEL FoundChannel;
167     SAC_CHANNEL_ATTRIBUTES SacChannelAttributes;
168     NTSTATUS Status;
169 
170     /* Initialize the connection manager lock */
171     SacInitializeMutexLock();
172     SacAcquireMutexLock();
173 
174     /* Setup the attributes for the raw SAC channel */
175     RtlZeroMemory(&SacChannelAttributes, sizeof(SacChannelAttributes));
176     SacChannelAttributes.ChannelType = VtUtf8;
177 
178     /* Get the right name for it */
179     pcwch = GetMessage(SAC_CHANNEL_NAME);
180     ASSERT(pcwch);
181     wcsncpy(SacChannelAttributes.NameBuffer, pcwch, SAC_CHANNEL_NAME_SIZE);
182     SacChannelAttributes.NameBuffer[SAC_CHANNEL_NAME_SIZE] = ANSI_NULL;
183 
184     /* Get the right description for it */
185     pcwch = GetMessage(SAC_CHANNEL_DESCRIPTION);
186     ASSERT(pcwch);
187     wcsncpy(SacChannelAttributes.DescriptionBuffer, pcwch, SAC_CHANNEL_DESCRIPTION_SIZE);
188     SacChannelAttributes.DescriptionBuffer[SAC_CHANNEL_DESCRIPTION_SIZE] = ANSI_NULL;
189 
190     /* Set all the right flags */
191     SacChannelAttributes.Flag = SAC_CHANNEL_FLAG_APPLICATION | SAC_CHANNEL_FLAG_INTERNAL;
192     SacChannelAttributes.CloseEvent = NULL;
193     SacChannelAttributes.HasNewDataEvent = NULL;
194     SacChannelAttributes.LockEvent = NULL;
195     SacChannelAttributes.RedrawEvent = NULL;
196     SacChannelAttributes.ChannelId = PRIMARY_SAC_CHANNEL_APPLICATION_GUID;
197 
198     /* Now create it */
199     Status = ChanMgrCreateChannel(&SacChannel, &SacChannelAttributes);
200     if (NT_SUCCESS(Status))
201     {
202         /* Try to get it back */
203         Status = ChanMgrGetByHandle(SacChannel->ChannelId, &FoundChannel);
204         if (NT_SUCCESS(Status))
205         {
206             /* Set it as the current and SAC channel */
207             SacChannel = CurrentChannel = FoundChannel;
208 
209             /* Disable writes for now and clear the display */
210             _InterlockedExchange(&FoundChannel->WriteEnabled, FALSE);
211             Status = HeadlessDispatch(HeadlessCmdClearDisplay, NULL, 0, NULL, NULL);
212             if (!NT_SUCCESS(Status))
213             {
214                 SAC_DBG(SAC_DBG_INIT, "SAC ConMgrInitialize: Failed dispatch\n");
215             }
216 
217             /* Display the initial prompt */
218             SacPutSimpleMessage(SAC_NEWLINE);
219             SacPutSimpleMessage(SAC_INIT_STATUS);
220             SacPutSimpleMessage(SAC_NEWLINE);
221             SacPutSimpleMessage(SAC_PROMPT);
222 
223             /* Display the current channel */
224             ConMgrDisplayCurrentChannel();
225         }
226     }
227 
228     /* Release the channel lock */
229     SacReleaseMutexLock();
230     return STATUS_SUCCESS;
231 }
232 
233 VOID
234 NTAPI
235 ConMgrEventMessage(IN PWCHAR EventMessage,
236                    IN BOOLEAN LockHeld)
237 {
238     /* Acquire the current channel lock if needed */
239     if (!LockHeld) SacAcquireMutexLock();
240 
241     /* Send out the event message */
242     SacPutSimpleMessage(2);
243     SacPutString(EventMessage);
244     SacPutSimpleMessage(3);
245 
246     /* Release the current channel lock if needed */
247     if (!LockHeld) SacReleaseMutexLock();
248 }
249 
250 BOOLEAN
251 NTAPI
252 ConMgrSimpleEventMessage(IN ULONG MessageIndex,
253                          IN BOOLEAN LockHeld)
254 {
255     PWCHAR MessageBuffer;
256     BOOLEAN Result;
257 
258     /* Get the message to send out */
259     MessageBuffer = GetMessage(MessageIndex);
260     if (MessageBuffer)
261     {
262         /* Send it */
263         ConMgrEventMessage(MessageBuffer, LockHeld);
264         Result = TRUE;
265     }
266     else
267     {
268         /* It doesn't exist, fail */
269         Result = FALSE;
270     }
271 
272     /* Return if the message was sent or not */
273     return Result;
274 }
275 
276 NTSTATUS
277 NTAPI
278 ConMgrDisplayFastChannelSwitchingInterface(IN PSAC_CHANNEL Channel)
279 {
280     /* FIXME: TODO */
281     ASSERT(FALSE);
282     return STATUS_NOT_IMPLEMENTED;
283 }
284 
285 NTSTATUS
286 NTAPI
287 ConMgrSetCurrentChannel(IN PSAC_CHANNEL Channel)
288 {
289     NTSTATUS Status;
290     BOOLEAN HasRedrawEvent;
291 
292     /* Make sure the lock is held */
293     SacAssertMutexLockHeld();
294 
295     /* Check if we have a redraw event */
296     Status = ChannelHasRedrawEvent(CurrentChannel, &HasRedrawEvent);
297     if (!NT_SUCCESS(Status)) return Status;
298 
299     /* Clear it */
300     if (HasRedrawEvent) ChannelClearRedrawEvent(CurrentChannel);
301 
302     /* Disable writes on the current channel */
303     _InterlockedExchange(&CurrentChannel->WriteEnabled, 0);
304 
305     /* Release the current channel */
306     Status = ChanMgrReleaseChannel(CurrentChannel);
307     if (!NT_SUCCESS(Status)) return Status;
308 
309     /* Set the new channel and also disable writes on it */
310     CurrentChannel = Channel;
311     _InterlockedExchange(&Channel->WriteEnabled, 0);
312     return STATUS_SUCCESS;
313 }
314 
315 NTSTATUS
316 NTAPI
317 ConMgrResetCurrentChannel(IN BOOLEAN KeepChannel)
318 {
319     NTSTATUS Status;
320     PSAC_CHANNEL Channel;
321 
322     /* Make sure the lock is held */
323     SacAssertMutexLockHeld();
324 
325     /* Get the current SAC channel */
326     Status = ChanMgrGetByHandle(SacChannel->ChannelId, &Channel);
327     if (NT_SUCCESS(Status))
328     {
329         /* Set this as the current SAC channel*/
330         SacChannel = Channel;
331         Status = ConMgrSetCurrentChannel(Channel);
332         if (NT_SUCCESS(Status))
333         {
334             /* Check if the caller wants to switch or not */
335             if (KeepChannel)
336             {
337                 /* Nope, keep the same channel */
338                 Status = ConMgrDisplayCurrentChannel();
339             }
340             else
341             {
342                 /* Yep, show the switching interface */
343                 Status = ConMgrDisplayFastChannelSwitchingInterface(CurrentChannel);
344             }
345         }
346     }
347 
348     /* All done */
349     return Status;
350 }
351 
352 NTSTATUS
353 NTAPI
354 ConMgrChannelClose(IN PSAC_CHANNEL Channel)
355 {
356     NTSTATUS Status = STATUS_SUCCESS;
357 
358     /* Check if we're in the right channel */
359     if (ConMgrIsWriteEnabled(Channel))
360     {
361         /* Yep, reset it */
362         Status = ConMgrResetCurrentChannel(FALSE);
363         ASSERT(NT_SUCCESS(Status));
364     }
365 
366     /* All done */
367     return Status;
368 }
369 
370 NTSTATUS
371 NTAPI
372 ConMgrShutdown(VOID)
373 {
374     NTSTATUS Status;
375 
376     /* Check if we have a SAC channel */
377     if (SacChannel)
378     {
379         /* Close it */
380         Status = ChannelClose(SacChannel);
381         if (!NT_SUCCESS(Status))
382         {
383             SAC_DBG(SAC_DBG_INIT, "SAC ConMgrShutdown: failed closing SAC channel.\n");
384         }
385 
386         /* No longer have one */
387         SacChannel = NULL;
388     }
389 
390     /* Check if we have a current channel */
391     if (CurrentChannel)
392     {
393         /* Release it */
394         Status = ChanMgrReleaseChannel(CurrentChannel);
395         if (!NT_SUCCESS(Status))
396         {
397             SAC_DBG(SAC_DBG_INIT, "SAC ConMgrShutdown: failed releasing current channel\n");
398         }
399 
400         /* No longer have one */
401         CurrentChannel = NULL;
402     }
403 
404     /* All done */
405     return STATUS_SUCCESS;
406 }
407 
408 NTSTATUS
409 NTAPI
410 ConMgrAdvanceCurrentChannel(VOID)
411 {
412     NTSTATUS Status;
413     ULONG Index;
414     PSAC_CHANNEL Channel;
415 
416     /* Should always be called with the lock held */
417     SacAssertMutexLockHeld();
418 
419     /* Get the next active channel */
420     Status = ChanMgrGetNextActiveChannel(CurrentChannel, &Index, &Channel);
421     if (NT_SUCCESS(Status))
422     {
423         /* Set it as the new channel */
424         Status = ConMgrSetCurrentChannel(Channel);
425         if (NT_SUCCESS(Status))
426         {
427             /* Let the user switch to it */
428             Status = ConMgrDisplayFastChannelSwitchingInterface(Channel);
429         }
430     }
431 
432     /* All done */
433     return Status;
434 }
435 
436 NTSTATUS
437 NTAPI
438 ConMgrChannelOWrite(IN PSAC_CHANNEL Channel,
439                     IN PVOID WriteBuffer)
440 {
441     NTSTATUS Status;
442 
443     /* Do the write with the lock held */
444     SacAcquireMutexLock();
445     ASSERT(FALSE);
446     Status = STATUS_NOT_IMPLEMENTED;// ChannelOWrite(Channel, WriteBuffer + 24, *(WriteBuffer + 20));
447     SacReleaseMutexLock();
448 
449     /* Return back to the caller */
450     ASSERT(NT_SUCCESS(Status) || Status == STATUS_NOT_FOUND);
451     return Status;
452 }
453 
454 VOID
455 NTAPI
456 ConMgrProcessInputLine(VOID)
457 {
458     BOOLEAN EnablePaging;
459     NTSTATUS Status;
460 
461     SAC_DBG(SAC_DBG_INIT, "SAC Input Test: %s\n", InputBuffer);
462 
463     if (!strncmp(InputBuffer, "t", 1))
464     {
465         DoTlistCommand();
466     }
467     else if (!strncmp(InputBuffer, "?", 1))
468     {
469         DoHelpCommand();
470     }
471     else if (!strncmp(InputBuffer, "help", 4))
472     {
473         DoHelpCommand();
474     }
475     else if (!strncmp(InputBuffer, "f", 1))
476     {
477         DoFullInfoCommand();
478     }
479     else if (!strncmp(InputBuffer, "p", 1))
480     {
481         DoPagingCommand();
482     }
483     else if (!strncmp(InputBuffer, "id", 2))
484     {
485         DoMachineInformationCommand();
486     }
487     else if (!strncmp(InputBuffer, "crashdump", 9))
488     {
489         DoCrashCommand();
490     }
491     else if (!strncmp(InputBuffer, "lock", 4))
492     {
493         DoLockCommand();
494     }
495     else if (!strncmp(InputBuffer, "shutdown", 8))
496     {
497         ExecutePostConsumerCommand = Shutdown;
498     }
499     else if (!strncmp(InputBuffer, "restart", 7))
500     {
501         ExecutePostConsumerCommand = Restart;
502     }
503     else if (!strncmp(InputBuffer, "d", 1))
504     {
505         EnablePaging = GlobalPagingNeeded;
506         Status = HeadlessDispatch(HeadlessCmdDisplayLog,
507                                   &EnablePaging,
508                                   sizeof(EnablePaging),
509                                   NULL,
510                                   NULL);
511         if (!NT_SUCCESS(Status)) SAC_DBG(SAC_DBG_INIT, "SAC Display Log failed.\n");
512     }
513     else if (!strncmp(InputBuffer, "cmd", 3))
514     {
515         if (CommandConsoleLaunchingEnabled)
516         {
517             DoCmdCommand(InputBuffer);
518         }
519         else
520         {
521             SacPutSimpleMessage(148);
522         }
523     }
524     else if (!(strncmp(InputBuffer, "ch", 2)) &&
525              (((strlen(InputBuffer) > 1) && (InputBuffer[2] == ' ')) ||
526               (strlen(InputBuffer) == 2)))
527     {
528         DoChannelCommand(InputBuffer);
529     }
530     else if (!(strncmp(InputBuffer, "k", 1)) &&
531              (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
532               (strlen(InputBuffer) == 1)))
533     {
534         DoKillCommand(InputBuffer);
535     }
536     else if (!(strncmp(InputBuffer, "l", 1)) &&
537              (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
538               (strlen(InputBuffer) == 1)))
539     {
540         DoLowerPriorityCommand(InputBuffer);
541     }
542     else if (!(strncmp(InputBuffer, "r", 1)) &&
543              (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
544               (strlen(InputBuffer) == 1)))
545     {
546         DoRaisePriorityCommand(InputBuffer);
547     }
548     else if (!(strncmp(InputBuffer, "m", 1)) &&
549              (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
550               (strlen(InputBuffer) == 1)))
551     {
552         DoLimitMemoryCommand(InputBuffer);
553     }
554     else if (!(strncmp(InputBuffer, "s", 1)) &&
555              (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
556               (strlen(InputBuffer) == 1)))
557     {
558         DoSetTimeCommand(InputBuffer);
559     }
560     else if (!(strncmp(InputBuffer, "i", 1)) &&
561              (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
562               (strlen(InputBuffer) == 1)))
563     {
564         DoSetIpAddressCommand(InputBuffer);
565     }
566     else if ((InputBuffer[0] != '\n') && (InputBuffer[0] != ANSI_NULL))
567     {
568         SacPutSimpleMessage(SAC_UNKNOWN_COMMAND);
569     }
570 }
571 
572 VOID
573 NTAPI
574 ConMgrSerialPortConsumer(VOID)
575 {
576     NTSTATUS Status;
577     CHAR Char;
578     WCHAR LastChar;
579     CHAR ReadBuffer[2];
580     ULONG ReadBufferSize, i;
581     WCHAR StringBuffer[2];
582     SAC_DBG(SAC_DBG_MACHINE, "SAC TimerDpcRoutine: Entering.\n"); //bug
583 
584     /* Acquire the manager lock and make sure a channel is selected */
585     SacAcquireMutexLock();
586     ASSERT(CurrentChannel);
587 
588     /* Read whatever came off the serial port */
589     for (Status = SerialBufferGetChar(&Char);
590          NT_SUCCESS(Status);
591          Status = SerialBufferGetChar(&Char))
592     {
593         /* If nothing came through, bail out */
594         if (Status == STATUS_NO_DATA_DETECTED) break;
595 
596         /* Check if ESC was pressed */
597         if (Char == '\x1B')
598         {
599             /* Was it already pressed? */
600             if (!InputInEscape)
601             {
602                 /* First time ESC is pressed! Remember and reset TAB state */
603                 InputInEscTab = FALSE;
604                 InputInEscape = TRUE;
605                 continue;
606             }
607         }
608         else if (Char == '\t')
609         {
610             /* TAB was pressed, is it following ESC (VT-100 sequence)? */
611             if (InputInEscape)
612             {
613                 /* Yes! This must be the only ESC-TAB we see in once moment */
614                 ASSERT(InputInEscTab == FALSE);
615 
616                 /* No longer treat us as being in ESC */
617                 InputInEscape = FALSE;
618 
619                 /* ESC-TAB is the sequence for changing channels */
620                 Status = ConMgrAdvanceCurrentChannel();
621                 if (!NT_SUCCESS(Status)) break;
622 
623                 /* Remember ESC-TAB was pressed */
624                 InputInEscTab = TRUE;
625                 continue;
626             }
627         }
628         else if ((Char == '0') && (InputInEscTab))
629         {
630             /* It this ESC-TAB-0? */
631             ASSERT(InputInEscape == FALSE);
632             InputInEscTab = FALSE;
633 
634             /* If writes are already enabled, don't do this */
635             if (!CurrentChannel->WriteEnabled)
636             {
637                 /* Reset the channel, this is our special sequence */
638                 Status = ConMgrResetCurrentChannel(FALSE);
639                 if (!NT_SUCCESS(Status)) break;
640             }
641 
642             continue;
643         }
644         else
645         {
646             /* This is ESC-TAB-something else */
647             InputInEscTab = FALSE;
648 
649             /* If writes are already enabled, don't do this */
650             if (!CurrentChannel->WriteEnabled)
651             {
652                 /* Display the current channel */
653                 InputInEscape = FALSE;
654                 Status = ConMgrDisplayCurrentChannel();
655                 if (!NT_SUCCESS(Status)) break;
656                 continue;
657             }
658         }
659 
660         /* Check if an ESC-sequence was being typed into a command channel */
661         if ((InputInEscape) && (CurrentChannel != SacChannel))
662         {
663             /* Store the ESC in the current channel buffer */
664             ReadBuffer[0] = '\x1B';
665             ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
666         }
667 
668         /* Check if we are no longer pressing ESC and exit the mode if so */
669         if (Char != '\x1B') InputInEscape = FALSE;
670 
671         /* Whatever was typed in, save it int eh current channel */
672         ChannelIWrite(CurrentChannel, &Char, sizeof(Char));
673 
674         /* If this is a command channel, we're done, nothing to process */
675         if (CurrentChannel != SacChannel) continue;
676 
677         /* Check for line feed right after a carriage return */
678         if ((ConMgrLastCharWasCR) && (Char == '\n'))
679         {
680             /* Ignore the line feed, but clear the carriage return */
681             ChannelIReadLast(CurrentChannel);
682             ConMgrLastCharWasCR = 0;
683             continue;
684         }
685 
686         /* Check if the user did a carriage return */
687         ConMgrLastCharWasCR = (Char == '\n');
688 
689         /* If the user did an "ENTER", we need to run the command */
690         if ((Char == '\n') || (Char == '\r'))
691         {
692             /* Echo back to the terminal */
693             SacPutString(L"\r\n");
694 
695 DoLineParsing:
696             /* Inhibit the character (either CR or LF) */
697             ChannelIReadLast(CurrentChannel);
698 
699             /* NULL-terminate the channel's input buffer */
700             ReadBuffer[0] = ANSI_NULL;
701             ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
702 
703             /* Loop over every last character */
704             do
705             {
706                 /* Read every character in the channel, and strip whitespace */
707                 LastChar = ChannelIReadLast(CurrentChannel);
708                 ReadBuffer[0] = (CHAR) LastChar;
709             } while ((!(LastChar) ||
710                        (LastChar == L' ') ||
711                        (LastChar == L'\t')) &&
712                      (ChannelIBufferLength(CurrentChannel)));
713 
714             /* Write back into the channel the last character */
715             ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
716 
717             /* NULL-terminate the input buffer */
718             ReadBuffer[0] = ANSI_NULL;
719             ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
720 
721             /* Now loop over every first character */
722             do
723             {
724                 /* Read every character in the channel, and strip whitespace */
725                 ChannelIRead(CurrentChannel,
726                              ReadBuffer,
727                              sizeof(ReadBuffer),
728                              &ReadBufferSize);
729             } while ((ReadBufferSize) &&
730                      ((ReadBuffer[0] == ' ') || (ReadBuffer[0] == '\t')));
731 
732             /* We read one more than we should, so treat that as our first one */
733             InputBuffer[0] = ReadBuffer[0];
734             i = 1;
735 
736             /* And now loop reading all the others */
737             do
738             {
739                 /* Read each character -- there should be max 80 */
740                 ChannelIRead(CurrentChannel,
741                              ReadBuffer,
742                              sizeof(ReadBuffer),
743                              &ReadBufferSize);
744                 ASSERT(i < SAC_VTUTF8_COL_WIDTH);
745                 InputBuffer[i++] = ReadBuffer[0];
746             } while (ReadBufferSize);
747 
748             /* Now go over the entire input stream */
749             for (i = 0; InputBuffer[i]; i++)
750             {
751                 /* Again it should be less than 80 characters */
752                 ASSERT(i < SAC_VTUTF8_COL_WIDTH);
753 
754                 /* And downbase each character */
755                 Char = InputBuffer[i];
756                 if ((Char >= 'A') && (Char <= 'Z')) InputBuffer[i] = Char + ' ';
757             }
758 
759             /* Ok, at this point, no pending command should exist */
760             ASSERT(ExecutePostConsumerCommand == Nothing);
761 
762             /* Go and process the input, then show the prompt again */
763             ConMgrProcessInputLine();
764             SacPutSimpleMessage(SAC_PROMPT);
765 
766             /* If the user typed a valid command, get out of here */
767             if (ExecutePostConsumerCommand != Nothing) break;
768 
769             /* Keep going */
770             continue;
771         }
772 
773         /* Check if the user typed backspace or delete */
774         if ((Char == '\b') || (Char == '\x7F'))
775         {
776             /* Omit the last character, which should be the DEL/BS itself */
777             if (ChannelIBufferLength(CurrentChannel))
778             {
779                 ChannelIReadLast(CurrentChannel);
780             }
781 
782             /* Omit the before-last character, which is the one to delete */
783             if (ChannelIBufferLength(CurrentChannel))
784             {
785                 /* Also send two backspaces back to the console */
786                 SacPutString(L"\b \b");
787                 ChannelIReadLast(CurrentChannel);
788             }
789 
790             /* Keep going */
791             continue;
792         }
793 
794         /* If the user pressed CTRL-C at this point, treat it like ENTER */
795         if (Char == '\x03') goto DoLineParsing;
796 
797         /* Check if the user pressed TAB */
798         if (Char == '\t')
799         {
800             /* Omit it, send a BELL, and keep going. We ignore TABs */
801             ChannelIReadLast(CurrentChannel);
802             SacPutString(L"\a");
803             continue;
804         }
805 
806         /* Check if the user is getting close to the end of the screen */
807         if (ChannelIBufferLength(CurrentChannel) == (SAC_VTUTF8_COL_WIDTH - 2))
808         {
809             /* Delete the last character, replacing it with this one instead */
810             swprintf(StringBuffer, L"\b%c", Char);
811             SacPutString(StringBuffer);
812 
813             /* Omit the last two characters from the buffer */
814             ChannelIReadLast(CurrentChannel);
815             ChannelIReadLast(CurrentChannel);
816 
817             /* Write the last character that was just typed in */
818             ReadBuffer[0] = Char;
819             ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
820             continue;
821         }
822 
823         /* Nothing of interest happened, just write the character back */
824         swprintf(StringBuffer, L"%c", Char);
825         SacPutString(StringBuffer);
826     }
827 
828     /* We're done, release the lock */
829     SacReleaseMutexLock();
830     SAC_DBG(SAC_DBG_MACHINE, "SAC TimerDpcRoutine: Exiting.\n"); //bug
831 }
832 
833 VOID
834 NTAPI
835 ConMgrWorkerProcessEvents(IN PSAC_DEVICE_EXTENSION DeviceExtension)
836 {
837     SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC WorkerProcessEvents: Entering.\n");
838 
839     /* Enter the main loop */
840     while (TRUE)
841     {
842         /* Wait for something to do */
843         KeWaitForSingleObject(&DeviceExtension->Event,
844                               Executive,
845                               KernelMode,
846                               FALSE,
847                               NULL);
848 
849         /* Consume data off the serial port */
850         ConMgrSerialPortConsumer();
851         switch (ExecutePostConsumerCommand)
852         {
853             case Restart:
854                 /* A reboot was sent, do it  */
855                 DoRebootCommand(FALSE);
856                 break;
857 
858             case Close:
859                 /* A close was sent, do it */
860                 ChanMgrCloseChannel(ExecutePostConsumerCommandData);
861                 ChanMgrReleaseChannel(ExecutePostConsumerCommandData);
862                 break;
863 
864             case Shutdown:
865                 /* A shutdown was sent, do it */
866                 DoRebootCommand(TRUE);
867                 break;
868         }
869 
870         /* Clear the serial port consumer state */
871         ExecutePostConsumerCommand = Nothing;
872         ExecutePostConsumerCommandData = NULL;
873     }
874 }
875 
876 NTSTATUS
877 NTAPI
878 ConMgrGetChannelCloseMessage(IN PSAC_CHANNEL Channel,
879                              IN NTSTATUS CloseStatus,
880                              OUT PWCHAR OutputBuffer)
881 {
882     ASSERT(FALSE);
883     return STATUS_NOT_IMPLEMENTED;
884 }
885 
886 NTSTATUS
887 NTAPI
888 ConMgrHandleEvent(IN ULONG EventCode,
889                   IN PSAC_CHANNEL Channel,
890                   OUT PVOID Data)
891 {
892     ASSERT(FALSE);
893     return STATUS_NOT_IMPLEMENTED;
894 }
895