__PdxSpawnPosixProcess() now initializes process parameters too. Almost working!
[reactos.git] / posix / lib / psxdll / misc / spawn.c
1 /* $Id: spawn.c,v 1.4 2002/03/11 20:46:38 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 NTSTATUS STDCALL __PdxSpawnPosixProcess
37 (
38 OUT PHANDLE ProcessHandle,
39 OUT PHANDLE ThreadHandle,
40 IN POBJECT_ATTRIBUTES FileObjectAttributes,
41 IN POBJECT_ATTRIBUTES ProcessObjectAttributes,
42 IN HANDLE InheritFromProcessHandle,
43 IN __PPDX_PDATA ProcessData
44 )
45 {
46 __PPDX_SERIALIZED_PDATA pspdProcessData;
47 IO_STATUS_BLOCK isbStatus;
48 PROCESS_BASIC_INFORMATION pbiProcessInfo;
49 ANSI_STRING strStartEntry;
50 PVOID pStartAddress;
51 INITIAL_TEB itInitialTeb;
52 PRTL_USER_PROCESS_PARAMETERS pppProcessParameters;
53 CONTEXT ctxThreadContext;
54 CLIENT_ID ciClientId;
55 NTSTATUS nErrCode;
56 HANDLE hExeFile;
57 HANDLE hExeImage;
58 HANDLE hProcess;
59 HANDLE hThread;
60 PVOID pPdataBuffer = 0;
61 PVOID pParamsBuffer = 0;
62 ULONG nDestBufferSize;
63 ULONG nCurFilDesOffset;
64 ULONG nVirtualSize;
65 ULONG nCommitSize;
66 PVOID pCommitBottom;
67 ULONG nOldProtect;
68 int i;
69
70 /* STEP 1: map executable image in memory */
71 /* 1.1: open the file for execution */
72 nErrCode = NtOpenFile
73 (
74 &hExeFile,
75 SYNCHRONIZE | FILE_EXECUTE,
76 FileObjectAttributes,
77 &isbStatus,
78 FILE_SHARE_READ | FILE_SHARE_DELETE,
79 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
80 );
81
82 /* failure */
83 if(!NT_SUCCESS(nErrCode))
84 {
85 ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode);
86 return (nErrCode);
87 }
88
89 /* 1.2: create an image section for the file */
90 nErrCode = NtCreateSection
91 (
92 &hExeImage,
93 SECTION_ALL_ACCESS,
94 NULL,
95 0,
96 PAGE_EXECUTE,
97 SEC_IMAGE,
98 hExeFile
99 );
100
101 /* close file handle (not needed anymore) */
102 NtClose(hExeFile);
103
104 /* failure */
105 if(!NT_SUCCESS(nErrCode))
106 {
107 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode);
108 return (nErrCode);
109 }
110
111 /* STEP 2: create process */
112 nErrCode = NtCreateProcess
113 (
114 &hProcess,
115 PROCESS_ALL_ACCESS,
116 ProcessObjectAttributes,
117 InheritFromProcessHandle,
118 FALSE,
119 hExeImage,
120 NULL,
121 NULL
122 );
123
124 /* close image handle (not needed anymore) */
125 NtClose(hExeImage);
126
127 /* failure */
128 if(!NT_SUCCESS(nErrCode))
129 {
130 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode);
131 return (nErrCode);
132 }
133
134 /* STEP 3: write process environment and process parameters */
135 /* 3.1: convert process data into process parameters */
136 __PdxProcessDataToProcessParameters
137 (
138 &pppProcessParameters,
139 ProcessData,
140 FileObjectAttributes->ObjectName
141 );
142
143 /* 3.2: serialize the process data for transfer */
144 /* FIXME: the serialized data can be allocated and written directly in the
145 destination process */
146 __PdxSerializeProcessData(ProcessData, &pspdProcessData);
147
148 /* 3.2.1: adjust some fields */
149 pspdProcessData->ProcessData.Spawned = TRUE;
150
151 /* 3.3: allocate memory in the destination process */
152 /* 3.3.1: process data */
153 nDestBufferSize = pspdProcessData->AllocSize;
154
155 nErrCode = NtAllocateVirtualMemory
156 (
157 hProcess,
158 &pPdataBuffer,
159 0,
160 &nDestBufferSize,
161 MEM_COMMIT,
162 PAGE_READWRITE
163 );
164
165 /* failure */
166 if(!NT_SUCCESS(nErrCode))
167 {
168 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
169 goto undoPData;
170 }
171
172 /* 3.3.2: process parameters */
173 nDestBufferSize = pppProcessParameters->Length;
174
175 nErrCode = NtAllocateVirtualMemory
176 (
177 hProcess,
178 &pParamsBuffer,
179 0,
180 &nDestBufferSize,
181 MEM_COMMIT,
182 PAGE_READWRITE
183 );
184
185 /* failure */
186 if(!NT_SUCCESS(nErrCode))
187 {
188 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
189 goto undoPData;
190 }
191
192 /* 3.4: get pointer to the PEB */
193 nErrCode = NtQueryInformationProcess
194 (
195 hProcess,
196 ProcessBasicInformation,
197 &pbiProcessInfo,
198 sizeof(pbiProcessInfo),
199 NULL
200 );
201
202 /* failure */
203 if(!NT_SUCCESS(nErrCode))
204 {
205 ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode);
206 goto undoPData;
207 }
208
209 /* 3.5: write pointers in the PEB */
210 /* 3.5.1: process data */
211 nErrCode = NtWriteVirtualMemory
212 (
213 hProcess,
214 (PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, SubSystemData)),
215 &pPdataBuffer,
216 sizeof(PVOID),
217 NULL
218 );
219
220 /* failure */
221 if(!NT_SUCCESS(nErrCode))
222 {
223 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
224 goto undoPData;
225 }
226
227 /* 3.5.2: process parameters */
228 nErrCode = NtWriteVirtualMemory
229 (
230 hProcess,
231 (PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, ProcessParameters)),
232 &pParamsBuffer,
233 sizeof(PVOID),
234 NULL
235 );
236
237 /* failure */
238 if(!NT_SUCCESS(nErrCode))
239 {
240 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
241 goto undoPData;
242 }
243
244 /* 3.6: write the process data */
245 nErrCode = NtWriteVirtualMemory
246 (
247 hProcess,
248 pPdataBuffer,
249 pspdProcessData,
250 pspdProcessData->AllocSize,
251 NULL
252 );
253
254 undoPData:
255 /* deallocate the temporary data block in the current process */
256 NtFreeVirtualMemory
257 (
258 NtCurrentProcess(),
259 (PVOID *)&pspdProcessData,
260 0,
261 MEM_RELEASE
262 );
263
264 /* destroy process parameters */
265 RtlDestroyProcessParameters(pppProcessParameters);
266
267 /* failure */
268 if(!NT_SUCCESS(nErrCode))
269 goto failProcess;
270
271 /* STEP 4: duplicate handles */
272 /* 4.1: handles in the structure itself */
273 /* 4.1.1: root directory */
274 nErrCode = NtDuplicateObject
275 (
276 NtCurrentProcess(),
277 ProcessData->RootHandle,
278 hProcess,
279 (PHANDLE)((ULONG)pPdataBuffer + offsetof(__PDX_PDATA, RootHandle)),
280 0,
281 0,
282 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
283 );
284
285 /* failure */
286 if(!NT_SUCCESS(nErrCode))
287 {
288 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
289 goto failProcess;
290 }
291
292 /* 4.2: file descriptors table */
293 for
294 (
295 /* pspdProcessData->ProcessData.FdTable.Descriptors contains the offset to
296 the descriptors array inside pspdProcessData->Buffer[], that is to the
297 first element of the array */
298 i = 0,
299 nCurFilDesOffset = (ULONG)pspdProcessData->ProcessData.FdTable.Descriptors;
300 /* iterate through all allocated descriptors */
301 i < ProcessData->FdTable.AllocatedDescriptors;
302 /* at every step, go on to next input descriptor, and increase the offset to
303 the next output descriptor */
304 i ++, nCurFilDesOffset += sizeof(__fildes_t)
305 )
306 /* FIXME? check the table's bitmap instead? */
307 if(ProcessData->FdTable.Descriptors[i].FileHandle != NULL)
308 {
309 /* duplicate the source handle, ProcessData->FdTable.Descriptors[i], from
310 the current process into the process identified by hProcess, at an
311 address calculated by adding to the serialized data block base address:
312 - the offset to the current descriptor
313 - the offset to the handle field of the descriptor */
314 nErrCode = NtDuplicateObject
315 (
316 NtCurrentProcess(),
317 ProcessData->FdTable.Descriptors[i].FileHandle,
318 hProcess,
319 (PHANDLE)(
320 (ULONG)pPdataBuffer + nCurFilDesOffset + offsetof(__fildes_t, FileHandle)
321 ),
322 0,
323 0,
324 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
325 );
326
327 /* failure */
328 if(!NT_SUCCESS(nErrCode))
329 {
330 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
331 goto failProcess;
332 }
333
334 /* duplicate standard handles */
335 /* standard input */
336 if(i == STDIN_FILENO)
337 {
338 nErrCode = NtDuplicateObject
339 (
340 NtCurrentProcess(),
341 ProcessData->FdTable.Descriptors[i].FileHandle,
342 hProcess,
343 (PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, InputHandle)),
344 0,
345 0,
346 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
347 );
348
349 /* failure */
350 if(!NT_SUCCESS(nErrCode))
351 {
352 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
353 goto failProcess;
354 }
355 }
356 /* standard output */
357 else if(i == STDOUT_FILENO)
358 {
359 nErrCode = NtDuplicateObject
360 (
361 NtCurrentProcess(),
362 ProcessData->FdTable.Descriptors[i].FileHandle,
363 hProcess,
364 (PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, OutputHandle)),
365 0,
366 0,
367 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
368 );
369
370 /* failure */
371 if(!NT_SUCCESS(nErrCode))
372 {
373 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
374 goto failProcess;
375 }
376 }
377 /* standard error */
378 else if(i == STDERR_FILENO)
379 {
380 nErrCode = NtDuplicateObject
381 (
382 NtCurrentProcess(),
383 ProcessData->FdTable.Descriptors[i].FileHandle,
384 hProcess,
385 (PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, ErrorHandle)),
386 0,
387 0,
388 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
389 );
390
391 /* failure */
392 if(!NT_SUCCESS(nErrCode))
393 {
394 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
395 goto failProcess;
396 }
397 }
398 }
399
400 /* STEP 5: create first thread */
401 /* 5.1: get thunk routine's address */
402 RtlInitAnsiString(&strStartEntry, "LdrInitializeThunk");
403
404 #if 1
405 nErrCode = LdrGetProcedureAddress
406 (
407 (PVOID)0x78460000, /* NTDLL_BASE */
408 &strStartEntry,
409 0,
410 &pStartAddress
411 );
412
413 /* failure */
414 if(!NT_SUCCESS(nErrCode))
415 {
416 ERR("LdrGetProcedureAddress() failed with status 0x%08X\n", nErrCode);
417 goto failProcess;
418 }
419 #else
420 pStartAddress = LdrGetProcedureAddress;
421 #endif
422
423 /* 5.2: set up the stack */
424 itInitialTeb.StackAllocate = NULL;
425 nVirtualSize = 0x100000;
426 nCommitSize = 0x100000 - PAGESIZE;
427
428 /* 5.2.1: reserve the stack */
429 nErrCode = NtAllocateVirtualMemory
430 (
431 hProcess,
432 &itInitialTeb.StackAllocate,
433 0,
434 &nVirtualSize,
435 MEM_RESERVE,
436 PAGE_READWRITE
437 );
438
439 /* failure */
440 if(!NT_SUCCESS(nErrCode))
441 {
442 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
443 goto failProcess;
444 }
445
446 itInitialTeb.StackBase =
447 (PVOID)((ULONG)itInitialTeb.StackAllocate + nVirtualSize);
448
449 itInitialTeb.StackLimit =
450 (PVOID)((ULONG)itInitialTeb.StackBase - nCommitSize);
451
452 /* 5.2.2: commit the stack */
453 nVirtualSize = nCommitSize + PAGESIZE;
454 pCommitBottom =
455 (PVOID)((ULONG)itInitialTeb.StackBase - nVirtualSize);
456
457 nErrCode = NtAllocateVirtualMemory
458 (
459 hProcess,
460 &pCommitBottom,
461 0,
462 &nVirtualSize,
463 MEM_COMMIT,
464 PAGE_READWRITE
465 );
466
467 /* failure */
468 if(!NT_SUCCESS(nErrCode))
469 {
470 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
471 goto failProcess;
472 }
473
474 /* 5.2.3: set up the guard page */
475 nVirtualSize = PAGESIZE;
476
477 nErrCode = NtProtectVirtualMemory
478 (
479 hProcess,
480 &pCommitBottom,
481 &nVirtualSize,
482 PAGE_GUARD | PAGE_READWRITE,
483 &nOldProtect
484 );
485
486 /* failure */
487 if(!NT_SUCCESS(nErrCode))
488 {
489 ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode);
490 goto failProcess;
491 }
492
493 /* 5.3: initialize the thread context */
494 memset(&ctxThreadContext, 0, sizeof(ctxThreadContext));
495
496 ctxThreadContext.Eip = (ULONG)pStartAddress;
497 ctxThreadContext.SegGs = USER_DS;
498 ctxThreadContext.SegFs = USER_DS;
499 ctxThreadContext.SegEs = USER_DS;
500 ctxThreadContext.SegDs = USER_DS;
501 ctxThreadContext.SegCs = USER_CS;
502 ctxThreadContext.SegSs = USER_DS;
503 /* skip five doublewords (four - unknown - parameters for LdrInitializeThunk,
504 and the return address) */
505 ctxThreadContext.Esp = (ULONG)itInitialTeb.StackBase - 5 * 4;
506 ctxThreadContext.EFlags = (1 << 1) + (1 << 9);
507
508 /* 5.4: create the thread object */
509 nErrCode = NtCreateThread
510 (
511 ThreadHandle,
512 THREAD_ALL_ACCESS,
513 NULL,
514 hProcess,
515 &ciClientId,
516 &ctxThreadContext,
517 &itInitialTeb,
518 FALSE
519 );
520
521 /* failure */
522 if(!NT_SUCCESS(nErrCode))
523 {
524 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode);
525 goto failProcess;
526 }
527
528 /* success */
529 return (STATUS_SUCCESS);
530
531 /* failure */
532 failProcess:
533 NtTerminateProcess
534 (
535 hProcess,
536 nErrCode
537 );
538
539 return (nErrCode);
540 }
541
542 /* EOF */
543