2 Copyright © 2008-2014, 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 "__posixc_intbase.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 PosixCIntBase
*PosixCBase
);
38 static void __exec_do_regular(struct PosixCIntBase
*PosixCBase
);
39 static void __exec_do_pretend_child(struct PosixCIntBase
*PosixCBase
);
40 static char * assign_filename(const char *filename
, int searchpath
, char **environ
, struct PosixCIntBase
*PosixCBase
);
41 static APTR
__exec_prepare_pretend_child(char *filename2
, char *const argv
[], char *const envp
[],
42 struct PosixCIntBase
*PosixCBase
);
43 static APTR
__exec_prepare_regular(char * filename2
, char *const argv
[], char *const envp
[],
44 char **environ
, struct PosixCIntBase
*PosixCBase
);
46 /* Public functions */
47 /********************/
49 void __exec_do(APTR id
)
51 struct PosixCIntBase
*PosixCBase
=
52 (struct PosixCIntBase
*)__aros_getbase_PosixCBase();
54 D(bug("[__exec_do] Entering, id(%x)\n", id
));
59 if (PosixCBase
->flags
& PRETEND_CHILD
)
61 /* forking parent executing child code prior to child taking over */
62 __exec_do_pretend_child(PosixCBase
);
66 /* exec* without fork or forked child executing exec* */
67 __exec_do_regular(PosixCBase
);
71 APTR
__exec_prepare(const char *filename
, int searchpath
, char *const argv
[], char *const envp
[])
73 struct PosixCIntBase
*PosixCBase
=
74 (struct PosixCIntBase
*)__aros_getbase_PosixCBase();
75 char *filename2
= NULL
;
76 char ***environptr
= __posixc_get_environptr();
77 char **environ
= (environptr
!= NULL
) ? *environptr
: NULL
;
79 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
80 filename
, searchpath
, argv
, envp
83 if (filename
== NULL
|| filename
[0] == '\0' || argv
== NULL
)
89 /* Use own memory to allocate so that no stdc.library functions need to be called
90 exec_pool can also be allocated in __exec_valist2array
92 if (!PosixCBase
->exec_pool
)
93 PosixCBase
->exec_pool
= CreatePool(MEMF_PUBLIC
, 1024, 512);
94 if (!PosixCBase
->exec_pool
)
100 /* Remember current stdcbase, child may overwrite it and mess up exiting */
101 PosixCBase
->exec_oldstdcbase
= PosixCBase
->PosixCBase
.StdCBase
;
103 filename2
= assign_filename(filename
, searchpath
, environ
, PosixCBase
);
107 if (PosixCBase
->flags
& PRETEND_CHILD
)
109 /* forking parent executing child code prior to child taking over */
110 return __exec_prepare_pretend_child(filename2
, argv
, envp
, PosixCBase
);
114 /* exec* without fork or forked child executing exec* */
115 return __exec_prepare_regular(filename2
, argv
, envp
, environ
, PosixCBase
);
119 __exec_cleanup(PosixCBase
);
124 static char * assign_filename(const char *filename
, int searchpath
, char **environ
, struct PosixCIntBase
*PosixCBase
)
126 /* Search path if asked and no directory separator is present in the file */
127 if (searchpath
&& index(filename
, '/') == NULL
&& index(filename
, ':') == NULL
)
130 char *path
= NULL
, *path_ptr
, *path_item
, *filename2
;
131 const char * filename2_dos
;
136 for (i
=0; environ
[i
]; i
++)
138 if (strncmp(environ
[i
], "PATH=", 5) == 0)
140 path
= &environ
[i
][5];
147 path
= getenv("PATH");
150 path
= ":/bin:/usr/bin";
152 D(bug("assign_filename: PATH('%s')\n", path
));
154 path_ptr
= AllocPooled(PosixCBase
->exec_pool
, strlen(path
) + 1);
155 strcpy(path_ptr
, path
);
158 D(bug("assign_filename: PATH('%s')\n", path
));
161 filename2
= AllocPooled(PosixCBase
->exec_pool
, size
);
168 for(path_ptr
= path
, lock
= (BPTR
)NULL
, path_item
= strsep(&path_ptr
, ",:");
169 lock
== (BPTR
)NULL
&& path_item
!= NULL
;
170 path_item
= strsep(&path_ptr
, ",:")
173 if(path_item
[0] == '\0')
176 len
= strlen(path_item
) + strlen(filename
) + 2;
180 FreePooled(PosixCBase
->exec_pool
, filename2
, size
);
182 filename2
= AllocPooled(PosixCBase
->exec_pool
, size
);
190 strcpy(filename2
, path_item
);
191 strcat(filename2
, "/");
192 strcat(filename2
, filename
);
193 filename2_dos
= __path_u2a(filename2
);
194 lock
= Lock(filename2_dos
, SHARED_LOCK
);
195 D(bug("assign_filename: Lock(\"%s\") == %x\n", filename2
, (APTR
)lock
));
198 if(lock
!= (BPTR
)NULL
)
209 return (char *)filename
;
212 static APTR
__exec_prepare_pretend_child(char *filename2
, char *const argv
[], char *const envp
[],
213 struct PosixCIntBase
*PosixCBase
)
215 struct vfork_data
*udata
= PosixCBase
->vfork_data
;
217 udata
->exec_filename
= filename2
;
218 udata
->exec_argv
= argv
;
219 udata
->exec_envp
= envp
;
221 /* Set this so the child knows that __exec_prepare was called */
222 udata
->child_executed
= 1;
224 D(bug("[__exec_prepare_pretend_child] Calling child\n"));
225 /* Now call child process, so it will call __exec_prepare */
226 SETPARENTSTATE(PARENT_STATE_EXEC_CALLED
);
227 Signal(udata
->child
, 1 << udata
->child_signal
);
229 D(bug("[__exec_prepare_pretend_child] Waiting for child to finish __exec_prepare\n"));
230 /* __exec_prepare should be finished now on child */
231 Wait(1 << udata
->parent_signal
);
232 ASSERTCHILDSTATE(CHILD_STATE_EXEC_PREPARE_FINISHED
);
237 D(bug("[__exec_prepare_pretend_child] Continue child immediately on error\n"));
238 SETPARENTSTATE(PARENT_STATE_EXEC_DO_FINISHED
);
239 Signal(udata
->child
, 1 << udata
->child_signal
);
244 D(bug("[__exec_prepare_pretend_child] Exiting from forked __exec_prepare id=%x, errno=%d\n",
245 udata
->exec_id
, udata
->child_errno
248 return (APTR
)PosixCBase
;
251 static APTR
__exec_prepare_regular(char * filename2
, char *const argv
[], char *const envp
[],
252 char **environ
, struct PosixCIntBase
*PosixCBase
)
254 const char *filename2_dos
= NULL
;
258 filename2_dos
= __path_u2a(filename2
);
260 PosixCBase
->exec_args
= AllocPooled(PosixCBase
->exec_pool
, argssize
);
261 PosixCBase
->exec_args
[0] = '\0';
263 /* Let's check if it's a script */
264 BPTR fh
= Open((CONST_STRPTR
)filename2_dos
, MODE_OLDFILE
);
267 if(FGetC(fh
) == '#' && FGetC(fh
) == '!')
269 char firstline
[128], *linebuf
, *inter
, *interargs
= NULL
;
271 /* It is a script, let's read the first line */
272 if(FGets(fh
, (STRPTR
)firstline
, sizeof(firstline
) - 1))
274 /* delete end of line if present */
275 if(firstline
[0] && firstline
[strlen(firstline
)-1] == '\n')
276 firstline
[strlen(firstline
)-1] = '\0';
278 while(isblank(*linebuf
)) linebuf
++;
281 /* Interpreter name is here */
283 while(*linebuf
!= '\0' && !isblank(*linebuf
)) linebuf
++;
287 while(isblank(*linebuf
)) linebuf
++;
289 /* Interpreter arguments are here */
293 /* Add interpeter args and the script name to command line args */
294 char *args
[] = {NULL
, NULL
, NULL
};
304 PosixCBase
->exec_args
= appendargs(
305 PosixCBase
->exec_args
, &argssize
, args
, PosixCBase
->exec_pool
307 if (!PosixCBase
->exec_args
)
313 /* Set file to execute as the script interpreter */
314 filename2
= AllocPooled(PosixCBase
->exec_pool
, strlen(inter
) + 1);
315 strcpy(filename2
, inter
);
316 filename2_dos
= __path_u2a(filename2
);
324 /* Simply assume it doesn't exist */
325 errno
= __stdc_ioerr2errno(IoErr());
329 /* Add arguments to command line args */
330 PosixCBase
->exec_args
= appendargs(PosixCBase
->exec_args
, &argssize
, argv
+ 1, PosixCBase
->exec_pool
);
331 if (!PosixCBase
->exec_args
)
337 /* End command line args with '\n' */
338 if(strlen(PosixCBase
->exec_args
) > 0)
339 PosixCBase
->exec_args
[strlen(PosixCBase
->exec_args
) - 1] = '\n';
341 strcat(PosixCBase
->exec_args
, "\n");
343 /* let's make some sanity tests */
345 if(stat(filename2
, &st
) == 0)
347 if(!(st
.st_mode
& S_IXUSR
) && !(st
.st_mode
& S_IXGRP
) && !(st
.st_mode
& S_IXOTH
))
349 /* file is not executable */
353 if(st
.st_mode
& S_IFDIR
)
355 /* it's a directory */
362 /* couldn't stat file */
367 PosixCBase
->exec_taskname
= AllocPooled(PosixCBase
->exec_pool
, strlen(filename2_dos
) + 1);
368 if (!PosixCBase
->exec_taskname
)
373 strcpy(PosixCBase
->exec_taskname
, filename2_dos
);
375 /* Load file to execute */
376 PosixCBase
->exec_seglist
= LoadSeg((CONST_STRPTR
)filename2_dos
);
377 if (!PosixCBase
->exec_seglist
)
383 me
= (struct Process
*)FindTask(NULL
);
385 if (envp
&& envp
!= environ
)
387 struct MinList tempenv
;
388 struct LocalVar
*lv
, *lv2
;
392 /* Remember previous environment variables so they can be put back
393 * if something goes wrong
396 ForeachNodeSafe(&me
->pr_LocalVars
, lv
, lv2
)
398 Remove((struct Node
*)lv
);
399 AddTail((struct List
*)&tempenv
, (struct Node
*)lv
);
402 NEWLIST(&me
->pr_LocalVars
);
405 for (envit
= envp
; *envit
&& env_ok
; envit
++)
406 env_ok
= putenv(*envit
) == 0;
409 /* Old vars may be freed */
410 ForeachNodeSafe(&tempenv
, lv
, lv2
)
412 Remove((struct Node
*)lv
);
413 FreeMem(lv
->lv_Value
, lv
->lv_Len
);
419 ForeachNodeSafe(&me
->pr_LocalVars
, lv
, lv2
)
421 Remove((struct Node
*)lv
);
422 FreeMem(lv
->lv_Value
, lv
->lv_Len
);
426 /* Put old ones back */
427 ForeachNodeSafe(&tempenv
, lv
, lv2
)
429 Remove((struct Node
*)lv
);
430 AddTail((struct List
*)&me
->pr_LocalVars
, (struct Node
*)lv
);
438 D(bug("[__exec_prepare_regular] Done, returning %p\n", PosixCBase
));
441 return (APTR
)PosixCBase
;
444 __exec_cleanup(PosixCBase
);
449 static void __exec_do_pretend_child(struct PosixCIntBase
*PosixCBase
)
451 /* When exec is called under vfork condition, the PRETEND_CHILD flag is set
452 and we need to signal child that exec is called.
455 struct vfork_data
*udata
= PosixCBase
->vfork_data
;
457 D(bug("[__exec_do_pretend_child] PRETEND_CHILD\n"));
459 __close_on_exec_fdescs();
461 D(bug("[__exec_do_pretend_child] Notify child to call __exec_do\n"));
463 /* Signal child that __exec_do is called */
464 SETPARENTSTATE(PARENT_STATE_EXEC_DO_FINISHED
);
465 Signal(udata
->child
, 1 << udata
->child_signal
);
467 /* Clean up in parent */
468 D(bug("[__exec_do_pretend_child] Cleaning up parent\n"));
469 __exec_cleanup(PosixCBase
);
471 /* Continue as parent process */
472 D(bug("[__exec_do_pretend_child] Stop running as PRETEND_CHILD\n"));
475 assert(0); /* Should not be reached */
478 static void __exec_do_regular(struct PosixCIntBase
*PosixCBase
)
481 struct CommandLineInterface
*cli
= Cli();
482 struct Task
*self
= FindTask(NULL
);
485 PosixCBase
->flags
|= EXEC_PARENT
;
487 oldtaskname
= self
->tc_Node
.ln_Name
;
488 self
->tc_Node
.ln_Name
= PosixCBase
->exec_taskname
;
489 SetProgramName((STRPTR
)PosixCBase
->exec_taskname
);
492 /* Set standard files to the standard files from arosc */
493 fdesc
*in
= __getfdesc(STDIN_FILENO
), *out
= __getfdesc(STDOUT_FILENO
),
494 *err
= __getfdesc(STDERR_FILENO
);
495 BPTR oldin
= BNULL
, oldout
= BNULL
, olderr
= BNULL
;
496 char inchanged
= 0, outchanged
= 0, errchanged
= 0;
497 struct Process
*me
= (struct Process
*)FindTask(NULL
);
499 if(in
&& in
->fcb
->handle
!= Input())
501 oldin
= SelectInput(in
->fcb
->handle
);
504 if(out
&& in
->fcb
->handle
!= Output())
506 oldout
= SelectOutput(out
->fcb
->handle
);
513 if (me
->pr_CES
!= err
->fcb
->handle
)
516 else /* me->pr_CES */
518 /* Only replace if stdout != stderr */
519 if (out
&& out
->fcb
->handle
!= err
->fcb
->handle
)
525 me
->pr_CES
= err
->fcb
->handle
;
529 D(bug("[__exec_do_regular] Running program, PosixCBase=%x\n", PosixCBase
));
530 returncode
= RunCommand(
531 PosixCBase
->exec_seglist
,
532 cli
->cli_DefaultStack
* CLI_DEFAULTSTACK_UNIT
,
533 (STRPTR
)PosixCBase
->exec_args
,
534 strlen(PosixCBase
->exec_args
)
537 /* RunCommand() does not Close() standard output so may not flush either.
538 So to be sure flush them here */
546 SelectOutput(oldout
);
550 D(bug("[__exec_do_regular] Program ran, PosixCBase=%x, __aros_getbase_PosixCBase()=%x\n",
551 PosixCBase
, __aros_getbase_PosixCBase()
555 self
->tc_Node
.ln_Name
= oldtaskname
;
556 SetProgramName((STRPTR
)oldtaskname
);
558 __exec_cleanup(PosixCBase
);
560 D(bug("[__exec_do_regular] exiting from __exec()\n"));
565 char *const *__exec_valist2array(const char *arg1
, va_list list
)
567 struct PosixCIntBase
*PosixCBase
=
568 (struct PosixCIntBase
*)__aros_getbase_PosixCBase();
570 static char *no_arg
[] = {NULL
};
574 assert(PosixCBase
->exec_tmparray
== NULL
);
576 va_copy(list2
, list
);
581 for (argit
= va_arg(list
, char *), argc
= 1;
583 argit
= va_arg(list
, char *)
587 if (!(PosixCBase
->exec_tmparray
= malloc((argc
+1)*(sizeof(char *)))))
589 D(bug("__exec_valist2array: Memory allocation failed\n"));
594 PosixCBase
->exec_tmparray
[0] = (char *)arg1
;
595 for (argit
= va_arg(list2
, char *), i
= 1;
596 i
<= argc
; /* i == argc will copy the NULL pointer */
597 argit
= va_arg(list2
, char *), i
++
600 D(bug("arg %d: %x\n", i
, argit
));
601 PosixCBase
->exec_tmparray
[i
] = argit
;
606 return PosixCBase
->exec_tmparray
;
610 void __exec_cleanup_array()
612 struct PosixCIntBase
*PosixCBase
=
613 (struct PosixCIntBase
*)__aros_getbase_PosixCBase();
615 if (PosixCBase
->exec_tmparray
)
617 free((void *)PosixCBase
->exec_tmparray
);
618 PosixCBase
->exec_tmparray
= NULL
;
623 /* Support functions */
624 /*********************/
626 /* Return TRUE if there are any white spaces in the string */
627 static BOOL
containswhite(const char *str
)
630 if(isspace(*str
++)) return TRUE
;
634 /* Escape the string and quote it */
635 static char *escape(const char *str
)
637 const char *strptr
= str
;
638 char *escaped
, *escptr
;
639 /* Additional two characters for '"', and one for '\0' */
640 int bufsize
= strlen(str
) + 3;
641 /* Take into account characters to ecape */
642 while(*strptr
!= '\0')
652 escptr
= escaped
= (char*) malloc(bufsize
);
656 for(strptr
= str
; *strptr
!= '\0'; strptr
++)
664 *escptr
++ = (*strptr
== '\n' ? 'N' : *strptr
);
676 /* Append arg string to argptr increasing argptr if needed */
677 static char *appendarg(char *argptr
, int *argptrsize
, const char *arg
, APTR pool
)
679 while(strlen(argptr
) + strlen(arg
) + 2 > *argptrsize
)
682 int argptrsize2
= 2*(*argptrsize
);
684 argptr2
= AllocPooled(pool
, argptrsize2
);
687 FreePooled(pool
, argptr
, *argptrsize
);
690 strcpy(argptr2
, argptr
);
691 FreePooled(pool
, argptr
, *argptrsize
);
693 *argptrsize
= argptrsize2
;
701 static char *appendargs(char *argptr
, int *argssizeptr
, char *const args
[], APTR pool
)
705 for (argsit
= args
; *argsit
&& argptr
; argsit
++)
707 if(containswhite(*argsit
))
709 char *escaped
= escape(*argsit
);
712 FreePooled(pool
, argptr
, *argssizeptr
);
715 argptr
= appendarg(argptr
, argssizeptr
, escaped
, pool
);
719 argptr
= appendarg(argptr
, argssizeptr
, *argsit
, pool
);
725 static void __exec_cleanup(struct PosixCIntBase
*PosixCBase
)
727 D(bug("[__exec_cleanup] me(%x)\n", FindTask(NULL
)));
729 if (PosixCBase
->exec_pool
)
731 DeletePool(PosixCBase
->exec_pool
);
732 PosixCBase
->exec_pool
= NULL
;
734 if (PosixCBase
->exec_seglist
)
736 UnLoadSeg(PosixCBase
->exec_seglist
);
737 PosixCBase
->exec_seglist
= (BPTR
)NULL
;
740 if (PosixCBase
->exec_oldstdcbase
)
742 PosixCBase
->PosixCBase
.StdCBase
= PosixCBase
->exec_oldstdcbase
;
743 PosixCBase
->exec_oldstdcbase
= NULL
;