r10664@lvps87-230-33-50: verhaegs | 2009-03-14 15:35:56 +0100
[cake.git] / compiler / clib / execve.c
blobe47e94244ff52c65f20d556f7c4f172c3531f3ff
1 /*
2 Copyright © 2008-2009, 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 LONG exec_command(BPTR seglist, char *taskname, char *args, ULONG stacksize)
97 char *oldtaskname;
98 LONG returncode;
100 oldtaskname = FindTask(NULL)->tc_Node.ln_Name;
101 FindTask(NULL)->tc_Node.ln_Name = taskname;
102 SetProgramName(taskname);
104 returncode = RunCommand(
105 seglist,
106 stacksize,
107 args,
108 strlen(args)
111 FindTask(NULL)->tc_Node.ln_Name = oldtaskname;
112 SetProgramName(oldtaskname);
114 return returncode;
117 /*****************************************************************************
119 NAME */
120 #include <unistd.h>
122 int execve(
124 /* SYNOPSIS */
125 const char *filename,
126 char *const argv[],
127 char *const envp[])
129 /* FUNCTION
130 Executes a file with given name.
132 INPUTS
133 filename - Name of the file to execute.
134 argv - Array of arguments provided to main() function of the executed
135 file.
136 envp - Array of environment variables passed as environment to the
137 executed program.
139 RESULT
140 Returns -1 and sets errno appropriately in case of error, otherwise
141 doesn't return.
143 NOTES
145 EXAMPLE
147 BUGS
149 SEE ALSO
151 INTERNALS
153 ******************************************************************************/
155 BPTR fh;
156 char firstline[128]; /* buffer to read first line of the script */
157 char *inter = NULL; /* interpreter in case of script */
158 char *interargs = ""; /* interpreter arguments */
159 char *linebuf;
160 char *escaped;
161 int argptrsize = 1024;
162 char *argptr = (char*) malloc(argptrsize); /* arguments buffer for
163 RunCommand() */
164 char **arg, **env;
165 char *varname, *varvalue;
166 BPTR seglist;
167 int returncode;
168 struct CommandLineInterface *cli = Cli();
169 char *afilename = NULL;
170 int saved_errno = 0;
171 struct Process *me = (struct Process *)FindTask(NULL);
172 struct MinList tempenv;
173 struct LocalVar *varNode;
174 struct Node *tempNode;
176 /* Sanity check */
177 if (filename == NULL || filename[0] == '\0' || argv == NULL)
179 errno = EINVAL;
180 return -1;
183 /* Let's check if it's a script */
184 if((fh = Open(__path_u2a(filename), MODE_OLDFILE)))
186 if(FGetC(fh) == '#' && FGetC(fh) == '!')
188 /* It is a script, let's read the first line */
189 if(FGets(fh, firstline, sizeof(firstline) - 1))
191 /* delete end of line if present */
192 if(firstline[0] && firstline[strlen(firstline)-1] == '\n')
193 firstline[strlen(firstline)-1] = '\0';
194 linebuf = firstline;
195 while(isblank(*linebuf)) linebuf++;
196 if(*linebuf != '\0')
198 /* Interpreter name is here */
199 inter = linebuf;
200 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
201 if(*linebuf != '\0')
203 *linebuf++ = '\0';
204 while(isblank(*linebuf)) linebuf++;
205 if(*linebuf != '\0')
206 /* Interpreter arguments are here */
207 interargs = linebuf;
212 Close(fh);
214 else
216 /* Simply assume it doesn't exist */
217 saved_errno = IoErr2errno(IoErr());
218 goto error;
221 /* Get the AmigaOS-like path */
222 afilename = strdup(__path_u2a(inter ? inter : (char*) filename));
223 if(!afilename)
225 saved_errno = errno;
226 goto error;
229 /* Build RunCommand argument string by escaping and appending all
230 arguments */
231 argptr[0] = '\0';
232 arg = (inter != NULL ? &interargs : (char**) argv + 1);
233 while(*arg)
235 if(containswhite(*arg))
237 escaped = escape(*arg);
238 if(!escaped) {
239 saved_errno = ENOMEM;
240 goto error;
242 argptr = appendarg(argptr, &argptrsize, escaped);
243 free(escaped);
245 else
246 argptr = appendarg(argptr, &argptrsize, *arg);
248 if(!argptr) {
249 saved_errno = ENOMEM;
250 goto error;
253 /* If interpreter args were first then we have to insert script name
254 here */
255 if(arg == &interargs)
257 argptr = appendarg(argptr, &argptrsize, filename);
258 if(!argptr) {
259 saved_errno = ENOMEM;
260 goto error;
262 /* Follow with argv */
263 arg = (char**) argv + 1;
265 else
266 arg++;
268 /* End argptr with '\n' */
269 if(strlen(argptr) > 0)
270 argptr[strlen(argptr) - 1] = '\n';
271 else
272 strcat(argptr, "\n");
274 /* let's make some sanity tests */
276 struct stat st;
277 if(stat(inter ? inter : (char*) filename, &st) == 0)
279 if(!(st.st_mode & S_IXUSR) && !(st.st_mode & S_IXGRP) && !(st.st_mode & S_IXOTH))
281 /* file is not executable */
282 saved_errno = EACCES;
283 goto error;
285 if(st.st_mode & S_IFDIR)
287 /* it's a directory */
288 saved_errno = EACCES;
289 goto error;
292 else
294 /* couldn't stat file */
295 saved_errno = errno;
296 goto error;
299 /* Store environment variables */
300 if(envp)
302 /* Backup and clear the environment */
303 NEWLIST(&tempenv);
304 ForeachNodeSafe(&me->pr_LocalVars, varNode, tempNode)
306 Remove((struct Node*) varNode);
307 AddTail((struct List*) &tempenv, (struct Node*) varNode);
310 NEWLIST(&me->pr_LocalVars);
311 env = (char**) envp;
312 while(*env)
314 varvalue = strchr(*env, '=');
315 if(!varvalue || varvalue == *env)
317 /* No variable value? Empty variable name? */
318 saved_errno = EINVAL;
319 goto error_env;
321 varname = malloc(1 + varvalue - *env);
322 if(!varname)
324 saved_errno = ENOMEM;
325 goto error_env;
327 varname[0] = '\0';
328 strncat(varname, *env, varvalue - *env);
329 /* skip '=' */
330 varvalue++;
331 setenv(varname, varvalue, 1);
332 free(varname);
333 env++;
337 /* Load and run the command */
338 if((seglist = LoadSeg((CONST_STRPTR) afilename)))
340 if(__get_arosc_privdata()->acpd_flags & PRETEND_CHILD)
342 struct vfork_data *udata = __get_arosc_privdata()->acpd_vfork_data;
343 udata->exec_arguments = AllocVec(strlen(argptr)+1, MEMF_ANY);
344 if(!udata->exec_arguments)
345 goto error_env;
346 udata->exec_taskname = AllocVec(strlen(inter ? inter : argv[0])+1, MEMF_ANY);
347 if(!udata->exec_taskname)
349 FreeVec(udata->exec_arguments);
350 goto error_env;
352 CopyMem(argptr, udata->exec_arguments, strlen(argptr)+1);
353 CopyMem((inter ? inter : argv[0]), udata->exec_taskname, strlen(inter ? inter : argv[0])+1);
354 free(argptr);
355 free(afilename);
357 struct Process *child = (struct Process*) udata->child;
358 /* Free old child process environment variables */
359 ForeachNodeSafe(&child->pr_LocalVars, varNode, tempNode)
361 FreeMem(varNode->lv_Value, varNode->lv_Len);
362 Remove((struct Node *)varNode);
363 FreeVec(varNode);
366 if(envp)
368 /* We set environment variables above in the parent process,
369 now we have to move them to the child process */
370 NEWLIST(&child->pr_LocalVars);
371 ForeachNodeSafe(&me->pr_LocalVars, varNode, tempNode)
373 Remove((struct Node*) varNode);
374 AddTail((struct List*) &child->pr_LocalVars, (struct Node*) varNode);
376 /* Restore old parent process variables */
377 NEWLIST(&me->pr_LocalVars);
378 ForeachNodeSafe(&tempenv, varNode, tempNode)
380 Remove((struct Node*) varNode);
381 AddTail((struct List*) &me->pr_LocalVars, (struct Node*) varNode);
383 } else {
384 /* envp is null, that means not initialized environ was used, so
385 we have to duplicate parent process environment variables */
387 struct LocalVar *newVar;
388 /* code taken from createnewproc.c */
389 ForeachNode(&me->pr_LocalVars, varNode)
391 LONG copyLength = strlen(varNode->lv_Node.ln_Name) + 1 +
392 sizeof(struct LocalVar);
394 newVar = (struct LocalVar *) AllocVec(copyLength, MEMF_PUBLIC | MEMF_CLEAR);
395 if (newVar == NULL)
397 saved_errno = ENOMEM;
398 goto error;
401 CopyMem(varNode, newVar, copyLength);
402 newVar->lv_Node.ln_Name = (char *)newVar + sizeof(struct LocalVar);
404 if (varNode->lv_Len)
406 newVar->lv_Value = AllocMem(varNode->lv_Len, MEMF_PUBLIC);
408 if (newVar->lv_Value == NULL)
410 /* Free variable node before shutting down */
411 FreeVec(newVar);
412 saved_errno = ENOMEM;
413 goto error;
416 CopyMem(varNode->lv_Value, newVar->lv_Value, varNode->lv_Len);
419 AddTail((struct List *)&child->pr_LocalVars, (struct Node *)newVar);
423 /* Store all local variables in udata to pass them to child */
424 udata->exec_seglist = seglist;
425 udata->exec_stacksize = cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT;
427 /* Set this so we know that execve was called */
428 udata->child_executed = 1;
430 /* Set the child process current directory */
431 if(((struct Process*) udata->child)->pr_CurrentDir)
432 UnLock(((struct Process*) udata->child)->pr_CurrentDir);
434 ((struct Process*) udata->child)->pr_CurrentDir = DupLock(((struct Process*) FindTask(NULL))->pr_CurrentDir);
436 D(bug("Calling child\n"));
437 /* Now call child process, so it will execute this command */
438 Signal(udata->child, 1 << udata->child_signal);
440 D(bug("exiting from forked execve()\n"));
441 _exit(0);
443 else
445 struct Library *aroscbase;
446 int parent_does_upath;
448 fdesc *in, *out, *err;
449 BPTR oldin, oldout, olderr;
450 in = __fd_array[STDIN_FILENO];
451 out = __fd_array[STDOUT_FILENO];
452 err = __fd_array[STDERR_FILENO];
454 /* update dos.library input, output and error handles */
455 if(in)
456 oldin = SelectInput(in->fcb->fh);
457 if(out)
458 oldout = SelectOutput(out->fcb->fh);
459 if(err)
460 olderr = SelectError(err->fcb->fh);
462 parent_does_upath = __doupath;
464 /* Force arosc.library to open with new private data */
465 __get_arosc_privdata()->acpd_flags |= CREATE_NEW_ACPD;
466 aroscbase = OpenLibrary("arosc.library", 0);
467 if(!aroscbase)
469 saved_errno = ENOMEM;
470 goto error_env;
472 __get_arosc_privdata()->acpd_flags |= KEEP_OLD_ACPD;
474 if(envp)
476 /* Everything went fine, execve won't return so we can free the old
477 environment variables */
478 ForeachNodeSafe(&tempenv, varNode, tempNode)
480 FreeMem(varNode->lv_Value, varNode->lv_Len);
481 Remove((struct Node *)varNode);
482 FreeVec(varNode);
486 __get_arosc_privdata()->acpd_parent_does_upath = parent_does_upath;
488 returncode = exec_command(
489 seglist,
490 (inter ? inter : argv[0]),
491 argptr,
492 cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT
495 CloseLibrary(aroscbase);
497 UnLoadSeg(seglist);
498 free(argptr);
499 free(afilename);
501 if(in)
502 SelectInput(oldin);
503 if(out)
504 SelectOutput(oldout);
505 if(err)
506 SelectError(olderr);
508 D(bug("exiting from non-forked execve()\n"));
509 _exit(returncode);
512 else
514 /* most likely file is not a executable */
515 saved_errno = ENOEXEC;
516 goto error_env;
519 error_env:
520 /* Restore environment in case of error */
521 if(envp)
523 NEWLIST(&me->pr_LocalVars);
524 ForeachNodeSafe(&tempenv, varNode, tempNode)
526 Remove((struct Node*) varNode);
527 AddTail((struct List*) &me->pr_LocalVars, (struct Node*) varNode);
530 error:
531 free(argptr);
532 if(afilename != NULL) free(afilename);
533 if(saved_errno) errno = saved_errno;
534 return -1;
535 } /* execve() */