2 Copyright © 2008, The AROS Development Team. All rights reserved.
6 #include <proto/exec.h>
9 #include <exec/tasks.h>
11 #include <dos/filesystem.h>
14 #include <sys/types.h>
22 #include "__arosc_privdata.h"
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);
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
;;
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
);
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
);
88 cpriv
->acpd_numslots
= ppriv
->acpd_numslots
;
93 (ppriv
->acpd_numslots
)*sizeof(fdesc
*)
96 /* Reinit stdio files to make them use parent's mempool */
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 */
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 */
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
;
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 */
143 GETUDATA
->child
->tc_SPLower
= GETUDATA
->child_SPLower
;
144 GETUDATA
->child
->tc_SPUpper
= GETUDATA
->child_SPUpper
;
145 AROS_GET_SP
= GETUDATA
->child_SPReg
;
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
])
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
);
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"));
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
)
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
));
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
);
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
},
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 */
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
));
304 /* Switch temporarily to mini stack, child process will use the old
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
312 AROS_GET_SP
= udata
->parent
->tc_SPUpper
- AROS_ALIGN(sizeof(udata
->ministack
)/2);
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 */
334 GETUDATA
->parent
->tc_SPLower
= GETUDATA
->parent_SPLower
;
335 GETUDATA
->parent
->tc_SPUpper
= GETUDATA
->parent_SPUpper
;
336 AROS_GET_SP
= GETUDATA
->parent_SPReg
;
339 /* Stack is restored, we can use local variables again */
341 /* Restore udata variable in case child clobbered it */
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]));
351 return (pid_t
) udata
->child_id
; // not reached