2 Copyright © 2008-2011, 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 arosc_privdata
*privdata
);
37 static void close_on_exec();
39 /* Public functions */
40 /********************/
42 APTR
__exec_prepare(const char *filename
, int searchpath
, char *const argv
[], char *const envp
[])
44 struct arosc_privdata
*privdata
= __get_arosc_privdata();
45 char *filename2
= 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 /* Use own memory to allocate so that no arosstdc.library functions need to be called
60 exec_pool can also be allocated in __exec_valist2array
62 if (!privdata
->acpd_exec_pool
)
63 privdata
->acpd_exec_pool
= CreatePool(MEMF_PUBLIC
, 1024, 512);
64 if (!privdata
->acpd_exec_pool
)
70 /* Search path if asked and no directory separator is present in the file */
71 if (searchpath
&& index(filename
, '/') == NULL
&& index(filename
, ':') == NULL
)
74 char *path
= NULL
, *path_ptr
, *path_item
;
79 for (i
=0; environ
[i
]; i
++)
81 if (strncmp(environ
[i
], "PATH=", 5) == 0)
83 path
= &environ
[i
][5];
90 path
= getenv("PATH");
93 path
= ":/bin:/usr/bin";
95 D(bug("__exec_prepare: PATH('%s')\n", path
));
97 path_ptr
= AllocPooled(privdata
->acpd_exec_pool
, strlen(path
) + 1);
98 strcpy(path_ptr
, path
);
101 D(bug("__exec_prepare: PATH('%s')\n", path
));
104 filename2
= AllocPooled(privdata
->acpd_exec_pool
, size
);
111 for(path_ptr
= path
, lock
= (BPTR
)NULL
, path_item
= strsep(&path_ptr
, ",:");
112 lock
== (BPTR
)NULL
&& path_item
!= NULL
;
113 path_item
= strsep(&path_ptr
, ",:")
116 if(path_item
[0] == '\0')
119 len
= strlen(path_item
) + strlen(filename
) + 2;
123 FreePooled(privdata
->acpd_exec_pool
, filename2
, size
);
125 filename2
= AllocPooled(privdata
->acpd_exec_pool
, size
);
133 strcpy(filename2
, path_item
);
134 strcat(filename2
, "/");
135 strcat(filename2
, filename
);
136 lock
= Lock(__path_u2a(filename2
), SHARED_LOCK
);
137 D(bug("__exec_prepare: Lock(\"%s\") == %x\n", filename2
, (APTR
)lock
));
140 if(lock
!= (BPTR
)NULL
)
149 filename2
= (char *)filename
;
152 if (privdata
->acpd_flags
& PRETEND_CHILD
)
154 struct vfork_data
*udata
= privdata
->acpd_vfork_data
;
156 udata
->exec_filename
= filename2
;
157 udata
->exec_argv
= argv
;
158 udata
->exec_envp
= envp
;
160 /* Set this so the child knows that __exec_prepare was called */
161 udata
->child_executed
= 1;
163 D(bug("__exec_prepare: Calling child\n"));
164 /* Now call child process, so it will call __exec_prepare */
165 Signal(udata
->child
, 1 << udata
->child_signal
);
167 D(bug("__exec_prepare: Waiting for child to finish __exec_prepare\n"));
168 /* __exec_prepare should be finished now on child */
169 Wait(1 << udata
->parent_signal
);
173 D(bug("__exec_prepare: Continue child immediately on error\n"));
174 Signal(udata
->child
, 1 << udata
->child_signal
);
179 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
180 udata
->exec_id
, udata
->child_errno
186 D(bug("__exec_prepare: Not running as PRETEND_CHILD\n"));
187 privdata
->acpd_exec_args
= AllocPooled(privdata
->acpd_exec_pool
, argssize
);
188 privdata
->acpd_exec_args
[0] = '\0';
190 /* Let's check if it's a script */
191 BPTR fh
= Open((CONST_STRPTR
)__path_u2a(filename2
), MODE_OLDFILE
);
194 if(FGetC(fh
) == '#' && FGetC(fh
) == '!')
196 char firstline
[128], *linebuf
, *inter
, *interargs
= NULL
;
198 /* It is a script, let's read the first line */
199 if(FGets(fh
, (STRPTR
)firstline
, sizeof(firstline
) - 1))
201 /* delete end of line if present */
202 if(firstline
[0] && firstline
[strlen(firstline
)-1] == '\n')
203 firstline
[strlen(firstline
)-1] = '\0';
205 while(isblank(*linebuf
)) linebuf
++;
208 /* Interpreter name is here */
210 while(*linebuf
!= '\0' && !isblank(*linebuf
)) linebuf
++;
214 while(isblank(*linebuf
)) linebuf
++;
216 /* Interpreter arguments are here */
220 /* Add interpeter args and the script name to command line args */
221 char *args
[] = {NULL
, NULL
, NULL
};
231 privdata
->acpd_exec_args
= appendargs(
232 privdata
->acpd_exec_args
, &argssize
, args
, privdata
->acpd_exec_pool
234 if (!privdata
->acpd_exec_args
)
240 /* Set file to execute as the script interpreter */
241 filename2
= AllocPooled(privdata
->acpd_exec_pool
, strlen(inter
) + 1);
242 strcpy(filename2
, inter
);
250 /* Simply assume it doesn't exist */
251 errno
= IoErr2errno(IoErr());
255 /* Add arguments to command line args */
256 privdata
->acpd_exec_args
= appendargs(privdata
->acpd_exec_args
, &argssize
, argv
+ 1, privdata
->acpd_exec_pool
);
257 if (!privdata
->acpd_exec_args
)
263 /* End command line args with '\n' */
264 if(strlen(privdata
->acpd_exec_args
) > 0)
265 privdata
->acpd_exec_args
[strlen(privdata
->acpd_exec_args
) - 1] = '\n';
267 strcat(privdata
->acpd_exec_args
, "\n");
270 /* let's make some sanity tests */
272 if(stat(filename2
, &st
) == 0)
274 if(!(st
.st_mode
& S_IXUSR
) && !(st
.st_mode
& S_IXGRP
) && !(st
.st_mode
& S_IXOTH
))
276 /* file is not executable */
280 if(st
.st_mode
& S_IFDIR
)
282 /* it's a directory */
289 /* couldn't stat file */
294 privdata
->acpd_exec_taskname
= AllocPooled(privdata
->acpd_exec_pool
, strlen(filename2
) + 1);
295 if (!privdata
->acpd_exec_taskname
)
300 strcpy(privdata
->acpd_exec_taskname
, filename2
);
302 /* Load file to execute */
303 privdata
->acpd_exec_seglist
= LoadSeg((CONST_STRPTR
)__path_u2a(filename2
));
304 if (!privdata
->acpd_exec_seglist
)
310 me
= (struct Process
*)FindTask(NULL
);
312 if (envp
&& envp
!= environ
)
314 struct MinList tempenv
;
315 struct LocalVar
*lv
, *lv2
;
319 /* Remember previous environment variables so they can be put back
320 * if something goes wrong
323 ForeachNodeSafe(&me
->pr_LocalVars
, lv
, lv2
)
325 Remove((struct Node
*)lv
);
326 AddTail((struct List
*)&tempenv
, (struct Node
*)lv
);
329 NEWLIST(&me
->pr_LocalVars
);
332 for (envit
= envp
; *envit
&& env_ok
; envit
++)
333 env_ok
= putenv(*envit
) == 0;
336 /* Old vars may be freed */
337 ForeachNodeSafe(&tempenv
, lv
, lv2
)
339 Remove((struct Node
*)lv
);
340 FreeMem(lv
->lv_Value
, lv
->lv_Len
);
346 ForeachNodeSafe(&me
->pr_LocalVars
, lv
, lv2
)
348 Remove((struct Node
*)lv
);
349 FreeMem(lv
->lv_Value
, lv
->lv_Len
);
353 /* Put old ones back */
354 ForeachNodeSafe(&tempenv
, lv
, lv2
)
356 Remove((struct Node
*)lv
);
357 AddTail((struct List
*)&me
->pr_LocalVars
, (struct Node
*)lv
);
365 /* Set standard files to the standard files from arosc */
366 fdesc
*in
= __getfdesc(STDIN_FILENO
), *out
= __getfdesc(STDOUT_FILENO
),
367 *err
= __getfdesc(STDERR_FILENO
);
370 privdata
->acpd_exec_oldin
= SelectInput(in
->fcb
->fh
);
372 privdata
->acpd_exec_oldout
= SelectOutput(out
->fcb
->fh
);
375 privdata
->acpd_exec_olderr
= me
->pr_CES
;
376 me
->pr_CES
= err
->fcb
->fh
;
379 /* Generate new privdata for the exec */
380 assert(!(privdata
->acpd_flags
& KEEP_OLD_ACPD
));
381 privdata
->acpd_flags
|= CREATE_NEW_ACPD
;
382 privdata
->acpd_exec_aroscbase
= OpenLibrary("arosc.library", 0);
383 if(!privdata
->acpd_exec_aroscbase
)
389 /* Initialize some data of the new generated privdata */
390 struct arosc_privdata
*newprivdata
= __get_arosc_privdata();
391 assert(!(newprivdata
->acpd_flags
& CREATE_NEW_ACPD
));
392 newprivdata
->acpd_flags
|= KEEP_OLD_ACPD
;
393 newprivdata
->acpd_parent_does_upath
= privdata
->acpd_doupath
;
396 return (APTR
)privdata
;
399 __exec_cleanup(privdata
);
405 void __exec_do(APTR id
)
407 struct arosc_privdata
*privdata
= id
;
409 struct CommandLineInterface
*cli
= Cli();
410 struct Task
*self
= FindTask(NULL
);
413 D(bug("[__exec_do] Entering, id(%x)\n", id
));
415 if (__get_arosc_privdata()->acpd_flags
& PRETEND_CHILD
)
417 struct vfork_data
*udata
= __get_arosc_privdata()->acpd_vfork_data
;
419 D(bug("[__exec_do] PRETEND_CHILD\n"));
423 /* Signal child that __exec_do is called */
424 Signal(udata
->child
, 1 << udata
->child_signal
);
426 /* Clean up in parent */
427 __exec_cleanup(privdata
);
429 /* Continue as parent process */
432 assert(0); /* Should not be reached */
436 D(bug("[__exec_do] !PRETEND_CHILD\n"));
438 oldtaskname
= self
->tc_Node
.ln_Name
;
439 self
->tc_Node
.ln_Name
= privdata
->acpd_exec_taskname
;
440 SetProgramName((STRPTR
)privdata
->acpd_exec_taskname
);
442 returncode
= RunCommand(
443 privdata
->acpd_exec_seglist
,
444 cli
->cli_DefaultStack
* CLI_DEFAULTSTACK_UNIT
,
445 (STRPTR
)privdata
->acpd_exec_args
,
446 strlen(privdata
->acpd_exec_args
)
449 self
->tc_Node
.ln_Name
= oldtaskname
;
450 SetProgramName((STRPTR
)oldtaskname
);
452 __exec_cleanup(privdata
);
454 D(bug("[__exec_do] exiting from non-forked\n"));
459 char *const *__exec_valist2array(const char *arg1
, va_list list
)
461 struct arosc_privdata
*privdata
= __get_arosc_privdata();
463 static char *no_arg
[] = {NULL
};
467 assert(privdata
->acpd_exec_tmparray
== NULL
);
469 va_copy(list2
, list
);
474 for (argit
= va_arg(list
, char *), argc
= 1;
476 argit
= va_arg(list
, char *)
480 if (!(privdata
->acpd_exec_tmparray
= malloc((argc
+1)*(sizeof(char *)))))
482 D(bug("__exec_valist2array: Memory allocation failed\n"));
487 privdata
->acpd_exec_tmparray
[0] = (char *)arg1
;
488 for (argit
= va_arg(list2
, char *), i
= 1;
489 i
<= argc
; /* i == argc will copy the NULL pointer */
490 argit
= va_arg(list2
, char *), i
++
493 D(bug("arg %d: %x\n", i
, argit
));
494 privdata
->acpd_exec_tmparray
[i
] = argit
;
499 return privdata
->acpd_exec_tmparray
;
503 void __exec_cleanup_array()
505 struct arosc_privdata
*privdata
= __get_arosc_privdata();
506 if (privdata
->acpd_exec_tmparray
)
508 free((void *)privdata
->acpd_exec_tmparray
);
509 privdata
->acpd_exec_tmparray
= NULL
;
514 /* Support functions */
515 /*********************/
517 /* Return TRUE if there are any white spaces in the string */
518 static BOOL
containswhite(const char *str
)
521 if(isspace(*str
++)) return TRUE
;
525 /* Escape the string and quote it */
526 static char *escape(const char *str
)
528 const char *strptr
= str
;
529 char *escaped
, *escptr
;
530 /* Additional two characters for '"', and one for '\0' */
531 int bufsize
= strlen(str
) + 3;
532 /* Take into account characters to ecape */
533 while(*strptr
!= '\0')
543 escptr
= escaped
= (char*) malloc(bufsize
);
547 for(strptr
= str
; *strptr
!= '\0'; strptr
++)
555 *escptr
++ = (*strptr
== '\n' ? 'N' : *strptr
);
567 /* Append arg string to argptr increasing argptr if needed */
568 static char *appendarg(char *argptr
, int *argptrsize
, const char *arg
, APTR pool
)
570 while(strlen(argptr
) + strlen(arg
) + 2 > *argptrsize
)
573 int argptrsize2
= 2*(*argptrsize
);
575 argptr2
= AllocPooled(pool
, argptrsize2
);
578 FreePooled(pool
, argptr
, *argptrsize
);
581 strcpy(argptr2
, argptr
);
582 FreePooled(pool
, argptr
, *argptrsize
);
584 *argptrsize
= argptrsize2
;
592 static char *appendargs(char *argptr
, int *argssizeptr
, char *const args
[], APTR pool
)
596 for (argsit
= args
; *argsit
&& argptr
; argsit
++)
598 if(containswhite(*argsit
))
600 char *escaped
= escape(*argsit
);
604 argptr
= appendarg(argptr
, argssizeptr
, escaped
, pool
);
608 argptr
= appendarg(argptr
, argssizeptr
, *argsit
, pool
);
614 static void __exec_cleanup(struct arosc_privdata
*privdata
)
616 D(bug("__exec_cleanup: me(%x)\n", FindTask(NULL
)));
618 /* Delete old private data */
619 if (privdata
->acpd_exec_aroscbase
)
621 CloseLibrary(privdata
->acpd_exec_aroscbase
);
622 privdata
->acpd_exec_aroscbase
= NULL
;
625 if(privdata
->acpd_exec_oldin
)
627 SelectInput(privdata
->acpd_exec_oldin
);
628 privdata
->acpd_exec_oldin
= (BPTR
)NULL
;
630 if(privdata
->acpd_exec_oldout
)
632 SelectOutput(privdata
->acpd_exec_oldout
);
633 privdata
->acpd_exec_oldout
= (BPTR
)NULL
;
635 if(privdata
->acpd_exec_olderr
)
637 struct Process
*me
= (struct Process
*)FindTask(NULL
);
639 me
->pr_CES
= privdata
->acpd_exec_olderr
;
640 privdata
->acpd_exec_olderr
= BNULL
;
643 if (privdata
->acpd_exec_pool
)
645 DeletePool(privdata
->acpd_exec_pool
);
646 privdata
->acpd_exec_pool
= NULL
;
648 if (privdata
->acpd_exec_seglist
)
650 UnLoadSeg(privdata
->acpd_exec_seglist
);
651 privdata
->acpd_exec_seglist
= (BPTR
)NULL
;
655 static void close_on_exec(void)
660 for (i
= __getfdslots() - 1; i
>= 0; i
--)
662 if ((fd
= __getfdesc(i
)) != NULL
)
664 D(bug("close_on_exec: checking fd %d\n", i
));
665 if (fd
->fdflags
& FD_CLOEXEC
)
667 D(bug("close_on_exec: closing fd %d\n", i
));
668 assert(close(i
) == 0);