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