Install crosstools into its own directory. Tools dir much cleaner now.
[AROS.git] / compiler / clib / __vfork.c
blob11212623268bb0a6fd0b2bd9c4c545da0a5e9f9f
1 /*
2 Copyright © 2008-2009, 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 <dos/filesystem.h>
12 #include <aros/cpu.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include <setjmp.h>
17 #include <errno.h>
18 #include <assert.h>
19 #include <stdlib.h>
20 #include <stdio.h>
22 #include "etask.h"
23 #include "__arosc_privdata.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 /* The following functions are used to update the childs and parents privdata
34 for the parent pretending to be running as child and for the child to take over. It is called
35 in the following sequence:
36 parent_enterpretendchild() is called in vfork so the parent pretends to be running as child
37 child_takeover() is called by child if exec*() so it can continue from the parent state
38 parent_leavepretendchild() is called by parent to switch back to be running as parent
40 static void parent_enterpretendchild(struct vfork_data *udata);
41 static void child_takeover(struct vfork_data *udata);
42 static void parent_leavepretendchild(struct vfork_data *udata);
44 LONG launcher()
46 D(bug("launcher: Entered child launcher\n"));
48 struct Task *this = FindTask(NULL);
49 struct vfork_data *udata = this->tc_UserData;
50 BYTE child_signal;
51 struct Library *aroscbase = NULL;
53 GetIntETask(this)->iet_startup = GetETask(this)->et_Result2 = AllocVec(sizeof(struct arosc_startup), MEMF_ANY | MEMF_CLEAR);
55 /* Allocate signal for parent->child communication */
56 child_signal = udata->child_signal = AllocSignal(-1);
57 D(bug("launcher: Allocated child signal: %d\n", udata->child_signal));
58 if(udata->child_signal == -1)
60 /* Lie */
61 udata->child_errno = ENOMEM;
62 Signal(udata->parent, 1 << udata->parent_signal);
63 return -1;
66 if(__register_init_fdarray(udata->ppriv))
67 aroscbase = OpenLibrary((STRPTR) "arosc.library", 0);
68 if(!aroscbase)
70 FreeSignal(child_signal);
71 udata->child_errno = ENOMEM;
72 Signal(udata->parent, 1 << udata->parent_signal);
73 return -1;
76 udata->cpriv = __get_arosc_privdata();
77 udata->cpriv->acpd_parent_does_upath = udata->ppriv->acpd_doupath;
78 udata->cpriv->acpd_flags |= DO_NOT_CLONE_ENV_VARS;
80 if(setjmp(__aros_startup_jmp_buf) == 0)
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 __doupath */
98 __doupath = udata->cpriv->acpd_parent_does_upath;
99 D(bug("launcher: __doupath == %d for __exec_prepare()\n", __doupath));
101 exec_id = udata->exec_id = __exec_prepare(
102 udata->exec_filename,
104 udata->exec_argv,
105 udata->exec_envp
108 udata->child_errno = errno;
110 /* Clear __doupath again, command will set it if wanted */
111 __doupath = 0;
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(aroscbase);
153 return 0;
156 pid_t __vfork(jmp_buf env)
158 struct Task *this = FindTask(NULL);
159 struct vfork_data *udata = AllocMem(sizeof(struct vfork_data), MEMF_ANY | MEMF_CLEAR);
160 if(udata == NULL)
162 errno = ENOMEM;
163 longjmp(env, -1);
165 D(bug("__vfork: allocated udata %p\n", udata));
166 bcopy(env, &udata->vfork_jump, sizeof(jmp_buf));
168 struct TagItem tags[] =
170 { NP_Entry, (IPTR) launcher },
171 { NP_CloseInput, (IPTR) FALSE },
172 { NP_CloseOutput, (IPTR) FALSE },
173 { NP_CloseError, (IPTR) FALSE },
174 { NP_Cli, (IPTR) TRUE },
175 { NP_Name, (IPTR) "vfork()" },
176 { NP_UserData, (IPTR) udata },
177 { NP_NotifyOnDeath, (IPTR) TRUE },
178 { TAG_DONE, 0 }
181 udata->parent = this;
183 struct arosc_privdata *ppriv = __get_arosc_privdata();
185 D(bug("__vfork: ppriv = %x\n", ppriv));
187 udata->ppriv = ppriv;
189 /* Store parent's vfork_data to restore it later */
190 udata->prev = __get_arosc_privdata()->acpd_vfork_data;
191 D(bug("__vfork: Saved old parent's vfork_data: %p\n", udata->prev));
193 D(bug("__vfork: backuping startup buffer\n"));
194 /* Backup startup buffer */
195 CopyMem(&__aros_startup_jmp_buf, &udata->startup_jmp_buf, sizeof(jmp_buf));
197 D(bug("__vfork: Allocating parent signal\n"));
198 /* Allocate signal for child->parent communication */
199 udata->parent_signal = AllocSignal(-1);
200 if(udata->parent_signal == -1)
202 /* Couldn't allocate the signal, return -1 */
203 FreeMem(udata, sizeof(struct vfork_data));
204 errno = ENOMEM;
205 longjmp(udata->vfork_jump, -1);
208 D(bug("__vfork: Creating child\n"));
209 udata->child = (struct Task*) CreateNewProc(tags);
211 if(udata->child == NULL)
213 /* Something went wrong, return -1 */
214 FreeMem(udata, sizeof(struct vfork_data));
215 errno = ENOMEM; /* Most likely */
216 longjmp(env, -1);
218 D(bug("__vfork: Child created %p, waiting to finish setup\n", udata->child));
219 udata->child_id = GetETaskID(udata->child);
220 D(bug("__vfork: Got unique child id: %d\n", udata->child_id));
222 /* Wait for child to finish setup */
223 Wait(1 << udata->parent_signal);
225 if(udata->child_errno)
227 /* An error occured during child setup */
228 errno = udata->child_errno;
229 longjmp(env, -1);
232 D(bug("__vfork: Setting jmp_buf at %p in %p\n", __aros_startup, &__aros_startup_jmp_buf));
233 if(setjmp(__aros_startup_jmp_buf))
235 D(bug("__vfork: child exited\n or executed\n"));
237 /* Reinitialize variables as they may have been overwritten during setjmp */
238 ppriv = __get_arosc_privdata();
239 udata = ppriv->acpd_vfork_data;
241 D(bug("__vfork: ppriv = %x\n", ppriv));
243 if(!udata->child_executed)
245 D(bug("__vfork: not executed\n"));
246 ((struct arosc_startup*) GetIntETask(udata->child)->iet_startup)->as_startup_error = __aros_startup_error;
247 D(bug("__vfork: Signaling child\n"));
248 Signal(udata->child, 1 << udata->child_signal);
251 D(bug("__vfork: Waiting for child to finish using udata\n"));
252 /* Wait for child to finish using udata */
253 Wait(1 << udata->parent_signal);
255 D(bug("__vfork: fflushing\n"));
256 fflush(NULL);
258 D(bug("__vfork: restoring startup buffer\n"));
259 /* Restore parent startup buffer */
260 CopyMem(&udata->startup_jmp_buf, &__aros_startup_jmp_buf, sizeof(jmp_buf));
262 D(bug("__vfork: freeing parent signal\n"));
263 FreeSignal(udata->parent_signal);
265 errno = udata->child_errno;
267 D(bug("__vfork: Remembering jmp_buf\n"));
268 jmp_buf env;
269 bcopy(&udata->vfork_jump, env, sizeof(jmp_buf));
271 parent_leavepretendchild(udata);
273 D(bug("__vfork: freeing udata\n"));
274 FreeMem(udata, sizeof(struct vfork_data));
276 D(bug("__vfork: Child(%d) jumping to jmp_buf %p\n", udata->child_id, &env));
277 D(bug("__vfork: ip: %p, stack: %p\n", env->retaddr, env->regs[_JMPLEN - 1]));
278 vfork_longjmp(env, udata->child_id);
279 assert(0); /* not reached */
280 return (pid_t) 1;
283 parent_enterpretendchild(udata);
285 D(bug("__vfork: Jumping to jmp_buf %p\n", &udata->vfork_jump));
286 D(bug("__vfork: ip: %p, stack: %p\n", udata->vfork_jump[0].retaddr, udata->vfork_jump[0].regs[_JMPLEN - 1]));
287 vfork_longjmp(udata->vfork_jump, 0);
288 assert(0); /* not reached */
289 return (pid_t) 0;
293 static void parent_enterpretendchild(struct vfork_data *udata)
295 D(bug("parent_enterpretendchild(%x): entered\n", udata));
297 udata->ppriv->acpd_vfork_data = udata;
299 /* Remember and switch malloc mempool */
300 udata->parent_mempool = udata->ppriv->acpd_mempool;
301 udata->ppriv->acpd_mempool = udata->cpriv->acpd_mempool;
303 /* Remember and switch fd descriptor table */
304 udata->parent_acpd_fd_mempool = udata->ppriv->acpd_fd_mempool;
305 udata->parent_acpd_numslots = udata->ppriv->acpd_numslots;
306 udata->parent_acpd_fd_array = udata->ppriv->acpd_fd_array;
307 udata->ppriv->acpd_fd_mempool = udata->cpriv->acpd_fd_mempool;
308 udata->ppriv->acpd_numslots = udata->cpriv->acpd_numslots;
309 udata->ppriv->acpd_fd_array = udata->cpriv->acpd_fd_array;
311 /* Remember and switch chdir fields */
312 udata->parent_cd_changed = __cd_changed;
313 udata->parent_cd_lock = __cd_lock;
314 __cd_changed = udata->cpriv->acpd_cd_changed;
315 __cd_lock = udata->cpriv->acpd_cd_lock;
316 udata->parent_curdir = CurrentDir(((struct Process *)udata->child)->pr_CurrentDir);
318 /* Pretend to be running as the child created by vfork */
319 udata->ppriv->acpd_flags |= PRETEND_CHILD;
321 D(bug("parent_enterpretendchild: leaving\n"));
324 static void child_takeover(struct vfork_data *udata)
326 D(bug("child_takeover(%x): entered\n", udata));
328 /* Set current dir to parent's current dir */
329 __cd_changed = udata->ppriv->acpd_cd_changed;
330 __cd_lock = udata->ppriv->acpd_cd_lock;
331 CurrentDir(((struct Process *)udata->parent)->pr_CurrentDir);
333 D(bug("child_takeover(): leaving\n"));
336 static void parent_leavepretendchild(struct vfork_data *udata)
338 D(bug("parent_leavepretendchild(%x): entered\n", udata));
340 /* Restore parent's malloc mempool */
341 udata->ppriv->acpd_mempool = udata->parent_mempool;
343 /* Restore parent's old fd_array */
344 udata->ppriv->acpd_fd_mempool = udata->parent_acpd_fd_mempool;
345 udata->ppriv->acpd_numslots = udata->parent_acpd_numslots;
346 udata->ppriv->acpd_fd_array = udata->parent_acpd_fd_array;
348 /* Switch to currentdir from before vfork() call */
349 __cd_changed = udata->parent_cd_changed;
350 __cd_lock = udata->parent_cd_lock;
351 CurrentDir(udata->parent_curdir);
353 /* Switch to previous vfork_data */
354 udata->ppriv->acpd_vfork_data = udata->prev;
355 if(udata->prev == NULL)
356 udata->ppriv->acpd_flags &= ~PRETEND_CHILD;
358 D(bug("parent_leavepretendchild: leaving\n"));