You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2849 lines
69 KiB
2849 lines
69 KiB
/*++
|
|
|
|
Copyright (c) 1999-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ConMgr.c
|
|
|
|
Abstract:
|
|
|
|
Routines for managing channels in the sac.
|
|
|
|
Author:
|
|
|
|
Brian Guarraci (briangu) March, 2001.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "sac.h"
|
|
#include "concmd.h"
|
|
#include "iomgr.h"
|
|
|
|
//
|
|
// Definitions for this file.
|
|
//
|
|
|
|
//
|
|
// The maximum # of times we will try to write data via
|
|
// headless dispatch
|
|
//
|
|
#define MAX_HEADLESS_DISPATCH_ATTEMPTS 32
|
|
|
|
//
|
|
// Spinlock macros
|
|
//
|
|
|
|
//
|
|
// we need this lock to:
|
|
//
|
|
// 1. prevent asynchronous messages being put to the sac channel
|
|
// 2. channels from dissapearing while they are the current channel
|
|
//
|
|
// we could get rid of this lock by:
|
|
//
|
|
// 1. providing some sort of event cue that gets processed when it's safe
|
|
// 2. providing a means to notify channels when they are not the current channel
|
|
// and have them stop outputting anymore
|
|
// This goes back to the unresolved issue of having the Oecho and OFlush routines
|
|
// managed by the conmgr - they could stop the outptu if the current channel changes,
|
|
// rather than having the channel do the work. This is definitely a TODO since
|
|
// having Headless dispatch calls in the channel I/O breaks the abstraction of the IoMgr.
|
|
//
|
|
#define INIT_CURRENT_CHANNEL_LOCK() \
|
|
KeInitializeMutex( \
|
|
&CurrentChannelLock, \
|
|
0 \
|
|
); \
|
|
CurrentChannelRefCount = 0;
|
|
|
|
#define LOCK_CURRENT_CHANNEL() \
|
|
KeWaitForMutexObject( \
|
|
&CurrentChannelLock, \
|
|
Executive, \
|
|
KernelMode, \
|
|
FALSE, \
|
|
NULL \
|
|
); \
|
|
ASSERT(CurrentChannelRefCount == 0); \
|
|
InterlockedIncrement((volatile long *)&CurrentChannelRefCount);
|
|
|
|
#define UNLOCK_CURRENT_CHANNEL() \
|
|
ASSERT(CurrentChannelRefCount == 1); \
|
|
InterlockedDecrement((volatile long *)&CurrentChannelRefCount); \
|
|
KeReleaseMutex( \
|
|
&CurrentChannelLock, \
|
|
FALSE \
|
|
);
|
|
|
|
#define ASSERT_LOCK_CURRENT_CHANNEL() \
|
|
ASSERT(CurrentChannelRefCount == 1); \
|
|
ASSERT(KeReadStateMutex(&CurrentChannelLock)==0);
|
|
|
|
//
|
|
// Serial Port Consumer globals
|
|
//
|
|
BOOLEAN ConMgrLastCharWasCR = FALSE;
|
|
BOOLEAN InputInEscape = FALSE;
|
|
BOOLEAN InputInEscTab = FALSE;
|
|
UCHAR InputBuffer[SAC_VTUTF8_COL_WIDTH];
|
|
|
|
//
|
|
// Pointer to the SAC channel object
|
|
//
|
|
PSAC_CHANNEL SacChannel = NULL;
|
|
|
|
//
|
|
// The index the SAC in the channel array
|
|
//
|
|
#define SAC_CHANNEL_INDEX 0
|
|
|
|
//
|
|
// lock for r/w access on current channel globals
|
|
//
|
|
KMUTEX CurrentChannelLock;
|
|
ULONG CurrentChannelRefCount;
|
|
|
|
//
|
|
//
|
|
//
|
|
EXECUTE_POST_CONSUMER_COMMAND_ENUM ExecutePostConsumerCommand = Nothing;
|
|
PVOID ExecutePostConsumerCommandData = NULL;
|
|
|
|
//
|
|
// Channel Manager info for the current channel.
|
|
// Depending on the application, the current channel
|
|
// can be accessed using one of these references.
|
|
//
|
|
PSAC_CHANNEL CurrentChannel = NULL;
|
|
|
|
//
|
|
// prototypes
|
|
//
|
|
VOID
|
|
ConMgrSerialPortConsumer(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
ConMgrProcessInputLine(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
ConMgrResetCurrentChannel(
|
|
BOOLEAN SwitchDirectlyToChannel
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
ConMgrInitialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Console Manager's IoMgrInitialize implementation.
|
|
|
|
Initialize the console manager
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PSAC_CHANNEL TmpChannel;
|
|
|
|
//
|
|
// Initialize the current channel lock
|
|
//
|
|
INIT_CURRENT_CHANNEL_LOCK();
|
|
|
|
//
|
|
// Lock down the current channel globals
|
|
//
|
|
// Note: we need to do this here since many of the ConMgr support
|
|
// routines do ASSERTs to ensure the current channel lock is held
|
|
//
|
|
LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
do {
|
|
|
|
PCWSTR pcwch;
|
|
|
|
SAC_CHANNEL_OPEN_ATTRIBUTES Attributes;
|
|
|
|
//
|
|
// Initialize the SAC channel attributes
|
|
//
|
|
RtlZeroMemory(&Attributes, sizeof(SAC_CHANNEL_OPEN_ATTRIBUTES));
|
|
|
|
Attributes.Type = ChannelTypeVTUTF8;
|
|
|
|
// attempt to copy the channel name
|
|
pcwch = GetMessage(PRIMARY_SAC_CHANNEL_NAME);
|
|
ASSERT(pcwch);
|
|
if (!pcwch) {
|
|
Status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
wcsncpy(Attributes.Name, pcwch, SAC_MAX_CHANNEL_NAME_LENGTH);
|
|
Attributes.Name[SAC_MAX_CHANNEL_NAME_LENGTH] = UNICODE_NULL;
|
|
|
|
// attempt to copy the channel description
|
|
pcwch = GetMessage(PRIMARY_SAC_CHANNEL_DESCRIPTION);
|
|
ASSERT(pcwch);
|
|
if (!pcwch) {
|
|
Status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
wcsncpy(Attributes.Description, pcwch, SAC_MAX_CHANNEL_DESCRIPTION_LENGTH);
|
|
Attributes.Description[SAC_MAX_CHANNEL_DESCRIPTION_LENGTH] = UNICODE_NULL;
|
|
|
|
Attributes.Flags = SAC_CHANNEL_FLAG_PRESERVE |
|
|
SAC_CHANNEL_FLAG_APPLICATION_TYPE;
|
|
Attributes.CloseEvent = NULL;
|
|
Attributes.HasNewDataEvent = NULL;
|
|
#if ENABLE_CHANNEL_LOCKING
|
|
Attributes.LockEvent = NULL;
|
|
#endif
|
|
Attributes.RedrawEvent = NULL;
|
|
Attributes.ApplicationType = PRIMARY_SAC_CHANNEL_APPLICATION_GUID;
|
|
|
|
//
|
|
// create the SAC channel
|
|
//
|
|
Status = ChanMgrCreateChannel(
|
|
&SacChannel,
|
|
&Attributes
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get a reference to the SAC channel
|
|
//
|
|
// Note: this is the channel manager's policy
|
|
// we need to get the reference of the channel
|
|
// before we use it.
|
|
//
|
|
Status = ChanMgrGetByHandle(
|
|
ChannelGetHandle(SacChannel),
|
|
&TmpChannel
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
SacChannel = TmpChannel;
|
|
|
|
//
|
|
// Assign the new current channel
|
|
//
|
|
CurrentChannel = SacChannel;
|
|
|
|
//
|
|
// Update the sent to screen status
|
|
//
|
|
ChannelSetSentToScreen(CurrentChannel, FALSE);
|
|
|
|
//
|
|
// Display the prompt
|
|
//
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdClearDisplay,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
IF_SAC_DEBUG(
|
|
SAC_DEBUG_FAILS,
|
|
KdPrint(("SAC ConMgrInitialize: Failed dispatch\n")));
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize the SAC display
|
|
//
|
|
SacPutSimpleMessage( SAC_ENTER );
|
|
SacPutSimpleMessage( SAC_INITIALIZED );
|
|
SacPutSimpleMessage( SAC_ENTER );
|
|
SacPutSimpleMessage( SAC_PROMPT );
|
|
|
|
//
|
|
// Flush the channel data to the screen
|
|
//
|
|
Status = ConMgrDisplayCurrentChannel();
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// We are done with the current channel globals
|
|
//
|
|
UNLOCK_CURRENT_CHANNEL();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrShutdown(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Console Manager's IoMgrShutdown implementation.
|
|
|
|
Shutdown the console manager
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// close the sac channel
|
|
//
|
|
if (SacChannel) {
|
|
|
|
Status = ChannelClose(SacChannel);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
IF_SAC_DEBUG(
|
|
SAC_DEBUG_FAILS,
|
|
KdPrint(("SAC ConMgrShutdown: failed closing SAC channel.\n"))
|
|
);
|
|
|
|
}
|
|
|
|
SacChannel = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Release the current channel
|
|
//
|
|
if (CurrentChannel) {
|
|
|
|
Status = ChanMgrReleaseChannel(CurrentChannel);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
IF_SAC_DEBUG(
|
|
SAC_DEBUG_FAILS,
|
|
KdPrint(("SAC ConMgrShutdown: failed releasing current channel\n"))
|
|
);
|
|
|
|
}
|
|
|
|
CurrentChannel = NULL;
|
|
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrDisplayFastChannelSwitchingInterface(
|
|
PSAC_CHANNEL Channel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine displays the fast-channel-switching interface
|
|
|
|
Note: caller must hold channel mutex
|
|
|
|
Arguments:
|
|
|
|
Channel - Channel to display
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
HEADLESS_CMD_POSITION_CURSOR SetCursor;
|
|
HEADLESS_CMD_SET_COLOR SetColor;
|
|
PCWSTR Message;
|
|
NTSTATUS Status;
|
|
ULONG OutputBufferSize;
|
|
PWSTR OutputBuffer;
|
|
PWSTR Name;
|
|
PWSTR Description;
|
|
PWSTR DescriptionWrapped;
|
|
GUID ApplicationType;
|
|
PWSTR ChannelTypeString;
|
|
SAC_CHANNEL_HANDLE Handle;
|
|
|
|
ASSERT_LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Initialize
|
|
//
|
|
OutputBufferSize = (11*SAC_VTUTF8_COL_WIDTH+1)*sizeof(WCHAR);
|
|
OutputBuffer = ALLOCATE_POOL(OutputBufferSize, GENERAL_POOL_TAG);
|
|
ASSERT_STATUS(OutputBuffer, STATUS_NO_MEMORY);
|
|
|
|
Name = NULL;
|
|
Description = NULL;
|
|
DescriptionWrapped = NULL;
|
|
|
|
//
|
|
// Display the Fast-Channel-Switching interface
|
|
//
|
|
do {
|
|
|
|
//
|
|
// We cannot use the standard SacPutString() functions, because those write
|
|
// over the channel screen buffer. We force directly onto the terminal here.
|
|
//
|
|
ASSERT(Utf8ConversionBuffer);
|
|
if (!Utf8ConversionBuffer) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Clear the terminal screen.
|
|
//
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdClearDisplay,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
SetCursor.Y = 0;
|
|
SetCursor.X = 0;
|
|
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdPositionCursor,
|
|
&SetCursor,
|
|
sizeof(HEADLESS_CMD_POSITION_CURSOR),
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Send starting colors.
|
|
//
|
|
SetColor.BkgColor = HEADLESS_TERM_DEFAULT_BKGD_COLOR;
|
|
SetColor.FgColor = HEADLESS_TERM_DEFAULT_TEXT_COLOR;
|
|
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdSetColor,
|
|
&SetColor,
|
|
sizeof(HEADLESS_CMD_SET_COLOR),
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdDisplayAttributesOff,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Display xml bundle
|
|
//
|
|
Status = UTF8EncodeAndSend(L"<channel-switch>\r\n");
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the channel's name
|
|
//
|
|
Status = ChannelGetName(
|
|
Channel,
|
|
&Name
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the channel's description
|
|
//
|
|
Status = ChannelGetDescription(
|
|
Channel,
|
|
&Description
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the channel handle
|
|
//
|
|
Handle = ChannelGetHandle(Channel);
|
|
|
|
//
|
|
// Get the channel's application type
|
|
//
|
|
ChannelGetApplicationType(
|
|
Channel,
|
|
&ApplicationType
|
|
);
|
|
|
|
//
|
|
// Determine the channel type string
|
|
//
|
|
switch (ChannelGetType(Channel)) {
|
|
case ChannelTypeVTUTF8:
|
|
case ChannelTypeCmd:
|
|
ChannelTypeString = L"VT-UTF8";
|
|
break;
|
|
case ChannelTypeRaw:
|
|
ChannelTypeString = L"Raw";
|
|
break;
|
|
default:
|
|
ChannelTypeString = L"UNKNOWN";
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Assemble xml blob
|
|
//
|
|
SAFE_SWPRINTF(
|
|
OutputBufferSize,
|
|
(OutputBuffer,
|
|
L"<name>%s</name>\r\n<description>%s</description>\r\n<type>%s</type>\r\n",
|
|
Name,
|
|
Description,
|
|
ChannelTypeString
|
|
));
|
|
|
|
Status = UTF8EncodeAndSend(OutputBuffer);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
SAFE_SWPRINTF(
|
|
OutputBufferSize,
|
|
(OutputBuffer,
|
|
L"<guid>%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x</guid>\r\n",
|
|
Handle.ChannelHandle.Data1,
|
|
Handle.ChannelHandle.Data2,
|
|
Handle.ChannelHandle.Data3,
|
|
Handle.ChannelHandle.Data4[0],
|
|
Handle.ChannelHandle.Data4[1],
|
|
Handle.ChannelHandle.Data4[2],
|
|
Handle.ChannelHandle.Data4[3],
|
|
Handle.ChannelHandle.Data4[4],
|
|
Handle.ChannelHandle.Data4[5],
|
|
Handle.ChannelHandle.Data4[6],
|
|
Handle.ChannelHandle.Data4[7]
|
|
));
|
|
|
|
Status = UTF8EncodeAndSend(OutputBuffer);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
SAFE_SWPRINTF(
|
|
OutputBufferSize,
|
|
(OutputBuffer,
|
|
L"<application-type>%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x</application-type>\r\n",
|
|
ApplicationType.Data1,
|
|
ApplicationType.Data2,
|
|
ApplicationType.Data3,
|
|
ApplicationType.Data4[0],
|
|
ApplicationType.Data4[1],
|
|
ApplicationType.Data4[2],
|
|
ApplicationType.Data4[3],
|
|
ApplicationType.Data4[4],
|
|
ApplicationType.Data4[5],
|
|
ApplicationType.Data4[6],
|
|
ApplicationType.Data4[7]
|
|
));
|
|
|
|
Status = UTF8EncodeAndSend(OutputBuffer);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
Status = UTF8EncodeAndSend(L"</channel-switch>\r\n");
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Clear the terminal screen.
|
|
//
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdClearDisplay,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
SetCursor.Y = 0;
|
|
SetCursor.X = 0;
|
|
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdPositionCursor,
|
|
&SetCursor,
|
|
sizeof(HEADLESS_CMD_POSITION_CURSOR),
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Send starting colors.
|
|
//
|
|
SetColor.BkgColor = HEADLESS_TERM_DEFAULT_BKGD_COLOR;
|
|
SetColor.FgColor = HEADLESS_TERM_DEFAULT_TEXT_COLOR;
|
|
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdSetColor,
|
|
&SetColor,
|
|
sizeof(HEADLESS_CMD_SET_COLOR),
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdDisplayAttributesOff,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Display the plaintext FCSwitching header
|
|
//
|
|
|
|
//
|
|
// Modify the Descripto to wrap if necessary
|
|
//
|
|
Status = CopyAndInsertStringAtInterval(
|
|
Description,
|
|
60,
|
|
L"\r\n ",
|
|
&DescriptionWrapped
|
|
);
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the Channel Switching Header
|
|
//
|
|
Message = GetMessage(SAC_CHANNEL_SWITCHING_HEADER);
|
|
|
|
// Name: %%s
|
|
// Description: %%s
|
|
// Type: %%s
|
|
// Channel GUID: %%08lx-%%04x-%%04x-%%02x%%02x-%%02x%%02x%%02x%%02x%%02x%%02x
|
|
// Application Type: %%08lx-%%04x-%%04x-%%02x%%02x-%%02x%%02x%%02x%%02x%%02x%%02x
|
|
//
|
|
// Use <esc> then <tab> for next channel.
|
|
// Use <esc> then 0 to return to the SAC channel.
|
|
// Use any other key to view this channel.
|
|
|
|
SAFE_SWPRINTF(
|
|
OutputBufferSize,
|
|
(OutputBuffer,
|
|
Message,
|
|
Name,
|
|
DescriptionWrapped,
|
|
ChannelTypeString,
|
|
Handle.ChannelHandle.Data1,
|
|
Handle.ChannelHandle.Data2,
|
|
Handle.ChannelHandle.Data3,
|
|
Handle.ChannelHandle.Data4[0],
|
|
Handle.ChannelHandle.Data4[1],
|
|
Handle.ChannelHandle.Data4[2],
|
|
Handle.ChannelHandle.Data4[3],
|
|
Handle.ChannelHandle.Data4[4],
|
|
Handle.ChannelHandle.Data4[5],
|
|
Handle.ChannelHandle.Data4[6],
|
|
Handle.ChannelHandle.Data4[7],
|
|
ApplicationType.Data1,
|
|
ApplicationType.Data2,
|
|
ApplicationType.Data3,
|
|
ApplicationType.Data4[0],
|
|
ApplicationType.Data4[1],
|
|
ApplicationType.Data4[2],
|
|
ApplicationType.Data4[3],
|
|
ApplicationType.Data4[4],
|
|
ApplicationType.Data4[5],
|
|
ApplicationType.Data4[6],
|
|
ApplicationType.Data4[7]
|
|
));
|
|
|
|
Status = UTF8EncodeAndSend(OutputBuffer);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
SAFE_FREE_POOL(&Name);
|
|
SAFE_FREE_POOL(&Description);
|
|
SAFE_FREE_POOL(&DescriptionWrapped);
|
|
SAFE_FREE_POOL(&OutputBuffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrResetCurrentChannel(
|
|
BOOLEAN SwitchDirectlyToChannel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes the SAC the current channel
|
|
|
|
Note: caller must hold channel mutex
|
|
|
|
Arguments:
|
|
|
|
SwitchDirectlyToChannel -
|
|
if false,
|
|
then show the switching interface,
|
|
else switch directly to the channel
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PSAC_CHANNEL TmpChannel;
|
|
|
|
ASSERT_LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Get a reference to the SAC channel
|
|
//
|
|
// Note: this is the channel manager's policy
|
|
// we need to get the reference of the channel
|
|
// before we use it.
|
|
//
|
|
Status = ChanMgrGetByHandle(
|
|
ChannelGetHandle(SacChannel),
|
|
&TmpChannel
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
SacChannel = TmpChannel;
|
|
|
|
//
|
|
// Make the SAC the current channel
|
|
//
|
|
Status = ConMgrSetCurrentChannel(SacChannel);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
if (SwitchDirectlyToChannel) {
|
|
|
|
//
|
|
// Flush the buffered channel data to the screen
|
|
//
|
|
// Note: we don't need to lock down the SAC, since we own it
|
|
//
|
|
Status = ConMgrDisplayCurrentChannel();
|
|
|
|
} else {
|
|
|
|
//
|
|
// Let the user know we switched via the Channel switching interface
|
|
//
|
|
Status = ConMgrDisplayFastChannelSwitchingInterface(CurrentChannel);
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ConMgrSetCurrentChannel(
|
|
IN PSAC_CHANNEL Channel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine release the current channel's ref count and sets
|
|
the currently active channel to the one given. This routine
|
|
assumes that the current channel was not release after it
|
|
became the current channel. Hence, the typical use sequence
|
|
for making a channel the current channel is:
|
|
|
|
1. ChanMgrGetByXXX --> Channel
|
|
(This gets a channel and increments its ref count by 1)
|
|
2. ConMgrSetCurrentChannel(Channel)
|
|
(This releases the current channel and makes the specified
|
|
channel the current channel)
|
|
3. ...
|
|
4. goto 1.
|
|
( a new channel is made teh current channel)
|
|
|
|
Arguments:
|
|
|
|
NewChannel - the new current channel
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN Present;
|
|
|
|
ASSERT_LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Check to see if the channel has a redraw event
|
|
//
|
|
Status = ChannelHasRedrawEvent(
|
|
CurrentChannel,
|
|
&Present
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Tell the channel to start the drawing
|
|
//
|
|
if (Present) {
|
|
ChannelClearRedrawEvent(CurrentChannel);
|
|
}
|
|
|
|
//
|
|
// Update the sent to screen status
|
|
//
|
|
ChannelSetSentToScreen(CurrentChannel, FALSE);
|
|
|
|
//
|
|
// We are done with the current channel
|
|
//
|
|
Status = ChanMgrReleaseChannel(CurrentChannel);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Assign the new current channel
|
|
//
|
|
CurrentChannel = Channel;
|
|
|
|
//
|
|
// Update the sent to screen status
|
|
//
|
|
ChannelSetSentToScreen(CurrentChannel, FALSE);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrDisplayCurrentChannel(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the currently active channel to the one given. It will transmit
|
|
the channel buffer to the terminal if SendToScreen is TRUE.
|
|
|
|
Note: caller must hold channel mutex
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN Present;
|
|
|
|
ASSERT_LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Check to see if the channel has a redraw event
|
|
//
|
|
Status = ChannelHasRedrawEvent(
|
|
CurrentChannel,
|
|
&Present
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// The channel buffer has been sent to the screen
|
|
//
|
|
ChannelSetSentToScreen(CurrentChannel, TRUE);
|
|
|
|
//
|
|
// Tell the channel to start the drawing
|
|
//
|
|
if (Present) {
|
|
ChannelSetRedrawEvent(CurrentChannel);
|
|
}
|
|
|
|
//
|
|
// Flush the buffered data to the screen
|
|
//
|
|
Status = ChannelOFlush(CurrentChannel);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrAdvanceCurrentChannel(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queries the channel manager for the next available
|
|
active channel and makes it the current channel.
|
|
|
|
Note: The SAC channel is always active and cannot be deleted.
|
|
Hence, we have a halting condition in that we will always
|
|
stop at the SAC channel. For example, if the SAC is the
|
|
only active channel, the current channel will remain the
|
|
SAC channel.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG NewIndex;
|
|
PSAC_CHANNEL Channel;
|
|
|
|
ASSERT_LOCK_CURRENT_CHANNEL();
|
|
|
|
do {
|
|
|
|
//
|
|
// Query the channel manager for an array of currently active channels
|
|
//
|
|
Status = ChanMgrGetNextActiveChannel(
|
|
CurrentChannel,
|
|
&NewIndex,
|
|
&Channel
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Change the current channel to the next active channel
|
|
//
|
|
Status = ConMgrSetCurrentChannel(Channel);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Let the user know we switched via the Channel switching interface
|
|
//
|
|
Status = ConMgrDisplayFastChannelSwitchingInterface(Channel);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
ConMgrIsWriteEnabled(
|
|
PSAC_CHANNEL Channel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Console Manager's IoMgrIsWriteEnabled implementation.
|
|
|
|
This routine determines if the channel in question is authorized
|
|
to write to use the IoMgr's WriteData routine. In the console
|
|
manager's case, this is TRUE if the channel is the current channel.
|
|
From the channel's perspective, if the channel is not enabled to
|
|
write, then it should buffer the data - to be released at a later
|
|
time by the io manager.
|
|
|
|
Arguments:
|
|
|
|
ChannelHandle - channel handle to compare against
|
|
|
|
Return Value:
|
|
|
|
TRUE - the specified channel is the current channel
|
|
|
|
--*/
|
|
{
|
|
SAC_CHANNEL_HANDLE Handle;
|
|
|
|
//
|
|
// Get the current channel's handle to compare against
|
|
//
|
|
Handle = ChannelGetHandle(CurrentChannel);
|
|
|
|
//
|
|
// Determine if the channel in question is the current channel
|
|
//
|
|
return ChannelIsEqual(
|
|
Channel,
|
|
&Handle
|
|
);
|
|
|
|
}
|
|
|
|
VOID
|
|
ConMgrWorkerProcessEvents(
|
|
IN PSAC_DEVICE_CONTEXT DeviceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Console Manager's IoMgrWorkerProcessEvents implementation.
|
|
|
|
This is the routine for the worker thread. It blocks on an event, when
|
|
the event is signalled, then that indicates a request is ready to be processed.
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - A pointer to this device.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC WorkerProcessEvents: Entering.\n")));
|
|
|
|
//
|
|
// Loop forever.
|
|
//
|
|
while (1) {
|
|
|
|
//
|
|
// Block until there is work to do.
|
|
//
|
|
Status = KeWaitForSingleObject(
|
|
(PVOID)&(DeviceContext->ProcessEvent),
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Process the serial port buffer and return a processing state
|
|
//
|
|
ConMgrSerialPortConsumer();
|
|
|
|
//
|
|
// if there is work to do,
|
|
// then something in the consumer wanted to perform
|
|
// some action that would result in deadlock
|
|
// contention for the Current channel lock.
|
|
//
|
|
switch(ExecutePostConsumerCommand) {
|
|
case Reboot:
|
|
|
|
DoRebootCommand(TRUE);
|
|
|
|
//
|
|
// we are done with this work
|
|
//
|
|
ExecutePostConsumerCommand = Nothing;
|
|
|
|
break;
|
|
|
|
case Shutdown:
|
|
|
|
DoRebootCommand(FALSE);
|
|
|
|
//
|
|
// we are done with this work
|
|
//
|
|
ExecutePostConsumerCommand = Nothing;
|
|
|
|
break;
|
|
|
|
case CloseChannel: {
|
|
|
|
PSAC_CHANNEL Channel;
|
|
|
|
//
|
|
// get the channel to close
|
|
//
|
|
Channel = (PSAC_CHANNEL)ExecutePostConsumerCommandData;
|
|
|
|
//
|
|
// attempt to close the channel
|
|
//
|
|
// Note: any error reporting necessary resulting
|
|
// from this action will be carried out via
|
|
// the IoMgrCloseChannel method
|
|
//
|
|
ChanMgrCloseChannel(Channel);
|
|
|
|
//
|
|
// We are done with the channel
|
|
//
|
|
ChanMgrReleaseChannel(Channel);
|
|
|
|
//
|
|
// we are done with this work
|
|
//
|
|
ExecutePostConsumerCommand = Nothing;
|
|
ExecutePostConsumerCommandData = NULL;
|
|
|
|
break;
|
|
}
|
|
|
|
case Nothing:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT(0);
|
|
}
|
|
|
|
|
|
VOID
|
|
ConMgrSerialPortConsumer(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a DPC routine that is queue'd by DriverEntry. It is used to check for any
|
|
user input and then processes them.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
UCHAR LocalTmpBuffer[4];
|
|
ULONG i;
|
|
UCHAR ch;
|
|
|
|
IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE_LOUD, KdPrint(("SAC TimerDpcRoutine: Entering.\n")));
|
|
|
|
//
|
|
// lock down the current channel globals
|
|
//
|
|
LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Make sure we have a current channel
|
|
//
|
|
// NOTE: we should at least have the SAC channel
|
|
//
|
|
ASSERT(CurrentChannel);
|
|
if (CurrentChannel == NULL) {
|
|
goto ConMgrSerialPortConsumerDone;
|
|
}
|
|
|
|
GetNextByte:
|
|
|
|
//
|
|
// Attempt to get a character from the serial port
|
|
//
|
|
Status = SerialBufferGetChar(&ch);
|
|
|
|
//
|
|
// Bail if there are no new characters to read or if there was an error
|
|
//
|
|
if (!NT_SUCCESS(Status) || Status == STATUS_NO_DATA_DETECTED) {
|
|
goto ConMgrSerialPortConsumerDone;
|
|
}
|
|
|
|
//
|
|
// Possible states and actions:
|
|
//
|
|
// Note: <x> == <something else>
|
|
//
|
|
// <esc>
|
|
// <tab> --> advance channel
|
|
// <0> --> reset current channel to SAC
|
|
// <x> --> display current channel
|
|
// <x> --> if CurrentChannel != SacChannel, then write <esc> in channel->ibuffer
|
|
// and write <x> in channel->ibuffer
|
|
// <x> --> write <x> to current channel->ibuffer
|
|
//
|
|
|
|
//
|
|
// Check for <esc>
|
|
//
|
|
// Note: we can arrive at this routine from:
|
|
//
|
|
// <x>
|
|
// <esc><x>
|
|
// <esc><tab><x>
|
|
//
|
|
// So we need to clear the InputInEscTab flag
|
|
//
|
|
// If we are already in the <esc><x> sequence, then
|
|
// skip this block. This way we can receive <esc><esc>
|
|
// sequences.
|
|
//
|
|
if (ch == 0x1B && (InputInEscape == FALSE)) {
|
|
|
|
//
|
|
// We are no longer in an <esc><tab><x> sequence
|
|
//
|
|
InputInEscTab = FALSE;
|
|
|
|
//
|
|
// We are now in an <esc><x> sequence
|
|
//
|
|
InputInEscape = TRUE;
|
|
|
|
goto GetNextByte;
|
|
|
|
}
|
|
|
|
//
|
|
// Check for <esc><tab>
|
|
//
|
|
// Note: we can arrive at this routine from:
|
|
//
|
|
// <esc><x>
|
|
//
|
|
if ((ch == '\t') && InputInEscape) {
|
|
|
|
//
|
|
// We should not be in an <esc><tab><x> sequence already
|
|
//
|
|
ASSERT(InputInEscTab == FALSE);
|
|
|
|
//
|
|
// We are no longer in an <esc><x> sequence
|
|
//
|
|
InputInEscape = FALSE;
|
|
|
|
//
|
|
// Find the next active channel and make it the current
|
|
//
|
|
Status = ConMgrAdvanceCurrentChannel();
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
goto ConMgrSerialPortConsumerDone;
|
|
}
|
|
|
|
//
|
|
// We are now in an <esc><tab><x> sequence
|
|
//
|
|
InputInEscTab = TRUE;
|
|
|
|
goto GetNextByte;
|
|
|
|
}
|
|
|
|
//
|
|
// If this screen has not yet been displayed and the user entered a 0,
|
|
// then switch to the SAC Channel
|
|
//
|
|
// Note: we can arrive at this routine from:
|
|
//
|
|
// <esc><tab><x>
|
|
//
|
|
if ((ch == '0') && InputInEscTab) {
|
|
|
|
//
|
|
// We should not be in an <esc><x> sequence at this point
|
|
//
|
|
ASSERT(InputInEscape == FALSE);
|
|
|
|
//
|
|
// We are no longer in an <esc><tab><x> sequence
|
|
//
|
|
InputInEscTab = FALSE;
|
|
|
|
//
|
|
// It is possible that the current channel has already been sent
|
|
// to the screen without having received the <x> of <esc><tab><x>
|
|
//
|
|
// For instance:
|
|
//
|
|
// 1. we received <esc><tab>
|
|
// a. InputInEscTab = TRUE
|
|
// b. the fast-channel-switching header is displayed
|
|
// c. sent to screen for current channel == false
|
|
// d. sent to screen for SAC channel == false
|
|
// 2. we leave the consumer since there is no new input
|
|
// 3. the current channel is closed by it's owner
|
|
// a. the current channel is removed
|
|
// b. the current channel becomes the SAC channel
|
|
// c. the current channel is displayed
|
|
// d. sent to screen for SAC channel == true
|
|
// 4. we receive <x> of <esc><tab><x> sequence
|
|
// 5. we end up here and are no longer in an EscTab sequence.
|
|
//
|
|
if (!ChannelSentToScreen(CurrentChannel)) {
|
|
|
|
//
|
|
// Make the current channel the SAC
|
|
//
|
|
// Note: There should not be anything modifying the SacChannel
|
|
// at this time, so this should be safe
|
|
//
|
|
Status = ConMgrResetCurrentChannel(FALSE);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
goto ConMgrSerialPortConsumerDone;
|
|
}
|
|
|
|
}
|
|
|
|
goto GetNextByte;
|
|
|
|
}
|
|
|
|
//
|
|
// If this screen has not yet been displayed,
|
|
// and the user entered a keystroke then display it.
|
|
//
|
|
// Note: we can arrive at this routine from:
|
|
//
|
|
// <x>
|
|
// <esc><x>
|
|
// <esc><tab><x>
|
|
//
|
|
// So we need to clear the esc sequence flags
|
|
//
|
|
if (!ChannelSentToScreen(CurrentChannel)) {
|
|
|
|
//
|
|
// We are no longer in an <esc><x> sequence
|
|
//
|
|
InputInEscape = FALSE;
|
|
|
|
//
|
|
// We are no longer in an <esc><tab><x> sequence
|
|
//
|
|
InputInEscTab = FALSE;
|
|
|
|
//
|
|
// Attempt to display the buffered contents of the current channel
|
|
//
|
|
Status = ConMgrDisplayCurrentChannel();
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
goto ConMgrSerialPortConsumerDone;
|
|
}
|
|
|
|
goto GetNextByte;
|
|
|
|
} else {
|
|
|
|
//
|
|
// It is possible that the current channel has already been sent
|
|
// to the screen without having received the <x> of <esc><tab><x>
|
|
//
|
|
// For instance:
|
|
//
|
|
// 1. we received <esc><tab>
|
|
// a. InputInEscTab = TRUE
|
|
// b. the fast-channel-switching header is displayed
|
|
// c. sent to screen for current channel == false
|
|
// d. sent to screen for SAC channel == false
|
|
// 2. we leave the consumer since there is no new input
|
|
// 3. the current channel is closed by it's owner
|
|
// a. the current channel is removed
|
|
// b. the current channel becomes the SAC channel
|
|
// c. the current channel is displayed
|
|
// d. sent to screen for SAC channel == true
|
|
// 4. we receive <x> of <esc><tab><x> sequence
|
|
// 5. we skip the (!ChannelSentToScreen(CurrentChannel)) block
|
|
// 6. we end up here. Since the <x> != 0 and we have already
|
|
// sent the current data to the screen, we are no longer
|
|
// in an EscTab sequence.
|
|
//
|
|
|
|
InputInEscTab = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// This is the beginning of the fall-through block.
|
|
// That is, if we get here, then the character is not a part
|
|
// of some special sequence that should have been processed
|
|
// above. Characters processed here are inserted into the
|
|
// current channel's input buffer.
|
|
//
|
|
// Note: we should not be in an <esc><tab><x> sequence here
|
|
//
|
|
ASSERT(InputInEscTab == FALSE);
|
|
|
|
//
|
|
// If the user was entering <esc><x> and the current channel
|
|
// is not the SAC, then store the <esc> in the current channel's
|
|
// ibuffer.
|
|
//
|
|
// Note: <esc><esc> buffers a single <esc>.
|
|
// This allows sending an real <esc><tab> to the channel.
|
|
//
|
|
if (InputInEscape && (CurrentChannel != SacChannel)) {
|
|
LocalTmpBuffer[0] = 0x1B;
|
|
Status = ChannelIWrite(
|
|
CurrentChannel,
|
|
LocalTmpBuffer,
|
|
sizeof(LocalTmpBuffer[0])
|
|
);
|
|
}
|
|
|
|
//
|
|
// If the current character is <esc>,
|
|
// then we still are in an escape sequence so
|
|
// don't change the InputInEscape.
|
|
// This allows <esc><esc> to be followed by <tab>
|
|
// and form a valid <esc><tab> sequence.
|
|
//
|
|
if (ch != 0x1B) {
|
|
//
|
|
// We are no longer in an <esc><x> sequence
|
|
//
|
|
InputInEscape = FALSE;
|
|
}
|
|
|
|
//
|
|
// Buffer this input to the current channel's IBuffer
|
|
//
|
|
ChannelIWrite(
|
|
CurrentChannel,
|
|
&ch,
|
|
sizeof(ch)
|
|
);
|
|
|
|
//
|
|
// If the current channel is not the SAC, then go and get more input.
|
|
// Otherwise, process the SAC's input buffer
|
|
//
|
|
if (CurrentChannel != SacChannel) {
|
|
|
|
goto GetNextByte;
|
|
|
|
} else {
|
|
|
|
ULONG ResponseLength;
|
|
WCHAR wch;
|
|
|
|
//
|
|
// Now do processing if the SAC is the active channel.
|
|
//
|
|
|
|
//
|
|
// Strip the LF if the last character was a CR
|
|
//
|
|
if (ConMgrLastCharWasCR && ch == (UCHAR)0x0A) {
|
|
ChannelIReadLast(CurrentChannel);
|
|
ConMgrLastCharWasCR = FALSE;
|
|
goto GetNextByte;
|
|
}
|
|
|
|
//
|
|
// Keep track of the of when we receive a CR so
|
|
// we can strip of the LF if it is next
|
|
//
|
|
ConMgrLastCharWasCR = (ch == 0x0D ? TRUE : FALSE);
|
|
|
|
//
|
|
// If this is a return, then we are done and need to return the line
|
|
//
|
|
if ((ch == '\n') || (ch == '\r')) {
|
|
SacPutString(L"\r\n");
|
|
ChannelIReadLast(CurrentChannel);
|
|
LocalTmpBuffer[0] = '\0';
|
|
ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
|
|
goto StripWhitespaceAndReturnLine;
|
|
}
|
|
|
|
//
|
|
// If this is a backspace or delete, then we need to do that.
|
|
//
|
|
if ((ch == 0x8) || (ch == 0x7F)) { // backspace (^H) or delete
|
|
|
|
//
|
|
// We want to:
|
|
// 1. remove the backspace or delete character
|
|
// 2. if the input buffer is non-empty, remove the last character
|
|
// (which is the character the user wanted to delete)
|
|
//
|
|
if (ChannelIBufferLength(CurrentChannel) > 0) {
|
|
ChannelIReadLast(CurrentChannel);
|
|
}
|
|
if (ChannelIBufferLength(CurrentChannel) > 0) {
|
|
SacPutString(L"\010 \010");
|
|
ChannelIReadLast(CurrentChannel);
|
|
}
|
|
} else if (ch == 0x3) { // Control-C
|
|
|
|
//
|
|
// Terminate the string and return it.
|
|
//
|
|
ChannelIReadLast(CurrentChannel);
|
|
LocalTmpBuffer[0] = '\0';
|
|
ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
|
|
goto StripWhitespaceAndReturnLine;
|
|
|
|
} else if (ch == 0x9) { // Tab
|
|
|
|
//
|
|
// Ignore tabs
|
|
//
|
|
ChannelIReadLast(CurrentChannel);
|
|
SacPutString(L"\007"); // send a BEL
|
|
goto GetNextByte;
|
|
|
|
} else if (ChannelIBufferLength(CurrentChannel) == SAC_VTUTF8_COL_WIDTH - 2) {
|
|
|
|
WCHAR Buffer[4];
|
|
|
|
//
|
|
// We are at the end of the screen - remove the last character from
|
|
// the terminal screen and replace it with this one.
|
|
//
|
|
swprintf(Buffer, L"\010%c", ch);
|
|
SacPutString(Buffer);
|
|
ChannelIReadLast(CurrentChannel);
|
|
ChannelIReadLast(CurrentChannel);
|
|
LocalTmpBuffer[0] = ch;
|
|
ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
|
|
|
|
} else {
|
|
|
|
WCHAR Buffer[4];
|
|
|
|
//
|
|
// Echo the character to the screen
|
|
//
|
|
swprintf(Buffer, L"%c", ch);
|
|
SacPutString(Buffer);
|
|
}
|
|
|
|
goto GetNextByte;
|
|
|
|
StripWhitespaceAndReturnLine:
|
|
|
|
//
|
|
// Before returning the input line, strip off all leading and trailing blanks
|
|
//
|
|
do {
|
|
LocalTmpBuffer[0] = (UCHAR)ChannelIReadLast(CurrentChannel);
|
|
} while (((LocalTmpBuffer[0] == '\0') ||
|
|
(LocalTmpBuffer[0] == ' ') ||
|
|
(LocalTmpBuffer[0] == '\t')) &&
|
|
(ChannelIBufferLength(CurrentChannel) > 0)
|
|
);
|
|
|
|
ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
|
|
LocalTmpBuffer[0] = '\0';
|
|
ChannelIWrite(CurrentChannel, LocalTmpBuffer, sizeof(LocalTmpBuffer[0]));
|
|
|
|
do {
|
|
|
|
Status = ChannelIRead(
|
|
CurrentChannel,
|
|
(PUCHAR)&wch,
|
|
sizeof(WCHAR),
|
|
&ResponseLength
|
|
);
|
|
|
|
LocalTmpBuffer[0] = (UCHAR)wch;
|
|
|
|
} while ((ResponseLength != 0) &&
|
|
((LocalTmpBuffer[0] == ' ') ||
|
|
(LocalTmpBuffer[0] == '\t')));
|
|
|
|
InputBuffer[0] = LocalTmpBuffer[0];
|
|
i = 1;
|
|
|
|
do {
|
|
|
|
Status = ChannelIRead(
|
|
CurrentChannel,
|
|
(PUCHAR)&wch,
|
|
sizeof(WCHAR),
|
|
&ResponseLength
|
|
);
|
|
|
|
ASSERT(i < SAC_VTUTF8_COL_WIDTH);
|
|
InputBuffer[i++] = (UCHAR)wch;
|
|
|
|
} while (ResponseLength != 0);
|
|
|
|
//
|
|
// Lower case all the characters. We do not use strlwr() or the like, so that
|
|
// the SAC (expecting ASCII always) doesn't accidently get DBCS or the like
|
|
// translation of the UCHAR stream.
|
|
//
|
|
for (i = 0; InputBuffer[i] != '\0'; i++) {
|
|
ASSERT(i < SAC_VTUTF8_COL_WIDTH);
|
|
if ((InputBuffer[i] >= 'A') && (InputBuffer[i] <= 'Z')) {
|
|
InputBuffer[i] = InputBuffer[i] - 'A' + 'a';
|
|
}
|
|
}
|
|
|
|
ASSERT(ExecutePostConsumerCommand == Nothing);
|
|
|
|
//
|
|
// Process the input line.
|
|
//
|
|
ConMgrProcessInputLine();
|
|
|
|
//
|
|
// Put the next command prompt
|
|
//
|
|
SacPutSimpleMessage(SAC_PROMPT);
|
|
|
|
//
|
|
// exit if we need to do some work
|
|
//
|
|
if (ExecutePostConsumerCommand != Nothing) {
|
|
goto ConMgrSerialPortConsumerDone;
|
|
}
|
|
|
|
//
|
|
// Keep on processing characters
|
|
//
|
|
goto GetNextByte;
|
|
|
|
}
|
|
|
|
ConMgrSerialPortConsumerDone:
|
|
|
|
//
|
|
// We are done with current channel globals
|
|
//
|
|
UNLOCK_CURRENT_CHANNEL();
|
|
|
|
IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE_LOUD, KdPrint(("SAC TimerDpcRoutine: Exiting.\n")));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
ConMgrProcessInputLine(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to process an input line.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
HEADLESS_CMD_DISPLAY_LOG Command;
|
|
PUCHAR InputLine;
|
|
BOOLEAN CommandFound = FALSE;
|
|
|
|
InputLine = &(InputBuffer[0]);
|
|
|
|
do {
|
|
|
|
if (!strcmp((LPSTR)InputLine, TLIST_COMMAND_STRING)) {
|
|
DoTlistCommand();
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if ((!strcmp((LPSTR)InputLine, HELP1_COMMAND_STRING)) ||
|
|
(!strcmp((LPSTR)InputLine, HELP2_COMMAND_STRING))) {
|
|
DoHelpCommand();
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!strcmp((LPSTR)InputLine, DUMP_COMMAND_STRING)) {
|
|
|
|
NTSTATUS Status;
|
|
|
|
Command.Paging = GlobalPagingNeeded;
|
|
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdDisplayLog,
|
|
&Command,
|
|
sizeof(HEADLESS_CMD_DISPLAY_LOG),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
IF_SAC_DEBUG(
|
|
SAC_DEBUG_FAILS,
|
|
KdPrint(("SAC Display Log failed.\n"))
|
|
);
|
|
|
|
}
|
|
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!strcmp((LPSTR)InputLine, FULLINFO_COMMAND_STRING)) {
|
|
DoFullInfoCommand();
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!strcmp((LPSTR)InputLine, PAGING_COMMAND_STRING)) {
|
|
DoPagingCommand();
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!strncmp((LPSTR)InputLine,
|
|
CHANNEL_COMMAND_STRING,
|
|
strlen(CHANNEL_COMMAND_STRING))) {
|
|
ULONG Length;
|
|
|
|
Length = (ULONG)strlen(CHANNEL_COMMAND_STRING);
|
|
|
|
if (((strlen((LPSTR)InputLine) > 1) && (InputLine[Length] == ' ')) ||
|
|
(strlen((LPSTR)InputLine) == strlen(CHANNEL_COMMAND_STRING))) {
|
|
DoChannelCommand(InputLine);
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!strcmp((LPSTR)InputLine, CMD_COMMAND_STRING)) {
|
|
|
|
#if ENABLE_CMD_SESSION_PERMISSION_CHECKING
|
|
|
|
//
|
|
// If we are not able to launch cmd sessions,
|
|
// then notify that we cannot peform this action
|
|
//
|
|
if (IsCommandConsoleLaunchingEnabled()) {
|
|
DoCmdCommand(InputLine);
|
|
} else {
|
|
|
|
//
|
|
// Notify the user
|
|
//
|
|
SacPutSimpleMessage(SAC_CMD_LAUNCHING_DISABLED);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
DoCmdCommand(InputLine);
|
|
|
|
#endif
|
|
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!strcmp((LPSTR)InputLine, REBOOT_COMMAND_STRING)) {
|
|
//
|
|
// Set the reboot flag so that when we exit the serial consumer
|
|
// we know to reboot the computer. This way, the reboot
|
|
// command is executed when we dont have the Current Channel mutex
|
|
//
|
|
ExecutePostConsumerCommand = Reboot;
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!strcmp((LPSTR)InputLine, SHUTDOWN_COMMAND_STRING)) {
|
|
//
|
|
// Set the shutdown flag so that when we exit the serial consumer
|
|
// we know to shutdown the computer. This way, the shutdown
|
|
// command is executed when we dont have the Current Channel mutex
|
|
//
|
|
ExecutePostConsumerCommand = Shutdown;
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!strcmp((LPSTR)InputLine, CRASH_COMMAND_STRING)) {
|
|
CommandFound = TRUE;
|
|
DoCrashCommand(); // this call does not return
|
|
break;
|
|
}
|
|
|
|
if (!strncmp((LPSTR)InputLine,
|
|
KILL_COMMAND_STRING,
|
|
sizeof(KILL_COMMAND_STRING) - sizeof(UCHAR))) {
|
|
if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
|
|
(strlen((LPSTR)InputLine) == 1)
|
|
) {
|
|
DoKillCommand(InputLine);
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
#if ENABLE_CHANNEL_LOCKING
|
|
if (!strcmp((LPSTR)InputLine, LOCK_COMMAND_STRING)) {
|
|
DoLockCommand();
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (!strncmp((LPSTR)InputLine,
|
|
LOWER_COMMAND_STRING,
|
|
sizeof(LOWER_COMMAND_STRING) - sizeof(UCHAR))) {
|
|
if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
|
|
(strlen((LPSTR)InputLine) == 1)
|
|
) {
|
|
DoLowerPriorityCommand(InputLine);
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!strncmp((LPSTR)InputLine,
|
|
RAISE_COMMAND_STRING,
|
|
sizeof(RAISE_COMMAND_STRING) - sizeof(UCHAR))) {
|
|
if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
|
|
(strlen((LPSTR)InputLine) == 1)
|
|
) {
|
|
DoRaisePriorityCommand(InputLine);
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!strncmp((LPSTR)InputLine,
|
|
LIMIT_COMMAND_STRING,
|
|
sizeof(LIMIT_COMMAND_STRING) - sizeof(UCHAR))) {
|
|
if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
|
|
(strlen((LPSTR)InputLine) == 1)
|
|
) {
|
|
DoLimitMemoryCommand(InputLine);
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!strncmp((LPSTR)InputLine,
|
|
TIME_COMMAND_STRING,
|
|
sizeof(TIME_COMMAND_STRING) - sizeof(UCHAR))) {
|
|
if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
|
|
(strlen((LPSTR)InputLine) == 1)
|
|
) {
|
|
DoSetTimeCommand(InputLine);
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!strcmp((LPSTR)InputLine, INFORMATION_COMMAND_STRING)) {
|
|
DoMachineInformationCommand();
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!strncmp((LPSTR)InputLine,
|
|
SETIP_COMMAND_STRING,
|
|
sizeof(SETIP_COMMAND_STRING) - sizeof(UCHAR))) {
|
|
if (((strlen((LPSTR)InputLine) > 1) && (InputLine[1] == ' ')) ||
|
|
(strlen((LPSTR)InputLine) == 1)
|
|
) {
|
|
DoSetIpAddressCommand(InputLine);
|
|
CommandFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((InputLine[0] == '\n') || (InputLine[0] == '\0')) {
|
|
CommandFound = TRUE;
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
if( !CommandFound ) {
|
|
//
|
|
// We don't know what this is.
|
|
//
|
|
SacPutSimpleMessage(SAC_UNKNOWN_COMMAND);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Utility routines for writing to the SAC
|
|
//
|
|
|
|
|
|
VOID
|
|
ConMgrEventMessageHaveLock(
|
|
IN PCWSTR String
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is for callers that want to deploy an event
|
|
message and already own the Current Channel Lock.
|
|
|
|
Arguments:
|
|
|
|
String - The string to display.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Currently, event messages are sent to the SAC channel
|
|
//
|
|
|
|
SacPutString(String);
|
|
|
|
}
|
|
|
|
VOID
|
|
ConMgrEventMessage(
|
|
IN PCWSTR String,
|
|
IN BOOLEAN HaveCurrentChannelLock
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deploys an event message
|
|
|
|
Arguments:
|
|
|
|
String - The string to display.
|
|
HaveLock - Whether or not the caller currently owns the Current Channel Lock
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Currently, event messages are sent to the SAC channel
|
|
//
|
|
|
|
if (! HaveCurrentChannelLock) {
|
|
LOCK_CURRENT_CHANNEL();
|
|
}
|
|
|
|
SacPutSimpleMessage(SAC_ENTER);
|
|
ConMgrEventMessageHaveLock(String);
|
|
SacPutSimpleMessage(SAC_PROMPT);
|
|
|
|
if (! HaveCurrentChannelLock) {
|
|
UNLOCK_CURRENT_CHANNEL();
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
ConMgrSimpleEventMessage(
|
|
IN ULONG MessageId,
|
|
IN BOOLEAN HaveCurrentChannelLock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves a message resource and sends it as an event message
|
|
|
|
Arguments:
|
|
|
|
MessageId - The message id of the resource to send
|
|
|
|
Return Value:
|
|
|
|
TRUE - the message was found
|
|
otherwise, FALSE
|
|
|
|
--*/
|
|
{
|
|
PCWSTR p;
|
|
|
|
p = GetMessage(MessageId);
|
|
|
|
if (p) {
|
|
ConMgrEventMessage(
|
|
p,
|
|
HaveCurrentChannelLock
|
|
);
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
VOID
|
|
SacPutString(
|
|
PCWSTR String
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a string and packages it into a command structure for the
|
|
HeadlessDispatch routine.
|
|
|
|
Arguments:
|
|
|
|
String - The string to display.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(FIELD_OFFSET(HEADLESS_CMD_PUT_STRING, String) == 0); // ASSERT if anyone changes this structure.
|
|
|
|
//
|
|
// Write the to the sac channel
|
|
//
|
|
Status = ChannelOWrite(
|
|
SacChannel,
|
|
(PCUCHAR)String,
|
|
(ULONG)(wcslen(String)*sizeof(WCHAR))
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
|
|
IF_SAC_DEBUG(
|
|
SAC_DEBUG_FAILS,
|
|
KdPrint(("SAC XmlMgrSacPutString: OWrite failed\n"))
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SacPutSimpleMessage(
|
|
ULONG MessageId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves a message resource and sends it to the SAC channel
|
|
|
|
Arguments:
|
|
|
|
MessageId - The message id of the resource to send
|
|
|
|
Return Value:
|
|
|
|
TRUE - the message was found
|
|
otherwise, FALSE
|
|
|
|
--*/
|
|
{
|
|
PCWSTR p;
|
|
|
|
p = GetMessage(MessageId);
|
|
|
|
if (p) {
|
|
SacPutString(p);
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrChannelOWrite(
|
|
IN PSAC_CHANNEL Channel,
|
|
IN PSAC_CMD_WRITE_CHANNEL ChannelWriteCmd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to write data to a channel
|
|
|
|
Arguments:
|
|
|
|
Channel - the channel to write to
|
|
ChannelWriteCmd - the write IOCTL command structure
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
//
|
|
//
|
|
LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Write the data to the channel's output buffer
|
|
//
|
|
Status = ChannelOWrite(
|
|
Channel,
|
|
&(ChannelWriteCmd->Buffer[0]),
|
|
ChannelWriteCmd->Size
|
|
);
|
|
|
|
//
|
|
//
|
|
//
|
|
UNLOCK_CURRENT_CHANNEL();
|
|
|
|
ASSERT(NT_SUCCESS(Status) || Status == STATUS_NOT_FOUND);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrGetChannelCloseMessage(
|
|
IN PSAC_CHANNEL Channel,
|
|
IN NTSTATUS CloseStatus,
|
|
OUT PWSTR* OutputBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine constructs an event message based
|
|
on the status of attempting to close a channel
|
|
|
|
Arguments:
|
|
|
|
Channel - the channel being closed
|
|
CloseStatus - the resulting status
|
|
OutputBuffer - on exit, contains the message
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Size;
|
|
PWSTR Name;
|
|
PCWSTR Message;
|
|
|
|
//
|
|
// default: we succeded
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
do {
|
|
|
|
//
|
|
// Get the channel's name
|
|
//
|
|
Status = ChannelGetName(
|
|
Channel,
|
|
&Name
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate a local temp buffer for display
|
|
//
|
|
|
|
if (NT_SUCCESS(CloseStatus)) {
|
|
|
|
//
|
|
// get the string resource
|
|
//
|
|
Message = GetMessage(SAC_CHANNEL_CLOSED);
|
|
|
|
if (Message == NULL) {
|
|
Status = STATUS_RESOURCE_DATA_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate the buffer memory
|
|
//
|
|
Size = (ULONG)((wcslen(Message) + SAC_MAX_CHANNEL_NAME_LENGTH + 1) * sizeof(WCHAR));
|
|
*OutputBuffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
|
|
ASSERT_STATUS(*OutputBuffer, STATUS_NO_MEMORY);
|
|
|
|
//
|
|
// report the channel has been closed
|
|
//
|
|
SAFE_SWPRINTF(
|
|
Size,
|
|
(*OutputBuffer,
|
|
Message,
|
|
Name
|
|
));
|
|
|
|
} else if (CloseStatus == STATUS_ALREADY_DISCONNECTED) {
|
|
|
|
//
|
|
// get the string resource
|
|
//
|
|
Message = GetMessage(SAC_CHANNEL_ALREADY_CLOSED);
|
|
|
|
if (Message == NULL) {
|
|
Status = STATUS_RESOURCE_DATA_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate the buffer memory
|
|
//
|
|
Size = (ULONG)((wcslen(Message) + SAC_MAX_CHANNEL_NAME_LENGTH + 1) * sizeof(WCHAR));
|
|
*OutputBuffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
|
|
ASSERT_STATUS(*OutputBuffer, STATUS_NO_MEMORY);
|
|
|
|
//
|
|
// report the channel was already closed
|
|
//
|
|
SAFE_SWPRINTF(
|
|
Size,
|
|
(*OutputBuffer,
|
|
Message,
|
|
Name
|
|
));
|
|
|
|
} else {
|
|
|
|
//
|
|
// get the string resource
|
|
//
|
|
Message = GetMessage(SAC_CHANNEL_FAILED_CLOSE);
|
|
|
|
if (Message == NULL) {
|
|
Status = STATUS_RESOURCE_DATA_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate the buffer memory
|
|
//
|
|
Size = (ULONG)((wcslen(Message) + SAC_MAX_CHANNEL_NAME_LENGTH + 1) * sizeof(WCHAR));
|
|
*OutputBuffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
|
|
ASSERT_STATUS(*OutputBuffer, STATUS_NO_MEMORY);
|
|
|
|
//
|
|
// report that we failed to close the channel
|
|
//
|
|
SAFE_SWPRINTF(
|
|
Size,
|
|
(*OutputBuffer,
|
|
Message,
|
|
Name
|
|
));
|
|
|
|
}
|
|
|
|
SAFE_FREE_POOL(&Name);
|
|
|
|
} while ( FALSE );
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrChannelClose(
|
|
PSAC_CHANNEL Channel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to close a channel.
|
|
If we successfully close the channel and this channel was
|
|
the current channel, we reset the current channel to the SAC channel
|
|
|
|
Arguments:
|
|
|
|
Channel - the channel to close
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the channel was closed
|
|
STATUS_ALREADY_DISCONNECTED - the channel was already closed
|
|
otherwise, error status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// default
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Attempt to make the specified channel inactive
|
|
//
|
|
do {
|
|
|
|
//
|
|
// The current channel is being closed,
|
|
// so reset the current channel to the SAC
|
|
//
|
|
// Note: disable this check if you don't want
|
|
// the conmgr to switch to the SAC channel
|
|
// when the current chanenl is closed.
|
|
//
|
|
if (ConMgrIsWriteEnabled(Channel)) {
|
|
|
|
Status = ConMgrResetCurrentChannel(FALSE);
|
|
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrHandleEvent(
|
|
IN IO_MGR_EVENT Event,
|
|
IN PSAC_CHANNEL Channel, OPTIONAL
|
|
IN PVOID Data OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Console Manager's IoMgrHandleEvent implementation.
|
|
|
|
This routine handles asynchronous events that effect
|
|
the channels, the console manager and the SAC driver as a whole.
|
|
|
|
Note that this routine only handles events that are important for
|
|
the proper operation of the console manager. Hence, not all
|
|
possible events that can happen in the SAC driver are here.
|
|
|
|
Arguments:
|
|
|
|
ChannelWriteCmd - the write IOCTL command structure
|
|
Channel - Optional: the channel the event is targeted at
|
|
Data - Optional: data for the specified event
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
switch(Event) {
|
|
case IO_MGR_EVENT_CHANNEL_CREATE: {
|
|
|
|
PWCHAR OutputBuffer;
|
|
ULONG Size;
|
|
PWSTR Name;
|
|
PCWSTR Message;
|
|
|
|
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_2);
|
|
|
|
//
|
|
// get the string resource
|
|
//
|
|
Message = GetMessage(SAC_NEW_CHANNEL_CREATED);
|
|
|
|
if (Message == NULL) {
|
|
Status = STATUS_RESOURCE_DATA_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Determine the size of the string buffer
|
|
//
|
|
Size = (ULONG)((wcslen(Message) + SAC_MAX_CHANNEL_NAME_LENGTH + 1) * sizeof(WCHAR));
|
|
|
|
//
|
|
// Allocate the buffer
|
|
//
|
|
OutputBuffer = ALLOCATE_POOL(Size, GENERAL_POOL_TAG);
|
|
ASSERT_STATUS(OutputBuffer, STATUS_NO_MEMORY);
|
|
|
|
do {
|
|
|
|
//
|
|
// Get the channel's name
|
|
//
|
|
Status = ChannelGetName(
|
|
Channel,
|
|
&Name
|
|
);
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Notify the SAC that a channel was created
|
|
//
|
|
SAFE_SWPRINTF(
|
|
Size,
|
|
(OutputBuffer,
|
|
Message,
|
|
Name
|
|
));
|
|
|
|
FREE_POOL(&Name);
|
|
|
|
ConMgrEventMessage(OutputBuffer, FALSE);
|
|
|
|
} while ( FALSE );
|
|
|
|
FREE_POOL(&OutputBuffer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IO_MGR_EVENT_CHANNEL_CLOSE: {
|
|
|
|
PWCHAR OutputBuffer;
|
|
|
|
OutputBuffer = NULL;
|
|
|
|
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_2);
|
|
ASSERT_STATUS(Data, STATUS_INVALID_PARAMETER_3);
|
|
|
|
//
|
|
// We need to lock down current channel globals
|
|
// in case we need to close the current channel
|
|
// which will result in the resetting of the
|
|
// current channel to the SAC channel.
|
|
//
|
|
LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Perform the console mgrs close channel response
|
|
//
|
|
ConMgrChannelClose(Channel);
|
|
|
|
//
|
|
// get the channel close status message
|
|
// using the status sent in by the channel
|
|
// manager when it tried to close the channel.
|
|
//
|
|
Status = ConMgrGetChannelCloseMessage(
|
|
Channel,
|
|
*((NTSTATUS*)Data),
|
|
&OutputBuffer
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Display the message
|
|
//
|
|
ConMgrEventMessage(OutputBuffer, TRUE);
|
|
|
|
//
|
|
// cleanup
|
|
//
|
|
SAFE_FREE_POOL(&OutputBuffer);
|
|
|
|
}
|
|
|
|
//
|
|
// We are done with the current channel globals
|
|
//
|
|
UNLOCK_CURRENT_CHANNEL();
|
|
|
|
break;
|
|
}
|
|
|
|
case IO_MGR_EVENT_CHANNEL_WRITE:
|
|
|
|
Status = ConMgrChannelOWrite(
|
|
Channel,
|
|
(PSAC_CMD_WRITE_CHANNEL)Data
|
|
);
|
|
|
|
break;
|
|
|
|
case IO_MGR_EVENT_REGISTER_SAC_CMD_EVENT:
|
|
|
|
Status = ConMgrSimpleEventMessage(SAC_CMD_SERVICE_REGISTERED, FALSE) ?
|
|
STATUS_SUCCESS :
|
|
STATUS_UNSUCCESSFUL;
|
|
|
|
break;
|
|
|
|
case IO_MGR_EVENT_UNREGISTER_SAC_CMD_EVENT:
|
|
|
|
Status = ConMgrSimpleEventMessage(SAC_CMD_SERVICE_UNREGISTERED, FALSE) ?
|
|
STATUS_SUCCESS :
|
|
STATUS_UNSUCCESSFUL;
|
|
|
|
break;
|
|
|
|
case IO_MGR_EVENT_SHUTDOWN:
|
|
|
|
//
|
|
// We need to lock down current channel globals
|
|
// in case we need to close the current channel
|
|
// which will result in the resetting of the
|
|
// current channel to the SAC channel.
|
|
//
|
|
LOCK_CURRENT_CHANNEL();
|
|
|
|
//
|
|
// Send the event message to the SAC
|
|
//
|
|
Status = ConMgrSimpleEventMessage(SAC_SHUTDOWN, TRUE) ?
|
|
STATUS_SUCCESS :
|
|
STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// switch to the SAC channel if it is not the current channel
|
|
//
|
|
if (SacChannel != CurrentChannel) {
|
|
|
|
//
|
|
// switch directly to the SAC channel so the user
|
|
// can see that the system is shutting down
|
|
//
|
|
ConMgrResetCurrentChannel(TRUE);
|
|
|
|
}
|
|
|
|
//
|
|
// We are done with the current channel globals
|
|
//
|
|
UNLOCK_CURRENT_CHANNEL();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrWriteData(
|
|
IN PSAC_CHANNEL Channel,
|
|
IN PCUCHAR Buffer,
|
|
IN ULONG BufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Console Manager's IoMgrWriteData implementation.
|
|
|
|
This routine takes the channel's data buffer and
|
|
sends it to the headless port.
|
|
|
|
Note: The channel sending the data should only call this function
|
|
if they received a TRUE from the IoMgrIsWriteEnabled. In
|
|
the console manager's implementation, the channel only receives
|
|
TRUE if the current channel lock is held for this channel. This
|
|
is how the virtual terminal scheme works.
|
|
|
|
Arguments:
|
|
|
|
Channel - The channel sending the data
|
|
Buffer - The data to be written to the headless port
|
|
BufferSize - The size in bytes of the data to be written
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Attempts;
|
|
|
|
//
|
|
// default: we were successful
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// We don't use teh channel structure in this implementation
|
|
//
|
|
UNREFERENCED_PARAMETER(Channel);
|
|
|
|
//
|
|
// default: we have made 0 attempts
|
|
//
|
|
Attempts = 0;
|
|
|
|
do {
|
|
|
|
//
|
|
// We are making another attempt
|
|
//
|
|
Attempts++;
|
|
|
|
//
|
|
// Attempt to write
|
|
//
|
|
Status = HeadlessDispatch(
|
|
HeadlessCmdPutData,
|
|
(PUCHAR)Buffer,
|
|
BufferSize,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// If we have made enough attempts to write,
|
|
// then don't attempt again, just return status.
|
|
//
|
|
if (Attempts > MAX_HEADLESS_DISPATCH_ATTEMPTS) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the HeadlessDispatch was unsuccessful,
|
|
// this means it was still processing another command,
|
|
// so delay for a short period and try again.
|
|
//
|
|
if (Status == STATUS_UNSUCCESSFUL) {
|
|
|
|
LARGE_INTEGER WaitTime;
|
|
|
|
//
|
|
// Define a delay of 10 ms
|
|
//
|
|
WaitTime.QuadPart = Int32x32To64((LONG)1, -100000);
|
|
|
|
//
|
|
// Wait...
|
|
//
|
|
KeDelayExecutionThread(KernelMode, FALSE, &WaitTime);
|
|
|
|
}
|
|
|
|
} while ( Status == STATUS_UNSUCCESSFUL );
|
|
|
|
//
|
|
// Catch any HeadlessDispatch failures
|
|
//
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ConMgrFlushData(
|
|
IN PSAC_CHANNEL Channel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the Console Manager's IoMgrFlushData implementation.
|
|
|
|
This routine completes the write data operation for a channel's
|
|
previous write data calls. For instance, if they console manager
|
|
were packet based - that is, it formed packets when we wrote data,
|
|
this function would tell the console manager to complete the packet
|
|
and send it, rather than wait for more data.
|
|
|
|
Arguments:
|
|
|
|
Channel - The channel sending the data
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(Channel);
|
|
|
|
NOTHING;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
ConMgrIsSacChannel(
|
|
IN PSAC_CHANNEL Channel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the specified channel is a SAC channel
|
|
|
|
Arguments:
|
|
|
|
Channel - The channel to compare
|
|
|
|
Return Value:
|
|
|
|
TRUE - the channel is a SAC channel
|
|
FALSE - otherwise
|
|
|
|
--*/
|
|
{
|
|
return (Channel == SacChannel) ? TRUE : FALSE;
|
|
}
|
|
|