1 /* $Id: spawn.c,v 1.1 2002/03/07 05:46:03 hyperion Exp $
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>
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
22 #include <ddk/ntddk.h>
23 #include <ntdll/base.h>
24 #include <napi/i386/segment.h>
29 #include <psx/debug.h>
30 #include <psx/pdata.h>
31 #include <psx/stdlib.h>
34 __PdxSerializeProcessData
36 IN __PPDX_PDATA ProcessData
,
37 OUT __PPDX_SERIALIZED_PDATA
*SerializedProcessData
40 __PPDX_SERIALIZED_PDATA pspdProcessData
= 0;
43 ULONG ulAllocSize
= sizeof(__PDX_SERIALIZED_PDATA
) - 1;
45 size_t *pnEnvVarsLengths
;
49 /* calculate buffer length */
50 /* FIXME please! this is the most inefficient way to do it */
53 pnArgLengths
= __malloc(ProcessData
->ArgCount
* sizeof(size_t));
55 for(i
= 0; i
< ProcessData
->ArgCount
; i
++)
57 int nStrLen
= strlen(ProcessData
->ArgVect
[i
]) + 1;
58 ulAllocSize
+= nStrLen
;
59 pnArgLengths
[i
] = nStrLen
;
63 "argument %d: \"%s\", length %d\n",
65 ProcessData
->ArgVect
[i
],
74 for(i
= 0; *(ProcessData
->Environment
)[i
] != 0; i
++)
76 int nStrLen
= strlen(*(ProcessData
->Environment
)[i
]) + 1;
77 ulAllocSize
+= nStrLen
;
80 __realloc(pnEnvVarsLengths
, nEnvVarsCount
* sizeof(size_t));
81 pnEnvVarsLengths
[i
] = nStrLen
;
85 "environment variable %d: \"%s\", length %d\n",
87 *(ProcessData
->Environment
)[i
],
92 INFO("(%d environment variables were found)\n", nEnvVarsCount
);
94 /* current directory */
95 ulAllocSize
+= ProcessData
->CurDir
.Length
;
98 "current directory: \"%Z\", length %d\n",
100 ProcessData
->CurDir
.Length
104 ulAllocSize
+= ProcessData
->RootPath
.Length
;
107 "root directory: \"%Z\", length %d\n",
108 &ProcessData
->RootPath
,
109 ProcessData
->RootPath
.Length
112 /* file descriptors table */
113 ulAllocSize
+= sizeof(__fildes_t
) * ProcessData
->FdTable
.AllocatedDescriptors
;
116 "descriptors table contains %d allocated descriptors, combined length %d\n",
117 ProcessData
->FdTable
.AllocatedDescriptors
,
118 sizeof(__fildes_t
) * ProcessData
->FdTable
.AllocatedDescriptors
121 /* extra descriptors data */
122 for(i
= 0; ProcessData
->FdTable
.AllocatedDescriptors
; i
++)
123 if(ProcessData
->FdTable
.Descriptors
[i
].ExtraData
!= NULL
)
125 ulAllocSize
+= ProcessData
->FdTable
.Descriptors
[i
].ExtraDataSize
;
129 "descriptor %d has %d bytes of associated data\n",
131 ProcessData
->FdTable
.Descriptors
[i
].ExtraDataSize
136 /* allocate return block */
137 INFO("about to allocate %d bytes\n", ulAllocSize
);
139 nErrCode
= NtAllocateVirtualMemory
142 (PVOID
*)&pspdProcessData
,
150 if(!NT_SUCCESS(nErrCode
))
152 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
153 __free(pnArgLengths
);
154 __free(pnEnvVarsLengths
);
155 *SerializedProcessData
= 0;
159 INFO("%d bytes actually allocated\n", ulAllocSize
);
160 pspdProcessData
->AllocSize
= ulAllocSize
;
164 memcpy(&pspdProcessData
->ProcessData
, ProcessData
, sizeof(__PDX_PDATA
));
167 pBufferTail
= &pspdProcessData
->Buffer
[0];
168 INFO("buffer tail begins at 0x%08X\n", pBufferTail
);
171 pspdProcessData
->ProcessData
.ArgVect
= 0;
173 for(i
= 0; i
< ProcessData
->ArgCount
; i
++)
177 "copying %d bytes of argument %d (\"%s\") to 0x%08X\n",
180 ProcessData
->ArgVect
[i
],
184 strncpy(pBufferTail
, ProcessData
->ArgVect
[i
], pnArgLengths
[i
]);
185 pBufferTail
+= pnArgLengths
[i
];
189 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
196 __free(pnArgLengths
);
199 pspdProcessData
->ProcessData
.Environment
= (char ***)nEnvVarsCount
;
201 for(i
= 0; i
< nEnvVarsCount
; i
++)
205 "copying %d bytes of environment variable %d (\"%s\") to 0x%08X\n",
208 ProcessData
->Environment
[i
],
212 strncpy(pBufferTail
, *ProcessData
->Environment
[i
], pnEnvVarsLengths
[i
]);
213 pBufferTail
+= pnEnvVarsLengths
[i
];
217 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
223 __free(pnEnvVarsLengths
);
225 /* current directory */
228 "copying %d bytes of current directory (\"%Z\") to 0x%08X\n",
229 ProcessData
->CurDir
.Length
,
230 &ProcessData
->CurDir
,
234 memcpy(pBufferTail
, ProcessData
->CurDir
.Buffer
, ProcessData
->CurDir
.Length
);
235 pBufferTail
+= ProcessData
->CurDir
.Length
;
239 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
240 ProcessData
->CurDir
.Length
,
247 "copying %d bytes of root directory (\"%Z\") to 0x%08X\n",
248 ProcessData
->RootPath
.Length
,
249 &ProcessData
->RootPath
,
256 ProcessData
->RootPath
.Buffer
,
257 ProcessData
->RootPath
.Length
260 pBufferTail
+= ProcessData
->RootPath
.Length
;
264 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
265 ProcessData
->RootPath
.Length
,
269 /* file descriptors table */
270 /* save the offset to the descriptors array */
271 pspdProcessData
->ProcessData
.FdTable
.Descriptors
=
272 (PVOID
)((ULONG
)pBufferTail
- (ULONG
)pspdProcessData
);
276 "descriptors table contains %d allocated descriptors, combined length %d\n",
277 ProcessData
->FdTable
.AllocatedDescriptors
,
278 sizeof(__fildes_t
) * ProcessData
->FdTable
.AllocatedDescriptors
284 ProcessData
->FdTable
.Descriptors
,
285 sizeof(__fildes_t
) * ProcessData
->FdTable
.AllocatedDescriptors
289 sizeof(__fildes_t
) * ProcessData
->FdTable
.AllocatedDescriptors
;
293 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
294 sizeof(__fildes_t
) * ProcessData
->FdTable
.AllocatedDescriptors
,
298 /* extra descriptors data */
299 for(i
= 0; ProcessData
->FdTable
.AllocatedDescriptors
; i
++)
300 if(ProcessData
->FdTable
.Descriptors
[i
].ExtraData
!= 0)
304 "descriptor %d has %d bytes of associated data\n",
306 ProcessData
->FdTable
.Descriptors
[i
].ExtraDataSize
312 ProcessData
->FdTable
.Descriptors
[i
].ExtraData
,
313 ProcessData
->FdTable
.Descriptors
[i
].ExtraDataSize
316 pBufferTail
+= ProcessData
->FdTable
.Descriptors
[i
].ExtraDataSize
;
320 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
321 ProcessData
->FdTable
.Descriptors
[i
].ExtraDataSize
,
327 *SerializedProcessData
= pspdProcessData
;
333 __PdxSpawnPosixProcess
335 OUT PHANDLE ProcessHandle
,
336 OUT PHANDLE ThreadHandle
,
337 IN POBJECT_ATTRIBUTES FileObjectAttributes
,
338 IN POBJECT_ATTRIBUTES ProcessObjectAttributes
,
339 IN HANDLE InheritFromProcessHandle
,
340 IN __PPDX_PDATA ProcessData
343 __PPDX_SERIALIZED_PDATA pspdProcessData
;
344 IO_STATUS_BLOCK isbStatus
;
345 PROCESS_BASIC_INFORMATION pbiProcessInfo
;
346 ANSI_STRING strStartEntry
;
348 INITIAL_TEB itInitialTeb
;
349 CONTEXT ctxThreadContext
;
350 CLIENT_ID ciClientId
;
357 ULONG nDestBufferSize
;
358 ULONG nCurFilDesOffset
;
361 /* STEP 1: map executable image in memory */
362 /* 1.1: open the file for execution */
363 nErrCode
= NtOpenFile
366 SYNCHRONIZE
| FILE_EXECUTE
,
367 FileObjectAttributes
,
369 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
370 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
374 if(!NT_SUCCESS(nErrCode
))
376 ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode
);
380 /* 1.2: create a memory section for the file */
381 nErrCode
= NtCreateSection
392 /* close file handle (not needed anymore) */
396 if(!NT_SUCCESS(nErrCode
))
398 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode
);
402 /* STEP 2: create process */
403 nErrCode
= NtCreateProcess
407 ProcessObjectAttributes
,
408 InheritFromProcessHandle
,
415 /* close image handle (not needed anymore) */
419 if(!NT_SUCCESS(nErrCode
))
421 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode
);
425 /* STEP 3: write process environment */
426 /* 3.1: serialize the process data for transfer */
427 /* FIXME: the serialized data can be allocated and written directly in the
428 destination process */
429 __PdxSerializeProcessData(ProcessData
, &pspdProcessData
);
431 /* 3.1.1: adjust some fields */
432 pspdProcessData
->ProcessData
.Spawned
= TRUE
;
434 /* 3.2: allocate memory in the destination process */
435 nDestBufferSize
= pspdProcessData
->AllocSize
;
437 nErrCode
= NtAllocateVirtualMemory
448 if(!NT_SUCCESS(nErrCode
))
450 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
454 /* 3.3: get pointer to the PEB */
455 nErrCode
= NtQueryInformationProcess
458 ProcessBasicInformation
,
460 sizeof(pbiProcessInfo
),
465 if(!NT_SUCCESS(nErrCode
))
467 ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode
);
471 /* 3.4: write pointer to process data in the SubSystemData field of the PEB */
472 nErrCode
= NtWriteVirtualMemory
475 (PVOID
)((ULONG
)pbiProcessInfo
.PebBaseAddress
+ offsetof(PEB
, SubSystemData
)),
482 if(!NT_SUCCESS(nErrCode
))
484 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode
);
488 /* 3.5: write the process data */
489 nErrCode
= NtWriteVirtualMemory
494 pspdProcessData
->AllocSize
,
499 /* deallocate the temporary data block in the current process */
503 (PVOID
*)&pspdProcessData
,
509 if(!NT_SUCCESS(nErrCode
))
512 /* STEP 4: duplicate handles */
513 /* 4.1: handles in the structure itself */
514 /* 4.1.1: root directory */
515 nErrCode
= NtDuplicateObject
518 ProcessData
->RootHandle
,
520 (PHANDLE
)((ULONG
)pDestBuffer
+ offsetof(__PDX_PDATA
, RootHandle
)),
523 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
527 if(!NT_SUCCESS(nErrCode
))
529 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
533 /* 4.2: file descriptors table */
536 /* pspdProcessData->ProcessData.FdTable.Descriptors contains the offset to
537 the descriptors array inside pspdProcessData->Buffer[], that is to the
538 first element of the array */
540 nCurFilDesOffset
= (ULONG
)pspdProcessData
->ProcessData
.FdTable
.Descriptors
;
541 /* iterate through all allocated descriptors */
542 i
< ProcessData
->FdTable
.AllocatedDescriptors
;
543 /* at every step, go on to next input descriptor, and increase the offset to
544 the next output descriptor */
545 i
++, nCurFilDesOffset
+= sizeof(__fildes_t
)
547 /* FIXME? check the table's bitmap instead? */
548 if(ProcessData
->FdTable
.Descriptors
[i
].FileHandle
!= NULL
)
550 /* duplicate the source handle, ProcessData->FdTable.Descriptors[i], from
551 the current process into the process identified by hProcess, at an
552 address calculated by adding to the serialized data block base address:
553 - the offset to the current descriptor
554 - the offset to the handle field of the descriptor */
555 nErrCode
= NtDuplicateObject
558 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
561 (ULONG
)pDestBuffer
+ nCurFilDesOffset
+ offsetof(__fildes_t
, FileHandle
)
565 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
569 if(!NT_SUCCESS(nErrCode
))
571 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
576 /* STEP 5: create first thread */
577 /* 5.1: get thunk routine's address */
578 RtlInitializeAnsiString(&strStartEntry
, "LdrInitializeThunk");
580 nErrCode
= LdrGetProcedureAddress
589 if(!NT_SUCCESS(nErrCode
))
591 ERR("LdrGetProcedureAddress() failed with status 0x%08X\n", nErrCode
);
595 /* 5.2: set up the initial TEB */
596 itInitialTeb
.StackAllocate
= NULL
;
598 /* FIXME: allow the caller to specify these values */
599 itInitialTeb
.StackReserve
= 0x100000;
600 itInitialTeb
.StackCommit
= itInitialTeb
.StackReserve
- PAGESIZE
;
603 itInitialTeb
.StackCommit
+= PAGESIZE
;
605 /* 5.2.1: set up the stack */
606 /* 5.2.1.1: reserve the stack */
607 nErrCode
= NtAllocateVirtualMemory
610 &itInitialTeb
.StackAllocate
,
612 &itInitialTeb
.StackReserve
,
618 if(!NT_SUCCESS(nErrCode
))
620 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
624 itInitialTeb
.StackBase
=
625 (PVOID
)((ULONG
)itInitialTeb
.StackAllocate
+ itInitialTeb
.StackReserve
);
627 itInitialTeb
.StackLimit
=
628 (PVOID
)((ULONG
)itInitialTeb
.StackBase
- itInitialTeb
.StackCommit
);
630 /* 5.2.1.2: commit the stack */
631 nErrCode
= NtAllocateVirtualMemory
634 &itInitialTeb
.StackLimit
,
636 &itInitialTeb
.StackCommit
,
642 if(!NT_SUCCESS(nErrCode
))
644 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
648 /* 5.2.1.3: set up the guard page */
649 nErrCode
= NtProtectVirtualMemory
652 itInitialTeb
.StackLimit
,
654 PAGE_GUARD
| PAGE_READWRITE
,
659 if(!NT_SUCCESS(nErrCode
))
661 ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode
);
665 /* 5.2.1.4: initialize the thread context */
666 memset(&ctxThreadContext
, 0, sizeof(ctxThreadContext
));
668 ctxThreadContext
.Eip
= (ULONG
)pStartAddress
;
669 ctxThreadContext
.SegGs
= USER_DS
;
670 ctxThreadContext
.SegFs
= USER_DS
;
671 ctxThreadContext
.SegEs
= USER_DS
;
672 ctxThreadContext
.SegDs
= USER_DS
;
673 ctxThreadContext
.SegCs
= USER_CS
;
674 ctxThreadContext
.SegSs
= USER_DS
;
675 /* skip five doublewords (four - unknown - parameters for LdrInitializeThunk,
676 and the return address) */
677 ctxThreadContext
.Esp
= (ULONG
)itInitialTeb
.StackBase
- 5 * 4;
678 ctxThreadContext
.EFlags
= (1 << 1) + (1 << 9);
680 /* 5.3: create the thread object */
681 nErrCode
= NtCreateThread
694 if(!NT_SUCCESS(nErrCode
))
696 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode
);
701 return (STATUS_SUCCESS
);