finally working spawn
[reactos.git] / posix / lib / psxdll / misc / spawn.c
1 /* $Id: spawn.c,v 1.5 2002/03/21 22:43:27 hyperion Exp $
2 */
3 /*
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS POSIX+ Subsystem
6 * FILE: subsys/psx/lib/psxdll/misc/spawn.c
7 * PURPOSE: Create the first POSIX+ process
8 * PROGRAMMER: KJK::Hyperion <noog@libero.it>
9 * UPDATE HISTORY:
10 * 25/02/2002: Created
11 */
12
13 /*
14 * NOTE by KJK::Hyperion:
15 * The __PdxSpawnPosixProcess() call solves the chicken-egg dilemma of
16 * creating the first POSIX+ process in a group without the ability to
17 * fork+exec (for example from inside a Win32 process). Processes created by
18 * __PdxSpawnPosixProcess() will *not* inherit anything from the parent, not
19 * even handles: all creation parameters have to be specified explicitely
20 */
21
22 #include <ddk/ntddk.h>
23 #include <ntdll/base.h>
24 #include <napi/i386/segment.h>
25 #include <ntdll/rtl.h>
26 #include <ntdll/ldr.h>
27 #include <stddef.h>
28 #include <string.h>
29 #include <inttypes.h>
30 #include <unistd.h>
31 #include <psx/debug.h>
32 #include <psx/pdata.h>
33 #include <psx/spawn.h>
34 #include <psx/stdlib.h>
35
36 #include <windows.h>
37
38 typedef struct _PORT_MESSAGE {
39 USHORT DataSize;
40 USHORT MessageSize;
41 USHORT MessageType;
42 USHORT VirtualRangesOffset;
43 CLIENT_ID ClientId;
44 ULONG MessageId;
45 ULONG SectionSize;
46 // UCHAR Data[];
47 } PORT_MESSAGE, *PPORT_MESSAGE;
48
49 NTSTATUS STDCALL CsrClientCallServer(
50 IN PVOID Message,
51 IN PVOID Unknown,
52 IN ULONG Opcode,
53 IN ULONG Size
54 );
55
56 NTSTATUS STDCALL __PdxSpawnPosixProcess
57 (
58 OUT PHANDLE ProcessHandle,
59 OUT PHANDLE ThreadHandle,
60 IN POBJECT_ATTRIBUTES FileObjectAttributes,
61 IN POBJECT_ATTRIBUTES ProcessObjectAttributes,
62 IN HANDLE InheritFromProcessHandle,
63 IN __PPDX_PDATA ProcessData
64 )
65 {
66 struct CSRSS_MESSAGE {
67 ULONG Unknown1;
68 ULONG Opcode;
69 ULONG Status;
70 ULONG Unknown2;
71 };
72
73 struct __tagcsrmsg{
74 PORT_MESSAGE PortMessage;
75 struct CSRSS_MESSAGE CsrssMessage;
76 PROCESS_INFORMATION ProcessInformation;
77 CLIENT_ID Debugger;
78 ULONG CreationFlags;
79 ULONG VdmInfo[2];
80 } csrmsg;
81
82 __PPDX_SERIALIZED_PDATA pspdProcessData;
83 IO_STATUS_BLOCK isbStatus;
84 PROCESS_BASIC_INFORMATION pbiProcessInfo;
85 INITIAL_TEB itInitialTeb;
86 PRTL_USER_PROCESS_PARAMETERS pppProcessParameters;
87 SECTION_IMAGE_INFORMATION siiInfo;
88 CONTEXT ctxThreadContext;
89 CLIENT_ID ciClientId;
90 NTSTATUS nErrCode;
91 HANDLE hExeFile;
92 HANDLE hExeImage;
93 HANDLE hProcess;
94 PVOID pPdataBuffer = 0;
95 PVOID pParamsBuffer = 0;
96 ULONG nDestBufferSize;
97 ULONG nCurFilDesOffset;
98 ULONG nVirtualSize;
99 ULONG nCommitSize;
100 PVOID pCommitBottom;
101 ULONG nOldProtect;
102 int i;
103
104 /* STEP 1: map executable image in memory */
105 /* 1.1: open the file for execution */
106 nErrCode = NtOpenFile
107 (
108 &hExeFile,
109 SYNCHRONIZE | FILE_EXECUTE,
110 FileObjectAttributes,
111 &isbStatus,
112 FILE_SHARE_READ | FILE_SHARE_DELETE,
113 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
114 );
115
116 /* failure */
117 if(!NT_SUCCESS(nErrCode))
118 {
119 ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode);
120 return (nErrCode);
121 }
122
123 /* 1.2: create an image section for the file */
124 nErrCode = NtCreateSection
125 (
126 &hExeImage,
127 SECTION_ALL_ACCESS,
128 NULL,
129 0,
130 PAGE_EXECUTE,
131 SEC_IMAGE,
132 hExeFile
133 );
134
135 /* close file handle (not needed anymore) */
136 NtClose(hExeFile);
137
138 /* failure */
139 if(!NT_SUCCESS(nErrCode))
140 {
141 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode);
142 return (nErrCode);
143 }
144
145 /* 1.3: get section image information */
146 nErrCode = NtQuerySection
147 (
148 hExeImage,
149 SectionImageInformation,
150 &siiInfo,
151 sizeof(siiInfo),
152 NULL
153 );
154
155 /* failure */
156 if(!NT_SUCCESS(nErrCode))
157 {
158 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode);
159 NtClose(hExeImage);
160 return (nErrCode);
161 }
162
163 /* STEP 2: create process */
164 nErrCode = NtCreateProcess
165 (
166 &hProcess,
167 PROCESS_ALL_ACCESS,
168 ProcessObjectAttributes,
169 InheritFromProcessHandle,
170 FALSE,
171 hExeImage,
172 NULL,
173 NULL
174 );
175
176 /* close image handle (not needed anymore) */
177 NtClose(hExeImage);
178
179 /* failure */
180 if(!NT_SUCCESS(nErrCode))
181 {
182 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode);
183 return (nErrCode);
184 }
185
186 /* STEP 3: write process environment and process parameters */
187 /* 3.1: convert process data into process parameters */
188 __PdxProcessDataToProcessParameters
189 (
190 &pppProcessParameters,
191 ProcessData,
192 FileObjectAttributes->ObjectName
193 );
194
195 /* 3.2: serialize the process data for transfer */
196 /* FIXME: the serialized data can be allocated and written directly in the
197 destination process */
198 __PdxSerializeProcessData(ProcessData, &pspdProcessData);
199
200 /* 3.2.1: adjust some fields */
201 pspdProcessData->ProcessData.Spawned = TRUE;
202
203 /* 3.3: allocate memory in the destination process */
204 /* 3.3.1: process data */
205 nDestBufferSize = pspdProcessData->AllocSize;
206
207 nErrCode = NtAllocateVirtualMemory
208 (
209 hProcess,
210 &pPdataBuffer,
211 0,
212 &nDestBufferSize,
213 MEM_COMMIT,
214 PAGE_READWRITE
215 );
216
217 /* failure */
218 if(!NT_SUCCESS(nErrCode))
219 {
220 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
221 goto undoPData;
222 }
223
224 /* 3.3.2: process parameters */
225 nDestBufferSize = pppProcessParameters->Length;
226
227 nErrCode = NtAllocateVirtualMemory
228 (
229 hProcess,
230 &pParamsBuffer,
231 0,
232 &nDestBufferSize,
233 MEM_COMMIT,
234 PAGE_READWRITE
235 );
236
237 /* failure */
238 if(!NT_SUCCESS(nErrCode))
239 {
240 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
241 goto undoPData;
242 }
243
244 /* 3.4: get pointer to the PEB */
245 nErrCode = NtQueryInformationProcess
246 (
247 hProcess,
248 ProcessBasicInformation,
249 &pbiProcessInfo,
250 sizeof(pbiProcessInfo),
251 NULL
252 );
253
254 /* failure */
255 if(!NT_SUCCESS(nErrCode))
256 {
257 ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode);
258 goto undoPData;
259 }
260
261 /* 3.5: write pointers in the PEB */
262 /* 3.5.1: process data */
263 nErrCode = NtWriteVirtualMemory
264 (
265 hProcess,
266 (PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, SubSystemData)),
267 &pPdataBuffer,
268 sizeof(PVOID),
269 NULL
270 );
271
272 /* failure */
273 if(!NT_SUCCESS(nErrCode))
274 {
275 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
276 goto undoPData;
277 }
278
279 /* 3.5.2: process parameters */
280 nErrCode = NtWriteVirtualMemory
281 (
282 hProcess,
283 (PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, ProcessParameters)),
284 &pParamsBuffer,
285 sizeof(PVOID),
286 NULL
287 );
288
289 /* failure */
290 if(!NT_SUCCESS(nErrCode))
291 {
292 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
293 goto undoPData;
294 }
295
296 /* 3.6: write data */
297 /* 3.6.1: process data */
298 nErrCode = NtWriteVirtualMemory
299 (
300 hProcess,
301 pPdataBuffer,
302 pspdProcessData,
303 pspdProcessData->AllocSize,
304 NULL
305 );
306
307 /* failure */
308 if(!NT_SUCCESS(nErrCode))
309 {
310 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
311 goto undoPData;
312 }
313
314 /* 3.6.2 process parameters */
315 nErrCode = NtWriteVirtualMemory
316 (
317 hProcess,
318 pParamsBuffer,
319 pppProcessParameters,
320 pppProcessParameters->Length,
321 NULL
322 );
323
324 /* failure */
325 if(!NT_SUCCESS(nErrCode))
326 {
327 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
328 goto undoPData;
329 }
330
331 undoPData:
332 /* deallocate the temporary data block in the current process */
333 NtFreeVirtualMemory
334 (
335 NtCurrentProcess(),
336 (PVOID *)&pspdProcessData,
337 0,
338 MEM_RELEASE
339 );
340
341 /* destroy process parameters */
342 RtlDestroyProcessParameters(pppProcessParameters);
343
344 /* failure */
345 if(!NT_SUCCESS(nErrCode))
346 goto failProcess;
347
348 /* STEP 4: duplicate handles */
349 /* 4.1: handles in the structure itself */
350 /* 4.1.1: root directory */
351 nErrCode = NtDuplicateObject
352 (
353 NtCurrentProcess(),
354 ProcessData->RootHandle,
355 hProcess,
356 (PHANDLE)((ULONG)pPdataBuffer + offsetof(__PDX_PDATA, RootHandle)),
357 0,
358 0,
359 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
360 );
361
362 /* failure */
363 if(!NT_SUCCESS(nErrCode))
364 {
365 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
366 goto failProcess;
367 }
368
369 /* 4.2: file descriptors table */
370 for
371 (
372 /* pspdProcessData->ProcessData.FdTable.Descriptors contains the offset to
373 the descriptors array inside pspdProcessData->Buffer[], that is to the
374 first element of the array */
375 i = 0,
376 nCurFilDesOffset = (ULONG)pspdProcessData->ProcessData.FdTable.Descriptors;
377 /* iterate through all allocated descriptors */
378 i < ProcessData->FdTable.AllocatedDescriptors;
379 /* at every step, go on to next input descriptor, and increase the offset to
380 the next output descriptor */
381 i ++, nCurFilDesOffset += sizeof(__fildes_t)
382 )
383 /* FIXME? check the table's bitmap instead? */
384 if(ProcessData->FdTable.Descriptors[i].FileHandle != NULL)
385 {
386 /* duplicate the source handle, ProcessData->FdTable.Descriptors[i], from
387 the current process into the process identified by hProcess, at an
388 address calculated by adding to the serialized data block base address:
389 - the offset to the current descriptor
390 - the offset to the handle field of the descriptor */
391 nErrCode = NtDuplicateObject
392 (
393 NtCurrentProcess(),
394 ProcessData->FdTable.Descriptors[i].FileHandle,
395 hProcess,
396 (PHANDLE)(
397 (ULONG)pPdataBuffer + nCurFilDesOffset + offsetof(__fildes_t, FileHandle)
398 ),
399 0,
400 0,
401 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
402 );
403
404 /* failure */
405 if(!NT_SUCCESS(nErrCode))
406 {
407 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
408 goto failProcess;
409 }
410
411 /* duplicate standard handles */
412 /* standard input */
413 if(i == STDIN_FILENO)
414 {
415 nErrCode = NtDuplicateObject
416 (
417 NtCurrentProcess(),
418 ProcessData->FdTable.Descriptors[i].FileHandle,
419 hProcess,
420 (PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, InputHandle)),
421 0,
422 0,
423 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
424 );
425
426 /* failure */
427 if(!NT_SUCCESS(nErrCode))
428 {
429 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
430 goto failProcess;
431 }
432 }
433 /* standard output */
434 else if(i == STDOUT_FILENO)
435 {
436 nErrCode = NtDuplicateObject
437 (
438 NtCurrentProcess(),
439 ProcessData->FdTable.Descriptors[i].FileHandle,
440 hProcess,
441 (PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, OutputHandle)),
442 0,
443 0,
444 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
445 );
446
447 /* failure */
448 if(!NT_SUCCESS(nErrCode))
449 {
450 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
451 goto failProcess;
452 }
453 }
454 /* standard error */
455 else if(i == STDERR_FILENO)
456 {
457 nErrCode = NtDuplicateObject
458 (
459 NtCurrentProcess(),
460 ProcessData->FdTable.Descriptors[i].FileHandle,
461 hProcess,
462 (PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, ErrorHandle)),
463 0,
464 0,
465 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
466 );
467
468 /* failure */
469 if(!NT_SUCCESS(nErrCode))
470 {
471 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
472 goto failProcess;
473 }
474 }
475 }
476
477 /* STEP 5: create first thread */
478 /* 5.1: set up the stack */
479 itInitialTeb.StackAllocate = NULL;
480 nVirtualSize = 0x100000;
481 nCommitSize = 0x100000 - PAGESIZE;
482
483 /* 5.1.1: reserve the stack */
484 nErrCode = NtAllocateVirtualMemory
485 (
486 hProcess,
487 &itInitialTeb.StackAllocate,
488 0,
489 &nVirtualSize,
490 MEM_RESERVE,
491 PAGE_READWRITE
492 );
493
494 /* failure */
495 if(!NT_SUCCESS(nErrCode))
496 {
497 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
498 goto failProcess;
499 }
500
501 itInitialTeb.StackBase =
502 (PVOID)((ULONG)itInitialTeb.StackAllocate + nVirtualSize);
503
504 itInitialTeb.StackLimit =
505 (PVOID)((ULONG)itInitialTeb.StackBase - nCommitSize);
506
507 /* 5.1.2: commit the stack */
508 nVirtualSize = nCommitSize + PAGESIZE;
509 pCommitBottom =
510 (PVOID)((ULONG)itInitialTeb.StackBase - nVirtualSize);
511
512 nErrCode = NtAllocateVirtualMemory
513 (
514 hProcess,
515 &pCommitBottom,
516 0,
517 &nVirtualSize,
518 MEM_COMMIT,
519 PAGE_READWRITE
520 );
521
522 /* failure */
523 if(!NT_SUCCESS(nErrCode))
524 {
525 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
526 goto failProcess;
527 }
528
529 /* 5.1.3: set up the guard page */
530 nVirtualSize = PAGESIZE;
531
532 nErrCode = NtProtectVirtualMemory
533 (
534 hProcess,
535 &pCommitBottom,
536 &nVirtualSize,
537 PAGE_GUARD | PAGE_READWRITE,
538 &nOldProtect
539 );
540
541 /* failure */
542 if(!NT_SUCCESS(nErrCode))
543 {
544 ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode);
545 goto failProcess;
546 }
547
548 /* 5.2: initialize the thread context */
549 memset(&ctxThreadContext, 0, sizeof(ctxThreadContext));
550
551 ctxThreadContext.Eip = (ULONG)siiInfo.EntryPoint;
552 ctxThreadContext.SegGs = USER_DS;
553 ctxThreadContext.SegFs = USER_DS;
554 ctxThreadContext.SegEs = USER_DS;
555 ctxThreadContext.SegDs = USER_DS;
556 ctxThreadContext.SegCs = USER_CS;
557 ctxThreadContext.SegSs = USER_DS;
558 ctxThreadContext.Esp = (ULONG)itInitialTeb.StackBase - 4;
559 ctxThreadContext.EFlags = (1 << 1) + (1 << 9);
560
561 /* 5.3: create the thread object */
562 nErrCode = NtCreateThread
563 (
564 ThreadHandle,
565 THREAD_ALL_ACCESS,
566 NULL,
567 hProcess,
568 &ciClientId,
569 &ctxThreadContext,
570 &itInitialTeb,
571 TRUE /* FIXME: the thread is only created in suspended state for easier
572 debugging. This behavior is subject to future changes */
573 );
574
575 /* failure */
576 if(!NT_SUCCESS(nErrCode))
577 {
578 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode);
579 goto failProcess;
580 }
581
582 /* 6: register the process with the Win32 subsystem (temporary code for
583 debugging purposes) */
584
585 memset(&csrmsg, 0, sizeof(csrmsg));
586
587 //csrmsg.PortMessage = {0};
588 //csrmsg.CsrssMessage = {0};
589 csrmsg.ProcessInformation.hProcess = hProcess;
590 csrmsg.ProcessInformation.hThread = *ThreadHandle;
591 csrmsg.ProcessInformation.dwProcessId = (DWORD)ciClientId.UniqueProcess;
592 csrmsg.ProcessInformation.dwThreadId = (DWORD)ciClientId.UniqueThread;
593 //csrmsg.Debugger = {0};
594 //csrmsg.CreationFlags = 0;
595 //csrmsg.VdmInfo = {0};
596
597 nErrCode = CsrClientCallServer(&csrmsg, 0, 0x10000, 0x24);
598
599 /* failure */
600 if(!NT_SUCCESS(nErrCode))
601 {
602 ERR("CsrClientCallServer() failed with status 0x%08X\n", nErrCode);
603 goto failProcess;
604 }
605
606 nErrCode = NtResumeThread(*ThreadHandle, NULL);
607
608 /* failure */
609 if(!NT_SUCCESS(nErrCode))
610 {
611 ERR("NtResumeThread() failed with status 0x%08X\n", nErrCode);
612 goto failProcess;
613 }
614
615 /* success */
616 return (STATUS_SUCCESS);
617
618 /* failure */
619 failProcess:
620 NtTerminateProcess
621 (
622 hProcess,
623 nErrCode
624 );
625
626 return (nErrCode);
627 }
628
629 /* EOF */
630