2 Copyright © 2008-2012, The AROS Development Team. All rights reserved.
6 #include <proto/exec.h>
9 #include <exec/tasks.h>
13 #include <sys/types.h>
21 #include "__arosc_privdata.h"
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
);
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
;
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)
57 udata
->child_errno
= ENOMEM
;
58 Signal(udata
->parent
, 1 << udata
->parent_signal
);
62 if(__register_init_fdarray(pbase
))
63 aroscbase
= (struct aroscbase
*)OpenLibrary((STRPTR
) "arosc.library", 0);
66 FreeSignal(child_signal
);
67 udata
->child_errno
= ENOMEM
;
68 Signal(udata
->parent
, 1 << udata
->parent_signal
);
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
)
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
,
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
);
127 D(bug("launcher: executing command\n"));
130 assert(0); /* Should not be reached */
134 D(bug("launcher: exit because execve returned with an error\n"));
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
);
149 D(bug("launcher: freeing child_signal\n"));
150 FreeSignal(child_signal
);
153 CloseLibrary((struct Library
*)aroscbase
);
155 D(bug("Child Done\n"));
160 /* This can be good for debugging */
167 #define SP _JMPLEN - 1
168 #define ALT _JMPLEN - 1
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
);
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
},
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
));
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 */
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
);
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"));
295 D(bug("__vfork: Parent: restoring startup buffer\n"));
296 /* Restore parent startup buffer */
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 */
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"));