2 Copyright © 2008-2009, The AROS Development Team. All rights reserved.
5 Support functions for POSIX exec*() functions.
7 #include <exec/types.h>
11 #include <aros/libcall.h>
22 #include "__arosc_privdata.h"
29 #include <aros/debug.h>
31 static BOOL
containswhite(const char *str
);
32 static char *escape(const char *str
);
33 static char *appendarg(char *argptr
, int *argptrsize
, const char *arg
);
34 static char *appendargs(char *argptr
, int *argptrsize
, char *const args
[]);
35 static void __exec_cleanup(struct arosc_privdata
*privdata
);
37 /* Public functions */
38 /********************/
40 APTR
__exec_prepare(const char *filename
, int searchpath
, char *const argv
[], char *const envp
[])
42 struct arosc_privdata
*privdata
= __get_arosc_privdata();
43 char *filename2
= NULL
, *filenamefree
= NULL
;
46 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
47 filename
, searchpath
, argv
, envp
50 if (filename
== NULL
|| filename
[0] == '\0' || argv
== NULL
)
56 if (privdata
->acpd_flags
& PRETEND_CHILD
)
58 struct vfork_data
*udata
= privdata
->acpd_vfork_data
;
60 udata
->exec_filename
= filename
;
61 udata
->exec_searchpath
= searchpath
;
62 udata
->exec_argv
= argv
;
63 udata
->exec_envp
= envp
;
65 /* Set this so the child knows that __exec_prepare was called */
66 udata
->child_executed
= 1;
68 D(bug("__exec_prepare: Calling child\n"));
69 /* Now call child process, so it will call __exec_prepare */
70 Signal(udata
->child
, 1 << udata
->child_signal
);
72 D(bug("__exec_prepare: Waiting for child to finish __exec_prepare\n"));
73 /* __exec_prepare should be finished now on child */
74 Wait(1 << udata
->parent_signal
);
78 D(bug("__exec_prepare: Continue child immediately on error\n"));
79 Signal(udata
->child
, 1 << udata
->child_signal
);
82 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
86 return udata
->exec_id
;
88 privdata
->acpd_exec_args
= malloc(argssize
);
89 privdata
->acpd_exec_args
[0] = '\0';
91 /* Search path if asked and no directory separator is present in the file */
92 if (searchpath
&& index(filename
, '/') == NULL
&& index(filename
, ':') == NULL
)
95 char *path
= NULL
, *path_ptr
, *path_item
;
100 for (i
=0; environ
[i
]; i
++)
102 if (strncmp(environ
[i
], "PATH=", 5) == 0)
104 path
= &environ
[i
][5];
111 path
= getenv("PATH");
113 path
= strdup(path
? path
: ":/bin:/usr/bin");
115 for(path_ptr
= path
, lock
= (BPTR
)NULL
, path_item
= strsep(&path_ptr
, ",:");
116 lock
== (BPTR
)NULL
&& path_item
!= NULL
;
117 path_item
= strsep(&path_ptr
, ",:")
123 if(path_item
[0] == '\0')
126 filenamefree
= filename2
= malloc(strlen(path_item
) + strlen(filename
) + 2);
130 strcat(filename2
, path_item
);
131 strcat(filename2
, "/");
132 strcat(filename2
, filename
);
133 lock
= Lock(__path_u2a(filename2
), SHARED_LOCK
);
134 D(bug("__exec_prepare: Lock(\"%s\") == %x\n", filename2
, (APTR
)lock
));
142 if(lock
!= (BPTR
)NULL
)
151 filename2
= (char *)filename
;
154 /* Let's check if it's a script */
155 BPTR fh
= Open((CONST_STRPTR
)__path_u2a(filename2
), MODE_OLDFILE
);
158 if(FGetC(fh
) == '#' && FGetC(fh
) == '!')
160 char firstline
[128], *linebuf
, *inter
, *interargs
= NULL
;
162 /* It is a script, let's read the first line */
163 if(FGets(fh
, (STRPTR
)firstline
, sizeof(firstline
) - 1))
165 /* delete end of line if present */
166 if(firstline
[0] && firstline
[strlen(firstline
)-1] == '\n')
167 firstline
[strlen(firstline
)-1] = '\0';
169 while(isblank(*linebuf
)) linebuf
++;
172 /* Interpreter name is here */
174 while(*linebuf
!= '\0' && !isblank(*linebuf
)) linebuf
++;
178 while(isblank(*linebuf
)) linebuf
++;
180 /* Interpreter arguments are here */
184 /* Add interpeter args and the script name to command line args */
185 char *args
[] = {NULL
, NULL
, NULL
};
195 privdata
->acpd_exec_args
= appendargs(
196 privdata
->acpd_exec_args
, &argssize
, args
198 if (!privdata
->acpd_exec_args
)
204 /* Set file to execute as the script interpreter */
207 filenamefree
= filename2
= strdup(inter
);
215 /* Simply assume it doesn't exist */
216 errno
= IoErr2errno(IoErr());
220 /* Add arguments to command line args */
221 privdata
->acpd_exec_args
= appendargs(privdata
->acpd_exec_args
, &argssize
, argv
+ 1);
222 if (!privdata
->acpd_exec_args
)
228 /* End command line args with '\n' */
229 if(strlen(privdata
->acpd_exec_args
) > 0)
230 privdata
->acpd_exec_args
[strlen(privdata
->acpd_exec_args
) - 1] = '\n';
232 strcat(privdata
->acpd_exec_args
, "\n");
235 /* let's make some sanity tests */
237 if(stat(filename2
, &st
) == 0)
239 if(!(st
.st_mode
& S_IXUSR
) && !(st
.st_mode
& S_IXGRP
) && !(st
.st_mode
& S_IXOTH
))
241 /* file is not executable */
245 if(st
.st_mode
& S_IFDIR
)
247 /* it's a directory */
254 /* couldn't stat file */
259 if (!(privdata
->acpd_exec_taskname
= strdup(filename2
)))
265 /* Load file to execute */
266 privdata
->acpd_exec_seglist
= LoadSeg((CONST_STRPTR
)__path_u2a(filename2
));
267 if (!privdata
->acpd_exec_seglist
)
273 if (envp
&& envp
!= environ
)
275 struct MinList tempenv
;
276 struct LocalVar
*lv
, *lv2
;
277 struct Process
*me
= (struct Process
*)FindTask(NULL
);
281 /* Remember previous environment variables so they can be put back
282 * if something goes wrong
285 ForeachNodeSafe(&me
->pr_LocalVars
, lv
, lv2
)
287 Remove((struct Node
*)lv
);
288 AddTail((struct List
*)&tempenv
, (struct Node
*)lv
);
291 NEWLIST(&me
->pr_LocalVars
);
294 for (envit
= envp
; *envit
&& env_ok
; envit
++)
295 env_ok
= putenv(*envit
) == 0;
298 /* Old vars may be freed */
299 ForeachNodeSafe(&tempenv
, lv
, lv2
)
301 Remove((struct Node
*)lv
);
302 FreeMem(lv
->lv_Value
, lv
->lv_Len
);
308 ForeachNodeSafe(&me
->pr_LocalVars
, lv
, lv2
)
310 Remove((struct Node
*)lv
);
311 FreeMem(lv
->lv_Value
, lv
->lv_Len
);
315 /* Put old ones back */
316 ForeachNodeSafe(&tempenv
, lv
, lv2
)
318 Remove((struct Node
*)lv
);
319 AddTail((struct List
*)&me
->pr_LocalVars
, (struct Node
*)lv
);
327 /* Set standard files to the standard files from arosc */
328 fdesc
*in
= privdata
->acpd_fd_array
[STDIN_FILENO
],
329 *out
= privdata
->acpd_fd_array
[STDOUT_FILENO
],
330 *err
= privdata
->acpd_fd_array
[STDERR_FILENO
];
333 privdata
->acpd_exec_oldin
= SelectInput(in
->fcb
->fh
);
335 privdata
->acpd_exec_oldout
= SelectOutput(out
->fcb
->fh
);
337 privdata
->acpd_exec_olderr
= SelectError(err
->fcb
->fh
);
340 if (privdata
->acpd_exec_tmparray
);
342 free((void *)privdata
->acpd_exec_tmparray
);
343 privdata
->acpd_exec_tmparray
= NULL
;
348 /* Generate new privdata for the exec */
349 assert(!(privdata
->acpd_flags
& KEEP_OLD_ACPD
));
350 privdata
->acpd_flags
|= CREATE_NEW_ACPD
;
351 privdata
->acpd_exec_aroscbase
= OpenLibrary("arosc.library", 0);
352 if(!privdata
->acpd_exec_aroscbase
)
358 /* Initialize some data of the new generated privdata */
359 struct arosc_privdata
*newprivdata
= __get_arosc_privdata();
360 assert(!(newprivdata
->acpd_flags
& CREATE_NEW_ACPD
));
361 newprivdata
->acpd_flags
|= KEEP_OLD_ACPD
;
362 newprivdata
->acpd_parent_does_upath
= privdata
->acpd_doupath
;
365 return (APTR
)privdata
;
368 __exec_cleanup(privdata
);
370 if (privdata
->acpd_exec_tmparray
);
372 free((void *)privdata
->acpd_exec_tmparray
);
373 privdata
->acpd_exec_tmparray
= NULL
;
382 void __exec_do(APTR id
)
384 struct arosc_privdata
*privdata
= id
;
386 struct CommandLineInterface
*cli
= Cli();
387 struct Task
*self
= FindTask(NULL
);
390 if (__get_arosc_privdata()->acpd_flags
& PRETEND_CHILD
)
392 struct vfork_data
*udata
= __get_arosc_privdata()->acpd_vfork_data
;
394 /* Signal child that __exec_do is called */
395 Signal(udata
->child
, 1 << udata
->child_signal
);
397 /* Continue as parent process */
400 assert(0); /* Should not be reached */
404 oldtaskname
= self
->tc_Node
.ln_Name
;
405 self
->tc_Node
.ln_Name
= privdata
->acpd_exec_taskname
;
406 SetProgramName((STRPTR
)privdata
->acpd_exec_taskname
);
408 returncode
= RunCommand(
409 privdata
->acpd_exec_seglist
,
410 cli
->cli_DefaultStack
* CLI_DEFAULTSTACK_UNIT
,
411 (STRPTR
)privdata
->acpd_exec_args
,
412 strlen(privdata
->acpd_exec_args
)
415 self
->tc_Node
.ln_Name
= oldtaskname
;
416 SetProgramName((STRPTR
)oldtaskname
);
418 __exec_cleanup(privdata
);
420 D(bug("exiting from non-forked execve()\n"));
425 char *const *__exec_valist2array(const char *arg1
, va_list list
)
427 struct arosc_privdata
*privdata
= __get_arosc_privdata();
429 static char *no_arg
[] = {NULL
};
433 assert(privdata
->acpd_exec_tmparray
== NULL
);
435 va_copy(list2
, list
);
440 for (argit
= va_arg(list
, char *), argc
= 1;
442 argit
= va_arg(list
, char *)
446 if (!(privdata
->acpd_exec_tmparray
= malloc((argc
+1)*(sizeof(char *)))))
448 D(bug("__exec_valist2array: Memory allocation failed\n"));
453 privdata
->acpd_exec_tmparray
[0] = (char *)arg1
;
454 for (argit
= va_arg(list2
, char *), i
= 1;
455 i
<= argc
; /* i == argc will copy the NULL pointer */
456 argit
= va_arg(list2
, char *), i
++
459 D(bug("arg %d: %x\n", i
, argit
));
460 privdata
->acpd_exec_tmparray
[i
] = argit
;
465 return privdata
->acpd_exec_tmparray
;
469 /* Support functions */
470 /*********************/
472 /* Return TRUE if there are any white spaces in the string */
473 static BOOL
containswhite(const char *str
)
476 if(isspace(*str
++)) return TRUE
;
480 /* Escape the string and quote it */
481 static char *escape(const char *str
)
483 const char *strptr
= str
;
484 char *escaped
, *escptr
;
485 /* Additional two characters for '"', and one for '\0' */
486 int bufsize
= strlen(str
) + 3;
487 /* Take into account characters to ecape */
488 while(*strptr
!= '\0')
498 escptr
= escaped
= (char*) malloc(bufsize
);
502 for(strptr
= str
; *strptr
!= '\0'; strptr
++)
510 *escptr
++ = (*strptr
== '\n' ? 'N' : *strptr
);
522 /* Append arg string to argptr increasing argptr if needed */
523 static char *appendarg(char *argptr
, int *argptrsize
, const char *arg
)
525 while(strlen(argptr
) + strlen(arg
) + 2 > *argptrsize
)
528 argptr
= realloc(argptr
, *argptrsize
);
538 static char *appendargs(char *argptr
, int *argssizeptr
, char *const args
[])
542 for (argsit
= args
; *argsit
&& argptr
; argsit
++)
544 if(containswhite(*argsit
))
546 char *escaped
= escape(*argsit
);
551 argptr
= appendarg(argptr
, argssizeptr
, escaped
);
555 argptr
= appendarg(argptr
, argssizeptr
, *argsit
);
561 void __exec_cleanup(struct arosc_privdata
*privdata
)
564 /* Delete old private data */
565 if (privdata
->acpd_exec_aroscbase
)
567 CloseLibrary(privdata
->acpd_exec_aroscbase
);
568 privdata
->acpd_exec_aroscbase
= NULL
;
571 if(privdata
->acpd_exec_oldin
)
573 SelectInput(privdata
->acpd_exec_oldin
);
574 privdata
->acpd_exec_oldin
= (BPTR
)NULL
;
576 if(privdata
->acpd_exec_oldout
)
578 SelectOutput(privdata
->acpd_exec_oldout
);
579 privdata
->acpd_exec_oldout
= (BPTR
)NULL
;
581 if(privdata
->acpd_exec_olderr
)
583 SelectError(privdata
->acpd_exec_olderr
);
584 privdata
->acpd_exec_olderr
= (BPTR
)NULL
;
587 if (privdata
->acpd_exec_args
)
589 free(privdata
->acpd_exec_args
);
590 privdata
->acpd_exec_args
= NULL
;
592 if (privdata
->acpd_exec_seglist
)
594 UnLoadSeg(privdata
->acpd_exec_seglist
);
595 privdata
->acpd_exec_seglist
= (BPTR
)NULL
;
597 if (privdata
->acpd_exec_taskname
)
599 free(privdata
->acpd_exec_taskname
);
600 privdata
->acpd_exec_taskname
= NULL
;