finally (and hopefully) functional __PdxSpawnPosixProcess. To be tested
[reactos.git] / posix / lib / psxdll / misc / spawn.c
1 /* $Id: spawn.c,v 1.3 2002/03/10 17:09:46 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 <pthread.h>
31 #include <psx/debug.h>
32 #include <psx/pdata.h>
33 #include <psx/stdlib.h>
34
35 NTSTATUS STDCALL __PdxSpawnPosixProcess
36 (
37 OUT PHANDLE ProcessHandle,
38 OUT PHANDLE ThreadHandle,
39 IN POBJECT_ATTRIBUTES FileObjectAttributes,
40 IN POBJECT_ATTRIBUTES ProcessObjectAttributes,
41 IN HANDLE InheritFromProcessHandle,
42 IN __PPDX_PDATA ProcessData
43 )
44 {
45 __PPDX_SERIALIZED_PDATA pspdProcessData;
46 IO_STATUS_BLOCK isbStatus;
47 PROCESS_BASIC_INFORMATION pbiProcessInfo;
48 ANSI_STRING strStartEntry;
49 PVOID pStartAddress;
50 INITIAL_TEB itInitialTeb;
51 CONTEXT ctxThreadContext;
52 CLIENT_ID ciClientId;
53 NTSTATUS nErrCode;
54 HANDLE hExeFile;
55 HANDLE hExeImage;
56 HANDLE hProcess;
57 HANDLE hThread;
58 PVOID pDestBuffer;
59 ULONG nDestBufferSize;
60 ULONG nCurFilDesOffset;
61 int i;
62
63 /* STEP 1: map executable image in memory */
64 /* 1.1: open the file for execution */
65 nErrCode = NtOpenFile
66 (
67 &hExeFile,
68 SYNCHRONIZE | FILE_EXECUTE,
69 FileObjectAttributes,
70 &isbStatus,
71 FILE_SHARE_READ | FILE_SHARE_DELETE,
72 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
73 );
74
75 /* failure */
76 if(!NT_SUCCESS(nErrCode))
77 {
78 ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode);
79 return (nErrCode);
80 }
81
82 /* 1.2: create a memory section for the file */
83 nErrCode = NtCreateSection
84 (
85 &hExeImage,
86 SECTION_ALL_ACCESS,
87 NULL,
88 0,
89 PAGE_EXECUTE,
90 SEC_IMAGE,
91 hExeFile
92 );
93
94 /* close file handle (not needed anymore) */
95 NtClose(hExeFile);
96
97 /* failure */
98 if(!NT_SUCCESS(nErrCode))
99 {
100 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode);
101 return (nErrCode);
102 }
103
104 /* STEP 2: create process */
105 nErrCode = NtCreateProcess
106 (
107 &hProcess,
108 PROCESS_ALL_ACCESS,
109 ProcessObjectAttributes,
110 InheritFromProcessHandle,
111 FALSE,
112 hExeImage,
113 NULL,
114 NULL
115 );
116
117 /* close image handle (not needed anymore) */
118 NtClose(hExeImage);
119
120 /* failure */
121 if(!NT_SUCCESS(nErrCode))
122 {
123 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode);
124 return (nErrCode);
125 }
126
127 /* STEP 3: write process environment */
128 /* 3.1: serialize the process data for transfer */
129 /* FIXME: the serialized data can be allocated and written directly in the
130 destination process */
131 __PdxSerializeProcessData(ProcessData, &pspdProcessData);
132
133 /* 3.1.1: adjust some fields */
134 pspdProcessData->ProcessData.Spawned = TRUE;
135
136 /* 3.2: allocate memory in the destination process */
137 nDestBufferSize = pspdProcessData->AllocSize;
138
139 nErrCode = NtAllocateVirtualMemory
140 (
141 hProcess,
142 &pDestBuffer,
143 0,
144 &nDestBufferSize,
145 MEM_COMMIT,
146 PAGE_READWRITE
147 );
148
149 /* failure */
150 if(!NT_SUCCESS(nErrCode))
151 {
152 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
153 goto undoPData;
154 }
155
156 /* 3.3: get pointer to the PEB */
157 nErrCode = NtQueryInformationProcess
158 (
159 hProcess,
160 ProcessBasicInformation,
161 &pbiProcessInfo,
162 sizeof(pbiProcessInfo),
163 NULL
164 );
165
166 /* failure */
167 if(!NT_SUCCESS(nErrCode))
168 {
169 ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode);
170 goto undoPData;
171 }
172
173 /* 3.4: write pointer to process data in the SubSystemData field of the PEB */
174 nErrCode = NtWriteVirtualMemory
175 (
176 hProcess,
177 (PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, SubSystemData)),
178 &pDestBuffer,
179 sizeof(PVOID),
180 NULL
181 );
182
183 /* failure */
184 if(!NT_SUCCESS(nErrCode))
185 {
186 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
187 goto undoPData;
188 }
189
190 /* 3.5: write the process data */
191 nErrCode = NtWriteVirtualMemory
192 (
193 hProcess,
194 pDestBuffer,
195 pspdProcessData,
196 pspdProcessData->AllocSize,
197 NULL
198 );
199
200 undoPData:
201 /* deallocate the temporary data block in the current process */
202 NtFreeVirtualMemory
203 (
204 NtCurrentProcess(),
205 (PVOID *)&pspdProcessData,
206 0,
207 MEM_RELEASE
208 );
209
210 /* failure */
211 if(!NT_SUCCESS(nErrCode))
212 return (nErrCode);
213
214 /* STEP 4: duplicate handles */
215 /* 4.1: handles in the structure itself */
216 /* 4.1.1: root directory */
217 nErrCode = NtDuplicateObject
218 (
219 NtCurrentProcess(),
220 ProcessData->RootHandle,
221 hProcess,
222 (PHANDLE)((ULONG)pDestBuffer + offsetof(__PDX_PDATA, RootHandle)),
223 0,
224 0,
225 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
226 );
227
228 /* failure */
229 if(!NT_SUCCESS(nErrCode))
230 {
231 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
232 goto failProcess;
233 }
234
235 /* 4.2: file descriptors table */
236 for
237 (
238 /* pspdProcessData->ProcessData.FdTable.Descriptors contains the offset to
239 the descriptors array inside pspdProcessData->Buffer[], that is to the
240 first element of the array */
241 i = 0,
242 nCurFilDesOffset = (ULONG)pspdProcessData->ProcessData.FdTable.Descriptors;
243 /* iterate through all allocated descriptors */
244 i < ProcessData->FdTable.AllocatedDescriptors;
245 /* at every step, go on to next input descriptor, and increase the offset to
246 the next output descriptor */
247 i ++, nCurFilDesOffset += sizeof(__fildes_t)
248 )
249 /* FIXME? check the table's bitmap instead? */
250 if(ProcessData->FdTable.Descriptors[i].FileHandle != NULL)
251 {
252 /* duplicate the source handle, ProcessData->FdTable.Descriptors[i], from
253 the current process into the process identified by hProcess, at an
254 address calculated by adding to the serialized data block base address:
255 - the offset to the current descriptor
256 - the offset to the handle field of the descriptor */
257 nErrCode = NtDuplicateObject
258 (
259 NtCurrentProcess(),
260 ProcessData->FdTable.Descriptors[i].FileHandle,
261 hProcess,
262 (PHANDLE)(
263 (ULONG)pDestBuffer + nCurFilDesOffset + offsetof(__fildes_t, FileHandle)
264 ),
265 0,
266 0,
267 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
268 );
269
270 /* failure */
271 if(!NT_SUCCESS(nErrCode))
272 {
273 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
274 goto failProcess;
275 }
276 }
277
278 /* STEP 5: create first thread */
279 /* 5.1: get thunk routine's address */
280 RtlInitAnsiString(&strStartEntry, "LdrInitializeThunk");
281
282 nErrCode = LdrGetProcedureAddress
283 (
284 (PVOID)NTDLL_BASE,
285 &strStartEntry,
286 0,
287 &pStartAddress
288 );
289
290 /* failure */
291 if(!NT_SUCCESS(nErrCode))
292 {
293 ERR("LdrGetProcedureAddress() failed with status 0x%08X\n", nErrCode);
294 goto failProcess;
295 }
296
297 /* 5.2: set up the initial TEB */
298 itInitialTeb.StackAllocate = NULL;
299
300 /* FIXME: allow the caller to specify these values */
301 itInitialTeb.StackReserve = 0x100000;
302 itInitialTeb.StackCommit = itInitialTeb.StackReserve - PAGESIZE;
303
304 /* guard page */
305 itInitialTeb.StackCommit += PAGESIZE;
306
307 /* 5.2.1: set up the stack */
308 /* 5.2.1.1: reserve the stack */
309 nErrCode = NtAllocateVirtualMemory
310 (
311 hProcess,
312 &itInitialTeb.StackAllocate,
313 0,
314 &itInitialTeb.StackReserve,
315 MEM_RESERVE,
316 PAGE_READWRITE
317 );
318
319 /* failure */
320 if(!NT_SUCCESS(nErrCode))
321 {
322 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
323 goto failProcess;
324 }
325
326 itInitialTeb.StackBase =
327 (PVOID)((ULONG)itInitialTeb.StackAllocate + itInitialTeb.StackReserve);
328
329 itInitialTeb.StackLimit =
330 (PVOID)((ULONG)itInitialTeb.StackBase - itInitialTeb.StackCommit);
331
332 /* 5.2.1.2: commit the stack */
333 nErrCode = NtAllocateVirtualMemory
334 (
335 hProcess,
336 &itInitialTeb.StackLimit,
337 0,
338 &itInitialTeb.StackCommit,
339 MEM_COMMIT,
340 PAGE_READWRITE
341 );
342
343 /* failure */
344 if(!NT_SUCCESS(nErrCode))
345 {
346 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
347 goto failProcess;
348 }
349
350 /* 5.2.1.3: set up the guard page */
351 nErrCode = NtProtectVirtualMemory
352 (
353 hProcess,
354 itInitialTeb.StackLimit,
355 PAGESIZE,
356 PAGE_GUARD | PAGE_READWRITE,
357 NULL
358 );
359
360 /* failure */
361 if(!NT_SUCCESS(nErrCode))
362 {
363 ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode);
364 goto failProcess;
365 }
366
367 /* 5.2.1.4: initialize the thread context */
368 memset(&ctxThreadContext, 0, sizeof(ctxThreadContext));
369
370 ctxThreadContext.Eip = (ULONG)pStartAddress;
371 ctxThreadContext.SegGs = USER_DS;
372 ctxThreadContext.SegFs = USER_DS;
373 ctxThreadContext.SegEs = USER_DS;
374 ctxThreadContext.SegDs = USER_DS;
375 ctxThreadContext.SegCs = USER_CS;
376 ctxThreadContext.SegSs = USER_DS;
377 /* skip five doublewords (four - unknown - parameters for LdrInitializeThunk,
378 and the return address) */
379 ctxThreadContext.Esp = (ULONG)itInitialTeb.StackBase - 5 * 4;
380 ctxThreadContext.EFlags = (1 << 1) + (1 << 9);
381
382 /* 5.3: create the thread object */
383 nErrCode = NtCreateThread
384 (
385 NULL,
386 THREAD_ALL_ACCESS,
387 NULL,
388 hProcess,
389 &ciClientId,
390 &ctxThreadContext,
391 &itInitialTeb,
392 FALSE
393 );
394
395 /* failure */
396 if(!NT_SUCCESS(nErrCode))
397 {
398 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode);
399 goto failProcess;
400 }
401
402 /* success */
403 return (STATUS_SUCCESS);
404
405 /* failure */
406 failProcess:
407 NtTerminateProcess
408 (
409 hProcess,
410 nErrCode
411 );
412
413 return (nErrCode);
414 }
415
416 /* EOF */
417