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