Detab
[AROS.git] / compiler / clib / __vfork.c
bloba6263b008a3802cd8de60d2492c8c12cb9bc0a19
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\n"));
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
140 D(bug("launcher: informing parent that we won't use udata anymore\n"));
141 /* Inform parent that we won't use udata anymore */
142 Signal(udata->parent, 1 << udata->parent_signal);
145 else
147 D(bug("launcher: freeing child_signal\n"));
148 FreeSignal(child_signal);
151 CloseLibrary((struct Library *)aroscbase);
153 return 0;
156 /* This can be good for debugging */
157 #ifdef __arm__
158 #define SP 8
159 #define ALT 27
160 #endif
162 #ifndef SP
163 #define SP _JMPLEN - 1
164 #define ALT _JMPLEN - 1
165 #endif
167 pid_t __vfork(jmp_buf env)
169 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
170 struct Task *this = FindTask(NULL);
171 struct ETask *etask = NULL;
172 struct vfork_data *udata = AllocMem(sizeof(struct vfork_data), MEMF_ANY | MEMF_CLEAR);
173 if(udata == NULL)
175 errno = ENOMEM;
176 vfork_longjmp(env, -1);
178 D(bug("__vfork: Parent: allocated udata %p, jmp_buf %p\n", udata, udata->vfork_jmp));
179 *udata->vfork_jmp = *env;
181 struct TagItem tags[] =
183 { NP_Entry, (IPTR) launcher },
184 { NP_CloseInput, (IPTR) FALSE },
185 { NP_CloseOutput, (IPTR) FALSE },
186 { NP_CloseError, (IPTR) FALSE },
187 { NP_Cli, (IPTR) TRUE },
188 { NP_Name, (IPTR) "vfork()" },
189 { NP_UserData, (IPTR) udata },
190 { NP_NotifyOnDeath, (IPTR) TRUE },
191 { TAG_DONE, 0 }
194 D(bug("__vfork: Parent: initial jmp_buf %p\n", env));
195 D(bug("__vfork: Parent: ip: %p, stack: %p, alt: 0x%p\n", env->retaddr, env->regs[SP], env->regs[ALT]));
196 D(bug("__vfork: Parent: Current altstack 0x%p\n", *((void **)this->tc_SPLower)));
197 D(hexdump(env, 0, sizeof(jmp_buf) + sizeof(void *) * 4));
199 udata->parent = this;
200 udata->prev = aroscbase->acb_vfork_data;
202 D(bug("__vfork: Parent: Saved old parent's vfork_data: %p\n", udata->prev));
203 udata->parent_aroscbase = aroscbase;
205 D(bug("__vfork: Parent: Allocating parent signal\n"));
206 /* Allocate signal for child->parent communication */
207 udata->parent_signal = AllocSignal(-1);
208 if(udata->parent_signal == -1)
210 /* Couldn't allocate the signal, return -1 */
211 FreeMem(udata, sizeof(struct vfork_data));
212 errno = ENOMEM;
213 vfork_longjmp(env, -1);
216 D(bug("__vfork: Parent: Creating child\n"));
217 udata->child = (struct Task*) CreateNewProc(tags);
219 if(udata->child == NULL)
221 /* Something went wrong, return -1 */
222 FreeMem(udata, sizeof(struct vfork_data));
223 errno = ENOMEM; /* Most likely */
224 vfork_longjmp(env, -1);
226 D(bug("__vfork: Parent: Child created %p, waiting to finish setup\n", udata->child));
227 udata->child_id = GetETaskID(udata->child);
228 D(bug("__vfork: Parent: Got unique child id: %d\n", udata->child_id));
230 /* Wait for child to finish setup */
231 Wait(1 << udata->parent_signal);
233 if(udata->child_errno)
235 /* An error occured during child setup */
236 errno = udata->child_errno;
237 vfork_longjmp(env, -1);
240 D(bug("__vfork: Parent: Setting jmp_buf at %p\n", udata->parent_newexitjmp));
241 if(setjmp(udata->parent_newexitjmp) == 0)
243 udata->parent_olderrorptr = __arosc_set_errorptr(&udata->child_error);
244 udata->child_error = *udata->parent_olderrorptr;
245 __arosc_set_exitjmp(udata->parent_newexitjmp, udata->parent_oldexitjmp);
247 parent_enterpretendchild(udata);
249 D(bug("__vfork: Child %d jumping to jmp_buf %p\n", udata->child_id, &udata->vfork_jmp));
250 D(bug("__vfork: ip: %p, stack: %p alt: %p\n", udata->vfork_jmp[0].retaddr, udata->vfork_jmp[0].regs[SP],
251 udata->vfork_jmp[0].regs[ALT]));
253 vfork_longjmp(udata->vfork_jmp, 0);
254 assert(0); /* not reached */
255 return (pid_t) 0;
257 else /* setjmp() != 0; so child has exited() */
259 D(bug("__vfork: Child: child exiting\n or executed\n"));
261 /* Stack may have been overwritten when we return here,
262 * we jump to here from a function lower in the call chain
264 aroscbase = __aros_getbase_aroscbase();
265 udata = aroscbase->acb_vfork_data;
267 D(bug("__vfork: Child: acb_vfork_data = %x\n", udata));
269 if(!udata->child_executed)
271 D(bug("__vfork: Child: not executed\n"));
273 /* et_Result is normally set in startup code but no exec was performed
274 so we have to mimic the startup code
276 etask = GetETask(udata->child);
277 if (etask)
278 etask->et_Result1 = udata->child_error;
280 D(bug("__vfork: Child: Signaling child %p, signal %d\n", udata->child, udata->child_signal));
281 Signal(udata->child, 1 << udata->child_signal);
284 D(bug("__vfork: Parent: Waiting for child to finish using udata, me=%p, signal %d\n", FindTask(NULL), udata->parent_signal));
285 /* Wait for child to finish using udata */
286 Wait(1 << udata->parent_signal);
288 D(bug("__vfork: Parent: fflushing\n"));
289 fflush(NULL);
291 D(bug("__vfork: Parent: restoring startup buffer\n"));
292 /* Restore parent startup buffer */
293 jmp_buf dummy;
294 __arosc_set_exitjmp(udata->parent_oldexitjmp, dummy);
296 D(bug("__vfork: Parent: freeing parent signal\n"));
297 FreeSignal(udata->parent_signal);
299 errno = udata->child_errno;
301 parent_leavepretendchild(udata);
303 /* Save some data from udata before udata is being freed */
304 ULONG child_id = udata->child_id;
305 jmp_buf env; *env = *udata->vfork_jmp;
307 D(bug("__vfork: Parent: freeing udata\n"));
308 FreeMem(udata, sizeof(struct vfork_data));
310 D(bug("__vfork: Parent jumping to jmp_buf %p (child=%d)\n", env, child_id));
311 D(bug("__vfork: ip: %p, stack: %p\n", env->retaddr, env->regs[SP]));
312 vfork_longjmp(env, child_id);
313 assert(0); /* not reached */
314 return (pid_t) 1;
319 static void parent_enterpretendchild(struct vfork_data *udata)
321 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
322 D(bug("parent_enterpretendchild(%x): entered\n", udata));
324 aroscbase->acb_vfork_data = udata;
326 /* Remember and switch malloc mempool */
327 udata->parent_mempool = aroscbase->acb_mempool;
328 aroscbase->acb_mempool = udata->child_aroscbase->acb_mempool;
330 /* Remember and switch env var list */
331 udata->parent_env_list = aroscbase->acb_env_list;
332 aroscbase->acb_env_list = udata->child_aroscbase->acb_env_list;
334 /* Remember and switch fd descriptor table */
335 udata->parent_internalpool = aroscbase->acb_internalpool;
336 aroscbase->acb_internalpool = udata->child_aroscbase->acb_internalpool;
337 __getfdarray((APTR *)&udata->parent_fd_array, &udata->parent_numslots);
338 __setfdarraybase(udata->child_aroscbase);
340 /* Remember and switch chdir fields */
341 udata->parent_cd_changed = aroscbase->acb_cd_changed;
342 aroscbase->acb_cd_changed = udata->child_aroscbase->acb_cd_changed;
343 udata->parent_cd_lock = aroscbase->acb_cd_lock;
344 aroscbase->acb_cd_lock = udata->child_aroscbase->acb_cd_lock;
345 udata->parent_curdir = CurrentDir(((struct Process *)udata->child)->pr_CurrentDir);
347 /* Pretend to be running as the child created by vfork */
348 aroscbase->acb_flags |= PRETEND_CHILD;
350 D(bug("parent_enterpretendchild: leaving\n"));
353 static void child_takeover(struct vfork_data *udata)
355 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
356 D(bug("child_takeover(%x): entered\n", udata));
358 /* Set current dir to parent's current dir */
359 aroscbase->acb_cd_changed = udata->parent_aroscbase->acb_cd_changed;
360 aroscbase->acb_cd_lock = udata->parent_aroscbase->acb_cd_lock;
361 CurrentDir(((struct Process *)udata->parent)->pr_CurrentDir);
363 D(bug("child_takeover(): leaving\n"));
366 static void parent_leavepretendchild(struct vfork_data *udata)
368 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
369 D(bug("parent_leavepretendchild(%x): entered\n", udata));
371 /* Restore parent's malloc mempool */
372 aroscbase->acb_mempool = udata->parent_mempool;
374 /* Restore parent's env var list */
375 aroscbase->acb_env_list = udata->parent_env_list;
377 /* Restore parent's old fd_array */
378 aroscbase->acb_internalpool = udata->parent_internalpool;
379 __setfdarray(udata->parent_fd_array, udata->parent_numslots);
381 /* Switch to currentdir from before vfork() call */
382 aroscbase->acb_cd_changed = udata->parent_cd_changed;
383 aroscbase->acb_cd_lock = udata->parent_cd_lock;
384 CurrentDir(udata->parent_curdir);
386 /* Switch to previous vfork_data */
387 aroscbase->acb_vfork_data = udata->prev;
388 if(aroscbase->acb_vfork_data == NULL)
389 aroscbase->acb_flags &= ~PRETEND_CHILD;
391 D(bug("parent_leavepretendchild: leaving\n"));