Removed double NAME entry.
[AROS.git] / compiler / clib / __vfork.c
blob8a9234dd6e7850dc0f2e1075d3f0af9122196ce9
1 /*
2 Copyright © 2008-2012, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <proto/exec.h>
7 #include <proto/dos.h>
8 #include <exec/exec.h>
9 #include <exec/tasks.h>
10 #include <dos/dos.h>
11 #include <aros/cpu.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <setjmp.h>
16 #include <errno.h>
17 #include <assert.h>
18 #include <stdlib.h>
19 #include <stdio.h>
21 #include "__arosc_privdata.h"
22 #include "__fdesc.h"
23 #include "__vfork.h"
24 #include "__exec.h"
26 #define DEBUG 0
28 #include <aros/debug.h>
29 #include <aros/startup.h>
31 /* The following functions are used to update the childs and parents privdata
32 for the parent pretending to be running as child and for the child to take over. It is called
33 in the following sequence:
34 parent_enterpretendchild() is called in vfork so the parent pretends to be running as child
35 child_takeover() is called by child if exec*() so it can continue from the parent state
36 parent_leavepretendchild() is called by parent to switch back to be running as parent
38 static void parent_enterpretendchild(struct vfork_data *udata);
39 static void child_takeover(struct vfork_data *udata);
40 static void parent_leavepretendchild(struct vfork_data *udata);
42 LONG launcher()
44 D(bug("launcher: Entered child launcher, ThisTask=%p\n", SysBase->ThisTask));
46 struct Task *this = FindTask(NULL);
47 struct vfork_data *udata = this->tc_UserData;
48 BYTE child_signal;
49 struct aroscbase *aroscbase = NULL, *pbase = udata->parent_aroscbase;
51 /* Allocate signal for parent->child communication */
52 child_signal = udata->child_signal = AllocSignal(-1);
53 D(bug("launcher: Allocated child signal: %d\n", udata->child_signal));
54 if(udata->child_signal == -1)
56 /* Lie */
57 udata->child_errno = ENOMEM;
58 Signal(udata->parent, 1 << udata->parent_signal);
59 return -1;
62 if(__register_init_fdarray(pbase))
63 aroscbase = (struct aroscbase *)OpenLibrary((STRPTR) "arosc.library", 0);
64 if(!aroscbase)
66 FreeSignal(child_signal);
67 udata->child_errno = ENOMEM;
68 Signal(udata->parent, 1 << udata->parent_signal);
69 return -1;
71 D(bug("launcher: Opened aroscbase: %x\n", aroscbase));
73 aroscbase->acb_flags |= VFORK_PARENT;
75 udata->child_aroscbase = aroscbase;
76 aroscbase->acb_parent_does_upath = pbase->acb_doupath;
78 if(setjmp(udata->child_exitjmp) == 0)
80 __arosc_program_startup(udata->child_exitjmp, &udata->child_error);
82 /* Setup complete, signal parent */
83 D(bug("launcher: Signaling parent that we finished setup\n"));
84 Signal(udata->parent, 1 << udata->parent_signal);
86 D(bug("launcher: Child waiting for exec or exit\n"));
87 Wait(1 << udata->child_signal);
89 if(udata->child_executed)
91 APTR exec_id;
93 D(bug("launcher: child executed\n"));
95 child_takeover(udata);
97 /* Filenames passed from parent obey parent's acb_doupath */
99 aroscbase->acb_doupath = udata->child_aroscbase->acb_parent_does_upath;
100 D(bug("launcher: acb_doupath == %d for __exec_prepare()\n", aroscbase->acb_doupath));
102 exec_id = udata->exec_id = __exec_prepare(
103 udata->exec_filename,
105 udata->exec_argv,
106 udata->exec_envp
109 /* Reset handling of upath */
110 aroscbase->acb_doupath = 0;
111 udata->child_errno = errno;
113 D(bug("launcher: informing parent that we have run __exec_prepare\n"));
114 /* Inform parent that we have run __exec_prepare */
115 Signal(udata->parent, 1 << udata->parent_signal);
117 /* Wait 'till __exec_do() is called on parent process */
118 D(bug("launcher: Waiting parent to get the result\n"));
119 Wait(1 << udata->child_signal);
121 D(bug("launcher: informing parent that we won't use udata anymore\n"));
122 /* Inform parent that we won't use udata anymore */
123 Signal(udata->parent, 1 << udata->parent_signal);
125 if (exec_id)
127 D(bug("launcher: executing command\n"));
128 __exec_do(exec_id);
130 assert(0); /* Should not be reached */
132 else
134 D(bug("launcher: exit because execve returned with an error\n"));
135 _exit(0);
138 else /* !udata->child_executed */
140 D(bug("launcher: child not executed\n"));
142 D(bug("launcher: informing parent that we won't use udata anymore\n"));
143 /* Inform parent that we won't use udata anymore */
144 Signal(udata->parent, 1 << udata->parent_signal);
147 else
149 D(bug("launcher: freeing child_signal\n"));
150 FreeSignal(child_signal);
153 CloseLibrary((struct Library *)aroscbase);
155 D(bug("Child Done\n"));
157 return 0;
160 /* This can be good for debugging */
161 #ifdef __arm__
162 #define SP 8
163 #define ALT 27
164 #endif
166 #ifndef SP
167 #define SP _JMPLEN - 1
168 #define ALT _JMPLEN - 1
169 #endif
171 pid_t __vfork(jmp_buf env)
173 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
174 struct Task *this = FindTask(NULL);
175 struct ETask *etask = NULL;
176 struct vfork_data *udata = AllocMem(sizeof(struct vfork_data), MEMF_ANY | MEMF_CLEAR);
177 if(udata == NULL)
179 errno = ENOMEM;
180 vfork_longjmp(env, -1);
182 D(bug("__vfork: Parent: allocated udata %p, jmp_buf %p\n", udata, udata->vfork_jmp));
183 *udata->vfork_jmp = *env;
185 struct TagItem tags[] =
187 { NP_Entry, (IPTR) launcher },
188 { NP_CloseInput, (IPTR) FALSE },
189 { NP_CloseOutput, (IPTR) FALSE },
190 { NP_CloseError, (IPTR) FALSE },
191 { NP_Cli, (IPTR) TRUE },
192 { NP_Name, (IPTR) "vfork()" },
193 { NP_UserData, (IPTR) udata },
194 { NP_NotifyOnDeath, (IPTR) TRUE },
195 { TAG_DONE, 0 }
198 D(bug("__vfork: Parent: initial jmp_buf %p\n", env));
199 D(bug("__vfork: Parent: ip: %p, stack: %p, alt: 0x%p\n", env->retaddr, env->regs[SP], env->regs[ALT]));
200 D(bug("__vfork: Parent: Current altstack 0x%p\n", *((void **)this->tc_SPLower)));
201 D(hexdump(env, 0, sizeof(jmp_buf) + sizeof(void *) * 4));
203 udata->parent = this;
204 udata->prev = aroscbase->acb_vfork_data;
206 D(bug("__vfork: Parent: Saved old parent's vfork_data: %p\n", udata->prev));
207 udata->parent_aroscbase = aroscbase;
209 D(bug("__vfork: Parent: Allocating parent signal\n"));
210 /* Allocate signal for child->parent communication */
211 udata->parent_signal = AllocSignal(-1);
212 if(udata->parent_signal == -1)
214 /* Couldn't allocate the signal, return -1 */
215 FreeMem(udata, sizeof(struct vfork_data));
216 errno = ENOMEM;
217 vfork_longjmp(env, -1);
220 D(bug("__vfork: Parent: Creating child\n"));
221 udata->child = (struct Task*) CreateNewProc(tags);
223 if(udata->child == NULL)
225 /* Something went wrong, return -1 */
226 FreeMem(udata, sizeof(struct vfork_data));
227 errno = ENOMEM; /* Most likely */
228 vfork_longjmp(env, -1);
230 D(bug("__vfork: Parent: Child created %p, waiting to finish setup\n", udata->child));
231 udata->child_id = GetETaskID(udata->child);
232 D(bug("__vfork: Parent: Got unique child id: %d\n", udata->child_id));
234 /* Wait for child to finish setup */
235 Wait(1 << udata->parent_signal);
237 if(udata->child_errno)
239 /* An error occured during child setup */
240 errno = udata->child_errno;
241 vfork_longjmp(env, -1);
244 D(bug("__vfork: Parent: Setting jmp_buf at %p\n", udata->parent_newexitjmp));
245 if(setjmp(udata->parent_newexitjmp) == 0)
247 udata->parent_olderrorptr = __arosc_set_errorptr(&udata->child_error);
248 udata->child_error = *udata->parent_olderrorptr;
249 __arosc_set_exitjmp(udata->parent_newexitjmp, udata->parent_oldexitjmp);
251 parent_enterpretendchild(udata);
253 D(bug("__vfork: Child %d jumping to jmp_buf %p\n", udata->child_id, &udata->vfork_jmp));
254 D(bug("__vfork: ip: %p, stack: %p alt: %p\n", udata->vfork_jmp[0].retaddr, udata->vfork_jmp[0].regs[SP],
255 udata->vfork_jmp[0].regs[ALT]));
257 vfork_longjmp(udata->vfork_jmp, 0);
258 assert(0); /* not reached */
259 return (pid_t) 0;
261 else /* setjmp() != 0; so child has exited() */
263 D(bug("__vfork: Child: child exiting\n or executed\n"));
265 /* Stack may have been overwritten when we return here,
266 * we jump to here from a function lower in the call chain
268 aroscbase = __aros_getbase_aroscbase();
269 udata = aroscbase->acb_vfork_data;
271 D(bug("__vfork: Child: acb_vfork_data = %x\n", udata));
273 if(!udata->child_executed)
275 D(bug("__vfork: Child: not executed\n"));
277 /* et_Result is normally set in startup code but no exec was performed
278 so we have to mimic the startup code
280 etask = GetETask(udata->child);
281 if (etask)
282 etask->et_Result1 = udata->child_error;
284 D(bug("__vfork: Child: Signaling child %p, signal %d\n", udata->child, udata->child_signal));
285 Signal(udata->child, 1 << udata->child_signal);
288 D(bug("__vfork: Parent: Waiting for child to finish using udata, me=%p, signal %d\n", FindTask(NULL), udata->parent_signal));
289 /* Wait for child to finish using udata */
290 Wait(1 << udata->parent_signal);
292 D(bug("__vfork: Parent: fflushing\n"));
293 fflush(NULL);
295 D(bug("__vfork: Parent: restoring startup buffer\n"));
296 /* Restore parent startup buffer */
297 jmp_buf dummy;
298 __arosc_set_exitjmp(udata->parent_oldexitjmp, dummy);
300 D(bug("__vfork: Parent: freeing parent signal\n"));
301 FreeSignal(udata->parent_signal);
303 errno = udata->child_errno;
305 parent_leavepretendchild(udata);
307 /* Save some data from udata before udata is being freed */
308 ULONG child_id = udata->child_id;
309 jmp_buf env; *env = *udata->vfork_jmp;
311 D(bug("__vfork: Parent: freeing udata\n"));
312 FreeMem(udata, sizeof(struct vfork_data));
314 D(bug("__vfork: Parent jumping to jmp_buf %p (child=%d)\n", env, child_id));
315 D(bug("__vfork: ip: %p, stack: %p\n", env->retaddr, env->regs[SP]));
316 vfork_longjmp(env, child_id);
317 assert(0); /* not reached */
318 return (pid_t) 1;
323 static void parent_enterpretendchild(struct vfork_data *udata)
325 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
326 D(bug("parent_enterpretendchild(%x): entered\n", udata));
328 aroscbase->acb_vfork_data = udata;
330 /* Remember and switch malloc mempool */
331 udata->parent_mempool = aroscbase->acb_mempool;
332 aroscbase->acb_mempool = udata->child_aroscbase->acb_mempool;
334 /* Remember and switch env var list */
335 udata->parent_env_list = aroscbase->acb_env_list;
336 aroscbase->acb_env_list = udata->child_aroscbase->acb_env_list;
338 /* Remember and switch fd descriptor table */
339 udata->parent_internalpool = aroscbase->acb_internalpool;
340 aroscbase->acb_internalpool = udata->child_aroscbase->acb_internalpool;
341 __getfdarray((APTR *)&udata->parent_fd_array, &udata->parent_numslots);
342 __setfdarraybase(udata->child_aroscbase);
344 /* Remember and switch chdir fields */
345 udata->parent_cd_changed = aroscbase->acb_cd_changed;
346 aroscbase->acb_cd_changed = udata->child_aroscbase->acb_cd_changed;
347 udata->parent_cd_lock = aroscbase->acb_cd_lock;
348 aroscbase->acb_cd_lock = udata->child_aroscbase->acb_cd_lock;
349 udata->parent_curdir = CurrentDir(((struct Process *)udata->child)->pr_CurrentDir);
351 /* Pretend to be running as the child created by vfork */
352 aroscbase->acb_flags |= PRETEND_CHILD;
354 D(bug("parent_enterpretendchild: leaving\n"));
357 static void child_takeover(struct vfork_data *udata)
359 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
360 D(bug("child_takeover(%x): entered\n", udata));
362 /* Set current dir to parent's current dir */
363 aroscbase->acb_cd_changed = udata->parent_aroscbase->acb_cd_changed;
364 aroscbase->acb_cd_lock = udata->parent_aroscbase->acb_cd_lock;
365 CurrentDir(((struct Process *)udata->parent)->pr_CurrentDir);
367 D(bug("child_takeover(): leaving\n"));
370 static void parent_leavepretendchild(struct vfork_data *udata)
372 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
373 D(bug("parent_leavepretendchild(%x): entered\n", udata));
375 /* Restore parent's malloc mempool */
376 aroscbase->acb_mempool = udata->parent_mempool;
378 /* Restore parent's env var list */
379 aroscbase->acb_env_list = udata->parent_env_list;
381 /* Restore parent's old fd_array */
382 aroscbase->acb_internalpool = udata->parent_internalpool;
383 __setfdarray(udata->parent_fd_array, udata->parent_numslots);
385 /* Switch to currentdir from before vfork() call */
386 aroscbase->acb_cd_changed = udata->parent_cd_changed;
387 aroscbase->acb_cd_lock = udata->parent_cd_lock;
388 CurrentDir(udata->parent_curdir);
390 /* Switch to previous vfork_data */
391 aroscbase->acb_vfork_data = udata->prev;
392 if(aroscbase->acb_vfork_data == NULL)
393 aroscbase->acb_flags &= ~PRETEND_CHILD;
395 D(bug("parent_leavepretendchild: leaving\n"));