Added setting position while duplicating file handles with FMF_WRITE mode. It's neede...
[cake.git] / compiler / clib / __vfork.c
blobcac26c3d65a7d5fc43a163a45eea3b8a42fb6814
1 /*
2 Copyright © 2008, 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 <stdlib.h>
19 #include <stdio.h>
21 #include "etask.h"
22 #include "__arosc_privdata.h"
23 #include "__open.h"
24 #include "__vfork.h"
26 #define DEBUG 0
28 #include <aros/debug.h>
29 #include <aros/startup.h>
31 BPTR DupFHFromfd(int fd, ULONG mode);
32 void vfork_longjmp (jmp_buf env, int val);
33 int __init_stdio(void);
35 LONG launcher()
37 D(bug("Entered child launcher\n"));
39 struct Task *this = FindTask(NULL);
40 struct vfork_data *udata;
41 udata = this->tc_UserData;
42 struct arosc_privdata *ppriv = GetIntETask(udata->parent)->iet_acpd;
43 struct arosc_privdata *cpriv;;
44 int i;
46 /* Allocate signal for parent->child communication */
47 udata->child_signal = AllocSignal(-1);
48 D(bug("Allocated child signal: %d\n", udata->child_signal));
49 Signal(udata->parent, 1 << udata->parent_signal);
51 udata->aroscbase = OpenLibrary("arosc.library", 0);
52 if (udata->aroscbase == NULL)
54 /* Most likely there's not enough memory */
55 udata->child_errno = ENOMEM;
56 FreeSignal(udata->child_signal);
57 Signal(udata->parent, 1 << udata->parent_signal);
58 return -1;
61 cpriv = GetIntETask(this)->iet_acpd;
63 /* store child's mempool */
64 udata->child_mempool = cpriv->acpd_startup_mempool;
65 /* Switch to parent's mempool to allow freeing of fdesc structures
66 allocated by parent during file closing. We can't copy them because
67 they contain reference counters for open files. */
68 cpriv->acpd_startup_mempool = ppriv->acpd_startup_mempool;
70 /* store child's fd_array */
71 udata->child_acpd_numslots = cpriv->acpd_numslots;
72 udata->child_acpd_fd_array = cpriv->acpd_fd_array;
74 /* Child inherits all file descriptors of the parent, allocate enough
75 memory and copy them */
76 cpriv->acpd_fd_array = malloc((ppriv->acpd_numslots)*sizeof(fdesc *));
77 if(cpriv->acpd_fd_array == NULL)
79 FreeSignal(udata->child_signal);
80 /* Restore overwritten mempool and fd_array before closing library */
81 cpriv->acpd_startup_mempool = udata->child_mempool;
82 cpriv->acpd_fd_array = udata->child_acpd_fd_array;
83 CloseLibrary(udata->aroscbase);
84 udata->child_errno = ENOMEM;
85 Signal(udata->parent, 1 << udata->parent_signal);
86 return -1;
88 cpriv->acpd_numslots = ppriv->acpd_numslots;
90 CopyMem(
91 ppriv->acpd_fd_array,
92 cpriv->acpd_fd_array,
93 (ppriv->acpd_numslots)*sizeof(fdesc *)
96 /* Reinit stdio files to make them use parent's mempool */
97 __init_stdio();
99 /* "Open" all copied descriptors */
100 for(i = 0; i < ppriv->acpd_numslots; i++)
102 if(ppriv->acpd_fd_array[i])
104 ppriv->acpd_fd_array[i]->opencount++;
108 /* Store child's stack to restore it later */
109 udata->child = this;
110 udata->child_SPLower = this->tc_SPLower;
111 udata->child_SPUpper = this->tc_SPUpper;
112 udata->child_SPReg = AROS_GET_SP;
113 this->tc_UserData = udata;
115 GetIntETask(this)->iet_startup = &udata->child_startup;
117 cpriv->acpd_parent_does_upath = ppriv->acpd_parent_does_upath;
118 cpriv->acpd_doupath = ppriv->acpd_doupath;
120 D(bug("Waiting for parent to set up his temporary stack\n"));
121 Wait(1 << udata->child_signal);
122 D(bug("Parent ready, stealing his stack\n"));
124 /* Steal parent's stack */
125 Forbid();
126 this->tc_SPLower = udata->parent_SPLower;
127 this->tc_SPUpper = udata->parent_SPUpper;
128 /* We have to switch stack before setjmp to assure correct %esp value after
129 longjmp during child exit */
130 AROS_GET_SP = udata->parent_SPReg;
131 Permit();
133 /* Now we can't use local variables anymore */
135 D(bug("Setting jmp_buf at %p\n", &__aros_startup_jmp_buf));
136 if(setjmp(__aros_startup_jmp_buf))
138 D(bug("Child exited\n"));
140 D(bug("Restoring stack to %p < %p < %p\n", GETUDATA->child_SPLower, GETUDATA->child_SPReg, GETUDATA->child_SPUpper));
141 /* Restoring child stack */
142 Forbid();
143 GETUDATA->child->tc_SPLower = GETUDATA->child_SPLower;
144 GETUDATA->child->tc_SPUpper = GETUDATA->child_SPUpper;
145 AROS_GET_SP = GETUDATA->child_SPReg;
146 Permit();
148 fflush(NULL);
150 /* Close all opened files in child */
151 for(i = 0; i < ((struct arosc_privdata *) GetIntETask(GETUDATA->child)->iet_acpd)->acpd_numslots; i++)
153 if(((struct arosc_privdata *) GetIntETask(GETUDATA->child)->iet_acpd)->acpd_fd_array[i])
155 close(i);
159 /* Switch back to child's fd_array and mempool */
160 ((struct arosc_privdata *) GetIntETask(GETUDATA->child)->iet_acpd)->acpd_numslots = GETUDATA->child_acpd_numslots;
161 free(((struct arosc_privdata *) GetIntETask(GETUDATA->child)->iet_acpd)->acpd_fd_array);
162 ((struct arosc_privdata *) GetIntETask(GETUDATA->child)->iet_acpd)->acpd_fd_array = GETUDATA->child_acpd_fd_array;
163 ((struct arosc_privdata *) GetIntETask(GETUDATA->child)->iet_acpd)->acpd_startup_mempool = GETUDATA->child_mempool;
165 D(bug("Closing aroscbase\n"));
166 CloseLibrary(GETUDATA->aroscbase);
168 /* If child called execve and just exited, it has already signaled
169 parent, so skip that step */
170 if(!GETUDATA->child_executed)
172 D(bug("Calling daddy\n"));
173 Signal(GETUDATA->parent, 1 << GETUDATA->parent_signal);
174 FreeSignal(GETUDATA->child_signal);
176 else
178 D(bug("Waiting for signal from the parent\n"));
179 Wait(1 << GETUDATA->child_signal);
180 FreeSignal(GETUDATA->child_signal);
181 /* Parent won't need udata anymore, we can safely free it */
182 FreeMem(GETUDATA, sizeof(struct vfork_data));
184 D(bug("Returning\n"));
185 return 0;
188 D(bug("Jumping to jmp_buf %p\n", &GETUDATA->vfork_jump));
189 vfork_longjmp(GETUDATA->vfork_jump, 0);
190 return 0; /* not reached */
193 void FreeAndJump(struct vfork_data *udata)
195 jmp_buf jump;
196 ULONG child_id = udata->child_id;
197 CopyMem(&udata->vfork_jump, &jump, sizeof(jmp_buf));
199 if(!udata->child_executed)
201 D(bug("Child not executed, freeing udata\n"));
202 /* If child process didn't call execve() then it's no longer alive and we
203 can safely free udata. Otherwise it's freed during child exit */
204 FreeMem(udata, sizeof(struct vfork_data));
206 else
208 D(bug("Child executed, signaling\n"));
209 /* Otherwise inform child that we don't need udata anymore */
210 Signal(udata->child, 1 << udata->child_signal);
212 D(bug("ip: %p, stack: %p\n", jump[0].retaddr, jump[0].regs[_JMPLEN - 1]));
213 longjmp(jump, child_id);
216 pid_t __vfork(jmp_buf env)
218 struct Task *this = FindTask(NULL);
219 struct vfork_data *udata = AllocMem(sizeof(struct vfork_data), MEMF_ANY | MEMF_CLEAR);
220 if(udata == NULL)
222 errno = ENOMEM;
223 longjmp(udata->vfork_jump, -1);
225 D(bug("allocated udata %p\n", udata));
226 udata->magic = VFORK_MAGIC;
228 struct TagItem tags[] =
230 { NP_Entry, (IPTR) launcher },
231 { NP_Input, (IPTR) NULL }, /* 1 */
232 { NP_Output, (IPTR) NULL }, /* 2 */
233 { NP_Error, (IPTR) NULL }, /* 3 */
234 { NP_CloseInput, (IPTR) FALSE },
235 { NP_CloseOutput, (IPTR) FALSE },
236 { NP_CloseError, (IPTR) FALSE },
237 { NP_Cli, (IPTR) TRUE },
238 { NP_Name, (IPTR) "vfork()" },
239 { NP_UserData, (IPTR) udata },
240 { NP_NotifyOnDeath, (IPTR) TRUE },
241 { TAG_DONE, 0 }
244 BPTR in, out, err;
245 in = Input();
246 out = Output();
247 err = Error();
248 D(bug("in = %p - out = %p - err = %p\n", BADDR(in), BADDR(out), BADDR(err)));
250 if (in) tags[1].ti_Data = (IPTR)in;
251 else tags[1].ti_Tag = TAG_IGNORE;
252 if (out) tags[2].ti_Data = (IPTR)out;
253 else tags[2].ti_Tag = TAG_IGNORE;
254 if (err) tags[3].ti_Data = (IPTR)err;
255 else tags[3].ti_Tag = TAG_IGNORE;
257 udata->parent = this;
258 /* Store parent's UserData to restore it later */
259 udata->old_UserData = udata->parent->tc_UserData;
260 D(bug("Saved old parent's UserData: %p\n", udata->old_UserData));
261 udata->parent->tc_UserData = udata;
263 /* Store parent's stack to restore it later */
264 udata->parent_SPLower = udata->parent->tc_SPLower;
265 udata->parent_SPUpper = udata->parent->tc_SPUpper;
266 udata->parent_SPReg = AROS_GET_SP;
267 bcopy(env, &udata->vfork_jump, sizeof(jmp_buf));
268 D(bug("Set jmp_buf %p\n", &udata->vfork_jump));
270 /* Allocate signal for child->parent communication */
271 udata->parent_signal = AllocSignal(-1);
272 if(udata->parent_signal == -1)
274 /* Couldn't allocate the signal, return -1 */
275 FreeMem(udata, sizeof(struct vfork_data));
276 longjmp(udata->vfork_jump, -1);
279 D(bug("Creating child\n"));
280 udata->child = (struct Task*) CreateNewProc(tags);
281 udata->child_id = GetETaskID(udata->child);
282 D(bug("Got unique child id: %d\n", udata->child_id));
284 if(udata->child == NULL)
286 /* Something went wrong, return -1 */
287 FreeMem(udata, sizeof(struct vfork_data));
288 errno = ENOMEM; /* Most likely */
289 longjmp(env, -1);
291 D(bug("Child created %p\n", udata->child));
293 /* Wait until children allocates a signal for communication */
294 Wait(1 << udata->parent_signal);
296 if(udata->child_signal == -1)
298 /* Child couldn't allocate the signal */
299 FreeSignal(udata->parent_signal);
300 FreeMem(udata, sizeof(struct vfork_data));
301 longjmp(env, -1);
304 /* Switch temporarily to mini stack, child process will use the old
305 stack soon */
306 Forbid();
307 udata->parent->tc_SPLower = (APTR) udata->ministack;
308 udata->parent->tc_SPUpper = (APTR) ((char*) udata->ministack + sizeof(udata->ministack));
309 /* Compiler needs some space to store function arguments.
310 We can't get the specific amount, but this should be
311 enough... */
312 AROS_GET_SP = udata->parent->tc_SPUpper - AROS_ALIGN(sizeof(udata->ministack)/2);
313 Permit();
315 /* Now we can't use local variables allocated on the parent's stack */
316 D(bug("Temporary stack set up, child can steal ours\n"));
317 Signal(GETUDATA->child, 1 << GETUDATA->child_signal);
318 D(bug("Child signaled\n"));
320 D(bug("Waiting for child to die or execve\n"));
321 Wait(1 << GETUDATA->parent_signal);
322 D(bug("Child died or execved, returning\n"));
324 if(GETUDATA->child_errno)
326 /* An error occured during child setup */
327 errno = udata->child_errno;
328 udata->child_id = -1;
331 D(bug("Restoring stack to %p < %p < %p\n", GETUDATA->parent_SPLower, GETUDATA->parent_SPReg, GETUDATA->parent_SPUpper));
332 /* Getting back our stack */
333 Forbid();
334 GETUDATA->parent->tc_SPLower = GETUDATA->parent_SPLower;
335 GETUDATA->parent->tc_SPUpper = GETUDATA->parent_SPUpper;
336 AROS_GET_SP = GETUDATA->parent_SPReg;
337 Permit();
339 /* Stack is restored, we can use local variables again */
341 /* Restore udata variable in case child clobbered it */
342 udata = GETUDATA;
344 /* Restore parent's UserData */
345 udata->parent->tc_UserData = udata->old_UserData;
346 D(bug("Restored old parent's UserData: %p\n", udata->old_UserData));
348 FreeSignal(udata->parent_signal);
349 D(bug("ip: %p, stack: %p\n", udata->vfork_jump[0].retaddr, udata->vfork_jump[0].regs[_JMPLEN - 1]));
350 FreeAndJump(udata);
351 return (pid_t) udata->child_id; // not reached