Initial code for the VT-100 terminal emulator that runs in the W32
[reactos.git] / posix / apps / csrterm / csrterm.c
1 /* $Id: csrterm.c,v 1.1 2002/03/17 22:15:39 ea Exp $
2 *
3 * PROJECT : ReactOS Operating System / POSIX+ Environment Subsystem
4 * DESCRIPTION: CSRTERM - A DEC VT-100 terminal emulator for the PSX subsystem
5 * DESCRIPTION: that runs in the Win32 subsystem.
6 * COPYRIGHT : Copyright (c) 2001-2002 Emanuele Aliberti
7 * LICENSE : GNU GPL v2
8 * DATE : 2001-05-05
9 * AUTHOR : Emanuele Aliberti <ea@iol.it>
10 * NOTE : This IS a Win32 program, but will fail if the PSX subsystem
11 * NOTE : is not installed. The PSX subsystem consists of two more
12 * NOTE : files: PSXSS.EXE, PSXDLL.DLL.
13 * WARNING : If you use this program under a real NT descendant, be
14 * WARNING : sure to have disabled the PSX subsystem.
15 * --------------------------------------------------------------------
16 *
17 * This software is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of the
20 * License, or (at your option) any later version.
21 *
22 * This software is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this software; see the file COPYING. If not, write
29 * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
30 * MA 02139, USA.
31 *
32 * --------------------------------------------------------------------
33 * 2002-03-16 EA Today it actually compiled.
34 */
35 #include <windows.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38
39 #define NTOS_MODE_USER
40 #include <ntos.h>
41 #include <psx/lpcproto.h>
42
43 #include "vt100.h"
44 #include "csrterm.h"
45
46 /*** OPTIONS *********************************************************/
47
48 #define PRIVATE static
49
50 #define INPUT_QUEUE_SIZE 32
51
52 #ifdef NDEBUG
53 #define TRACE
54 #else
55 #define TRACE OutputDebugString(__FUNCTION__)
56 #endif
57
58 /*** GLOBALS *********************************************************/
59
60 PRIVATE LPCSTR MyName = "CSRTERM";
61 PRIVATE CSRTERM_SESSION Session;
62
63 /*** PRIVATE FUNCTIONS ***********************************************/
64 VOID STDCALL Debug_Print (LPCSTR Format, ...)
65 {
66 CHAR Buffer [512];
67 va_list ArgumentPointer;
68
69 va_start(ArgumentPointer, Format);
70 vsprintf(Buffer, Format, ArgumentPointer);
71 va_end(ArgumentPointer);
72 OutputDebugStringA (Buffer);
73 }
74 /**********************************************************************
75 * OutPort/2 PRIVATE
76 *
77 * DESCRIPTION
78 * Notify to PSXSS that input data is ready by sending a
79 * software interrupt on the \POSIX+\SessionPort port.
80 */
81 PRIVATE DWORD STDCALL OutPort (PCHAR Buffer, ULONG Size)
82 {
83 NTSTATUS Status;
84 PSX_TERMINAL_READ TerminalRead;
85 TRACE;
86 if (Size > 0)
87 {
88 /* LPC */
89 TerminalRead.Header.MessageType = LPC_NEW_MESSAGE;
90 TerminalRead.PsxHeader.Context = PSX_CONNECTION_TYPE_TERMINAL;
91 TerminalRead.PsxHeader.Procedure = PSX_TERMINAL_INTERRUPT;
92 /* Terminal I/O */
93 TerminalRead.Size = Size;
94 RtlCopyMemory (TerminalRead.Buffer, Buffer, Size);
95 #if 0
96 Status = NtRequestWaitReplyPort (
97 Session.ServerPort.Handle,
98 & TerminalRead
99 /* FIXME */
100 );
101 #endif
102 if (!NT_SUCCESS(Status))
103 {
104 vtprintf ("%s: %s: NtRequestWaitReplyPort failed with %08x\n",
105 MyName, __FUNCTION__, Status);
106 return 0;
107 }
108 }
109 return Size;
110 }
111 /**********************************************************************
112 * ProcessConnectionRequest/1 PRIVATE
113 *
114 * DESCRIPTION
115 * Initialize our data for managing the control connection
116 * initiated by the PSXSS.EXE process.
117 */
118 PRIVATE NTSTATUS STDCALL ProcessConnectionRequest (PPSX_MAX_MESSAGE Request)
119 {
120 TRACE;
121 Session.SsLinkIsActive = TRUE;
122 return STATUS_SUCCESS;
123 }
124 /**********************************************************************
125 * ProcessRequest/1 PRIVATE
126 *
127 * DESCRIPTION
128 *
129 */
130 PRIVATE NTSTATUS STDCALL ProcessRequest (PPSX_MAX_MESSAGE Request)
131 {
132 TRACE;
133 /* TODO */
134 vtprintf("TEST VT-100\n");
135
136 return STATUS_SUCCESS;
137 }
138 /**********************************************************************
139 * PsxSessionPortListener/1 PRIVATE
140 *
141 * DESCRIPTION
142 * Manage messages from the PSXSS, that is LPC messages we get
143 * from the PSXSS process to our \POSIX+\Sessions\P<pid> port.
144 *
145 * NOTE
146 * This function is the thread 's entry point created in
147 * CreateSessionObiects().
148 */
149 PRIVATE DWORD STDCALL PsxSessionPortListener (LPVOID Arg)
150 {
151 NTSTATUS Status;
152 LPC_TYPE RequestType;
153 PSX_MAX_MESSAGE Request;
154 PPSX_MAX_MESSAGE Reply = NULL;
155 BOOL NullReply = FALSE;
156
157 TRACE;
158
159 while (TRUE)
160 {
161 Reply = NULL;
162 while (!NullReply)
163 {
164 Status = NtReplyWaitReceivePort (
165 Session.Port.Handle,
166 0,
167 (PLPC_MESSAGE) Reply,
168 (PLPC_MESSAGE) & Request
169 );
170 if (!NT_SUCCESS(Status))
171 {
172 break;
173 }
174 RequestType = PORT_MESSAGE_TYPE(Request);
175 switch (RequestType)
176 {
177 case LPC_CONNECTION_REQUEST:
178 ProcessConnectionRequest (& Request);
179 NullReply = TRUE;
180 continue;
181 case LPC_CLIENT_DIED:
182 case LPC_PORT_CLOSED:
183 case LPC_DEBUG_EVENT:
184 case LPC_ERROR_EVENT:
185 case LPC_EXCEPTION:
186 NullReply = TRUE;
187 continue;
188 default:
189 if (RequestType != LPC_REQUEST)
190 {
191 NullReply = TRUE;
192 continue;
193 }
194 }
195 Reply = & Request;
196 Reply->PsxHeader.Status = ProcessRequest (& Request);
197 NullReply = FALSE;
198 }
199 if ((STATUS_INVALID_HANDLE == Status) ||
200 (STATUS_OBJECT_TYPE_MISMATCH == Status))
201 {
202 break;
203 }
204 }
205 Session.SsLinkIsActive = FALSE;
206 TerminateThread (GetCurrentThread(), Status);
207 }
208 /**********************************************************************
209 * CreateSessionObiects/1 PRIVATE
210 *
211 * DESCRIPTION
212 * Create the session objects which are mananged by our side:
213 *
214 * \POSIX+\Sessions\P<pid>
215 * \POSIX+\Sessions\D<pid>
216 */
217 PRIVATE NTSTATUS STDCALL CreateSessionObjects (DWORD Pid)
218 {
219 NTSTATUS Status;
220 ULONG Id = 0;
221 OBJECT_ATTRIBUTES Oa;
222
223 TRACE;
224
225
226 /* Critical section */
227 Status = RtlInitializeCriticalSection (& Session.Lock);
228 if (!NT_SUCCESS(Status))
229 {
230 vtprintf (
231 "%s: %s: RtlInitializeCriticalSection failed with %08x\n",
232 MyName, __FUNCTION__, Status);
233 return Status;
234 }
235 /* Port and port management thread */
236 swprintf (
237 Session.Port.NameBuffer,
238 PSX_NS_SESSION_PORT_TEMPLATE,
239 PSX_NS_SUBSYSTEM_DIRECTORY_NAME,
240 PSX_NS_SESSION_DIRECTORY_NAME,
241 Pid
242 );
243 RtlInitUnicodeString (& Session.Port.Name, Session.Port.NameBuffer);
244 InitializeObjectAttributes (& Oa, & Session.Port.Name, 0, NULL, NULL);
245 Status = NtCreatePort (& Session.Port.Handle, & Oa, 0, 0, 0x10000);
246 if (!NT_SUCCESS(Status))
247 {
248 RtlDeleteCriticalSection (& Session.Lock);
249 vtprintf ("%s: %s: NtCreatePort failed with %08x\n",
250 MyName, __FUNCTION__, Status);
251 return Status;
252 }
253 Session.Port.Thread.Handle =
254 CreateThread (
255 NULL,
256 0,
257 PsxSessionPortListener,
258 0,
259 CREATE_SUSPENDED,
260 & Session.Port.Thread.Id
261 );
262 if ((HANDLE) NULL == Session.Port.Thread.Handle)
263 {
264 Status = (NTSTATUS) GetLastError();
265 NtClose (Session.Port.Handle);
266 RtlDeleteCriticalSection (& Session.Lock);
267 vtprintf ("%s: %s: CreateThread failed with %d\n",
268 MyName, __FUNCTION__, Status);
269 return Status;
270 }
271 /* Section */
272 swprintf (
273 Session.Section.NameBuffer,
274 PSX_NS_SESSION_DATA_TEMPLATE,
275 PSX_NS_SUBSYSTEM_DIRECTORY_NAME,
276 PSX_NS_SESSION_DIRECTORY_NAME,
277 Pid
278 );
279 RtlInitUnicodeString (& Session.Section.Name, Session.Section.NameBuffer);
280 InitializeObjectAttributes (& Oa, & Session.Section.Name, 0, 0, 0);
281 Status = NtCreateSection (
282 & Session.Section.Handle,
283 0, /* DesiredAccess */
284 & Oa,
285 NULL, /* SectionSize OPTIONAL */
286 PAGE_READWRITE, /* Protect 4 */
287 SEC_COMMIT, /* Attributes */
288 0 /* FileHandle: 0=pagefile.sys */
289 );
290 if (!NT_SUCCESS(Status))
291 {
292 NtClose (Session.Port.Handle);
293 NtTerminateThread (Session.Port.Thread.Handle, Status);
294 RtlDeleteCriticalSection (& Session.Lock);
295 vtprintf ("%s: %s: NtCreateSection failed with %08x\n",
296 MyName, __FUNCTION__, Status);
297 return Status;
298 }
299 Session.Section.BaseAddress = NULL;
300 Session.Section.ViewSize = 0;
301 Status = NtMapViewOfSection (
302 Session.Section.Handle,
303 NtCurrentProcess(),
304 & Session.Section.BaseAddress,
305 0, /* ZeroBits */
306 0, /* Commitsize */
307 0, /* SectionOffset */
308 & Session.Section.ViewSize,
309 ViewUnmap,
310 0, /* AllocationType */
311 PAGE_READWRITE /* Protect 4 */
312 );
313 if (!NT_SUCCESS(Status))
314 {
315 NtClose (Session.Port.Handle);
316 NtTerminateThread (Session.Port.Thread.Handle, Status);
317 NtClose (Session.Section.Handle);
318 RtlDeleteCriticalSection (& Session.Lock);
319 vtprintf ("%s: %s: NtMapViewOfSection failed with %08x\n",
320 MyName, __FUNCTION__, Status);
321 return Status;
322 }
323 return Status;
324 }
325
326 /**********************************************************************
327 * CreateTerminalToPsxChannel/0 PRIVATE
328 *
329 * DESCRIPTION
330 *
331 */
332 PRIVATE NTSTATUS STDCALL CreateTerminalToPsxChannel (VOID)
333 {
334 PSX_CONNECT_PORT_DATA ConnectData;
335 ULONG ConnectDataLength = sizeof ConnectData;
336 SECURITY_QUALITY_OF_SERVICE Sqos;
337 NTSTATUS Status;
338
339 TRACE;
340
341
342 /*
343 * Initialize the connection data object before
344 * calling PSXSS.
345 */
346 ConnectData.ConnectionType = PSX_CONNECTION_TYPE_TERMINAL;
347 ConnectData.Version = PSX_LPC_PROTOCOL_VERSION;
348 /*
349 * Try connecting to \POSIX+\SessionPort.
350 */
351 Status = NtConnectPort (
352 & Session.ServerPort.Handle,
353 & Session.ServerPort.Name,
354 & Sqos,
355 NULL,
356 NULL,
357 0,
358 & ConnectData,
359 & ConnectDataLength
360 );
361 if (STATUS_SUCCESS != Status)
362 {
363 vtprintf ("%s: %s: NtConnectPort failed with %08x\n",
364 MyName, __FUNCTION__, Status);
365 return Status;
366 }
367 Session.Identifier = ConnectData.PortIdentifier;
368 return STATUS_SUCCESS;
369 }
370
371 /**********************************************************************
372 * InitializeSsIoChannel PRIVATE
373 *
374 * DESCRIPTION
375 * Create our objects in the system name space
376 * (CreateSessionObjects) and then connect to the session port
377 * (CreateControChannel).
378 */
379 PRIVATE NTSTATUS STDCALL InitializeSsIoChannel (VOID)
380 {
381 NTSTATUS Status = STATUS_SUCCESS;
382 DWORD Pid = GetCurrentProcessId();
383
384 TRACE;
385
386
387 Status = CreateSessionObjects (Pid);
388 if (STATUS_SUCCESS != Status)
389 {
390 vtprintf ("%s: %s: CreateSessionObjects failed with %08x\n",
391 MyName, __FUNCTION__, Status);
392 return Status;
393 }
394 Status = CreateTerminalToPsxChannel ();
395 if (STATUS_SUCCESS != Status)
396 {
397 vtprintf ("%s: %s: CreateTerminalToPsxChannel failed with %08x\n",
398 MyName, __FUNCTION__, Status);
399 return Status;
400 }
401 return STATUS_SUCCESS;
402 }
403 /**********************************************************************
404 * PsxCreateLeaderProcess/1 PRIVATE
405 *
406 * DESCRIPTION
407 * Create a new PSXSS process.
408 */
409 PRIVATE NTSTATUS STDCALL PsxCreateLeaderProcess (char * Command)
410 {
411
412 TRACE;
413
414 if (NULL == Command)
415 {
416 Command = "/bin/sh";
417 }
418 /* TODO: request PSXSS to init the process slot */
419 return STATUS_NOT_IMPLEMENTED;
420 }
421 /**********************************************************************
422 * PrintInformationProcess/0
423 *
424 * DESCRIPTION
425 */
426 PRIVATE VOID STDCALL PrintInformationProcess (VOID)
427 {
428
429 TRACE;
430
431 vtputs ("Leader:");
432 vtprintf (" UniqueProcess %08x\n", Session.Client.UniqueProcess);
433 vtprintf (" UniqueThread %08x\n", Session.Client.UniqueThread);
434 }
435 /**********************************************************************
436 * PostMortem/0
437 *
438 * DESCRIPTION
439 */
440 PRIVATE INT STDCALL PostMortem (VOID)
441 {
442 DWORD ExitCode;
443
444 TRACE;
445
446
447 PrintInformationProcess ();
448 if (TRUE == GetExitCodeProcess (Session.Client.UniqueProcess, & ExitCode))
449 {
450 vtprintf (" ExitCode %d\n", ExitCode);
451 }
452 return 0;
453 }
454 /**********************************************************************
455 * InputTerminalEmulator/0
456 *
457 * DESCRIPTION
458 * Process user terminal input.
459 *
460 * NOTE
461 * This code is run in the main thread.
462 */
463 PRIVATE BOOL STDCALL InputTerminalEmulator (VOID)
464 {
465 HANDLE StandardInput;
466 INPUT_RECORD InputRecord [INPUT_QUEUE_SIZE];
467 DWORD NumberOfEventsRead = 0;
468 INT CurrentEvent;
469
470
471 TRACE;
472
473 StandardInput = GetStdHandle (STD_INPUT_HANDLE);
474 if (INVALID_HANDLE_VALUE == StandardInput)
475 {
476 return FALSE;
477 }
478 while ((TRUE == Session.SsLinkIsActive) &&
479 ReadConsoleInput (
480 StandardInput,
481 InputRecord,
482 (sizeof InputRecord) / sizeof (INPUT_RECORD),
483 & NumberOfEventsRead
484 ))
485 {
486 for ( CurrentEvent = 0;
487 (CurrentEvent < NumberOfEventsRead);
488 CurrentEvent ++
489 )
490 {
491 switch (InputRecord [CurrentEvent].EventType)
492 {
493 case KEY_EVENT:
494 OutPort (& InputRecord [CurrentEvent].Event.KeyEvent.uChar.AsciiChar, 1);
495 break;
496 case MOUSE_EVENT:
497 /* TODO: send a sequence of move cursor codes */
498 /* InputRecord [CurrentEvent].Event.MouseEvent; */
499 break;
500 case WINDOW_BUFFER_SIZE_EVENT:
501 /* TODO: send a SIGWINCH signal to the leader process. */
502 /* InputRecord [CurrentEvent].Event.WindowBufferSizeEvent.dwSize; */
503 break;
504 /* Next events should be ignored. */
505 case MENU_EVENT:
506 vtprintf ("%s: %s: MENU_EVENT received from CSRSS\n", MyName, __FUNCTION__);
507 case FOCUS_EVENT:
508 vtprintf ("%s: %s: FOCUS_EVENT received from CSRSS\n", MyName, __FUNCTION__);
509 break;
510 }
511 }
512 NumberOfEventsRead = 0;
513 }
514 return TRUE;
515 }
516 /**********************************************************************
517 * Startup/1
518 *
519 * DESCRIPTION
520 * Initialize the program.
521 */
522 PRIVATE VOID STDCALL Startup (LPSTR Command)
523 {
524 NTSTATUS Status;
525 DWORD ThreadId;
526
527
528 TRACE;
529
530 /* PSX process info */
531 Session.Client.UniqueProcess = INVALID_HANDLE_VALUE;
532 Session.Client.UniqueThread = INVALID_HANDLE_VALUE;
533 /* Initialize the VT-100 emulator */
534 vtInitVT100 ();
535 /* Connect to PSXSS */
536 Status = InitializeSsIoChannel ();
537 if (!NT_SUCCESS(Status))
538 {
539 vtprintf ("%s: failed to connect to PSXSS (Status=%08x)!\n",
540 MyName, Status);
541 exit (EXIT_FAILURE);
542 }
543 /* Create the leading process for this session */
544 Status = PsxCreateLeaderProcess (Command);
545 if (!NT_SUCCESS(Status))
546 {
547 vtprintf ("%s: failed to create the PSX process (Status=%08x)!\n",
548 MyName, Status);
549 exit (EXIT_FAILURE);
550 }
551 }
552 /**********************************************************************
553 * Shutdown/0 PRIVATE
554 *
555 * DESCRIPTION
556 * Shutdown the program.
557 */
558 PRIVATE INT STDCALL Shutdown (VOID)
559 {
560
561 TRACE;
562
563 /* TODO: try exiting cleanly: close any open resource */
564 /* TODO: notify PSXSS the session is terminating */
565 RtlDeleteCriticalSection (& Session.Lock);
566 return PostMortem ();
567 }
568 /**********************************************************************
569 *
570 * ENTRY POINT PUBLIC
571 *
572 *********************************************************************/
573 int main (int argc, char * argv [])
574 {
575
576 TRACE;
577
578 Startup (argv[1]); /* Initialization */
579 InputTerminalEmulator (); /* Process user input */
580 return Shutdown ();
581 }
582 /* EOF */