2 Copyright © 2008-2009, The AROS Development Team. All rights reserved.
5 POSIX function execve().
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>
26 #include "__arosc_privdata.h"
29 /* Return TRUE if there are any white spaces in the string */
30 BOOL
containswhite(const char *str
)
33 if(isspace(*str
++)) return TRUE
;
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')
55 escptr
= escaped
= (char*) malloc(bufsize
);
59 for(strptr
= str
; *strptr
!= '\0'; strptr
++)
67 *escptr
++ = (*strptr
== '\n' ? 'N' : *strptr
);
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
)
85 argptr
= realloc(argptr
, *argptrsize
);
95 LONG
exec_command(BPTR seglist
, char *taskname
, char *args
, ULONG stacksize
)
100 oldtaskname
= FindTask(NULL
)->tc_Node
.ln_Name
;
101 FindTask(NULL
)->tc_Node
.ln_Name
= taskname
;
102 SetProgramName(taskname
);
104 returncode
= RunCommand(
111 FindTask(NULL
)->tc_Node
.ln_Name
= oldtaskname
;
112 SetProgramName(oldtaskname
);
117 /*****************************************************************************
125 const char *filename
,
130 Executes a file with given name.
133 filename - Name of the file to execute.
134 argv - Array of arguments provided to main() function of the executed
136 envp - Array of environment variables passed as environment to the
140 Returns -1 and sets errno appropriately in case of error, otherwise
153 ******************************************************************************/
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 */
161 int argptrsize
= 1024;
162 char *argptr
= (char*) malloc(argptrsize
); /* arguments buffer for
165 char *varname
, *varvalue
;
168 struct CommandLineInterface
*cli
= Cli();
169 char *afilename
= NULL
;
171 struct Process
*me
= (struct Process
*)FindTask(NULL
);
172 struct MinList tempenv
;
173 struct LocalVar
*varNode
;
174 struct Node
*tempNode
;
177 if (filename
== NULL
|| filename
[0] == '\0' || argv
== NULL
)
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';
195 while(isblank(*linebuf
)) linebuf
++;
198 /* Interpreter name is here */
200 while(*linebuf
!= '\0' && !isblank(*linebuf
)) linebuf
++;
204 while(isblank(*linebuf
)) linebuf
++;
206 /* Interpreter arguments are here */
216 /* Simply assume it doesn't exist */
217 saved_errno
= IoErr2errno(IoErr());
221 /* Get the AmigaOS-like path */
222 afilename
= strdup(__path_u2a(inter
? inter
: (char*) filename
));
229 /* Build RunCommand argument string by escaping and appending all
232 arg
= (inter
!= NULL
? &interargs
: (char**) argv
+ 1);
235 if(containswhite(*arg
))
237 escaped
= escape(*arg
);
239 saved_errno
= ENOMEM
;
242 argptr
= appendarg(argptr
, &argptrsize
, escaped
);
246 argptr
= appendarg(argptr
, &argptrsize
, *arg
);
249 saved_errno
= ENOMEM
;
253 /* If interpreter args were first then we have to insert script name
255 if(arg
== &interargs
)
257 argptr
= appendarg(argptr
, &argptrsize
, filename
);
259 saved_errno
= ENOMEM
;
262 /* Follow with argv */
263 arg
= (char**) argv
+ 1;
268 /* End argptr with '\n' */
269 if(strlen(argptr
) > 0)
270 argptr
[strlen(argptr
) - 1] = '\n';
272 strcat(argptr
, "\n");
274 /* let's make some sanity tests */
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
;
285 if(st
.st_mode
& S_IFDIR
)
287 /* it's a directory */
288 saved_errno
= EACCES
;
294 /* couldn't stat file */
299 /* Store environment variables */
302 /* Backup and clear the environment */
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
);
314 varvalue
= strchr(*env
, '=');
315 if(!varvalue
|| varvalue
== *env
)
317 /* No variable value? Empty variable name? */
318 saved_errno
= EINVAL
;
321 varname
= malloc(1 + varvalue
- *env
);
324 saved_errno
= ENOMEM
;
328 strncat(varname
, *env
, varvalue
- *env
);
331 setenv(varname
, varvalue
, 1);
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
)
346 udata
->exec_taskname
= AllocVec(strlen(inter
? inter
: argv
[0])+1, MEMF_ANY
);
347 if(!udata
->exec_taskname
)
349 FreeVec(udata
->exec_arguments
);
352 CopyMem(argptr
, udata
->exec_arguments
, strlen(argptr
)+1);
353 CopyMem((inter
? inter
: argv
[0]), udata
->exec_taskname
, strlen(inter
? inter
: argv
[0])+1);
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
);
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
);
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
);
397 saved_errno
= ENOMEM
;
401 CopyMem(varNode
, newVar
, copyLength
);
402 newVar
->lv_Node
.ln_Name
= (char *)newVar
+ sizeof(struct LocalVar
);
406 newVar
->lv_Value
= AllocMem(varNode
->lv_Len
, MEMF_PUBLIC
);
408 if (newVar
->lv_Value
== NULL
)
410 /* Free variable node before shutting down */
412 saved_errno
= ENOMEM
;
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"));
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 */
456 oldin
= SelectInput(in
->fcb
->fh
);
458 oldout
= SelectOutput(out
->fcb
->fh
);
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);
469 saved_errno
= ENOMEM
;
472 __get_arosc_privdata()->acpd_flags
|= KEEP_OLD_ACPD
;
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
);
486 __get_arosc_privdata()->acpd_parent_does_upath
= parent_does_upath
;
488 returncode
= exec_command(
490 (inter
? inter
: argv
[0]),
492 cli
->cli_DefaultStack
* CLI_DEFAULTSTACK_UNIT
495 CloseLibrary(aroscbase
);
504 SelectOutput(oldout
);
508 D(bug("exiting from non-forked execve()\n"));
514 /* most likely file is not a executable */
515 saved_errno
= ENOEXEC
;
520 /* Restore environment in case of error */
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
);
532 if(afilename
!= NULL
) free(afilename
);
533 if(saved_errno
) errno
= saved_errno
;