reactos/posix/apps/posixw32/posixw32.c

650 lines
18 KiB
C
Raw Normal View History

/* $Id$
*
* PROJECT : ReactOS Operating System / POSIX+ Environment Subsystem
* DESCRIPTION: POSIXW32 - A DEC VT-100 terminal emulator for the PSX subsystem
* DESCRIPTION: that runs in the Win32 subsystem.
* COPYRIGHT : Copyright (c) 2001-2002 Emanuele Aliberti
* LICENSE : GNU GPL v2
* DATE : 2001-05-05
* AUTHOR : Emanuele Aliberti <ea@iol.it>
* NOTE : This IS a Win32 program, but will fail if the PSX subsystem
* NOTE : is not installed. The PSX subsystem consists of two more
* NOTE : files: PSXSS.EXE, PSXDLL.DLL.
* WARNING : If you use this program under a real NT descendant, be
* WARNING : sure to have disabled the PSX subsystem.
* --------------------------------------------------------------------
*
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write
* to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
* MA 02139, USA.
*
* --------------------------------------------------------------------
* 2002-03-16 EA Today it actually compiled.
* 2002-06-08 EA Renamed (old name was CSRTERM)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define NTOS_MODE_USER
#include <ntos.h>
#include <sm/helper.h>
#include <psx/lpcproto.h>
#include "vt100.h"
#include "posixw32.h"
/*** OPTIONS *********************************************************/
#define PRIVATE static
#define INPUT_QUEUE_SIZE 32
#ifdef NDEBUG
#define TRACE
#else
//#define TRACE OutputDebugString(__FUNCTION__)
#define TRACE vtprintf("%s\n",__FUNCTION__)
#endif
/*** GLOBALS *********************************************************/
PRIVATE LPCSTR MyName = "POSIXW32";
PRIVATE CSRTERM_SESSION Session =
{
0, //Identifier
{ //ServerPort
{0,0,NULL},
L"\\"PSX_NS_SUBSYSTEM_DIRECTORY_NAME"\\"PSX_NS_SESSIONAPI_PORT_NAME,
INVALID_HANDLE_VALUE
}
};
/*** PRIVATE FUNCTIONS ***********************************************/
VOID STDCALL Debug_Print (LPCSTR Format, ...)
{
CHAR Buffer [512];
va_list ArgumentPointer;
va_start(ArgumentPointer, Format);
vsprintf(Buffer, Format, ArgumentPointer);
va_end(ArgumentPointer);
OutputDebugStringA (Buffer);
}
/**********************************************************************
* OutPort/2 PRIVATE
*
* DESCRIPTION
* Notify to PSXSS that input data is ready by sending a
* software interrupt on the \POSIX+\SessionPort port.
*/
PRIVATE DWORD STDCALL OutPort (PCHAR Buffer, ULONG Size)
{
NTSTATUS Status;
PSX_TERMINAL_READ TerminalRead;
TRACE;
if (Size > 0)
{
/* LPC */
TerminalRead.Header.MessageType = LPC_NEW_MESSAGE;
TerminalRead.PsxHeader.Context = PSX_CONNECTION_TYPE_TERMINAL;
TerminalRead.PsxHeader.Procedure = PSX_TERMINAL_INTERRUPT;
/* Terminal I/O */
TerminalRead.Size = Size;
#if 0
RtlCopyMemory (TerminalRead.Buffer, Buffer, Size);
Status = NtRequestWaitReplyPort (
Session.ServerPort.Handle,
& TerminalRead
/* FIXME */
);
#endif
if (!NT_SUCCESS(Status))
{
vtprintf ("%s: %s: NtRequestWaitReplyPort failed with %08x\n",
MyName, __FUNCTION__, Status);
return 0;
}
}
return Size;
}
/**********************************************************************
* ProcessConnectionRequest/1 PRIVATE
*
* DESCRIPTION
* Initialize our data for managing the control connection
* initiated by the PSXSS.EXE process.
*/
PRIVATE NTSTATUS STDCALL ProcessConnectionRequest (PLPC_MAX_MESSAGE Request)
{
PPSX_CONNECT_PORT_DATA ConnectData = (PPSX_CONNECT_PORT_DATA) & Request->Data;
TRACE;
if (PSX_CONNECTION_TYPE_SERVER != ConnectData->ConnectionType)
{
return STATUS_UNSUCCESSFUL;
}
if (PSX_LPC_PROTOCOL_VERSION != ConnectData->Version)
{
return STATUS_UNSUCCESSFUL;
}
Session.SsLinkIsActive = TRUE;
return STATUS_SUCCESS;
}
/**********************************************************************
* ProcessRequest/1 PRIVATE
*
* DESCRIPTION
*
*/
PRIVATE NTSTATUS STDCALL ProcessRequest (PPSX_MAX_MESSAGE Request)
{
TRACE;
/* TODO */
vtprintf("TEST VT-100\n");
return STATUS_SUCCESS;
}
/**********************************************************************
* PsxSessionPortListener/1 PRIVATE
*
* DESCRIPTION
* Manage messages from the PSXSS, that is LPC messages we get
* from the PSXSS process to our \POSIX+\Sessions\P<pid> port.
*
* NOTE
* This function is the thread 's entry point created in
* CreateSessionObiects().
*/
PRIVATE DWORD STDCALL PsxSessionPortListener (LPVOID Arg)
{
NTSTATUS Status;
LPC_TYPE RequestType;
PSX_MAX_MESSAGE Request;
PPSX_MAX_MESSAGE Reply = NULL;
BOOL NullReply = FALSE;
TRACE;
while (TRUE)
{
Reply = NULL;
NullReply = FALSE;
while (!NullReply)
{
Status = NtReplyWaitReceivePort (
Session.Port.Handle,
0,
(PLPC_MESSAGE) Reply,
(PLPC_MESSAGE) & Request
);
if (!NT_SUCCESS(Status))
{
break;
}
RequestType = PORT_MESSAGE_TYPE(Request);
switch (RequestType)
{
case LPC_CONNECTION_REQUEST:
ProcessConnectionRequest ((PLPC_MAX_MESSAGE) & Request);
NullReply = TRUE;
continue;
case LPC_CLIENT_DIED:
case LPC_PORT_CLOSED:
case LPC_DEBUG_EVENT:
case LPC_ERROR_EVENT:
case LPC_EXCEPTION:
NullReply = TRUE;
continue;
default:
if (RequestType != LPC_REQUEST)
{
NullReply = TRUE;
continue;
}
}
Reply = & Request;
Reply->PsxHeader.Status = ProcessRequest (& Request);
NullReply = FALSE;
}
if ((STATUS_INVALID_HANDLE == Status) ||
(STATUS_OBJECT_TYPE_MISMATCH == Status))
{
break;
}
}
Session.SsLinkIsActive = FALSE;
TerminateThread (GetCurrentThread(), Status);
}
/**********************************************************************
* CreateSessionObiects/1 PRIVATE
*
* DESCRIPTION
* Create the session objects which are mananged by our side:
*
* \POSIX+\Sessions\P<pid>
* \POSIX+\Sessions\D<pid>
*/
PRIVATE NTSTATUS STDCALL CreateSessionObjects (DWORD Pid)
{
NTSTATUS Status;
ULONG Id = 0;
OBJECT_ATTRIBUTES Oa;
LARGE_INTEGER SectionSize = {PSX_TERMINAL_SECTION_SIZE,0};
TRACE;
/* Critical section */
Status = RtlInitializeCriticalSection (& Session.Lock);
if (!NT_SUCCESS(Status))
{
vtprintf (
"%s: %s: RtlInitializeCriticalSection failed with %08x\n",
MyName, __FUNCTION__, Status);
return Status;
}
/* Port and port management thread */
swprintf (
Session.Port.NameBuffer,
PSX_NS_SESSION_PORT_TEMPLATE,
PSX_NS_SUBSYSTEM_DIRECTORY_NAME,
PSX_NS_SESSION_DIRECTORY_NAME,
Pid
);
OutputDebugStringW(Session.Port.NameBuffer);
RtlInitUnicodeString (& Session.Port.Name, Session.Port.NameBuffer);
InitializeObjectAttributes (& Oa, & Session.Port.Name, 0, NULL, NULL);
Status = NtCreatePort (& Session.Port.Handle, & Oa, 0, 0, 0x10000);
if (!NT_SUCCESS(Status))
{
RtlDeleteCriticalSection (& Session.Lock);
vtprintf ("%s: %s: NtCreatePort failed with %08x\n",
MyName, __FUNCTION__, Status);
return Status;
}
Session.Port.Thread.Handle =
CreateThread (
NULL,
0,
PsxSessionPortListener,
0,
CREATE_SUSPENDED,
& Session.Port.Thread.Id
);
if ((HANDLE) NULL == Session.Port.Thread.Handle)
{
Status = (NTSTATUS) GetLastError();
NtClose (Session.Port.Handle);
RtlDeleteCriticalSection (& Session.Lock);
vtprintf ("%s: %s: CreateThread failed with %d\n",
MyName, __FUNCTION__, Status);
return Status;
}
/* Section */
swprintf (
Session.Section.NameBuffer,
PSX_NS_SESSION_DATA_TEMPLATE,
PSX_NS_SUBSYSTEM_DIRECTORY_NAME,
PSX_NS_SESSION_DIRECTORY_NAME,
Pid
);
OutputDebugStringW(Session.Section.NameBuffer);
RtlInitUnicodeString (& Session.Section.Name, Session.Section.NameBuffer);
InitializeObjectAttributes (& Oa, & Session.Section.Name, 0, 0, 0);
Status = NtCreateSection (
& Session.Section.Handle,
SECTION_ALL_ACCESS, /* DesiredAccess */
& Oa,
& SectionSize,
PAGE_READWRITE, /* Protect 4 */
SEC_COMMIT, /* Attributes */
0 /* FileHandle: 0=pagefile.sys */
);
if (!NT_SUCCESS(Status))
{
NtClose (Session.Port.Handle);
NtTerminateThread (Session.Port.Thread.Handle, Status);
RtlDeleteCriticalSection (& Session.Lock);
vtprintf ("%s: %s: NtCreateSection failed with %08x\n",
MyName, __FUNCTION__, Status);
return Status;
}
Session.Section.BaseAddress = NULL;
Session.Section.ViewSize = SectionSize.u.LowPart;
Status = NtMapViewOfSection (
Session.Section.Handle,
NtCurrentProcess(),
& Session.Section.BaseAddress,
0, /* ZeroBits */
0, /* Commitsize */
0, /* SectionOffset */
& Session.Section.ViewSize,
ViewUnmap,
0, /* AllocationType */
PAGE_READWRITE /* Protect 4 */
);
if (!NT_SUCCESS(Status))
{
NtClose (Session.Port.Handle);
NtTerminateThread (Session.Port.Thread.Handle, Status);
NtClose (Session.Section.Handle);
RtlDeleteCriticalSection (& Session.Lock);
vtprintf ("%s: %s: NtMapViewOfSection failed with %08x\n",
MyName, __FUNCTION__, Status);
return Status;
}
return Status;
}
/**********************************************************************
* RunPsxSs/0
*
* DESCRIPTION
* This function is called only when initializing the session
* with PSXSS fails. We assume that it failed because the
* subsystem, being optional, is not running, therefore we
* ask the SM to start it up.
*/
PRIVATE NTSTATUS RunPsxSs(VOID)
{
NTSTATUS Status;
HANDLE SmApiPort;
UNICODE_STRING Program;
RtlInitUnicodeString (& Program, L"POSIX");
Status = SmConnectApiPort (NULL, 0, 0, & SmApiPort);
if(!NT_SUCCESS(Status))
{
return Status;
}
Status = SmExecuteProgram (SmApiPort, & Program);
NtClose (SmApiPort);
return Status;
}
/**********************************************************************
* CreateTerminalToPsxChannel/0 PRIVATE
*
* DESCRIPTION
*
*/
PRIVATE NTSTATUS STDCALL CreateTerminalToPsxChannel (VOID)
{
PSX_CONNECT_PORT_DATA ConnectData;
ULONG ConnectDataLength = sizeof ConnectData;
SECURITY_QUALITY_OF_SERVICE Sqos;
NTSTATUS Status;
LONG Count = 2;
TRACE;
/*
* Initialize the connection data object before
* calling PSXSS.
*/
ConnectData.ConnectionType = PSX_CONNECTION_TYPE_TERMINAL;
ConnectData.Version = PSX_LPC_PROTOCOL_VERSION;
/*
* Try connecting to \POSIX+\SessionPort.
*/
RtlInitUnicodeString (& Session.ServerPort.Name, Session.ServerPort.NameBuffer);
while (Count--)
{
OutputDebugStringW(Session.ServerPort.Name.Buffer);
Status = NtConnectPort (
& Session.ServerPort.Handle,
& Session.ServerPort.Name,
& Sqos,
NULL,
NULL,
0,
& ConnectData,
& ConnectDataLength
);
if (STATUS_SUCCESS != Status)
{
if(Count)
{
vtprintf("%s: %s: asking SM to start PSXSS...\n",MyName,__FUNCTION__);
RunPsxSs();
continue;
}
vtprintf ("%s: %s: NtConnectPort failed with %08x\n",
MyName, __FUNCTION__, Status);
return Status;
}
break;
}
Session.Identifier = ConnectData.PortIdentifier;
return STATUS_SUCCESS;
}
/**********************************************************************
* InitializeSsIoChannel PRIVATE
*
* DESCRIPTION
* Connect to the session port (CreateControChannel) of the PSX
* subsystem. If that succeeds, create our objects in the system
* name space (CreateSessionObjects).
*/
PRIVATE NTSTATUS STDCALL InitializeSsIoChannel (VOID)
{
NTSTATUS Status = STATUS_SUCCESS;
DWORD Pid = GetCurrentProcessId();
TRACE;
Status = CreateTerminalToPsxChannel ();
if (STATUS_SUCCESS != Status)
{
vtprintf ("%s: %s: CreateTerminalToPsxChannel failed with %08x\n",
MyName, __FUNCTION__, Status);
return Status;
}
Status = CreateSessionObjects (Pid);
if (STATUS_SUCCESS != Status)
{
vtprintf ("%s: %s: CreateSessionObjects failed with %08x\n",
MyName, __FUNCTION__, Status);
return Status;
}
return STATUS_SUCCESS;
}
/**********************************************************************
* PsxCreateLeaderProcess/1 PRIVATE
*
* DESCRIPTION
* Create a new PSXSS process. We are running under Win32 server
* and can not run directly a PSX image: we make SM run it for us.
*
*/
PRIVATE NTSTATUS STDCALL PsxCreateLeaderProcess (char * Command)
{
NTSTATUS Status;
TRACE;
if (NULL == Command)
{
Command = "sh";
}
/* TODO: request SM to create the process: LPC:SmExecPgm() */
vtprintf ("%s: %s: calling SMSS not implemented!", MyName, __FUNCTION__);
return STATUS_SUCCESS;
}
/**********************************************************************
* PrintInformationProcess/0
*
* DESCRIPTION
*/
PRIVATE VOID STDCALL PrintInformationProcess (VOID)
{
TRACE;
vtputs ("Leader:");
vtprintf (" UniqueProcess %08x\n", Session.Client.UniqueProcess);
vtprintf (" UniqueThread %08x\n", Session.Client.UniqueThread);
}
/**********************************************************************
* PostMortem/0
*
* DESCRIPTION
*/
PRIVATE INT STDCALL PostMortem (VOID)
{
DWORD ExitCode;
TRACE;
PrintInformationProcess ();
if (TRUE == GetExitCodeProcess (Session.Client.UniqueProcess, & ExitCode))
{
vtprintf (" ExitCode %d\n", ExitCode);
}
return 0;
}
/**********************************************************************
* InputTerminalEmulator/0
*
* DESCRIPTION
* Process user terminal input.
*
* NOTE
* This code is run in the main thread.
*/
PRIVATE BOOL STDCALL InputTerminalEmulator (VOID)
{
HANDLE StandardInput;
INPUT_RECORD InputRecord [INPUT_QUEUE_SIZE];
DWORD NumberOfEventsRead = 0;
INT CurrentEvent;
TRACE;
StandardInput = GetStdHandle (STD_INPUT_HANDLE);
if (INVALID_HANDLE_VALUE == StandardInput)
{
return FALSE;
}
while ((TRUE == Session.SsLinkIsActive) &&
ReadConsoleInput (
StandardInput,
InputRecord,
(sizeof InputRecord) / sizeof (INPUT_RECORD),
& NumberOfEventsRead
))
{
for ( CurrentEvent = 0;
(CurrentEvent < NumberOfEventsRead);
CurrentEvent ++
)
{
switch (InputRecord [CurrentEvent].EventType)
{
case KEY_EVENT:
OutPort (& InputRecord [CurrentEvent].Event.KeyEvent.uChar.AsciiChar, 1);
break;
case MOUSE_EVENT:
/* TODO: send a sequence of move cursor codes */
/* InputRecord [CurrentEvent].Event.MouseEvent; */
break;
case WINDOW_BUFFER_SIZE_EVENT:
/* TODO: send a SIGWINCH signal to the leader process. */
/* InputRecord [CurrentEvent].Event.WindowBufferSizeEvent.dwSize; */
break;
/* Next events should be ignored. */
case MENU_EVENT:
vtprintf ("%s: %s: MENU_EVENT received from CSRSS\n", MyName, __FUNCTION__);
case FOCUS_EVENT:
vtprintf ("%s: %s: FOCUS_EVENT received from CSRSS\n", MyName, __FUNCTION__);
break;
}
}
NumberOfEventsRead = 0;
}
return TRUE;
}
/**********************************************************************
* Startup/1
*
* DESCRIPTION
* Initialize the program.
*/
PRIVATE VOID STDCALL Startup (LPSTR Command)
{
NTSTATUS Status;
DWORD ThreadId;
TRACE;
/* PSX process info */
Session.Client.UniqueProcess = INVALID_HANDLE_VALUE;
Session.Client.UniqueThread = INVALID_HANDLE_VALUE;
/* Initialize the VT-100 emulator */
vtInitVT100 ();
/* Connect to PSXSS */
Status = InitializeSsIoChannel ();
if (!NT_SUCCESS(Status))
{
vtprintf ("%s: failed to connect to PSXSS (Status=%08x)!\n",
MyName, Status);
exit (EXIT_FAILURE);
}
/* Create the leading process for this session */
Status = PsxCreateLeaderProcess (Command);
if (!NT_SUCCESS(Status))
{
vtprintf ("%s: failed to create the PSX process (Status=%08x)!\n",
MyName, Status);
exit (EXIT_FAILURE);
}
}
/**********************************************************************
* Shutdown/0 PRIVATE
*
* DESCRIPTION
* Shutdown the program.
*/
PRIVATE INT STDCALL Shutdown (VOID)
{
TRACE;
/* TODO: try exiting cleanly: close any open resource */
/* TODO: notify PSXSS the session is terminating */
RtlDeleteCriticalSection (& Session.Lock);
return PostMortem ();
}
/**********************************************************************
*
* ENTRY POINT PUBLIC
*
*********************************************************************/
int main (int argc, char * argv [])
{
TRACE;
Startup (argv[1]); /* Initialization */
InputTerminalEmulator (); /* Process user input */
return Shutdown ();
}
/* EOF */