start service tasks separately in-case platforms need to perform additional set-up...
[AROS.git] / compiler / posixc / __vfork.c
blob829adce655609b331db54c03991658d3f956adaf
1 /*
2 Copyright © 2008-2013, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <proto/exec.h>
7 #include <proto/dos.h>
8 #include <proto/stdc.h>
9 #include <exec/exec.h>
10 #include <exec/tasks.h>
11 #include <dos/dos.h>
12 #include <libraries/stdc.h>
13 #include <aros/cpu.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #include <setjmp.h>
18 #include <errno.h>
19 #include <assert.h>
20 #include <stdlib.h>
21 #include <stdio.h>
23 #include "__posixc_intbase.h"
24 #include "__fdesc.h"
25 #include "__vfork.h"
26 #include "__exec.h"
28 #define DEBUG 0
30 #include <aros/debug.h>
31 #include <aros/startup.h>
33 /*****************************************************************************
35 NAME
36 #include <unistd.h>
38 pid_t vfork(
40 SYNOPSIS
41 void)
43 FUNCTION
44 Function to create a subprocess of the current process.
46 This is there to ease porting of software using the fork()/vfork()
47 POSIX functions. Due to a different memory and process model, fork()
48 is not implemented at the moment in the C library. vfork() is provided
49 with some extended functionality. In the POSIX standard the only
50 guaranteed functionality for vfork() is to have an exec*() function or
51 exit() called right after the vfork() in the child.
53 Extra functionality for vfork():
54 - The child has its own memory heap; memory allocation/deallocation
55 is allowed and the heap will be removed when calling _exit() or will
56 be used for the code started by the exec*() functions.
57 - The child will have a copy of the file descriptors as specified by
58 the POSIX standard for the fork() function. File I/O is possible in
59 the child, as is file manipulation with dup() etc.
61 Difference with fork():
62 - The virtual memory heap is not duplicated as in POSIX but the memory
63 is shared between parent and child. AROS lives in one big single
64 memory region so changes to memory in the child are also seen by the
65 parent.
67 Behaviour for other resources not described in this doc may not be
68 relied on for future compatibility.
70 INPUTS
73 RESULT
74 -1: error, no child is started; errno will be set.
75 0: Running in child
76 >0: Running in parent, pid of child is return value.
78 NOTES
79 Current implementation of vfork() will only really start running things
80 in parallel on an exec*() call. After vfork(), child code will run until
81 _exit() or exec*(). With _exit(), the child will exit and the parent
82 will continue; with exec*(), the child will be detached and the parent
83 will continue.
85 EXAMPLE
87 BUGS
89 SEE ALSO
90 execl(), execve(), execlp(), execv(), execvp()
92 INTERNALS
94 ******************************************************************************/
96 /* The following functions are used to update the child's and parent's privdata
97 for the parent pretending to be running as child and for the child to take
98 over. It is called in the following sequence:
99 parent_enterpretendchild() is called in vfork() so the parent pretends to be
100 running as child; child_takeover() is called by child if exec*() so it can
101 continue from the parent state; parent_leavepretendchild() is called by
102 parent to switch back to be running as parent
104 static void parent_enterpretendchild(struct vfork_data *udata);
105 static void child_takeover(struct vfork_data *udata);
106 static void parent_leavepretendchild(struct vfork_data *udata);
107 static void parent_createchild(struct vfork_data *udata);
109 static __attribute__((noinline)) void __vfork_exit_controlled_stack(struct vfork_data *udata);
111 LONG launcher()
113 D(bug("launcher: Entered child launcher, ThisTask=%p\n", FindTask(NULL)));
115 struct Task *this = FindTask(NULL);
116 struct vfork_data *udata = this->tc_UserData;
117 BYTE child_signal;
118 struct PosixCIntBase *PosixCBase = NULL;
119 jmp_buf exec_exitjmp; /* jmp_buf for when calling __exec_do */
120 int exec_error; /* errno for when calling __exec_do */
121 LONG ret = 0;
123 /* Allocate signal for parent->child communication */
124 child_signal = udata->child_signal = AllocSignal(-1);
125 D(bug("launcher: Allocated child signal: %d\n", udata->child_signal));
126 if (udata->child_signal == -1)
128 /* Lie */
129 udata->child_errno = ENOMEM;
130 SETCHILDSTATE(CHILD_STATE_SETUP_FAILED);
131 Signal(udata->parent, 1 << udata->parent_signal);
132 return -1;
135 /* TODO: Can we avoid opening posixc.library for child process? */
136 PosixCBase = (struct PosixCIntBase *)OpenLibrary((STRPTR) "posixc.library", 0);
137 if (PosixCBase)
139 PosixCBase->PosixCBase.StdCBase = (struct StdCBase *)OpenLibrary((STRPTR) "stdc.library", 0);
140 if (!PosixCBase->PosixCBase.StdCBase)
142 CloseLibrary((struct Library *)PosixCBase);
143 PosixCBase = NULL;
146 if (!PosixCBase)
148 D(bug("launcher:Failed to open libraries!\n"));
149 FreeSignal(child_signal);
150 udata->child_errno = ENOMEM;
151 SETCHILDSTATE(CHILD_STATE_SETUP_FAILED);
152 Signal(udata->parent, 1 << udata->parent_signal);
153 return -1;
155 D(bug("launcher: Opened PosixCBase: %x, StdCBase: %x\n",
156 PosixCBase, PosixCBase->PosixCBase.StdCBase
159 udata->child_posixcbase = PosixCBase;
161 if (setjmp(exec_exitjmp) == 0)
163 /* Setup complete, signal parent */
164 D(bug("launcher: Signaling parent that we finished setup\n"));
165 SETCHILDSTATE(CHILD_STATE_SETUP_FINISHED);
166 Signal(udata->parent, 1 << udata->parent_signal);
168 D(bug("launcher: Child waiting for exec or exit\n"));
169 Wait(1 << udata->child_signal);
170 ASSERTPARENTSTATE(PARENT_STATE_EXEC_CALLED | PARENT_STATE_EXIT_CALLED);
171 PRINTSTATE;
173 if (udata->child_executed)
175 APTR exec_id;
177 D(bug("launcher: child executed\n"));
179 child_takeover(udata);
181 /* Filenames passed from parent obey parent's doupath */
183 PosixCBase->doupath = udata->parent_posixcbase->doupath;
184 D(bug("launcher: doupath == %d for __exec_prepare()\n", PosixCBase->doupath));
186 exec_id = udata->exec_id = __exec_prepare(
187 udata->exec_filename,
189 udata->exec_argv,
190 udata->exec_envp
193 /* Reset handling of upath */
194 PosixCBase->doupath = 0;
195 udata->child_errno = errno;
197 D(bug("launcher: informing parent that we have run __exec_prepare\n"));
198 /* Inform parent that we have run __exec_prepare */
199 SETCHILDSTATE(CHILD_STATE_EXEC_PREPARE_FINISHED);
200 Signal(udata->parent, 1 << udata->parent_signal);
202 /* Wait 'till __exec_do() is called on parent process */
203 D(bug("launcher: Waiting parent to get the result\n"));
204 Wait(1 << udata->child_signal);
205 ASSERTPARENTSTATE(PARENT_STATE_EXEC_DO_FINISHED);
206 PRINTSTATE;
208 D(bug("launcher: informing parent that we won't use udata anymore\n"));
209 /* Inform parent that we won't use udata anymore */
210 SETCHILDSTATE(CHILD_STATE_UDATA_NOT_USED);
211 Signal(udata->parent, 1 << udata->parent_signal);
213 D(bug("launcher: waiting for parent to be after _exit()\n"));
214 Wait(1 << udata->child_signal);
215 ASSERTPARENTSTATE(PARENT_STATE_STOPPED_PRETENDING);
216 PRINTSTATE;
218 if (exec_id)
220 D(bug("launcher: catch _exit()\n"));
221 __stdc_program_startup(exec_exitjmp, &exec_error);
223 D(bug("launcher: executing command\n"));
224 __exec_do(exec_id);
226 assert(0); /* Should not be reached */
228 else
230 D(bug("launcher: __exec_prepare returned with an error\n"));
231 ret = -1;
234 else /* !udata->child_executed */
236 D(bug("launcher: child not executed\n"));
238 D(bug("launcher: informing parent that we won't use udata anymore\n"));
239 /* Inform parent that we won't use udata anymore */
240 SETCHILDSTATE(CHILD_STATE_UDATA_NOT_USED);
241 Signal(udata->parent, 1 << udata->parent_signal);
244 else
246 /* child exited */
247 D(bug("launcher: catched _exit(), errno=%d\n", exec_error));
250 D(bug("launcher: freeing child_signal\n"));
251 FreeSignal(child_signal);
253 D(bug("launcher: closing libraries\n"));
254 CloseLibrary((struct Library *)PosixCBase->PosixCBase.StdCBase);
255 CloseLibrary((struct Library *)PosixCBase);
257 D(bug("Child Done\n"));
259 return ret;
262 /* This can be good for debugging */
263 #ifdef __arm__
264 #define SP 8
265 #define ALT 27
266 #endif
268 #ifndef SP
269 #define SP _JMPLEN - 1
270 #define ALT _JMPLEN - 1
271 #endif
273 pid_t __vfork(jmp_buf env)
275 struct PosixCIntBase *PosixCBase =
276 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
277 struct Task *this = FindTask(NULL);
278 struct ETask *etask = NULL;
279 struct vfork_data *udata = AllocMem(sizeof(struct vfork_data), MEMF_ANY | MEMF_CLEAR);
280 if (udata == NULL)
282 errno = ENOMEM;
283 vfork_longjmp(env, -1);
285 D(bug("__vfork: Parent: allocated udata %p, jmp_buf %p\n", udata, udata->vfork_jmp));
286 *udata->vfork_jmp = *env;
288 D(bug("__vfork: Parent: initial jmp_buf %p\n", env));
289 D(bug("__vfork: Parent: ip: %p, stack: %p, alt: 0x%p\n", env->retaddr, env->regs[SP], env->regs[ALT]));
290 D(bug("__vfork: Parent: Current altstack 0x%p\n", *((void **) this->tc_SPLower)));
291 D(hexdump(env, 0, sizeof(jmp_buf) + sizeof(void *) * 4));
293 udata->parent = this;
294 udata->prev = PosixCBase->vfork_data;
296 D(bug("__vfork: Parent: Saved old parent's vfork_data: %p\n", udata->prev));
297 udata->parent_posixcbase = PosixCBase;
299 parent_createchild(udata);
301 D(bug("__vfork: Parent: Setting jmp_buf at %p\n", udata->parent_newexitjmp));
302 if (setjmp(udata->parent_newexitjmp) == 0)
304 udata->parent_olderrorptr = __stdc_set_errorptr(&udata->child_error);
305 udata->child_error = *udata->parent_olderrorptr;
306 __stdc_set_exitjmp(udata->parent_newexitjmp, udata->parent_oldexitjmp);
308 parent_enterpretendchild(udata);
310 D(bug("__vfork: ParentPretendingChild(%p) jumping to jmp_buf %p\n",
311 udata->parent, &udata->vfork_jmp
313 D(bug("__vfork: ip: %p, stack: %p alt: %p\n",
314 udata->vfork_jmp[0].retaddr, udata->vfork_jmp[0].regs[SP],
315 udata->vfork_jmp[0].regs[ALT]
318 vfork_longjmp(udata->vfork_jmp, 0);
319 assert(0); /* not reached */
320 return (pid_t) 0;
322 else /* setjmp() != 0; _exit() was called */
324 D(bug("__vfork: ParentPretendingChild: exiting or executed\n"));
326 /* Stack may have been overwritten when we return here,
327 * we jump to here from a function lower in the call chain
329 PosixCBase = (struct PosixCIntBase *)__aros_getbase_PosixCBase();
330 udata = PosixCBase->vfork_data;
332 D(bug("__vfork: ParentPretendingChild: vfork_data = %x\n", udata));
334 if (!udata->child_executed)
336 D(bug("__vfork: ParentPretendingChild: not executed\n"));
338 /* et_Result is normally set in startup code but no exec was performed
339 so we have to mimic the startup code
341 etask = GetETask(udata->child);
342 if (etask)
343 etask->et_Result1 = udata->child_error;
345 D(bug("__vfork: ParentPretendingChild: Signaling child %p, signal %d\n", udata->child, udata->child_signal));
346 SETPARENTSTATE(PARENT_STATE_EXIT_CALLED);
347 Signal(udata->child, 1 << udata->child_signal);
350 D(bug("__vfork: ParentPretendingChild: Waiting for child to finish using udata, me=%p, signal %d\n", FindTask(NULL),
351 udata->parent_signal));
352 /* Wait for child to finish using udata */
353 Wait(1 << udata->parent_signal);
354 ASSERTCHILDSTATE(CHILD_STATE_UDATA_NOT_USED);
355 PRINTSTATE;
357 D(bug("__vfork: ParentPretendingChild: fflushing\n"));
358 fflush(NULL);
360 __vfork_exit_controlled_stack(udata);
362 assert(0); /* not reached */
363 return (pid_t) 1;
368 * The sole purpose of this function is to enable control over allocation of dummy and env.
369 * Previously they were allocated in the ending code of __vfork function. On ARM however
370 * this was causing immediate allocation of space at entry to the __vfork function. Moreover
371 * the jmp_buf is aligned(16) and as such is represented on the stack as a pointer to stack
372 * region instead of offset from stack base.
374 * The exit block of __vfork function represents code that underwent a number of longjumps. The
375 * stack there is not guaranteed to be preserved, thus the on-stack pointer representing dummy
376 * and env were also damaged. Extracting the code below allows to control when the variables
377 * are allocated (as long as the function remains not inlined).
379 static __attribute__((noinline)) void __vfork_exit_controlled_stack(struct vfork_data *udata)
381 jmp_buf dummy;
382 jmp_buf env;
384 D(bug("__vfork: ParentPretendingChild: freeing parent signal\n"));
385 FreeSignal(udata->parent_signal);
387 errno = udata->child_errno;
389 /* leavepretendchild will restore old StdCBase and thus also
390 old startup jmp_buf.
391 This is also the reason this function has to be called before
392 signaling child that we are after _exit().
394 parent_leavepretendchild(udata);
396 if(udata->child_executed)
398 D(bug("__vfork: Inform child that we are after _exit()\n"));
399 SETPARENTSTATE(PARENT_STATE_STOPPED_PRETENDING);
400 Signal(udata->child, 1 << udata->child_signal);
403 D(bug("__vfork: Parent: restoring startup buffer\n"));
404 /* Restore parent errorptr and startup buffer */
405 __stdc_set_errorptr(udata->parent_olderrorptr);
406 __stdc_set_exitjmp(udata->parent_oldexitjmp, dummy);
408 /* Save some data from udata before udata is being freed */
409 *env = *udata->vfork_jmp;
411 D(bug("__vfork: Parent: freeing udata\n"));
412 FreeMem(udata, sizeof(struct vfork_data));
414 D(bug("__vfork: Parent jumping to jmp_buf %p\n", env));
415 D(bug("__vfork: ip: %p, stack: %p\n", env->retaddr, env->regs[SP]));
416 vfork_longjmp(env, GetETaskID(udata->child));
419 static void parent_createchild(struct vfork_data *udata)
421 struct PosixCIntBase *PosixCBase =
422 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
423 jmp_buf vfork_jmp;
425 *vfork_jmp = *udata->vfork_jmp;
427 struct TagItem tags[] =
429 { NP_Entry, (IPTR) launcher },
430 { NP_CloseInput, (IPTR) FALSE },
431 { NP_CloseOutput, (IPTR) FALSE },
432 { NP_CloseError, (IPTR) FALSE },
433 { NP_Cli, (IPTR) TRUE },
434 { NP_Name, (IPTR) "vfork()" },
435 { NP_UserData, (IPTR) udata },
436 { NP_NotifyOnDeath, (IPTR) TRUE },
437 { TAG_DONE, 0 }
440 D(bug("__vfork: Parent: Allocating parent signal\n"));
441 /* Allocate signal for child->parent communication */
442 udata->parent_signal = AllocSignal(-1);
443 if (udata->parent_signal == -1)
445 /* Couldn't allocate the signal, return -1 */
446 FreeMem(udata, sizeof(struct vfork_data));
447 errno = ENOMEM;
448 vfork_longjmp(vfork_jmp, -1);
451 PosixCBase->flags |= VFORK_PARENT;
453 D(bug("__vfork: Parent: Creating child\n"));
454 udata->child = (struct Task*) CreateNewProc(tags);
456 if (udata->child == NULL)
458 D(bug("__vfork: Child could not be created\n"));
459 /* Something went wrong, return -1 */
460 FreeMem(udata, sizeof(struct vfork_data));
461 errno = ENOMEM; /* Most likely */
462 vfork_longjmp(vfork_jmp, -1);
464 D(bug("__vfork: Parent: Child created %p, waiting to finish setup\n", udata->child));
466 /* Wait for child to finish setup */
467 Wait(1 << udata->parent_signal);
468 ASSERTCHILDSTATE(CHILD_STATE_SETUP_FAILED | CHILD_STATE_SETUP_FINISHED);
469 PRINTSTATE;
471 if (udata->child_errno)
473 /* An error occured during child setup */
474 D(bug("__vfork: Child returned an error (%d)\n", udata->child_errno));
475 errno = udata->child_errno;
476 vfork_longjmp(vfork_jmp, -1);
479 PosixCBase->flags &= ~VFORK_PARENT;
482 static void parent_enterpretendchild(struct vfork_data *udata)
484 struct PosixCIntBase *PosixCBase =
485 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
486 D(bug("parent_enterpretendchild(%x): entered\n", udata));
488 PosixCBase->vfork_data = udata;
490 /* Remember and switch StdCBase */
491 udata->parent_stdcbase = PosixCBase->PosixCBase.StdCBase;
492 PosixCBase->PosixCBase.StdCBase = udata->child_posixcbase->PosixCBase.StdCBase;
493 /* _[eE]xit() can also be called with the switched StdCBase so we also
494 register the exit jmp_buf in this StdCBase. We don't need to remember
495 old as child will overwrite these if it should call __exec_do().
497 __stdc_program_startup(udata->parent_newexitjmp, &udata->child_error);
499 /* Remember and switch env var list */
500 udata->parent_env_list = PosixCBase->env_list;
501 PosixCBase->env_list = udata->child_posixcbase->env_list;
503 /* Remember and switch fd descriptor table */
504 udata->parent_internalpool = PosixCBase->internalpool;
505 PosixCBase->internalpool = udata->child_posixcbase->internalpool;
506 __getfdarray((APTR *)&udata->parent_fd_array, &udata->parent_numslots);
507 __setfdarraybase(udata->child_posixcbase);
509 /* Remember and switch chdir fields */
510 udata->parent_cd_changed = PosixCBase->cd_changed;
511 PosixCBase->cd_changed = udata->child_posixcbase->cd_changed;
512 udata->parent_cd_lock = PosixCBase->cd_lock;
513 PosixCBase->cd_lock = udata->child_posixcbase->cd_lock;
514 udata->parent_curdir = CurrentDir(((struct Process *)udata->child)->pr_CurrentDir);
516 /* Remember and switch upathbuf */
517 udata->parent_upathbuf = PosixCBase->upathbuf;
518 PosixCBase->upathbuf = udata->child_posixcbase->upathbuf;
520 /* Pretend to be running as the child created by vfork */
521 udata->parent_flags = PosixCBase->flags;
522 PosixCBase->flags |= PRETEND_CHILD;
524 D(bug("parent_enterpretendchild: leaving\n"));
527 static void child_takeover(struct vfork_data *udata)
529 struct PosixCIntBase *PosixCBase =
530 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
531 D(bug("child_takeover(%x): entered\n", udata));
533 /* Set current dir to parent's current dir */
534 PosixCBase->cd_changed = udata->parent_posixcbase->cd_changed;
535 PosixCBase->cd_lock = udata->parent_posixcbase->cd_lock;
536 CurrentDir(((struct Process *)udata->parent)->pr_CurrentDir);
538 D(bug("child_takeover(): leaving\n"));
541 static void parent_leavepretendchild(struct vfork_data *udata)
543 struct PosixCIntBase *PosixCBase =
544 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
545 D(bug("parent_leavepretendchild(%x): entered\n", udata));
547 /* Restore parent's StdCBase */
548 PosixCBase->PosixCBase.StdCBase = udata->parent_stdcbase;
550 /* Restore parent's env var list */
551 PosixCBase->env_list = udata->parent_env_list;
553 /* Restore parent's old fd_array */
554 PosixCBase->internalpool = udata->parent_internalpool;
555 __setfdarray(udata->parent_fd_array, udata->parent_numslots);
557 /* Switch to currentdir from before vfork() call */
558 PosixCBase->cd_changed = udata->parent_cd_changed;
559 PosixCBase->cd_lock = udata->parent_cd_lock;
560 CurrentDir(udata->parent_curdir);
562 /* Restore parent's upathbuf */
563 PosixCBase->upathbuf = udata->parent_upathbuf;
565 /* Switch to previous vfork_data */
566 PosixCBase->vfork_data = udata->prev;
567 PosixCBase->flags = udata->parent_flags;
569 D(bug("parent_leavepretendchild: leaving\n"));