Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / drivers / sac / driver / conmgr.c
diff --git a/drivers/sac/driver/conmgr.c b/drivers/sac/driver/conmgr.c
new file mode 100644 (file)
index 0000000..86b445e
--- /dev/null
@@ -0,0 +1,894 @@
+/*
+ * PROJECT:     ReactOS Drivers
+ * LICENSE:     BSD - See COPYING.ARM in the top level directory
+ * FILE:        drivers/sac/driver/conmgr.c
+ * PURPOSE:     Driver for the Server Administration Console (SAC) for EMS
+ * PROGRAMMERS: ReactOS Portable Systems Group
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "sacdrv.h"
+
+#include <initguid.h>
+
+/* GLOBALS ********************************************************************/
+
+DEFINE_GUID(PRIMARY_SAC_CHANNEL_APPLICATION_GUID,
+            0x63D02270,
+            0x8AA4,
+            0x11D5,
+            0xBC, 0xCF, 0x80, 0x6D, 0x61, 0x72, 0x69, 0x6F);
+
+LONG CurrentChannelRefCount;
+KMUTEX CurrentChannelLock;
+
+PSAC_CHANNEL CurrentChannel;
+PSAC_CHANNEL SacChannel;
+
+ULONG ExecutePostConsumerCommand;
+PSAC_CHANNEL ExecutePostConsumerCommandData;
+
+BOOLEAN InputInEscape, InputInEscTab, ConMgrLastCharWasCR;
+CHAR InputBuffer[80];
+
+BOOLEAN GlobalPagingNeeded, GlobalDoThreads;
+
+/* FUNCTIONS ******************************************************************/
+
+VOID
+NTAPI
+SacPutString(IN PWCHAR String)
+{
+    NTSTATUS Status;
+
+    /* Write the string on the main SAC channel */
+    Status = ChannelOWrite(SacChannel,
+                           (PCHAR)String,
+                           wcslen(String) * sizeof(WCHAR));
+    if (!NT_SUCCESS(Status))
+    {
+        SAC_DBG(SAC_DBG_INIT, "SAC XmlMgrSacPutString: OWrite failed\n");
+    }
+}
+
+BOOLEAN
+NTAPI
+SacPutSimpleMessage(IN ULONG MessageIndex)
+{
+    PWCHAR MessageBuffer;
+    BOOLEAN Result;
+
+    /* Get the message */
+    MessageBuffer = GetMessage(MessageIndex);
+    if (MessageBuffer)
+    {
+        /* Output it */
+        SacPutString(MessageBuffer);
+        Result = TRUE;
+    }
+    else
+    {
+        Result = FALSE;
+    }
+
+    /* All done */
+    return Result;
+}
+
+NTSTATUS
+NTAPI
+ConMgrDisplayCurrentChannel(VOID)
+{
+    NTSTATUS Status;
+    BOOLEAN HasRedraw;
+
+    /* Make sure the lock is held */
+    SacAssertMutexLockHeld();
+
+    /* Check if we can redraw */
+    Status = ChannelHasRedrawEvent(CurrentChannel, &HasRedraw);
+    if (NT_SUCCESS(Status))
+    {
+        /* Enable writes */
+        _InterlockedExchange(&CurrentChannel->WriteEnabled, 1);
+        if (HasRedraw)
+        {
+            /* If we can redraw, set the event */
+            ChannelSetRedrawEvent(CurrentChannel);
+        }
+
+        /* Flush the output */
+        Status = ChannelOFlush(CurrentChannel);
+    }
+
+    /* All done, return the status */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+ConMgrWriteData(IN PSAC_CHANNEL Channel,
+                IN PVOID Buffer,
+                IN ULONG BufferLength)
+{
+    ULONG i;
+    NTSTATUS Status;
+    LARGE_INTEGER Interval;
+
+    /* Loop up to 32 times */
+    for (i = 0; i < 32; i++)
+    {
+        /* Attempt sending the data */
+        Status = HeadlessDispatch(HeadlessCmdPutData, Buffer, BufferLength, NULL, NULL);
+        if (Status != STATUS_UNSUCCESSFUL) break;
+
+        /* Sending the data on the port failed, wait a second... */
+        Interval.HighPart = -1;
+        Interval.LowPart = -100000;
+        KeDelayExecutionThread(KernelMode, FALSE, &Interval);
+    }
+
+    /* After 32 attempts it should really have worked... */
+    ASSERT(NT_SUCCESS(Status));
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+ConMgrFlushData(IN PSAC_CHANNEL Channel)
+{
+    /* Nothing to do */
+    return STATUS_SUCCESS;
+}
+
+BOOLEAN
+NTAPI
+ConMgrIsSacChannel(IN PSAC_CHANNEL Channel)
+{
+    /* Check which channel is active */
+    return Channel == SacChannel;
+}
+
+BOOLEAN
+NTAPI
+ConMgrIsWriteEnabled(IN PSAC_CHANNEL Channel)
+{
+    /* If the current channel is active, allow writes */
+    return ChannelIsEqual(Channel, &CurrentChannel->ChannelId);
+}
+
+NTSTATUS
+NTAPI
+ConMgrInitialize(VOID)
+{
+    PWCHAR pcwch;
+    PSAC_CHANNEL FoundChannel;
+    SAC_CHANNEL_ATTRIBUTES SacChannelAttributes;
+    NTSTATUS Status;
+
+    /* Initialize the connection manager lock */
+    SacInitializeMutexLock();
+    SacAcquireMutexLock();
+
+    /* Setup the attributes for the raw SAC channel */
+    RtlZeroMemory(&SacChannelAttributes, sizeof(SacChannelAttributes));
+    SacChannelAttributes.ChannelType = VtUtf8;
+
+    /* Get the right name for it */
+    pcwch = GetMessage(SAC_CHANNEL_NAME);
+    ASSERT(pcwch);
+    wcsncpy(SacChannelAttributes.NameBuffer, pcwch, SAC_CHANNEL_NAME_SIZE);
+    SacChannelAttributes.NameBuffer[SAC_CHANNEL_NAME_SIZE] = ANSI_NULL;
+
+    /* Get the right description for it */
+    pcwch = GetMessage(SAC_CHANNEL_DESCRIPTION);
+    ASSERT(pcwch);
+    wcsncpy(SacChannelAttributes.DescriptionBuffer, pcwch, SAC_CHANNEL_DESCRIPTION_SIZE);
+    SacChannelAttributes.DescriptionBuffer[SAC_CHANNEL_DESCRIPTION_SIZE] = ANSI_NULL;
+
+    /* Set all the right flags */
+    SacChannelAttributes.Flag = SAC_CHANNEL_FLAG_APPLICATION | SAC_CHANNEL_FLAG_INTERNAL;
+    SacChannelAttributes.CloseEvent = NULL;
+    SacChannelAttributes.HasNewDataEvent = NULL;
+    SacChannelAttributes.LockEvent = NULL;
+    SacChannelAttributes.RedrawEvent = NULL;
+    SacChannelAttributes.ChannelId = PRIMARY_SAC_CHANNEL_APPLICATION_GUID;
+
+    /* Now create it */
+    Status = ChanMgrCreateChannel(&SacChannel, &SacChannelAttributes);
+    if (NT_SUCCESS(Status))
+    {
+        /* Try to get it back */
+        Status = ChanMgrGetByHandle(SacChannel->ChannelId, &FoundChannel);
+        if (NT_SUCCESS(Status))
+        {
+            /* Set it as the current and SAC channel */
+            SacChannel = CurrentChannel = FoundChannel;
+
+            /* Disable writes for now and clear the display */
+            _InterlockedExchange(&FoundChannel->WriteEnabled, FALSE);
+            Status = HeadlessDispatch(HeadlessCmdClearDisplay, NULL, 0, NULL, NULL);
+            if (!NT_SUCCESS(Status))
+            {
+                SAC_DBG(SAC_DBG_INIT, "SAC ConMgrInitialize: Failed dispatch\n");
+            }
+
+            /* Display the initial prompt */
+            SacPutSimpleMessage(SAC_NEWLINE);
+            SacPutSimpleMessage(SAC_INIT_STATUS);
+            SacPutSimpleMessage(SAC_NEWLINE);
+            SacPutSimpleMessage(SAC_PROMPT);
+
+            /* Display the current channel */
+            ConMgrDisplayCurrentChannel();
+        }
+    }
+
+    /* Release the channel lock */
+    SacReleaseMutexLock();
+    return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+ConMgrEventMessage(IN PWCHAR EventMessage,
+                   IN BOOLEAN LockHeld)
+{
+    /* Acquire the current channel lock if needed */
+    if (!LockHeld) SacAcquireMutexLock();
+
+    /* Send out the event message */
+    SacPutSimpleMessage(2);
+    SacPutString(EventMessage);
+    SacPutSimpleMessage(3);
+
+    /* Release the current channel lock if needed */
+    if (!LockHeld) SacReleaseMutexLock();
+}
+
+BOOLEAN
+NTAPI
+ConMgrSimpleEventMessage(IN ULONG MessageIndex,
+                         IN BOOLEAN LockHeld)
+{
+    PWCHAR MessageBuffer;
+    BOOLEAN Result;
+
+    /* Get the message to send out */
+    MessageBuffer = GetMessage(MessageIndex);
+    if (MessageBuffer)
+    {
+        /* Send it */
+        ConMgrEventMessage(MessageBuffer, LockHeld);
+        Result = TRUE;
+    }
+    else
+    {
+        /* It doesn't exist, fail */
+        Result = FALSE;
+    }
+
+    /* Return if the message was sent or not */
+    return Result;
+}
+
+NTSTATUS
+NTAPI
+ConMgrDisplayFastChannelSwitchingInterface(IN PSAC_CHANNEL Channel)
+{
+    /* FIXME: TODO */
+    ASSERT(FALSE);
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+ConMgrSetCurrentChannel(IN PSAC_CHANNEL Channel)
+{
+    NTSTATUS Status;
+    BOOLEAN HasRedrawEvent;
+
+    /* Make sure the lock is held */
+    SacAssertMutexLockHeld();
+
+    /* Check if we have a redraw event */
+    Status = ChannelHasRedrawEvent(CurrentChannel, &HasRedrawEvent);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Clear it */
+    if (HasRedrawEvent) ChannelClearRedrawEvent(CurrentChannel);
+
+    /* Disable writes on the current channel */
+    _InterlockedExchange(&CurrentChannel->WriteEnabled, 0);
+
+    /* Release the current channel */
+    Status = ChanMgrReleaseChannel(CurrentChannel);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Set the new channel and also disable writes on it */
+    CurrentChannel = Channel;
+    _InterlockedExchange(&Channel->WriteEnabled, 0);
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+ConMgrResetCurrentChannel(IN BOOLEAN KeepChannel)
+{
+    NTSTATUS Status;
+    PSAC_CHANNEL Channel;
+
+    /* Make sure the lock is held */
+    SacAssertMutexLockHeld();
+
+    /* Get the current SAC channel */
+    Status = ChanMgrGetByHandle(SacChannel->ChannelId, &Channel);
+    if (NT_SUCCESS(Status))
+    {
+        /* Set this as the current SAC channel*/
+        SacChannel = Channel;
+        Status = ConMgrSetCurrentChannel(Channel);
+        if (NT_SUCCESS(Status))
+        {
+            /* Check if the caller wants to switch or not */
+            if (KeepChannel)
+            {
+                /* Nope, keep the same channel */
+                Status = ConMgrDisplayCurrentChannel();
+            }
+            else
+            {
+                /* Yep, show the switching interface */
+                Status = ConMgrDisplayFastChannelSwitchingInterface(CurrentChannel);
+            }
+        }
+    }
+
+    /* All done */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+ConMgrChannelClose(IN PSAC_CHANNEL Channel)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* Check if we're in the right channel */
+    if (ConMgrIsWriteEnabled(Channel))
+    {
+        /* Yep, reset it */
+        Status = ConMgrResetCurrentChannel(FALSE);
+        ASSERT(NT_SUCCESS(Status));
+    }
+
+    /* All done */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+ConMgrShutdown(VOID)
+{
+    NTSTATUS Status;
+
+    /* Check if we have a SAC channel */
+    if (SacChannel)
+    {
+        /* Close it */
+        Status = ChannelClose(SacChannel);
+        if (!NT_SUCCESS(Status))
+        {
+            SAC_DBG(SAC_DBG_INIT, "SAC ConMgrShutdown: failed closing SAC channel.\n");
+        }
+
+        /* No longer have one */
+        SacChannel = NULL;
+    }
+
+    /* Check if we have a current channel */
+    if (CurrentChannel)
+    {
+        /* Release it */
+        Status = ChanMgrReleaseChannel(CurrentChannel);
+        if (!NT_SUCCESS(Status))
+        {
+            SAC_DBG(SAC_DBG_INIT, "SAC ConMgrShutdown: failed releasing current channel\n");
+        }
+
+        /* No longer have one */
+        CurrentChannel = NULL;
+    }
+
+    /* All done */
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+ConMgrAdvanceCurrentChannel(VOID)
+{
+    NTSTATUS Status;
+    ULONG Index;
+    PSAC_CHANNEL Channel;
+
+    /* Should always be called with the lock held */
+    SacAssertMutexLockHeld();
+
+    /* Get the next active channel */
+    Status = ChanMgrGetNextActiveChannel(CurrentChannel, &Index, &Channel);
+    if (NT_SUCCESS(Status))
+    {
+        /* Set it as the new channel */
+        Status = ConMgrSetCurrentChannel(Channel);
+        if (NT_SUCCESS(Status))
+        {
+            /* Let the user switch to it */
+            Status = ConMgrDisplayFastChannelSwitchingInterface(Channel);
+        }
+    }
+
+    /* All done */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+ConMgrChannelOWrite(IN PSAC_CHANNEL Channel,
+                    IN PVOID WriteBuffer)
+{
+    NTSTATUS Status;
+
+    /* Do the write with the lock held */
+    SacAcquireMutexLock();
+    ASSERT(FALSE);
+    Status = STATUS_NOT_IMPLEMENTED;// ChannelOWrite(Channel, WriteBuffer + 24, *(WriteBuffer + 20));
+    SacReleaseMutexLock();
+
+    /* Return back to the caller */
+    ASSERT(NT_SUCCESS(Status) || Status == STATUS_NOT_FOUND);
+    return Status;
+}
+
+VOID
+NTAPI
+ConMgrProcessInputLine(VOID)
+{
+    BOOLEAN EnablePaging;
+    NTSTATUS Status;
+
+    SAC_DBG(SAC_DBG_INIT, "SAC Input Test: %s\n", InputBuffer);
+
+    if (!strncmp(InputBuffer, "t", 1))
+    {
+        DoTlistCommand();
+    }
+    else if (!strncmp(InputBuffer, "?", 1))
+    {
+        DoHelpCommand();
+    }
+    else if (!strncmp(InputBuffer, "help", 4))
+    {
+        DoHelpCommand();
+    }
+    else if (!strncmp(InputBuffer, "f", 1))
+    {
+        DoFullInfoCommand();
+    }
+    else if (!strncmp(InputBuffer, "p", 1))
+    {
+        DoPagingCommand();
+    }
+    else if (!strncmp(InputBuffer, "id", 2))
+    {
+        DoMachineInformationCommand();
+    }
+    else if (!strncmp(InputBuffer, "crashdump", 9))
+    {
+        DoCrashCommand();
+    }
+    else if (!strncmp(InputBuffer, "lock", 4))
+    {
+        DoLockCommand();
+    }
+    else if (!strncmp(InputBuffer, "shutdown", 8))
+    {
+        ExecutePostConsumerCommand = Shutdown;
+    }
+    else if (!strncmp(InputBuffer, "restart", 7))
+    {
+        ExecutePostConsumerCommand = Restart;
+    }
+    else if (!strncmp(InputBuffer, "d", 1))
+    {
+        EnablePaging = GlobalPagingNeeded;
+        Status = HeadlessDispatch(HeadlessCmdDisplayLog,
+                                  &EnablePaging,
+                                  sizeof(EnablePaging),
+                                  NULL,
+                                  0);
+        if (!NT_SUCCESS(Status)) SAC_DBG(SAC_DBG_INIT, "SAC Display Log failed.\n");
+    }
+    else if (!strncmp(InputBuffer, "cmd", 3))
+    {
+        if (CommandConsoleLaunchingEnabled)
+        {
+            DoCmdCommand(InputBuffer);
+        }
+        else
+        {
+            SacPutSimpleMessage(148);
+        }
+    }
+    else if (!(strncmp(InputBuffer, "ch", 2)) &&
+             (((strlen(InputBuffer) > 1) && (InputBuffer[2] == ' ')) ||
+              (strlen(InputBuffer) == 2)))
+    {
+        DoChannelCommand(InputBuffer);
+    }
+    else if (!(strncmp(InputBuffer, "k", 1)) &&
+             (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
+              (strlen(InputBuffer) == 1)))
+    {
+        DoKillCommand(InputBuffer);
+    }
+    else if (!(strncmp(InputBuffer, "l", 1)) &&
+             (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
+              (strlen(InputBuffer) == 1)))
+    {
+        DoLowerPriorityCommand(InputBuffer);
+    }
+    else if (!(strncmp(InputBuffer, "r", 1)) &&
+             (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
+              (strlen(InputBuffer) == 1)))
+    {
+        DoRaisePriorityCommand(InputBuffer);
+    }
+    else if (!(strncmp(InputBuffer, "m", 1)) &&
+             (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
+              (strlen(InputBuffer) == 1)))
+    {
+        DoLimitMemoryCommand(InputBuffer);
+    }
+    else if (!(strncmp(InputBuffer, "s", 1)) &&
+             (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
+              (strlen(InputBuffer) == 1)))
+    {
+        DoSetTimeCommand(InputBuffer);
+    }
+    else if (!(strncmp(InputBuffer, "i", 1)) &&
+             (((strlen(InputBuffer) > 1) && (InputBuffer[1] == ' ')) ||
+              (strlen(InputBuffer) == 1)))
+    {
+        DoSetIpAddressCommand(InputBuffer);
+    }
+    else if ((InputBuffer[0] != '\n') && (InputBuffer[0] != ANSI_NULL))
+    {
+        SacPutSimpleMessage(SAC_UNKNOWN_COMMAND);
+    }
+}
+
+VOID
+NTAPI
+ConMgrSerialPortConsumer(VOID)
+{
+    NTSTATUS Status;
+    CHAR Char;
+    WCHAR LastChar;
+    CHAR ReadBuffer[2];
+    ULONG ReadBufferSize, i;
+    WCHAR StringBuffer[2];
+    SAC_DBG(SAC_DBG_MACHINE, "SAC TimerDpcRoutine: Entering.\n"); //bug
+
+    /* Acquire the manager lock and make sure a channel is selected */
+    SacAcquireMutexLock();
+    ASSERT(CurrentChannel);
+
+    /* Read whatever came off the serial port */
+    for (Status = SerialBufferGetChar(&Char);
+         NT_SUCCESS(Status);
+         Status = SerialBufferGetChar(&Char))
+    {
+        /* If nothing came through, bail out */
+        if (Status == STATUS_NO_DATA_DETECTED) break;
+
+        /* Check if ESC was pressed */
+        if (Char == '\x1B')
+        {
+            /* Was it already pressed? */
+            if (!InputInEscape)
+            {
+                /* First time ESC is pressed! Remember and reset TAB state */
+                InputInEscTab = FALSE;
+                InputInEscape = TRUE;
+                continue;
+            }
+        }
+        else if (Char == '\t')
+        {
+            /* TAB was pressed, is it following ESC (VT-100 sequence)? */
+            if (InputInEscape)
+            {
+                /* Yes! This must be the only ESC-TAB we see in once moment */
+                ASSERT(InputInEscTab == FALSE);
+
+                /* No longer treat us as being in ESC */
+                InputInEscape = FALSE;
+
+                /* ESC-TAB is the sequence for changing channels */
+                Status = ConMgrAdvanceCurrentChannel();
+                if (!NT_SUCCESS(Status)) break;
+
+                /* Remember ESC-TAB was pressed */
+                InputInEscTab = TRUE;
+                continue;
+            }
+        }
+        else if ((Char == '0') && (InputInEscTab))
+        {
+            /* It this ESC-TAB-0? */
+            ASSERT(InputInEscape == FALSE);
+            InputInEscTab = FALSE;
+
+            /* If writes are already enabled, don't do this */
+            if (!CurrentChannel->WriteEnabled)
+            {
+                /* Reset the channel, this is our special sequence */
+                Status = ConMgrResetCurrentChannel(FALSE);
+                if (!NT_SUCCESS(Status)) break;
+            }
+
+            continue;
+        }
+        else
+        {
+            /* This is ESC-TAB-something else */
+            InputInEscTab = FALSE;
+
+            /* If writes are already enabled, don't do this */
+            if (!CurrentChannel->WriteEnabled)
+            {
+                /* Display the current channel */
+                InputInEscape = FALSE;
+                Status = ConMgrDisplayCurrentChannel();
+                if (!NT_SUCCESS(Status)) break;
+                continue;
+            }
+        }
+
+        /* Check if an ESC-sequence was being typed into a command channel */
+        if ((InputInEscape) && (CurrentChannel != SacChannel))
+        {
+            /* Store the ESC in the current channel buffer */
+            ReadBuffer[0] = '\x1B';
+            ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
+        }
+
+        /* Check if we are no longer pressing ESC and exit the mode if so */
+        if (Char != '\x1B') InputInEscape = FALSE;
+
+        /* Whatever was typed in, save it int eh current channel */
+        ChannelIWrite(CurrentChannel, &Char, sizeof(Char));
+
+        /* If this is a command channel, we're done, nothing to process */
+        if (CurrentChannel != SacChannel) continue;
+
+        /* Check for line feed right after a carriage return */
+        if ((ConMgrLastCharWasCR) && (Char == '\n'))
+        {
+            /* Ignore the line feed, but clear the carriage return */
+            ChannelIReadLast(CurrentChannel);
+            ConMgrLastCharWasCR = 0;
+            continue;
+        }
+
+        /* Check if the user did a carriage return */
+        ConMgrLastCharWasCR = (Char == '\n');
+
+        /* If the user did an "ENTER", we need to run the command */
+        if ((Char == '\n') || (Char == '\r'))
+        {
+            /* Echo back to the terminal */
+            SacPutString(L"\r\n");
+
+DoLineParsing:
+            /* Inhibit the character (either CR or LF) */
+            ChannelIReadLast(CurrentChannel);
+
+            /* NULL-terminate the channel's input buffer */
+            ReadBuffer[0] = ANSI_NULL;
+            ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
+
+            /* Loop over every last character */
+            do
+            {
+                /* Read every character in the channel, and strip whitespace */
+                LastChar = ChannelIReadLast(CurrentChannel);
+                ReadBuffer[0] = (CHAR) LastChar;
+            } while ((!(LastChar) ||
+                       (LastChar == L' ') ||
+                       (LastChar == L'\t')) &&
+                     (ChannelIBufferLength(CurrentChannel)));
+
+            /* Write back into the channel the last character */
+            ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
+
+            /* NULL-terminate the input buffer */
+            ReadBuffer[0] = ANSI_NULL;
+            ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
+
+            /* Now loop over every first character */
+            do
+            {
+                /* Read every character in the channel, and strip whitespace */
+                ChannelIRead(CurrentChannel,
+                             ReadBuffer,
+                             sizeof(ReadBuffer),
+                             &ReadBufferSize);
+            } while ((ReadBufferSize) &&
+                     ((ReadBuffer[0] == ' ') || (ReadBuffer[0] == '\t')));
+
+            /* We read one more than we should, so treat that as our first one */
+            InputBuffer[0] = ReadBuffer[0];
+            i = 1;
+
+            /* And now loop reading all the others */
+            do
+            {
+                /* Read each character -- there should be max 80 */
+                ChannelIRead(CurrentChannel,
+                             ReadBuffer,
+                             sizeof(ReadBuffer),
+                             &ReadBufferSize);
+                ASSERT(i < SAC_VTUTF8_COL_WIDTH);
+                InputBuffer[i++] = ReadBuffer[0];
+            } while (ReadBufferSize);
+
+            /* Now go over the entire input stream */
+            for (i = 0; InputBuffer[i]; i++)
+            {
+                /* Again it should be less than 80 characters */
+                ASSERT(i < SAC_VTUTF8_COL_WIDTH);
+
+                /* And downbase each character */
+                Char = InputBuffer[i];
+                if ((Char >= 'A') && (Char <= 'Z')) InputBuffer[i] = Char + ' ';
+            }
+
+            /* Ok, at this point, no pending command should exist */
+            ASSERT(ExecutePostConsumerCommand == Nothing);
+
+            /* Go and process the input, then show the prompt again */
+            ConMgrProcessInputLine();
+            SacPutSimpleMessage(SAC_PROMPT);
+
+            /* If the user typed a valid command, get out of here */
+            if (ExecutePostConsumerCommand != Nothing) break;
+
+            /* Keep going */
+            continue;
+        }
+
+        /* Check if the user typed backspace or delete */
+        if ((Char == '\b') || (Char == '\x7F'))
+        {
+            /* Omit the last character, which should be the DEL/BS itself */
+            if (ChannelIBufferLength(CurrentChannel))
+            {
+                ChannelIReadLast(CurrentChannel);
+            }
+
+            /* Omit the before-last character, which is the one to delete */
+            if (ChannelIBufferLength(CurrentChannel))
+            {
+                /* Also send two backspaces back to the console */
+                SacPutString(L"\b \b");
+                ChannelIReadLast(CurrentChannel);
+            }
+
+            /* Keep going */
+            continue;
+        }
+
+        /* If the user pressed CTRL-C at this point, treat it like ENTER */
+        if (Char == '\x03') goto DoLineParsing;
+
+        /* Check if the user pressed TAB */
+        if (Char == '\t')
+        {
+            /* Omit it, send a BELL, and keep going. We ignore TABs */
+            ChannelIReadLast(CurrentChannel);
+            SacPutString(L"\a");
+            continue;
+        }
+
+        /* Check if the user is getting close to the end of the screen */
+        if (ChannelIBufferLength(CurrentChannel) == (SAC_VTUTF8_COL_WIDTH - 2))
+        {
+            /* Delete the last character, replacing it with this one instead */
+            swprintf(StringBuffer, L"\b%c", Char);
+            SacPutString(StringBuffer);
+
+            /* Omit the last two characters from the buffer */
+            ChannelIReadLast(CurrentChannel);
+            ChannelIReadLast(CurrentChannel);
+
+            /* Write the last character that was just typed in */
+            ReadBuffer[0] = Char;
+            ChannelIWrite(CurrentChannel, ReadBuffer, sizeof(CHAR));
+            continue;
+        }
+
+        /* Nothing of interest happened, just write the character back */
+        swprintf(StringBuffer, L"%c", Char);
+        SacPutString(StringBuffer);
+    }
+
+    /* We're done, release the lock */
+    SacReleaseMutexLock();
+    SAC_DBG(SAC_DBG_MACHINE, "SAC TimerDpcRoutine: Exiting.\n"); //bug
+}
+
+VOID
+NTAPI
+ConMgrWorkerProcessEvents(IN PSAC_DEVICE_EXTENSION DeviceExtension)
+{
+    SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC WorkerProcessEvents: Entering.\n");
+
+    /* Enter the main loop */
+    while (TRUE)
+    {
+        /* Wait for something to do */
+        KeWaitForSingleObject(&DeviceExtension->Event,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+
+        /* Consume data off the serial port */
+        ConMgrSerialPortConsumer();
+        switch (ExecutePostConsumerCommand)
+        {
+            case Restart:
+                /* A reboot was sent, do it  */
+                DoRebootCommand(FALSE);
+                break;
+
+            case Close:
+                /* A close was sent, do it */
+                ChanMgrCloseChannel(ExecutePostConsumerCommandData);
+                ChanMgrReleaseChannel(ExecutePostConsumerCommandData);
+                break;
+
+            case Shutdown:
+                /* A shutdown was sent, do it */
+                DoRebootCommand(TRUE);
+                break;
+        }
+
+        /* Clear the serial port consumer state */
+        ExecutePostConsumerCommand = Nothing;
+        ExecutePostConsumerCommandData = NULL;
+    }
+}
+
+NTSTATUS
+NTAPI
+ConMgrGetChannelCloseMessage(IN PSAC_CHANNEL Channel,
+                             IN NTSTATUS CloseStatus,
+                             OUT PWCHAR OutputBuffer)
+{
+    ASSERT(FALSE);
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+ConMgrHandleEvent(IN ULONG EventCode,
+                  IN PSAC_CHANNEL Channel,
+                  OUT PVOID Data)
+{
+    ASSERT(FALSE);
+    return STATUS_NOT_IMPLEMENTED;
+}