2b8572d8974d592804772995fdb1edbe46ef5b47
[reactos.git] / posix / lib / psxdll / misc / spawn.c
1 /* $Id: spawn.c,v 1.1 2002/03/07 05:46:03 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 <stddef.h>
26 #include <string.h>
27 #include <inttypes.h>
28 #include <pthread.h>
29 #include <psx/debug.h>
30 #include <psx/pdata.h>
31 #include <psx/stdlib.h>
32
33 VOID
34 __PdxSerializeProcessData
35 (
36 IN __PPDX_PDATA ProcessData,
37 OUT __PPDX_SERIALIZED_PDATA *SerializedProcessData
38 )
39 {
40 __PPDX_SERIALIZED_PDATA pspdProcessData = 0;
41 NTSTATUS nErrCode;
42 PBYTE pBufferTail;
43 ULONG ulAllocSize = sizeof(__PDX_SERIALIZED_PDATA) - 1;
44 size_t *pnArgLengths;
45 size_t *pnEnvVarsLengths;
46 int nEnvVarsCount;
47 int i;
48
49 /* calculate buffer length */
50 /* FIXME please! this is the most inefficient way to do it */
51
52 /* argv */
53 pnArgLengths = __malloc(ProcessData->ArgCount * sizeof(size_t));
54
55 for(i = 0; i < ProcessData->ArgCount; i ++)
56 {
57 int nStrLen = strlen(ProcessData->ArgVect[i]) + 1;
58 ulAllocSize += nStrLen;
59 pnArgLengths[i] = nStrLen;
60
61 INFO
62 (
63 "argument %d: \"%s\", length %d\n",
64 i,
65 ProcessData->ArgVect[i],
66 nStrLen
67 );
68 }
69
70 /* environ */
71 pnEnvVarsLengths = 0;
72 nEnvVarsCount = 0;
73
74 for(i = 0; *(ProcessData->Environment)[i] != 0; i++)
75 {
76 int nStrLen = strlen(*(ProcessData->Environment)[i]) + 1;
77 ulAllocSize += nStrLen;
78
79 nEnvVarsCount ++;
80 __realloc(pnEnvVarsLengths, nEnvVarsCount * sizeof(size_t));
81 pnEnvVarsLengths[i] = nStrLen;
82
83 INFO
84 (
85 "environment variable %d: \"%s\", length %d\n",
86 i,
87 *(ProcessData->Environment)[i],
88 nStrLen
89 );
90 }
91
92 INFO("(%d environment variables were found)\n", nEnvVarsCount);
93
94 /* current directory */
95 ulAllocSize += ProcessData->CurDir.Length;
96 INFO
97 (
98 "current directory: \"%Z\", length %d\n",
99 &ProcessData->CurDir,
100 ProcessData->CurDir.Length
101 );
102
103 /* root directory */
104 ulAllocSize += ProcessData->RootPath.Length;
105 INFO
106 (
107 "root directory: \"%Z\", length %d\n",
108 &ProcessData->RootPath,
109 ProcessData->RootPath.Length
110 );
111
112 /* file descriptors table */
113 ulAllocSize += sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors;
114 INFO
115 (
116 "descriptors table contains %d allocated descriptors, combined length %d\n",
117 ProcessData->FdTable.AllocatedDescriptors,
118 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors
119 );
120
121 /* extra descriptors data */
122 for(i = 0; ProcessData->FdTable.AllocatedDescriptors; i ++)
123 if(ProcessData->FdTable.Descriptors[i].ExtraData != NULL)
124 {
125 ulAllocSize += ProcessData->FdTable.Descriptors[i].ExtraDataSize;
126
127 INFO
128 (
129 "descriptor %d has %d bytes of associated data\n",
130 i,
131 ProcessData->FdTable.Descriptors[i].ExtraDataSize
132 );
133
134 }
135
136 /* allocate return block */
137 INFO("about to allocate %d bytes\n", ulAllocSize);
138
139 nErrCode = NtAllocateVirtualMemory
140 (
141 NtCurrentProcess(),
142 (PVOID *)&pspdProcessData,
143 0,
144 &ulAllocSize,
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 __free(pnArgLengths);
154 __free(pnEnvVarsLengths);
155 *SerializedProcessData = 0;
156 return;
157 }
158
159 INFO("%d bytes actually allocated\n", ulAllocSize);
160 pspdProcessData->AllocSize = ulAllocSize;
161
162 /* copy data */
163 /* static data */
164 memcpy(&pspdProcessData->ProcessData, ProcessData, sizeof(__PDX_PDATA));
165
166 /* buffers */
167 pBufferTail = &pspdProcessData->Buffer[0];
168 INFO("buffer tail begins at 0x%08X\n", pBufferTail);
169
170 /* argv */
171 pspdProcessData->ProcessData.ArgVect = 0;
172
173 for(i = 0; i < ProcessData->ArgCount; i ++)
174 {
175 INFO
176 (
177 "copying %d bytes of argument %d (\"%s\") to 0x%08X\n",
178 pnArgLengths[i],
179 i,
180 ProcessData->ArgVect[i],
181 pBufferTail
182 );
183
184 strncpy(pBufferTail, ProcessData->ArgVect[i], pnArgLengths[i]);
185 pBufferTail += pnArgLengths[i];
186
187 INFO
188 (
189 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
190 pnArgLengths[i],
191 pBufferTail
192 );
193
194 }
195
196 __free(pnArgLengths);
197
198 /* environ */
199 pspdProcessData->ProcessData.Environment = (char ***)nEnvVarsCount;
200
201 for(i = 0; i < nEnvVarsCount; i ++)
202 {
203 INFO
204 (
205 "copying %d bytes of environment variable %d (\"%s\") to 0x%08X\n",
206 pnEnvVarsLengths[i],
207 i,
208 ProcessData->Environment[i],
209 pBufferTail
210 );
211
212 strncpy(pBufferTail, *ProcessData->Environment[i], pnEnvVarsLengths[i]);
213 pBufferTail += pnEnvVarsLengths[i];
214
215 INFO
216 (
217 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
218 pnEnvVarsLengths[i],
219 pBufferTail
220 );
221 }
222
223 __free(pnEnvVarsLengths);
224
225 /* current directory */
226 INFO
227 (
228 "copying %d bytes of current directory (\"%Z\") to 0x%08X\n",
229 ProcessData->CurDir.Length,
230 &ProcessData->CurDir,
231 pBufferTail
232 );
233
234 memcpy(pBufferTail, ProcessData->CurDir.Buffer, ProcessData->CurDir.Length);
235 pBufferTail += ProcessData->CurDir.Length;
236
237 INFO
238 (
239 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
240 ProcessData->CurDir.Length,
241 pBufferTail
242 );
243
244 /* root directory */
245 INFO
246 (
247 "copying %d bytes of root directory (\"%Z\") to 0x%08X\n",
248 ProcessData->RootPath.Length,
249 &ProcessData->RootPath,
250 pBufferTail
251 );
252
253 memcpy
254 (
255 pBufferTail,
256 ProcessData->RootPath.Buffer,
257 ProcessData->RootPath.Length
258 );
259
260 pBufferTail += ProcessData->RootPath.Length;
261
262 INFO
263 (
264 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
265 ProcessData->RootPath.Length,
266 pBufferTail
267 );
268
269 /* file descriptors table */
270 /* save the offset to the descriptors array */
271 pspdProcessData->ProcessData.FdTable.Descriptors =
272 (PVOID)((ULONG)pBufferTail - (ULONG)pspdProcessData);
273
274 INFO
275 (
276 "descriptors table contains %d allocated descriptors, combined length %d\n",
277 ProcessData->FdTable.AllocatedDescriptors,
278 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors
279 );
280
281 memcpy
282 (
283 pBufferTail,
284 ProcessData->FdTable.Descriptors,
285 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors
286 );
287
288 pBufferTail +=
289 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors;
290
291 INFO
292 (
293 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
294 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors,
295 pBufferTail
296 );
297
298 /* extra descriptors data */
299 for(i = 0; ProcessData->FdTable.AllocatedDescriptors; i ++)
300 if(ProcessData->FdTable.Descriptors[i].ExtraData != 0)
301 {
302 INFO
303 (
304 "descriptor %d has %d bytes of associated data\n",
305 i,
306 ProcessData->FdTable.Descriptors[i].ExtraDataSize
307 );
308
309 memcpy
310 (
311 pBufferTail,
312 ProcessData->FdTable.Descriptors[i].ExtraData,
313 ProcessData->FdTable.Descriptors[i].ExtraDataSize
314 );
315
316 pBufferTail += ProcessData->FdTable.Descriptors[i].ExtraDataSize;
317
318 INFO
319 (
320 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
321 ProcessData->FdTable.Descriptors[i].ExtraDataSize,
322 pBufferTail
323 );
324 }
325
326 /* success */
327 *SerializedProcessData = pspdProcessData;
328 }
329
330 NTSYSAPI
331 NTSTATUS
332 NTAPI
333 __PdxSpawnPosixProcess
334 (
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
341 )
342 {
343 __PPDX_SERIALIZED_PDATA pspdProcessData;
344 IO_STATUS_BLOCK isbStatus;
345 PROCESS_BASIC_INFORMATION pbiProcessInfo;
346 ANSI_STRING strStartEntry;
347 PVOID pStartAddress;
348 INITIAL_TEB itInitialTeb;
349 CONTEXT ctxThreadContext;
350 CLIENT_ID ciClientId;
351 NTSTATUS nErrCode;
352 HANDLE hExeFile;
353 HANDLE hExeImage;
354 HANDLE hProcess;
355 HANDLE hThread;
356 PVOID pDestBuffer;
357 ULONG nDestBufferSize;
358 ULONG nCurFilDesOffset;
359 int i;
360
361 /* STEP 1: map executable image in memory */
362 /* 1.1: open the file for execution */
363 nErrCode = NtOpenFile
364 (
365 &hExeFile,
366 SYNCHRONIZE | FILE_EXECUTE,
367 FileObjectAttributes,
368 &isbStatus,
369 FILE_SHARE_READ | FILE_SHARE_DELETE,
370 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
371 );
372
373 /* failure */
374 if(!NT_SUCCESS(nErrCode))
375 {
376 ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode);
377 return (nErrCode);
378 }
379
380 /* 1.2: create a memory section for the file */
381 nErrCode = NtCreateSection
382 (
383 &hExeImage,
384 SECTION_ALL_ACCESS,
385 NULL,
386 0,
387 PAGE_EXECUTE,
388 SEC_IMAGE,
389 hExeFile
390 );
391
392 /* close file handle (not needed anymore) */
393 NtClose(hExeFile);
394
395 /* failure */
396 if(!NT_SUCCESS(nErrCode))
397 {
398 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode);
399 return (nErrCode);
400 }
401
402 /* STEP 2: create process */
403 nErrCode = NtCreateProcess
404 (
405 &hProcess,
406 PROCESS_ALL_ACCESS,
407 ProcessObjectAttributes,
408 InheritFromProcessHandle,
409 FALSE,
410 hExeImage,
411 NULL,
412 NULL
413 );
414
415 /* close image handle (not needed anymore) */
416 NtClose(hExeImage);
417
418 /* failure */
419 if(!NT_SUCCESS(nErrCode))
420 {
421 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode);
422 return (nErrCode);
423 }
424
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);
430
431 /* 3.1.1: adjust some fields */
432 pspdProcessData->ProcessData.Spawned = TRUE;
433
434 /* 3.2: allocate memory in the destination process */
435 nDestBufferSize = pspdProcessData->AllocSize;
436
437 nErrCode = NtAllocateVirtualMemory
438 (
439 hProcess,
440 &pDestBuffer,
441 0,
442 &nDestBufferSize,
443 MEM_COMMIT,
444 PAGE_READWRITE
445 );
446
447 /* failure */
448 if(!NT_SUCCESS(nErrCode))
449 {
450 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
451 goto undoPData;
452 }
453
454 /* 3.3: get pointer to the PEB */
455 nErrCode = NtQueryInformationProcess
456 (
457 hProcess,
458 ProcessBasicInformation,
459 &pbiProcessInfo,
460 sizeof(pbiProcessInfo),
461 NULL
462 );
463
464 /* failure */
465 if(!NT_SUCCESS(nErrCode))
466 {
467 ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode);
468 goto undoPData;
469 }
470
471 /* 3.4: write pointer to process data in the SubSystemData field of the PEB */
472 nErrCode = NtWriteVirtualMemory
473 (
474 hProcess,
475 (PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, SubSystemData)),
476 &pDestBuffer,
477 sizeof(PVOID),
478 NULL
479 );
480
481 /* failure */
482 if(!NT_SUCCESS(nErrCode))
483 {
484 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
485 goto undoPData;
486 }
487
488 /* 3.5: write the process data */
489 nErrCode = NtWriteVirtualMemory
490 (
491 hProcess,
492 pDestBuffer,
493 pspdProcessData,
494 pspdProcessData->AllocSize,
495 NULL
496 );
497
498 undoPData:
499 /* deallocate the temporary data block in the current process */
500 NtFreeVirtualMemory
501 (
502 NtCurrentProcess(),
503 (PVOID *)&pspdProcessData,
504 0,
505 MEM_RELEASE
506 );
507
508 /* failure */
509 if(!NT_SUCCESS(nErrCode))
510 return (nErrCode);
511
512 /* STEP 4: duplicate handles */
513 /* 4.1: handles in the structure itself */
514 /* 4.1.1: root directory */
515 nErrCode = NtDuplicateObject
516 (
517 NtCurrentProcess(),
518 ProcessData->RootHandle,
519 hProcess,
520 (PHANDLE)((ULONG)pDestBuffer + offsetof(__PDX_PDATA, RootHandle)),
521 0,
522 0,
523 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
524 );
525
526 /* failure */
527 if(!NT_SUCCESS(nErrCode))
528 {
529 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
530 goto failProcess;
531 }
532
533 /* 4.2: file descriptors table */
534 for
535 (
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 */
539 i = 0,
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)
546 )
547 /* FIXME? check the table's bitmap instead? */
548 if(ProcessData->FdTable.Descriptors[i].FileHandle != NULL)
549 {
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
556 (
557 NtCurrentProcess(),
558 ProcessData->FdTable.Descriptors[i].FileHandle,
559 hProcess,
560 (PHANDLE)(
561 (ULONG)pDestBuffer + nCurFilDesOffset + offsetof(__fildes_t, FileHandle)
562 ),
563 0,
564 0,
565 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
566 );
567
568 /* failure */
569 if(!NT_SUCCESS(nErrCode))
570 {
571 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
572 goto failProcess;
573 }
574 }
575
576 /* STEP 5: create first thread */
577 /* 5.1: get thunk routine's address */
578 RtlInitializeAnsiString(&strStartEntry, "LdrInitializeThunk");
579
580 nErrCode = LdrGetProcedureAddress
581 (
582 (PVOID)NTDLL_BASE,
583 &strStartEntry,
584 0,
585 &pStartAddress
586 );
587
588 /* failure */
589 if(!NT_SUCCESS(nErrCode))
590 {
591 ERR("LdrGetProcedureAddress() failed with status 0x%08X\n", nErrCode);
592 goto failProcess;
593 }
594
595 /* 5.2: set up the initial TEB */
596 itInitialTeb.StackAllocate = NULL;
597
598 /* FIXME: allow the caller to specify these values */
599 itInitialTeb.StackReserve = 0x100000;
600 itInitialTeb.StackCommit = itInitialTeb.StackReserve - PAGESIZE;
601
602 /* guard page */
603 itInitialTeb.StackCommit += PAGESIZE;
604
605 /* 5.2.1: set up the stack */
606 /* 5.2.1.1: reserve the stack */
607 nErrCode = NtAllocateVirtualMemory
608 (
609 hProcess,
610 &itInitialTeb.StackAllocate,
611 0,
612 &itInitialTeb.StackReserve,
613 MEM_RESERVE,
614 PAGE_READWRITE
615 );
616
617 /* failure */
618 if(!NT_SUCCESS(nErrCode))
619 {
620 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
621 goto failProcess;
622 }
623
624 itInitialTeb.StackBase =
625 (PVOID)((ULONG)itInitialTeb.StackAllocate + itInitialTeb.StackReserve);
626
627 itInitialTeb.StackLimit =
628 (PVOID)((ULONG)itInitialTeb.StackBase - itInitialTeb.StackCommit);
629
630 /* 5.2.1.2: commit the stack */
631 nErrCode = NtAllocateVirtualMemory
632 (
633 hProcess,
634 &itInitialTeb.StackLimit,
635 0,
636 &itInitialTeb.StackCommit,
637 MEM_COMMIT,
638 PAGE_READWRITE
639 );
640
641 /* failure */
642 if(!NT_SUCCESS(nErrCode))
643 {
644 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
645 goto failProcess;
646 }
647
648 /* 5.2.1.3: set up the guard page */
649 nErrCode = NtProtectVirtualMemory
650 (
651 hProcess,
652 itInitialTeb.StackLimit,
653 PAGESIZE,
654 PAGE_GUARD | PAGE_READWRITE,
655 NULL
656 );
657
658 /* failure */
659 if(!NT_SUCCESS(nErrCode))
660 {
661 ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode);
662 goto failProcess;
663 }
664
665 /* 5.2.1.4: initialize the thread context */
666 memset(&ctxThreadContext, 0, sizeof(ctxThreadContext));
667
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);
679
680 /* 5.3: create the thread object */
681 nErrCode = NtCreateThread
682 (
683 NULL,
684 THREAD_ALL_ACCESS,
685 NULL,
686 hProcess,
687 &ciClientId,
688 &ctxThreadContext,
689 &itInitialTeb,
690 FALSE
691 );
692
693 /* failure */
694 if(!NT_SUCCESS(nErrCode))
695 {
696 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode);
697 goto failProcess;
698 }
699
700 /* success */
701 return (STATUS_SUCCESS);
702
703 /* failure */
704 failProcess:
705 NtTerminateProcess
706 (
707 hProcess,
708 nErrCode
709 );
710
711 return (nErrCode);
712 }
713
714 /* EOF */
715