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