Small cleanup of extensions code
[AROS.git] / compiler / clib / __vfork.c
blobe10d7a666879bcb49ff77d584e094ae31977e819
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 <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 LONG exec_command(BPTR seglist, char *taskname, char *args, ULONG stacksize);
35 LONG launcher()
37 D(bug("launcher: Entered child launcher\n"));
39 struct Task *this = FindTask(NULL);
40 struct vfork_data *udata = this->tc_UserData;
41 BYTE child_signal;
42 LONG ret = 0;
43 struct Library *aroscbase = NULL;
45 GetIntETask(this)->iet_startup = GetETask(this)->et_Result2 = AllocVec(sizeof(struct aros_startup), MEMF_ANY | MEMF_CLEAR);
47 /* Allocate signal for parent->child communication */
48 child_signal = udata->child_signal = AllocSignal(-1);
49 D(bug("launcher: Allocated child signal: %d\n", udata->child_signal));
50 if(udata->child_signal == -1)
52 /* Lie */
53 udata->child_errno = ENOMEM;
54 Signal(udata->parent, 1 << udata->parent_signal);
55 return -1;
58 if(__register_init_fdarray(udata->ppriv->acpd_fd_array, udata->ppriv->acpd_numslots))
59 aroscbase = OpenLibrary((STRPTR) "arosc.library", 0);
60 if(!aroscbase)
62 FreeSignal(child_signal);
63 udata->child_errno = ENOMEM;
64 Signal(udata->parent, 1 << udata->parent_signal);
65 return -1;
68 udata->cpriv = __get_arosc_privdata();
69 __get_arosc_privdata()->acpd_parent_does_upath = udata->ppriv->acpd_doupath;
70 __get_arosc_privdata()->acpd_flags |= KEEP_OLD_ACPD | DO_NOT_CLONE_ENV_VARS;
72 /* Setup complete, signal parent */
73 D(bug("launcher: Signaling parent that we finished setup\n"));
74 Signal(udata->parent, 1 << udata->parent_signal);
76 D(bug("launcher: Child waiting for execve or exit\n"));
77 Wait(1 << udata->child_signal);
79 if(udata->child_executed)
81 D(bug("launcher: child executed\n"));
82 BPTR seglist = udata->exec_seglist;
83 char *taskname = udata->exec_taskname;
84 char *arguments = udata->exec_arguments;
85 ULONG stacksize = udata->exec_stacksize;
87 fdesc *in, *out, *err;
89 in = __fd_array[STDIN_FILENO];
90 if(in)
91 SelectInput(in->fcb->fh);
93 out = __fd_array[STDOUT_FILENO];
94 if(out)
95 SelectOutput(out->fcb->fh);
97 err = __fd_array[STDERR_FILENO];
98 if(err)
99 SelectError(err->fcb->fh);
101 D(bug("launcher: informing parent that we won't use udata anymore\n"));
102 /* Inform parent that we won't use udata anymore */
103 Signal(udata->parent, 1 << udata->parent_signal);
105 D(bug("launcher: executing command\n"));
106 /* Run executed command */
107 ret = exec_command(
108 seglist,
109 taskname,
110 arguments,
111 stacksize
114 D(bug("launcher: freeing taskname and arguments\n"));
115 UnLoadSeg(seglist);
116 FreeVec(taskname);
117 FreeVec(arguments);
118 __aros_startup_error = ret;
119 D(bug("launcher: exec_command returned %d\n", ret));
121 else
123 D(bug("launcher: informing parent that we won't use udata anymore\n"));
124 /* Inform parent that we won't use udata anymore */
125 Signal(udata->parent, 1 << udata->parent_signal);
127 D(bug("launcher: freeing child_signal\n"));
128 FreeSignal(child_signal);
129 CloseLibrary(aroscbase);
130 return 0;
133 void FreeAndJump(struct vfork_data *udata)
135 jmp_buf env;
136 ULONG child_id = udata->child_id;
137 bcopy(&udata->vfork_jump, env, sizeof(jmp_buf));
138 D(bug("FreeAndJump: Restoring old vfork_data: %p\n", udata->prev));
139 __get_arosc_privdata()->acpd_vfork_data = udata->prev;
140 __get_arosc_privdata()->acpd_flags = udata->old_acpd_flags;
141 D(bug("FreeAndJump: freeing udata\n"));
142 FreeMem(udata, sizeof(struct vfork_data));
143 longjmp(env, child_id);
146 pid_t __vfork(jmp_buf env)
148 struct Task *this = FindTask(NULL);
149 struct vfork_data *udata = AllocMem(sizeof(struct vfork_data), MEMF_ANY | MEMF_CLEAR);
150 if(udata == NULL)
152 errno = ENOMEM;
153 longjmp(env, -1);
155 D(bug("__vfork: allocated udata %p\n", udata));
156 bcopy(env, &udata->vfork_jump, sizeof(jmp_buf));
158 struct TagItem tags[] =
160 { NP_Entry, (IPTR) launcher },
161 { NP_CloseInput, (IPTR) FALSE },
162 { NP_CloseOutput, (IPTR) FALSE },
163 { NP_CloseError, (IPTR) FALSE },
164 { NP_Cli, (IPTR) TRUE },
165 { NP_Name, (IPTR) "vfork()" },
166 { NP_UserData, (IPTR) udata },
167 { NP_NotifyOnDeath, (IPTR) TRUE },
168 { TAG_DONE, 0 }
171 udata->parent = this;
172 /* Store parent's vfork_data to restore it later */
173 udata->old_acpd_flags = __get_arosc_privdata()->acpd_flags;
174 udata->prev = __get_arosc_privdata()->acpd_vfork_data;
175 D(bug("__vfork: Saved old parent's vfork_data: %p\n", udata->prev));
176 udata->ppriv = __get_arosc_privdata();
178 D(bug("__vfork: backuping startup buffer\n"));
179 /* Backup startup buffer */
180 CopyMem(&__aros_startup_jmp_buf, &udata->startup_jmp_buf, sizeof(jmp_buf));
182 D(bug("__vfork: backuping current directory\n"));
183 udata->parent_curdir = CurrentDir(NULL);
185 CurrentDir(DupLock(udata->parent_curdir));
187 D(bug("__vfork: backuping descriptor table\n"));
188 /* Backup parent fd descriptor table */
189 struct arosc_privdata *ppriv = GetIntETask(this)->iet_acpd;
191 udata->parent_acpd_fd_mempool = ppriv->acpd_fd_mempool;
192 udata->parent_acpd_numslots = ppriv->acpd_numslots;
193 udata->parent_acpd_fd_array = ppriv->acpd_fd_array;
195 D(bug("__vfork: Allocating parent signal\n"));
196 /* Allocate signal for child->parent communication */
197 udata->parent_signal = AllocSignal(-1);
198 if(udata->parent_signal == -1)
200 /* Couldn't allocate the signal, return -1 */
201 FreeMem(udata, sizeof(struct vfork_data));
202 errno = ENOMEM;
203 longjmp(udata->vfork_jump, -1);
206 D(bug("__vfork: Creating child\n"));
207 udata->child = (struct Task*) CreateNewProc(tags);
209 if(udata->child == NULL)
211 /* Something went wrong, return -1 */
212 FreeMem(udata, sizeof(struct vfork_data));
213 errno = ENOMEM; /* Most likely */
214 longjmp(env, -1);
216 D(bug("__vfork: Child created %p, waiting to finish setup\n", udata->child));
217 udata->child_id = GetETaskID(udata->child);
218 D(bug("__vfork: Got unique child id: %d\n", udata->child_id));
220 /* Wait for child to finish setup */
221 Wait(1 << udata->parent_signal);
223 if(udata->child_errno)
225 /* An error occured during child setup */
226 errno = udata->child_errno;
227 longjmp(env, -1);
230 D(bug("__vfork: Setting jmp_buf at %p in %p\n", __aros_startup, &__aros_startup_jmp_buf));
231 if(setjmp(__aros_startup_jmp_buf))
233 D(bug("__vfork: child exited\n or executed\n"));
234 __get_arosc_privdata()->acpd_flags &= ~PRETEND_CHILD;
236 if(!GETUDATA->child_executed)
238 D(bug("__vfork: not executed\n"));
239 ((struct aros_startup*) GetIntETask(GETUDATA->child)->iet_startup)->as_startup_error = __aros_startup_error;
240 D(bug("__vfork: Signaling child\n"));
241 Signal(GETUDATA->child, 1 << GETUDATA->child_signal);
244 D(bug("__vfork: Waiting for child to finish using udata\n"));
245 /* Wait for child to finish using GETUDATA */
246 Wait(1 << GETUDATA->parent_signal);
248 D(bug("__vfork: fflushing\n"));
249 fflush(NULL);
251 D(bug("__vfork: Restoring current directory\n"));
252 UnLock(CurrentDir(GETUDATA->parent_curdir));
254 D(bug("__vfork: restoring old fd_array\n"));
255 /* Restore parent's old fd_array */
256 ((struct arosc_privdata *) GetIntETask(GETUDATA->parent)->iet_acpd)->acpd_fd_mempool = GETUDATA->parent_acpd_fd_mempool;
257 ((struct arosc_privdata *) GetIntETask(GETUDATA->parent)->iet_acpd)->acpd_numslots = GETUDATA->parent_acpd_numslots;
258 ((struct arosc_privdata *) GetIntETask(GETUDATA->parent)->iet_acpd)->acpd_fd_array = GETUDATA->parent_acpd_fd_array;
260 D(bug("__vfork: restoring startup buffer\n"));
261 /* Restore parent startup buffer */
262 CopyMem(&GETUDATA->startup_jmp_buf, &__aros_startup_jmp_buf, sizeof(jmp_buf));
264 D(bug("__vfork: freeing parent signal\n"));
265 FreeSignal(GETUDATA->parent_signal);
267 FreeAndJump(GETUDATA);
268 return (pid_t) 1; /* not reached */
271 ppriv->acpd_vfork_data = udata;
272 ppriv->acpd_flags |= PRETEND_CHILD;
273 ppriv->acpd_fd_mempool = udata->cpriv->acpd_fd_mempool;
274 ppriv->acpd_numslots = udata->cpriv->acpd_numslots;
275 ppriv->acpd_fd_array = udata->cpriv->acpd_fd_array;
277 D(bug("__vfork: Jumping to jmp_buf %p\n", &udata->vfork_jump));
278 D(bug("__vfork: ip: %p, stack: %p\n", udata->vfork_jump[0].retaddr, udata->vfork_jump[0].regs[_JMPLEN - 1]));
279 vfork_longjmp(udata->vfork_jump, 0);
280 return (pid_t) 0; /* not reached */