mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
894 lines
25 KiB
C
894 lines
25 KiB
C
/*
|
|
* 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;
|
|
}
|