Added setting position while duplicating file handles with FMF_WRITE mode. It's neede...
[cake.git] / compiler / clib / execve.c
blob4383d069dc9f7b5282ff58245080683aa89f55e2
1 /*
2 Copyright © 2008, The AROS Development Team. All rights reserved.
3 $Id$
5 POSIX function execve().
6 */
8 #define DEBUG 0
10 #include <exec/types.h>
11 #include <exec/lists.h>
12 #include <proto/exec.h>
13 #include <proto/dos.h>
14 #include <aros/debug.h>
15 #include <dos/filesystem.h>
17 #include <ctype.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
23 #include <__errno.h>
24 #include "__upath.h"
25 #include "__open.h"
26 #include "__arosc_privdata.h"
27 #include "__vfork.h"
29 /* Return TRUE if there are any white spaces in the string */
30 BOOL containswhite(const char *str)
32 while(*str != '\0')
33 if(isspace(*str++)) return TRUE;
34 return FALSE;
37 /* Escape the string and quote it */
38 char *escape(const char *str)
40 const char *strptr = str;
41 char *escaped, *escptr;
42 /* Additional two characters for '"', and one for '\0' */
43 int bufsize = strlen(str) + 3;
44 /* Take into account characters to ecape */
45 while(*strptr != '\0')
47 switch(*strptr++)
49 case '\n':
50 case '"':
51 case '*':
52 bufsize++;
55 escptr = escaped = (char*) malloc(bufsize);
56 if(!escaped)
57 return NULL;
58 *escptr++ = '"';
59 for(strptr = str; *strptr != '\0'; strptr++)
61 switch(*strptr)
63 case '\n':
64 case '"':
65 case '*':
66 *escptr++ = '*';
67 *escptr++ = (*strptr == '\n' ? 'N' : *strptr);
68 break;
69 default:
70 *escptr++ = *strptr;
71 break;
74 *escptr++ = '"';
75 *escptr = '\0';
76 return escaped;
79 /* Append arg string to argptr increasing argptr if needed */
80 char * appendarg(char *argptr, int *argptrsize, const char *arg)
82 while(strlen(argptr) + strlen(arg) + 2 > *argptrsize)
84 *argptrsize *= 2;
85 argptr = realloc(argptr, *argptrsize);
86 if(!argptr)
87 return NULL;
89 strcat(argptr, arg);
90 strcat(argptr, " ");
92 return argptr;
95 BPTR DupFHFromfd(int fd, ULONG mode);
97 LONG exec_command(BPTR seglist, char *taskname, char *args, ULONG stacksize)
99 BPTR oldin = MKBADDR(NULL), oldout = MKBADDR(NULL), olderr = MKBADDR(NULL);
100 BPTR in, out, err;
101 char *oldtaskname;
102 APTR udata;
103 LONG returncode;
105 in = DupFHFromfd(STDIN_FILENO, FMF_READ);
106 out = DupFHFromfd(STDOUT_FILENO, FMF_WRITE);
107 err = DupFHFromfd(STDERR_FILENO, FMF_WRITE);
109 if(in)
110 oldin = SelectInput(in);
111 if(out)
112 oldout = SelectOutput(out);
113 if(err)
114 olderr = SelectError(err);
116 oldtaskname = FindTask(NULL)->tc_Node.ln_Name;
117 FindTask(NULL)->tc_Node.ln_Name = taskname;
118 SetProgramName(taskname);
120 udata = FindTask(NULL)->tc_UserData;
121 FindTask(NULL)->tc_UserData = NULL;
122 returncode = RunCommand(
123 seglist,
124 stacksize,
125 args,
126 strlen(args)
128 FindTask(NULL)->tc_UserData = udata;
130 FindTask(NULL)->tc_Node.ln_Name = oldtaskname;
131 SetProgramName(oldtaskname);
132 UnLoadSeg(seglist);
134 if(in)
136 Close(in);
137 SelectInput(oldin);
139 if(out)
141 Close(out);
142 SelectOutput(oldout);
144 if(err)
146 Close(err);
147 SelectError(olderr);
150 return returncode;
153 /*****************************************************************************
155 NAME */
156 #include <unistd.h>
158 int execve(
160 /* SYNOPSIS */
161 const char *filename,
162 char *const argv[],
163 char *const envp[])
165 /* FUNCTION
166 Executes a file with given name.
168 INPUTS
169 filename - Name of the file to execute.
170 argv - Array of arguments provided to main() function of the executed
171 file.
172 envp - Array of environment variables passed as environment to the
173 executed program.
175 RESULT
176 Returns -1 and sets errno appropriately in case of error, otherwise
177 doesn't return.
179 NOTES
181 EXAMPLE
183 BUGS
185 SEE ALSO
187 INTERNALS
189 ******************************************************************************/
191 FILE *program;
192 char firstline[128]; /* buffer to read first line of the script */
193 char *inter = NULL; /* interpreter in case of script */
194 char *interargs = ""; /* interpreter arguments */
195 char *linebuf;
196 char *escaped;
197 int argptrsize = 1024;
198 char *argptr = (char*) malloc(argptrsize); /* arguments buffer for
199 RunCommand() */
200 char **arg, **env;
201 char *varname, *varvalue;
202 BPTR seglist;
203 int returncode;
204 struct CommandLineInterface *cli = Cli();
205 char *afilename = NULL;
206 int saved_errno = 0;
207 struct Process *me = (struct Process *)FindTask(NULL);
208 struct MinList tempenv;
209 struct LocalVar *varNode;
210 struct Node *tempNode;
212 /* Sanity check */
213 if (filename == NULL || filename[0] == '\0' || argv == NULL)
215 errno = EINVAL;
216 return -1;
219 /* Let's check if it's a script */
220 if((program = fopen(filename, "r")))
222 if(fgetc(program) == '#' && fgetc(program) == '!')
224 /* It is a script, let's read the first line */
225 if(fgets(firstline, sizeof(firstline) - 1, program))
227 /* delete end of line if present */
228 if(firstline[0] && firstline[strlen(firstline)-1] == '\n')
229 firstline[strlen(firstline)-1] = '\0';
230 linebuf = firstline;
231 while(isblank(*linebuf)) linebuf++;
232 if(*linebuf != '\0')
234 /* Interpreter name is here */
235 inter = linebuf;
236 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
237 if(*linebuf != '\0')
239 *linebuf++ = '\0';
240 while(isblank(*linebuf)) linebuf++;
241 if(*linebuf != '\0')
242 /* Interpreter arguments are here */
243 interargs = linebuf;
248 fclose(program);
250 else
252 /* Simply assume it doesn't exist */
253 saved_errno = ENOENT;
254 goto error;
257 /* Build RunCommand argument string by escaping and appending all
258 arguments */
259 argptr[0] = '\0';
260 arg = (inter != NULL ? &interargs : (char**) argv + 1);
261 while(*arg)
263 if(containswhite(*arg))
265 escaped = escape(*arg);
266 if(!escaped) {
267 saved_errno = ENOMEM;
268 goto error;
270 argptr = appendarg(argptr, &argptrsize, escaped);
271 free(escaped);
273 else
274 argptr = appendarg(argptr, &argptrsize, *arg);
276 if(!argptr) {
277 saved_errno = ENOMEM;
278 goto error;
281 /* If interpreter args were first then we have to insert script name
282 here */
283 if(arg == &interargs)
285 argptr = appendarg(argptr, &argptrsize, filename);
286 if(!argptr) {
287 saved_errno = ENOMEM;
288 goto error;
290 /* Follow with argv */
291 arg = (char**) argv + 1;
293 else
294 arg++;
296 /* End argptr with '\n' */
297 if(strlen(argptr) > 0)
298 argptr[strlen(argptr) - 1] = '\n';
299 else
300 strcat(argptr, "\n");
302 /* Get the path for LoadSeg() */
303 afilename = strdup(__path_u2a(inter ? inter : (char*) filename));
304 if(!afilename)
306 saved_errno = errno;
307 goto error;
310 /* let's make some sanity tests */
312 struct stat st;
313 if(stat(inter ? inter : (char*) filename, &st) == 0)
315 if(!(st.st_mode & S_IXUSR) && !(st.st_mode & S_IXGRP) && !(st.st_mode & S_IXOTH))
317 /* file is not executable */
318 saved_errno = EACCES;
319 goto error;
322 else
324 /* couldn't stat file */
325 saved_errno = errno;
326 goto error;
329 /* Store environment variables */
330 if(envp)
332 /* Backup and clear the environment */
333 NEWLIST(&tempenv);
334 ForeachNodeSafe(&me->pr_LocalVars, varNode, tempNode)
336 Remove((struct Node*) varNode);
337 AddTail((struct List*) &tempenv, (struct Node*) varNode);
340 NEWLIST(&me->pr_LocalVars);
341 env = (char**) envp;
342 while(*env)
344 varvalue = strchr(*env, '=');
345 if(!varvalue || varvalue == *env)
347 /* No variable value? Empty variable name? */
348 saved_errno = EINVAL;
349 goto error_env;
351 varname = malloc(1 + varvalue - *env);
352 if(!varname)
354 saved_errno = ENOMEM;
355 goto error_env;
357 varname[0] = '\0';
358 strncat(varname, *env, varvalue - *env);
359 /* skip '=' */
360 varvalue++;
361 setenv(varname, varvalue, 1);
362 free(varname);
363 env++;
367 /* Load and run the command */
368 if((seglist = LoadSeg((CONST_STRPTR) afilename)))
370 free(afilename);
372 if(envp)
374 /* Everything went fine, execve won't return so we can free the old
375 environment variables */
376 ForeachNodeSafe(&tempenv, varNode, tempNode)
378 FreeMem(varNode->lv_Value, varNode->lv_Len);
379 Remove((struct Node *)varNode);
380 FreeVec(varNode);
384 struct vfork_data *udata = FindTask(NULL)->tc_UserData;
385 if(udata && udata->magic == VFORK_MAGIC)
387 /* This execve() was called from vfork()ed process. We are going
388 to switch from stack borrowed from the parent process to the
389 original stack of this process. First we have to store all
390 local variables in udata to let them survive the stack change */
391 udata->exec_seglist = seglist;
392 udata->exec_arguments = argptr;
393 udata->exec_stacksize = cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT;
394 udata->exec_taskname = (inter ? inter : argv[0]);
396 /* Set this so we know that execve was called */
397 udata->child_executed = 1;
399 D(bug("Restoring child stack to %p < %p < %p\n",
400 udata->child_SPLower,
401 udata->child_SPReg,
402 udata->child_SPUpper)
405 /* Restoring child stack */
406 Forbid();
407 udata->child->tc_SPLower = udata->child_SPLower;
408 udata->child->tc_SPUpper = udata->child_SPUpper;
409 /* Since we are switching to child's stack and in return jmp_buf we
410 have parent's stack pointer saved, we have to set stack value in
411 jmp_buf to child's stack pointer, otherwise it will be outside
412 tc_SPLower-tc_SPUpper range after longjmp and exception will
413 occur. */
414 /* Create space on child stack for the return address written during longjmp. Otherwise
415 data on the stack would be overwritten. */
416 udata->child_startup.as_startup_jmp_buf[0].regs[STACK_INDEX] = (unsigned long) udata->child_SPReg - sizeof(APTR);
417 AROS_GET_SP = udata->child_SPReg;
418 Permit();
420 D(bug("Calling daddy\n"));
421 /* Now call parent process, so it will resume his work */
422 Signal(GETUDATA->parent, 1 << GETUDATA->parent_signal);
424 GETUDATA->exec_returncode = exec_command(
425 GETUDATA->exec_seglist,
426 GETUDATA->exec_taskname,
427 GETUDATA->exec_arguments,
428 GETUDATA->exec_stacksize
431 free(GETUDATA->exec_arguments);
433 D(bug("exiting from forked execve()\n"));
434 _exit(GETUDATA->exec_returncode);
436 else
438 returncode = exec_command(
439 seglist,
440 (inter ? inter : argv[0]),
441 argptr,
442 cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT
445 free(argptr);
447 D(bug("exiting from non-forked execve()\n"));
448 _exit(returncode);
451 else
453 /* most likely file is not a executable */
454 saved_errno = ENOEXEC;
455 goto error_env;
458 error_env:
459 /* Restore environment in case of error */
460 NEWLIST(&me->pr_LocalVars);
461 ForeachNodeSafe(&tempenv, varNode, tempNode)
463 Remove((struct Node*) varNode);
464 AddTail((struct List*) &me->pr_LocalVars, (struct Node*) varNode);
466 error:
467 free(argptr);
468 if(afilename != NULL) free(afilename);
469 if(saved_errno) errno = saved_errno;
470 return -1;
471 } /* execve() */