2 Copyright © 2008-2012, The AROS Development Team. All rights reserved.
5 Support functions for POSIX exec*() functions.
7 #include <exec/types.h>
11 #include <aros/libcall.h>
23 #include "__arosc_privdata.h"
30 #include <aros/debug.h>
32 static BOOL
containswhite(const char *str
);
33 static char *escape(const char *str
);
34 static char *appendarg(char *argptr
, int *argptrsize
, const char *arg
, APTR pool
);
35 static char *appendargs(char *argptr
, int *argptrsize
, char *const args
[], APTR pool
);
36 static void __exec_cleanup(struct aroscbase
*aroscbase
);
38 /* Public functions */
39 /********************/
41 APTR
__exec_prepare(const char *filename
, int searchpath
, char *const argv
[], char *const envp
[])
43 struct aroscbase
*aroscbase
= __GM_GetBase();
44 char *filename2
= NULL
;
47 char **environ
= NULL
;
49 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
50 filename
, searchpath
, argv
, envp
53 if (filename
== NULL
|| filename
[0] == '\0' || argv
== NULL
)
59 if (aroscbase
->acb_environptr
)
60 environ
= *aroscbase
->acb_environptr
;
62 /* Use own memory to allocate so that no arosstdc.library functions need to be called
63 exec_pool can also be allocated in __exec_valist2array
65 if (!aroscbase
->acb_exec_pool
)
66 aroscbase
->acb_exec_pool
= CreatePool(MEMF_PUBLIC
, 1024, 512);
67 if (!aroscbase
->acb_exec_pool
)
73 /* Search path if asked and no directory separator is present in the file */
74 if (searchpath
&& index(filename
, '/') == NULL
&& index(filename
, ':') == NULL
)
77 char *path
= NULL
, *path_ptr
, *path_item
;
82 for (i
=0; environ
[i
]; i
++)
84 if (strncmp(environ
[i
], "PATH=", 5) == 0)
86 path
= &environ
[i
][5];
93 path
= getenv("PATH");
96 path
= ":/bin:/usr/bin";
98 D(bug("__exec_prepare: PATH('%s')\n", path
));
100 path_ptr
= AllocPooled(aroscbase
->acb_exec_pool
, strlen(path
) + 1);
101 strcpy(path_ptr
, path
);
104 D(bug("__exec_prepare: PATH('%s')\n", path
));
107 filename2
= AllocPooled(aroscbase
->acb_exec_pool
, size
);
114 for(path_ptr
= path
, lock
= (BPTR
)NULL
, path_item
= strsep(&path_ptr
, ",:");
115 lock
== (BPTR
)NULL
&& path_item
!= NULL
;
116 path_item
= strsep(&path_ptr
, ",:")
119 if(path_item
[0] == '\0')
122 len
= strlen(path_item
) + strlen(filename
) + 2;
126 FreePooled(aroscbase
->acb_exec_pool
, filename2
, size
);
128 filename2
= AllocPooled(aroscbase
->acb_exec_pool
, size
);
136 strcpy(filename2
, path_item
);
137 strcat(filename2
, "/");
138 strcat(filename2
, filename
);
139 lock
= Lock(__path_u2a(filename2
), SHARED_LOCK
);
140 D(bug("__exec_prepare: Lock(\"%s\") == %x\n", filename2
, (APTR
)lock
));
143 if(lock
!= (BPTR
)NULL
)
152 filename2
= (char *)filename
;
155 if (aroscbase
->acb_flags
& PRETEND_CHILD
)
157 struct vfork_data
*udata
= aroscbase
->acb_vfork_data
;
159 udata
->exec_filename
= filename2
;
160 udata
->exec_argv
= argv
;
161 udata
->exec_envp
= envp
;
163 /* Set this so the child knows that __exec_prepare was called */
164 udata
->child_executed
= 1;
166 D(bug("__exec_prepare: Calling child\n"));
167 /* Now call child process, so it will call __exec_prepare */
168 Signal(udata
->child
, 1 << udata
->child_signal
);
170 D(bug("__exec_prepare: Waiting for child to finish __exec_prepare\n"));
171 /* __exec_prepare should be finished now on child */
172 Wait(1 << udata
->parent_signal
);
176 D(bug("__exec_prepare: Continue child immediately on error\n"));
177 Signal(udata
->child
, 1 << udata
->child_signal
);
182 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
183 udata
->exec_id
, udata
->child_errno
189 D(bug("__exec_prepare: Not running as PRETEND_CHILD\n"));
190 aroscbase
->acb_exec_args
= AllocPooled(aroscbase
->acb_exec_pool
, argssize
);
191 aroscbase
->acb_exec_args
[0] = '\0';
193 /* Let's check if it's a script */
194 BPTR fh
= Open((CONST_STRPTR
)__path_u2a(filename2
), MODE_OLDFILE
);
197 if(FGetC(fh
) == '#' && FGetC(fh
) == '!')
199 char firstline
[128], *linebuf
, *inter
, *interargs
= NULL
;
201 /* It is a script, let's read the first line */
202 if(FGets(fh
, (STRPTR
)firstline
, sizeof(firstline
) - 1))
204 /* delete end of line if present */
205 if(firstline
[0] && firstline
[strlen(firstline
)-1] == '\n')
206 firstline
[strlen(firstline
)-1] = '\0';
208 while(isblank(*linebuf
)) linebuf
++;
211 /* Interpreter name is here */
213 while(*linebuf
!= '\0' && !isblank(*linebuf
)) linebuf
++;
217 while(isblank(*linebuf
)) linebuf
++;
219 /* Interpreter arguments are here */
223 /* Add interpeter args and the script name to command line args */
224 char *args
[] = {NULL
, NULL
, NULL
};
234 aroscbase
->acb_exec_args
= appendargs(
235 aroscbase
->acb_exec_args
, &argssize
, args
, aroscbase
->acb_exec_pool
237 if (!aroscbase
->acb_exec_args
)
243 /* Set file to execute as the script interpreter */
244 filename2
= AllocPooled(aroscbase
->acb_exec_pool
, strlen(inter
) + 1);
245 strcpy(filename2
, inter
);
253 /* Simply assume it doesn't exist */
254 errno
= __arosc_ioerr2errno(IoErr());
258 /* Add arguments to command line args */
259 aroscbase
->acb_exec_args
= appendargs(aroscbase
->acb_exec_args
, &argssize
, argv
+ 1, aroscbase
->acb_exec_pool
);
260 if (!aroscbase
->acb_exec_args
)
266 /* End command line args with '\n' */
267 if(strlen(aroscbase
->acb_exec_args
) > 0)
268 aroscbase
->acb_exec_args
[strlen(aroscbase
->acb_exec_args
) - 1] = '\n';
270 strcat(aroscbase
->acb_exec_args
, "\n");
272 /* let's make some sanity tests */
274 if(stat(filename2
, &st
) == 0)
276 if(!(st
.st_mode
& S_IXUSR
) && !(st
.st_mode
& S_IXGRP
) && !(st
.st_mode
& S_IXOTH
))
278 /* file is not executable */
282 if(st
.st_mode
& S_IFDIR
)
284 /* it's a directory */
291 /* couldn't stat file */
296 aroscbase
->acb_exec_taskname
= AllocPooled(aroscbase
->acb_exec_pool
, strlen(filename2
) + 1);
297 if (!aroscbase
->acb_exec_taskname
)
302 strcpy(aroscbase
->acb_exec_taskname
, filename2
);
304 /* Load file to execute */
305 aroscbase
->acb_exec_seglist
= LoadSeg((CONST_STRPTR
)__path_u2a(filename2
));
306 if (!aroscbase
->acb_exec_seglist
)
312 me
= (struct Process
*)FindTask(NULL
);
314 if (envp
&& envp
!= environ
)
316 struct MinList tempenv
;
317 struct LocalVar
*lv
, *lv2
;
321 /* Remember previous environment variables so they can be put back
322 * if something goes wrong
325 ForeachNodeSafe(&me
->pr_LocalVars
, lv
, lv2
)
327 Remove((struct Node
*)lv
);
328 AddTail((struct List
*)&tempenv
, (struct Node
*)lv
);
331 NEWLIST(&me
->pr_LocalVars
);
334 for (envit
= envp
; *envit
&& env_ok
; envit
++)
335 env_ok
= putenv(*envit
) == 0;
338 /* Old vars may be freed */
339 ForeachNodeSafe(&tempenv
, lv
, lv2
)
341 Remove((struct Node
*)lv
);
342 FreeMem(lv
->lv_Value
, lv
->lv_Len
);
348 ForeachNodeSafe(&me
->pr_LocalVars
, lv
, lv2
)
350 Remove((struct Node
*)lv
);
351 FreeMem(lv
->lv_Value
, lv
->lv_Len
);
355 /* Put old ones back */
356 ForeachNodeSafe(&tempenv
, lv
, lv2
)
358 Remove((struct Node
*)lv
);
359 AddTail((struct List
*)&me
->pr_LocalVars
, (struct Node
*)lv
);
367 /* Set standard files to the standard files from arosc */
368 fdesc
*in
= __getfdesc(STDIN_FILENO
), *out
= __getfdesc(STDOUT_FILENO
),
369 *err
= __getfdesc(STDERR_FILENO
);
372 aroscbase
->acb_exec_oldin
= SelectInput(in
->fcb
->fh
);
374 aroscbase
->acb_exec_oldout
= SelectOutput(out
->fcb
->fh
);
377 aroscbase
->acb_exec_olderr
= me
->pr_CES
;
378 me
->pr_CES
= err
->fcb
->fh
;
382 return (APTR
)aroscbase
;
385 __exec_cleanup(aroscbase
);
391 void __exec_do(APTR id
)
393 struct aroscbase
*aroscbase
= __GM_GetBase();
395 struct CommandLineInterface
*cli
= Cli();
396 struct Task
*self
= FindTask(NULL
);
399 D(bug("[__exec_do] Entering, id(%x)\n", id
));
403 /* When exec is not called under vfork condition id == __GM_GetBase()
404 When exec is called under vfork condition we need to use __GM_GetBase() in the
405 parent to check for PRETEND_CHILD and find the udata for signaling the child
408 if (aroscbase
->acb_flags
& PRETEND_CHILD
)
410 struct vfork_data
*udata
= aroscbase
->acb_vfork_data
;
412 D(bug("[__exec_do] PRETEND_CHILD\n"));
414 __close_on_exec_fdescs();
416 D(bug("Notify child to call __exec_do\n"));
418 /* Signal child that __exec_do is called */
419 Signal(udata
->child
, 1 << udata
->child_signal
);
421 /* Clean up in parent */
422 __exec_cleanup(aroscbase
);
424 /* Continue as parent process */
427 assert(0); /* Should not be reached */
431 D(bug("[__exec_do] !PRETEND_CHILD\n"));
433 aroscbase
->acb_flags
|= EXEC_PARENT
;
435 oldtaskname
= self
->tc_Node
.ln_Name
;
436 self
->tc_Node
.ln_Name
= aroscbase
->acb_exec_taskname
;
437 SetProgramName((STRPTR
)aroscbase
->acb_exec_taskname
);
439 D(bug("[__exec_do] Running program, aroscbase=%x\n", aroscbase
));
441 returncode
= RunCommand(
442 aroscbase
->acb_exec_seglist
,
443 cli
->cli_DefaultStack
* CLI_DEFAULTSTACK_UNIT
,
444 (STRPTR
)aroscbase
->acb_exec_args
,
445 strlen(aroscbase
->acb_exec_args
)
448 D(bug("[__exec_do] Program ran, aroscbase=%x, __GM_GetBase()=%x\n",
449 aroscbase
, __GM_GetBase()
453 self
->tc_Node
.ln_Name
= oldtaskname
;
454 SetProgramName((STRPTR
)oldtaskname
);
456 __exec_cleanup(aroscbase
);
458 D(bug("[__exec_do] exiting from non-forked\n"));
463 char *const *__exec_valist2array(const char *arg1
, va_list list
)
465 struct aroscbase
*aroscbase
= __GM_GetBase();
467 static char *no_arg
[] = {NULL
};
471 assert(aroscbase
->acb_exec_tmparray
== NULL
);
473 va_copy(list2
, list
);
478 for (argit
= va_arg(list
, char *), argc
= 1;
480 argit
= va_arg(list
, char *)
484 if (!(aroscbase
->acb_exec_tmparray
= malloc((argc
+1)*(sizeof(char *)))))
486 D(bug("__exec_valist2array: Memory allocation failed\n"));
491 aroscbase
->acb_exec_tmparray
[0] = (char *)arg1
;
492 for (argit
= va_arg(list2
, char *), i
= 1;
493 i
<= argc
; /* i == argc will copy the NULL pointer */
494 argit
= va_arg(list2
, char *), i
++
497 D(bug("arg %d: %x\n", i
, argit
));
498 aroscbase
->acb_exec_tmparray
[i
] = argit
;
503 return aroscbase
->acb_exec_tmparray
;
507 void __exec_cleanup_array()
509 struct aroscbase
*aroscbase
= __GM_GetBase();
510 if (aroscbase
->acb_exec_tmparray
)
512 free((void *)aroscbase
->acb_exec_tmparray
);
513 aroscbase
->acb_exec_tmparray
= NULL
;
518 /* Support functions */
519 /*********************/
521 /* Return TRUE if there are any white spaces in the string */
522 static BOOL
containswhite(const char *str
)
525 if(isspace(*str
++)) return TRUE
;
529 /* Escape the string and quote it */
530 static char *escape(const char *str
)
532 const char *strptr
= str
;
533 char *escaped
, *escptr
;
534 /* Additional two characters for '"', and one for '\0' */
535 int bufsize
= strlen(str
) + 3;
536 /* Take into account characters to ecape */
537 while(*strptr
!= '\0')
547 escptr
= escaped
= (char*) malloc(bufsize
);
551 for(strptr
= str
; *strptr
!= '\0'; strptr
++)
559 *escptr
++ = (*strptr
== '\n' ? 'N' : *strptr
);
571 /* Append arg string to argptr increasing argptr if needed */
572 static char *appendarg(char *argptr
, int *argptrsize
, const char *arg
, APTR pool
)
574 while(strlen(argptr
) + strlen(arg
) + 2 > *argptrsize
)
577 int argptrsize2
= 2*(*argptrsize
);
579 argptr2
= AllocPooled(pool
, argptrsize2
);
582 FreePooled(pool
, argptr
, *argptrsize
);
585 strcpy(argptr2
, argptr
);
586 FreePooled(pool
, argptr
, *argptrsize
);
588 *argptrsize
= argptrsize2
;
596 static char *appendargs(char *argptr
, int *argssizeptr
, char *const args
[], APTR pool
)
600 for (argsit
= args
; *argsit
&& argptr
; argsit
++)
602 if(containswhite(*argsit
))
604 char *escaped
= escape(*argsit
);
607 FreePooled(pool
, argptr
, *argssizeptr
);
610 argptr
= appendarg(argptr
, argssizeptr
, escaped
, pool
);
614 argptr
= appendarg(argptr
, argssizeptr
, *argsit
, pool
);
620 static void __exec_cleanup(struct aroscbase
*aroscbase
)
622 D(bug("__exec_cleanup: me(%x)\n", FindTask(NULL
)));
624 if(aroscbase
->acb_exec_oldin
)
626 SelectInput(aroscbase
->acb_exec_oldin
);
627 aroscbase
->acb_exec_oldin
= (BPTR
)NULL
;
629 if(aroscbase
->acb_exec_oldout
)
631 SelectOutput(aroscbase
->acb_exec_oldout
);
632 aroscbase
->acb_exec_oldout
= (BPTR
)NULL
;
634 if(aroscbase
->acb_exec_olderr
)
636 struct Process
*me
= (struct Process
*)FindTask(NULL
);
638 me
->pr_CES
= aroscbase
->acb_exec_olderr
;
639 aroscbase
->acb_exec_olderr
= BNULL
;
642 if (aroscbase
->acb_exec_pool
)
644 DeletePool(aroscbase
->acb_exec_pool
);
645 aroscbase
->acb_exec_pool
= NULL
;
647 if (aroscbase
->acb_exec_seglist
)
649 UnLoadSeg(aroscbase
->acb_exec_seglist
);
650 aroscbase
->acb_exec_seglist
= (BPTR
)NULL
;