compiler/clib: Removed unused arosc_init.h file.
[AROS.git] / compiler / clib / __vfork.c
blobbcc860b716bf77e7bd96868d78c1e7eb1b41614a
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(aroscbase->acb_acud.acud_startup_jmp_buf) == 0)
81 /* Setup complete, signal parent */
82 D(bug("launcher: Signaling parent that we finished setup\n"));
83 Signal(udata->parent, 1 << udata->parent_signal);
85 D(bug("launcher: Child waiting for exec or exit\n"));
86 Wait(1 << udata->child_signal);
88 if(udata->child_executed)
90 APTR exec_id;
92 D(bug("launcher: child executed\n"));
94 child_takeover(udata);
96 /* Filenames passed from parent obey parent's acb_doupath */
98 aroscbase->acb_doupath = udata->child_aroscbase->acb_parent_does_upath;
99 D(bug("launcher: acb_doupath == %d for __exec_prepare()\n", aroscbase->acb_doupath));
101 exec_id = udata->exec_id = __exec_prepare(
102 udata->exec_filename,
104 udata->exec_argv,
105 udata->exec_envp
108 /* Reset handling of upath */
109 aroscbase->acb_doupath = 0;
110 udata->child_errno = errno;
112 D(bug("launcher: informing parent that we have run __exec_prepare\n"));
113 /* Inform parent that we have run __exec_prepare */
114 Signal(udata->parent, 1 << udata->parent_signal);
116 /* Wait 'till __exec_do() is called on parent process */
117 D(bug("launcher: Waiting parent to get the result\n"));
118 Wait(1 << udata->child_signal);
120 D(bug("launcher: informing parent that we won't use udata anymore\n"));
121 /* Inform parent that we won't use udata anymore */
122 Signal(udata->parent, 1 << udata->parent_signal);
124 if (exec_id)
126 D(bug("launcher: executing command\n"));
127 __exec_do(exec_id);
129 assert(0); /* Should not be reached */
131 else
133 D(bug("launcher: exit because execve returned with an error\n"));
134 _exit(0);
137 else
139 D(bug("launcher: informing parent that we won't use udata anymore\n"));
140 /* Inform parent that we won't use udata anymore */
141 Signal(udata->parent, 1 << udata->parent_signal);
144 else
146 D(bug("launcher: freeing child_signal\n"));
147 FreeSignal(child_signal);
150 CloseLibrary((struct Library *)aroscbase);
152 return 0;
155 /* This can be good for debugging */
156 #ifdef __arm__
157 #define SP 8
158 #define ALT 27
159 #endif
161 #ifndef SP
162 #define SP _JMPLEN - 1
163 #define ALT _JMPLEN - 1
164 #endif
166 pid_t __vfork(jmp_buf env)
168 struct aroscbase *aroscbase = __GM_GetBase();
169 struct Task *this = FindTask(NULL);
170 struct ETask *etask = NULL;
171 struct vfork_data *udata = AllocMem(sizeof(struct vfork_data), MEMF_ANY | MEMF_CLEAR);
172 if(udata == NULL)
174 errno = ENOMEM;
175 longjmp(env, -1);
177 D(bug("__vfork: allocated udata %p\n", udata));
178 bcopy(env, &udata->vfork_jump, sizeof(jmp_buf));
180 struct TagItem tags[] =
182 { NP_Entry, (IPTR) launcher },
183 { NP_CloseInput, (IPTR) FALSE },
184 { NP_CloseOutput, (IPTR) FALSE },
185 { NP_CloseError, (IPTR) FALSE },
186 { NP_Cli, (IPTR) TRUE },
187 { NP_Name, (IPTR) "vfork()" },
188 { NP_UserData, (IPTR) udata },
189 { NP_NotifyOnDeath, (IPTR) TRUE },
190 { TAG_DONE, 0 }
193 D(bug("__vfork: initial jmp_buf %p\n", env));
194 D(bug("__vfork: ip: %p, stack: %p, alt: 0x%p\n", env->retaddr, env->regs[SP], env->regs[ALT]));
195 D(bug("__vfork: Current altstack 0x%p\n", *((void **)this->tc_SPLower)));
196 D(hexdump(env, 0, sizeof(jmp_buf) + sizeof(void *) * 4));
198 udata->parent = this;
199 udata->prev = aroscbase->acb_vfork_data;
201 D(bug("__vfork: Saved old parent's vfork_data: %p\n", udata->prev));
202 udata->parent_aroscbase = aroscbase;
204 D(bug("__vfork: backuping startup buffer\n"));
205 /* Backup startup buffer */
206 CopyMem(&__arosc_startup_jmp_buf, &udata->startup_jmp_buf, sizeof(jmp_buf));
208 D(bug("__vfork: Allocating parent signal\n"));
209 /* Allocate signal for child->parent communication */
210 udata->parent_signal = AllocSignal(-1);
211 if(udata->parent_signal == -1)
213 /* Couldn't allocate the signal, return -1 */
214 FreeMem(udata, sizeof(struct vfork_data));
215 errno = ENOMEM;
216 longjmp(udata->vfork_jump, -1);
219 D(bug("__vfork: Creating child\n"));
220 udata->child = (struct Task*) CreateNewProc(tags);
222 if(udata->child == NULL)
224 /* Something went wrong, return -1 */
225 FreeMem(udata, sizeof(struct vfork_data));
226 errno = ENOMEM; /* Most likely */
227 longjmp(env, -1);
229 D(bug("__vfork: Child created %p, waiting to finish setup\n", udata->child));
230 udata->child_id = GetETaskID(udata->child);
231 D(bug("__vfork: Got unique child id: %d\n", udata->child_id));
233 /* Wait for child to finish setup */
234 Wait(1 << udata->parent_signal);
236 if(udata->child_errno)
238 /* An error occured during child setup */
239 errno = udata->child_errno;
240 longjmp(env, -1);
243 D(bug("__vfork: Setting jmp_buf at %p\n", __arosc_startup_jmp_buf));
244 if(setjmp(__arosc_startup_jmp_buf))
246 ULONG child_id;
248 D(bug("__vfork: child exited\n or executed\n"));
250 /* Stack may have been overwritten when we return here,
251 * we jump to here from a function lower in the call chain
253 aroscbase = __GM_GetBase();
254 udata = aroscbase->acb_vfork_data;
256 D(bug("__vfork: acb_vfork_data = %x\n", udata));
258 if(!udata->child_executed)
260 D(bug("__vfork: not executed\n"));
261 udata->child_aroscbase->acb_acud.acud_startup_error = __arosc_startup_error;
263 /* et_Result is normally set in startup code but no exec was performed
264 so we have to mimic the startup code
266 etask = GetETask(udata->child);
267 if (etask)
268 etask->et_Result1 = __arosc_startup_error;
270 D(bug("__vfork: Signaling child\n"));
271 Signal(udata->child, 1 << udata->child_signal);
274 D(bug("__vfork: Waiting for child to finish using udata\n"));
275 /* Wait for child to finish using udata */
276 Wait(1 << udata->parent_signal);
278 D(bug("__vfork: fflushing\n"));
279 fflush(NULL);
281 D(bug("__vfork: restoring startup buffer\n"));
282 /* Restore parent startup buffer */
283 CopyMem(&udata->startup_jmp_buf, &__arosc_startup_jmp_buf, sizeof(jmp_buf));
285 D(bug("__vfork: freeing parent signal\n"));
286 FreeSignal(udata->parent_signal);
288 errno = udata->child_errno;
290 D(bug("__vfork: Remembering jmp_buf\n"));
291 jmp_buf env;
292 bcopy(&udata->vfork_jump, env, sizeof(jmp_buf));
294 parent_leavepretendchild(udata);
296 /* save child id before freeing udata */
297 child_id = udata->child_id;
299 D(bug("__vfork: freeing udata\n"));
300 FreeMem(udata, sizeof(struct vfork_data));
302 D(bug("__vfork: Child(%d) jumping to jmp_buf %p\n", child_id, &env));
303 D(bug("__vfork: ip: %p, stack: %p\n", env->retaddr, env->regs[SP]));
304 vfork_longjmp(env, child_id);
305 assert(0); /* not reached */
306 return (pid_t) 1;
309 parent_enterpretendchild(udata);
311 D(bug("__vfork: Jumping to jmp_buf %p\n", &udata->vfork_jump));
312 D(bug("__vfork: ip: %p, stack: %p alt: %p\n", udata->vfork_jump[0].retaddr, udata->vfork_jump[0].regs[SP],
313 udata->vfork_jump[0].regs[ALT]));
315 vfork_longjmp(udata->vfork_jump, 0);
316 assert(0); /* not reached */
317 return (pid_t) 0;
321 static void parent_enterpretendchild(struct vfork_data *udata)
323 struct aroscbase *aroscbase = __GM_GetBase();
324 D(bug("parent_enterpretendchild(%x): entered\n", udata));
326 aroscbase->acb_vfork_data = udata;
328 /* Remember and switch malloc mempool */
329 udata->parent_mempool = aroscbase->acb_mempool;
330 aroscbase->acb_mempool = udata->child_aroscbase->acb_mempool;
332 /* Remember and switch env var list */
333 udata->parent_env_list = aroscbase->acb_env_list;
334 aroscbase->acb_env_list = udata->child_aroscbase->acb_env_list;
336 /* Remember and switch fd descriptor table */
337 udata->parent_fd_mempool = aroscbase->acb_fd_mempool;
338 aroscbase->acb_fd_mempool = udata->child_aroscbase->acb_fd_mempool;
339 __getfdarray((APTR *)&udata->parent_fd_array, &udata->parent_numslots);
340 __setfdarraybase(udata->child_aroscbase);
342 /* Remember and switch chdir fields */
343 udata->parent_cd_changed = aroscbase->acb_cd_changed;
344 aroscbase->acb_cd_changed = udata->child_aroscbase->acb_cd_changed;
345 udata->parent_cd_lock = aroscbase->acb_cd_lock;
346 aroscbase->acb_cd_lock = udata->child_aroscbase->acb_cd_lock;
347 udata->parent_curdir = CurrentDir(((struct Process *)udata->child)->pr_CurrentDir);
349 /* Pretend to be running as the child created by vfork */
350 aroscbase->acb_flags |= PRETEND_CHILD;
352 D(bug("parent_enterpretendchild: leaving\n"));
355 static void child_takeover(struct vfork_data *udata)
357 struct aroscbase *aroscbase = __GM_GetBase();
358 D(bug("child_takeover(%x): entered\n", udata));
360 /* Set current dir to parent's current dir */
361 aroscbase->acb_cd_changed = udata->parent_aroscbase->acb_cd_changed;
362 aroscbase->acb_cd_lock = udata->parent_aroscbase->acb_cd_lock;
363 CurrentDir(((struct Process *)udata->parent)->pr_CurrentDir);
365 D(bug("child_takeover(): leaving\n"));
368 static void parent_leavepretendchild(struct vfork_data *udata)
370 struct aroscbase *aroscbase = __GM_GetBase();
371 D(bug("parent_leavepretendchild(%x): entered\n", udata));
373 /* Restore parent's malloc mempool */
374 aroscbase->acb_mempool = udata->parent_mempool;
376 /* Restore parent's env var list */
377 aroscbase->acb_env_list = udata->parent_env_list;
379 /* Restore parent's old fd_array */
380 aroscbase->acb_fd_mempool = udata->parent_fd_mempool;
381 __setfdarray(udata->parent_fd_array, udata->parent_numslots);
383 /* Switch to currentdir from before vfork() call */
384 aroscbase->acb_cd_changed = udata->parent_cd_changed;
385 aroscbase->acb_cd_lock = udata->parent_cd_lock;
386 CurrentDir(udata->parent_curdir);
388 /* Switch to previous vfork_data */
389 aroscbase->acb_vfork_data = udata->prev;
390 if(aroscbase->acb_vfork_data == NULL)
391 aroscbase->acb_flags &= ~PRETEND_CHILD;
393 D(bug("parent_leavepretendchild: leaving\n"));