aa34e96988722e74d99ce0025a65548b4564b913
[reactos.git] / posix / apps / csrterm / csrterm.c
1 /* $Id: csrterm.c,v 1.4 2002/04/10 21:12:41 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 0, //Identifier
64 { //ServerPort
65 {0,0,NULL},
66 L"\\"PSX_NS_SUBSYSTEM_DIRECTORY_NAME"\\"PSX_NS_SESSIONAPI_PORT_NAME,
67 INVALID_HANDLE_VALUE
68 }
69 };
70
71 /*** PRIVATE FUNCTIONS ***********************************************/
72 VOID STDCALL Debug_Print (LPCSTR Format, ...)
73 {
74 CHAR Buffer [512];
75 va_list ArgumentPointer;
76
77 va_start(ArgumentPointer, Format);
78 vsprintf(Buffer, Format, ArgumentPointer);
79 va_end(ArgumentPointer);
80 OutputDebugStringA (Buffer);
81 }
82 /**********************************************************************
83 * OutPort/2 PRIVATE
84 *
85 * DESCRIPTION
86 * Notify to PSXSS that input data is ready by sending a
87 * software interrupt on the \POSIX+\SessionPort port.
88 */
89 PRIVATE DWORD STDCALL OutPort (PCHAR Buffer, ULONG Size)
90 {
91 NTSTATUS Status;
92 PSX_TERMINAL_READ TerminalRead;
93 TRACE;
94 if (Size > 0)
95 {
96 /* LPC */
97 TerminalRead.Header.MessageType = LPC_NEW_MESSAGE;
98 TerminalRead.PsxHeader.Context = PSX_CONNECTION_TYPE_TERMINAL;
99 TerminalRead.PsxHeader.Procedure = PSX_TERMINAL_INTERRUPT;
100 /* Terminal I/O */
101 TerminalRead.Size = Size;
102 #if 0
103 RtlCopyMemory (TerminalRead.Buffer, Buffer, Size);
104 Status = NtRequestWaitReplyPort (
105 Session.ServerPort.Handle,
106 & TerminalRead
107 /* FIXME */
108 );
109 #endif
110 if (!NT_SUCCESS(Status))
111 {
112 vtprintf ("%s: %s: NtRequestWaitReplyPort failed with %08x\n",
113 MyName, __FUNCTION__, Status);
114 return 0;
115 }
116 }
117 return Size;
118 }
119 /**********************************************************************
120 * ProcessConnectionRequest/1 PRIVATE
121 *
122 * DESCRIPTION
123 * Initialize our data for managing the control connection
124 * initiated by the PSXSS.EXE process.
125 */
126 PRIVATE NTSTATUS STDCALL ProcessConnectionRequest (PLPC_MAX_MESSAGE Request)
127 {
128 PPSX_CONNECT_PORT_DATA ConnectData = (PPSX_CONNECT_PORT_DATA) & Request->Data;
129
130 TRACE;
131 if (PSX_CONNECTION_TYPE_SERVER != ConnectData->ConnectionType)
132 {
133
134 return STATUS_UNSUCCESSFUL;
135 }
136 if (PSX_LPC_PROTOCOL_VERSION != ConnectData->Version)
137 {
138
139 return STATUS_UNSUCCESSFUL;
140 }
141 Session.SsLinkIsActive = TRUE;
142 return STATUS_SUCCESS;
143 }
144 /**********************************************************************
145 * ProcessRequest/1 PRIVATE
146 *
147 * DESCRIPTION
148 *
149 */
150 PRIVATE NTSTATUS STDCALL ProcessRequest (PPSX_MAX_MESSAGE Request)
151 {
152 TRACE;
153 /* TODO */
154 vtprintf("TEST VT-100\n");
155
156 return STATUS_SUCCESS;
157 }
158 /**********************************************************************
159 * PsxSessionPortListener/1 PRIVATE
160 *
161 * DESCRIPTION
162 * Manage messages from the PSXSS, that is LPC messages we get
163 * from the PSXSS process to our \POSIX+\Sessions\P<pid> port.
164 *
165 * NOTE
166 * This function is the thread 's entry point created in
167 * CreateSessionObiects().
168 */
169 PRIVATE DWORD STDCALL PsxSessionPortListener (LPVOID Arg)
170 {
171 NTSTATUS Status;
172 LPC_TYPE RequestType;
173 PSX_MAX_MESSAGE Request;
174 PPSX_MAX_MESSAGE Reply = NULL;
175 BOOL NullReply = FALSE;
176
177 TRACE;
178
179 while (TRUE)
180 {
181 Reply = NULL;
182 NullReply = FALSE;
183 while (!NullReply)
184 {
185 Status = NtReplyWaitReceivePort (
186 Session.Port.Handle,
187 0,
188 (PLPC_MESSAGE) Reply,
189 (PLPC_MESSAGE) & Request
190 );
191 if (!NT_SUCCESS(Status))
192 {
193 break;
194 }
195 RequestType = PORT_MESSAGE_TYPE(Request);
196 switch (RequestType)
197 {
198 case LPC_CONNECTION_REQUEST:
199 ProcessConnectionRequest ((PLPC_MAX_MESSAGE) & Request);
200 NullReply = TRUE;
201 continue;
202 case LPC_CLIENT_DIED:
203 case LPC_PORT_CLOSED:
204 case LPC_DEBUG_EVENT:
205 case LPC_ERROR_EVENT:
206 case LPC_EXCEPTION:
207 NullReply = TRUE;
208 continue;
209 default:
210 if (RequestType != LPC_REQUEST)
211 {
212 NullReply = TRUE;
213 continue;
214 }
215 }
216 Reply = & Request;
217 Reply->PsxHeader.Status = ProcessRequest (& Request);
218 NullReply = FALSE;
219 }
220 if ((STATUS_INVALID_HANDLE == Status) ||
221 (STATUS_OBJECT_TYPE_MISMATCH == Status))
222 {
223 break;
224 }
225 }
226 Session.SsLinkIsActive = FALSE;
227 TerminateThread (GetCurrentThread(), Status);
228 }
229 /**********************************************************************
230 * CreateSessionObiects/1 PRIVATE
231 *
232 * DESCRIPTION
233 * Create the session objects which are mananged by our side:
234 *
235 * \POSIX+\Sessions\P<pid>
236 * \POSIX+\Sessions\D<pid>
237 */
238 PRIVATE NTSTATUS STDCALL CreateSessionObjects (DWORD Pid)
239 {
240 NTSTATUS Status;
241 ULONG Id = 0;
242 OBJECT_ATTRIBUTES Oa;
243 LARGE_INTEGER SectionSize = {PSX_TERMINAL_SECTION_SIZE,0};
244
245 TRACE;
246
247
248 /* Critical section */
249 Status = RtlInitializeCriticalSection (& Session.Lock);
250 if (!NT_SUCCESS(Status))
251 {
252 vtprintf (
253 "%s: %s: RtlInitializeCriticalSection failed with %08x\n",
254 MyName, __FUNCTION__, Status);
255 return Status;
256 }
257 /* Port and port management thread */
258 swprintf (
259 Session.Port.NameBuffer,
260 PSX_NS_SESSION_PORT_TEMPLATE,
261 PSX_NS_SUBSYSTEM_DIRECTORY_NAME,
262 PSX_NS_SESSION_DIRECTORY_NAME,
263 Pid
264 );
265 OutputDebugStringW(Session.Port.NameBuffer);
266 RtlInitUnicodeString (& Session.Port.Name, Session.Port.NameBuffer);
267 InitializeObjectAttributes (& Oa, & Session.Port.Name, 0, NULL, NULL);
268 Status = NtCreatePort (& Session.Port.Handle, & Oa, 0, 0, 0x10000);
269 if (!NT_SUCCESS(Status))
270 {
271 RtlDeleteCriticalSection (& Session.Lock);
272 vtprintf ("%s: %s: NtCreatePort failed with %08x\n",
273 MyName, __FUNCTION__, Status);
274 return Status;
275 }
276 Session.Port.Thread.Handle =
277 CreateThread (
278 NULL,
279 0,
280 PsxSessionPortListener,
281 0,
282 CREATE_SUSPENDED,
283 & Session.Port.Thread.Id
284 );
285 if ((HANDLE) NULL == Session.Port.Thread.Handle)
286 {
287 Status = (NTSTATUS) GetLastError();
288 NtClose (Session.Port.Handle);
289 RtlDeleteCriticalSection (& Session.Lock);
290 vtprintf ("%s: %s: CreateThread failed with %d\n",
291 MyName, __FUNCTION__, Status);
292 return Status;
293 }
294 /* Section */
295 swprintf (
296 Session.Section.NameBuffer,
297 PSX_NS_SESSION_DATA_TEMPLATE,
298 PSX_NS_SUBSYSTEM_DIRECTORY_NAME,
299 PSX_NS_SESSION_DIRECTORY_NAME,
300 Pid
301 );
302 OutputDebugStringW(Session.Section.NameBuffer);
303 RtlInitUnicodeString (& Session.Section.Name, Session.Section.NameBuffer);
304 InitializeObjectAttributes (& Oa, & Session.Section.Name, 0, 0, 0);
305 Status = NtCreateSection (
306 & Session.Section.Handle,
307 SECTION_ALL_ACCESS, /* DesiredAccess */
308 & Oa,
309 & SectionSize,
310 PAGE_READWRITE, /* Protect 4 */
311 SEC_COMMIT, /* Attributes */
312 0 /* FileHandle: 0=pagefile.sys */
313 );
314 if (!NT_SUCCESS(Status))
315 {
316 NtClose (Session.Port.Handle);
317 NtTerminateThread (Session.Port.Thread.Handle, Status);
318 RtlDeleteCriticalSection (& Session.Lock);
319 vtprintf ("%s: %s: NtCreateSection failed with %08x\n",
320 MyName, __FUNCTION__, Status);
321 return Status;
322 }
323 Session.Section.BaseAddress = NULL;
324 Session.Section.ViewSize = SectionSize.u.LowPart;
325 Status = NtMapViewOfSection (
326 Session.Section.Handle,
327 NtCurrentProcess(),
328 & Session.Section.BaseAddress,
329 0, /* ZeroBits */
330 0, /* Commitsize */
331 0, /* SectionOffset */
332 & Session.Section.ViewSize,
333 ViewUnmap,
334 0, /* AllocationType */
335 PAGE_READWRITE /* Protect 4 */
336 );
337 if (!NT_SUCCESS(Status))
338 {
339 NtClose (Session.Port.Handle);
340 NtTerminateThread (Session.Port.Thread.Handle, Status);
341 NtClose (Session.Section.Handle);
342 RtlDeleteCriticalSection (& Session.Lock);
343 vtprintf ("%s: %s: NtMapViewOfSection failed with %08x\n",
344 MyName, __FUNCTION__, Status);
345 return Status;
346 }
347 return Status;
348 }
349
350 /**********************************************************************
351 * CreateTerminalToPsxChannel/0 PRIVATE
352 *
353 * DESCRIPTION
354 *
355 */
356 PRIVATE NTSTATUS STDCALL CreateTerminalToPsxChannel (VOID)
357 {
358 PSX_CONNECT_PORT_DATA ConnectData;
359 ULONG ConnectDataLength = sizeof ConnectData;
360 SECURITY_QUALITY_OF_SERVICE Sqos;
361 NTSTATUS Status;
362
363 TRACE;
364
365
366 /*
367 * Initialize the connection data object before
368 * calling PSXSS.
369 */
370 ConnectData.ConnectionType = PSX_CONNECTION_TYPE_TERMINAL;
371 ConnectData.Version = PSX_LPC_PROTOCOL_VERSION;
372 /*
373 * Try connecting to \POSIX+\SessionPort.
374 */
375 RtlInitUnicodeString (& Session.ServerPort.Name, Session.ServerPort.NameBuffer);
376 OutputDebugStringW(Session.ServerPort.Name.Buffer);
377 Status = NtConnectPort (
378 & Session.ServerPort.Handle,
379 & Session.ServerPort.Name,
380 & Sqos,
381 NULL,
382 NULL,
383 0,
384 & ConnectData,
385 & ConnectDataLength
386 );
387 if (STATUS_SUCCESS != Status)
388 {
389 vtprintf ("%s: %s: NtConnectPort failed with %08x\n",
390 MyName, __FUNCTION__, Status);
391 return Status;
392 }
393 Session.Identifier = ConnectData.PortIdentifier;
394 return STATUS_SUCCESS;
395 }
396
397 /**********************************************************************
398 * InitializeSsIoChannel PRIVATE
399 *
400 * DESCRIPTION
401 * Create our objects in the system name space
402 * (CreateSessionObjects) and then connect to the session port
403 * (CreateControChannel).
404 */
405 PRIVATE NTSTATUS STDCALL InitializeSsIoChannel (VOID)
406 {
407 NTSTATUS Status = STATUS_SUCCESS;
408 DWORD Pid = GetCurrentProcessId();
409
410 TRACE;
411
412
413 Status = CreateSessionObjects (Pid);
414 if (STATUS_SUCCESS != Status)
415 {
416 vtprintf ("%s: %s: CreateSessionObjects failed with %08x\n",
417 MyName, __FUNCTION__, Status);
418 return Status;
419 }
420 Status = CreateTerminalToPsxChannel ();
421 if (STATUS_SUCCESS != Status)
422 {
423 vtprintf ("%s: %s: CreateTerminalToPsxChannel failed with %08x\n",
424 MyName, __FUNCTION__, Status);
425 return Status;
426 }
427 return STATUS_SUCCESS;
428 }
429 /**********************************************************************
430 * PsxCreateLeaderProcess/1 PRIVATE
431 *
432 * DESCRIPTION
433 * Create a new PSXSS process.
434 */
435 PRIVATE NTSTATUS STDCALL PsxCreateLeaderProcess (char * Command)
436 {
437 NTSTATUS Status;
438 TRACE;
439
440 if (NULL == Command)
441 {
442 Command = "sh";
443 }
444 /* TODO: request PSXSS to init the process slot */
445 vtprintf ("%s: %s: calling CSRSS not implemented!", MyName, __FUNCTION__);
446 return STATUS_SUCCESS;
447 }
448 /**********************************************************************
449 * PrintInformationProcess/0
450 *
451 * DESCRIPTION
452 */
453 PRIVATE VOID STDCALL PrintInformationProcess (VOID)
454 {
455
456 TRACE;
457
458 vtputs ("Leader:");
459 vtprintf (" UniqueProcess %08x\n", Session.Client.UniqueProcess);
460 vtprintf (" UniqueThread %08x\n", Session.Client.UniqueThread);
461 }
462 /**********************************************************************
463 * PostMortem/0
464 *
465 * DESCRIPTION
466 */
467 PRIVATE INT STDCALL PostMortem (VOID)
468 {
469 DWORD ExitCode;
470
471 TRACE;
472
473
474 PrintInformationProcess ();
475 if (TRUE == GetExitCodeProcess (Session.Client.UniqueProcess, & ExitCode))
476 {
477 vtprintf (" ExitCode %d\n", ExitCode);
478 }
479 return 0;
480 }
481 /**********************************************************************
482 * InputTerminalEmulator/0
483 *
484 * DESCRIPTION
485 * Process user terminal input.
486 *
487 * NOTE
488 * This code is run in the main thread.
489 */
490 PRIVATE BOOL STDCALL InputTerminalEmulator (VOID)
491 {
492 HANDLE StandardInput;
493 INPUT_RECORD InputRecord [INPUT_QUEUE_SIZE];
494 DWORD NumberOfEventsRead = 0;
495 INT CurrentEvent;
496
497
498 TRACE;
499
500 StandardInput = GetStdHandle (STD_INPUT_HANDLE);
501 if (INVALID_HANDLE_VALUE == StandardInput)
502 {
503 return FALSE;
504 }
505 while ((TRUE == Session.SsLinkIsActive) &&
506 ReadConsoleInput (
507 StandardInput,
508 InputRecord,
509 (sizeof InputRecord) / sizeof (INPUT_RECORD),
510 & NumberOfEventsRead
511 ))
512 {
513 for ( CurrentEvent = 0;
514 (CurrentEvent < NumberOfEventsRead);
515 CurrentEvent ++
516 )
517 {
518 switch (InputRecord [CurrentEvent].EventType)
519 {
520 case KEY_EVENT:
521 OutPort (& InputRecord [CurrentEvent].Event.KeyEvent.uChar.AsciiChar, 1);
522 break;
523 case MOUSE_EVENT:
524 /* TODO: send a sequence of move cursor codes */
525 /* InputRecord [CurrentEvent].Event.MouseEvent; */
526 break;
527 case WINDOW_BUFFER_SIZE_EVENT:
528 /* TODO: send a SIGWINCH signal to the leader process. */
529 /* InputRecord [CurrentEvent].Event.WindowBufferSizeEvent.dwSize; */
530 break;
531 /* Next events should be ignored. */
532 case MENU_EVENT:
533 vtprintf ("%s: %s: MENU_EVENT received from CSRSS\n", MyName, __FUNCTION__);
534 case FOCUS_EVENT:
535 vtprintf ("%s: %s: FOCUS_EVENT received from CSRSS\n", MyName, __FUNCTION__);
536 break;
537 }
538 }
539 NumberOfEventsRead = 0;
540 }
541 return TRUE;
542 }
543 /**********************************************************************
544 * Startup/1
545 *
546 * DESCRIPTION
547 * Initialize the program.
548 */
549 PRIVATE VOID STDCALL Startup (LPSTR Command)
550 {
551 NTSTATUS Status;
552 DWORD ThreadId;
553
554
555 TRACE;
556
557 /* PSX process info */
558 Session.Client.UniqueProcess = INVALID_HANDLE_VALUE;
559 Session.Client.UniqueThread = INVALID_HANDLE_VALUE;
560 /* Initialize the VT-100 emulator */
561 vtInitVT100 ();
562 /* Connect to PSXSS */
563 Status = InitializeSsIoChannel ();
564 if (!NT_SUCCESS(Status))
565 {
566 vtprintf ("%s: failed to connect to PSXSS (Status=%08x)!\n",
567 MyName, Status);
568 exit (EXIT_FAILURE);
569 }
570 /* Create the leading process for this session */
571 Status = PsxCreateLeaderProcess (Command);
572 if (!NT_SUCCESS(Status))
573 {
574 vtprintf ("%s: failed to create the PSX process (Status=%08x)!\n",
575 MyName, Status);
576 exit (EXIT_FAILURE);
577 }
578 }
579 /**********************************************************************
580 * Shutdown/0 PRIVATE
581 *
582 * DESCRIPTION
583 * Shutdown the program.
584 */
585 PRIVATE INT STDCALL Shutdown (VOID)
586 {
587
588 TRACE;
589
590 /* TODO: try exiting cleanly: close any open resource */
591 /* TODO: notify PSXSS the session is terminating */
592 RtlDeleteCriticalSection (& Session.Lock);
593 return PostMortem ();
594 }
595 /**********************************************************************
596 *
597 * ENTRY POINT PUBLIC
598 *
599 *********************************************************************/
600 int main (int argc, char * argv [])
601 {
602
603 TRACE;
604
605 Startup (argv[1]); /* Initialization */
606 InputTerminalEmulator (); /* Process user input */
607 return Shutdown ();
608 }
609 /* EOF */