backport
[AROS.git] / compiler / clib / __exec.c
blob7f480cf1fc57b2a66222a7ba569a30a9b23746a4
1 /*
2 Copyright © 2008-2012, The AROS Development Team. All rights reserved.
3 $Id$
5 Support functions for POSIX exec*() functions.
6 */
7 #include <exec/types.h>
8 #include <dos/dos.h>
9 #include <proto/dos.h>
11 #include <aros/libcall.h>
13 #include <errno.h>
14 #include <ctype.h>
15 #include <assert.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <unistd.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
23 #include "__arosc_privdata.h"
24 #include "__exec.h"
25 #include "__upath.h"
26 #include "__fdesc.h"
27 #include "__vfork.h"
29 #define DEBUG 0
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;
45 int argssize = 512;
46 struct Process *me;
47 char **environ = NULL;
49 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
50 filename, searchpath, argv, envp
51 ));
52 /* Sanity check */
53 if (filename == NULL || filename[0] == '\0' || argv == NULL)
55 errno = EINVAL;
56 goto error;
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)
69 errno = ENOMEM;
70 goto error;
73 /* Search path if asked and no directory separator is present in the file */
74 if (searchpath && index(filename, '/') == NULL && index(filename, ':') == NULL)
76 int i, len, size;
77 char *path = NULL, *path_ptr, *path_item;
78 BPTR lock;
80 if (environ)
82 for (i=0; environ[i]; i++)
84 if (strncmp(environ[i], "PATH=", 5) == 0)
86 path = &environ[i][5];
87 break;
92 if (!path)
93 path = getenv("PATH");
95 if (!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);
102 path = path_ptr;
104 D(bug("__exec_prepare: PATH('%s')\n", path));
106 size = 128;
107 filename2 = AllocPooled(aroscbase->acb_exec_pool, size);
108 if (!filename2)
110 errno = ENOMEM;
111 goto error;
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')
120 path_item = ".";
122 len = strlen(path_item) + strlen(filename) + 2;
124 if (len > size)
126 FreePooled(aroscbase->acb_exec_pool, filename2, size);
127 size = len;
128 filename2 = AllocPooled(aroscbase->acb_exec_pool, size);
129 if (!filename2)
131 errno = ENOMEM;
132 goto error;
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)
144 UnLock(lock);
145 else
147 errno = ENOENT;
148 goto error;
151 else
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);
174 if (!udata->exec_id)
176 D(bug("__exec_prepare: Continue child immediately on error\n"));
177 Signal(udata->child, 1 << udata->child_signal);
179 return NULL;
182 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
183 udata->exec_id, udata->child_errno
186 return aroscbase;
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);
195 if(fh)
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';
207 linebuf = firstline;
208 while(isblank(*linebuf)) linebuf++;
209 if(*linebuf != '\0')
211 /* Interpreter name is here */
212 inter = linebuf;
213 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
214 if(*linebuf != '\0')
216 *linebuf++ = '\0';
217 while(isblank(*linebuf)) linebuf++;
218 if(*linebuf != '\0')
219 /* Interpreter arguments are here */
220 interargs = linebuf;
223 /* Add interpeter args and the script name to command line args */
224 char *args[] = {NULL, NULL, NULL};
225 if (interargs)
227 args[0] = interargs;
228 args[1] = filename2;
230 else
232 args[0] = filename2;
234 aroscbase->acb_exec_args = appendargs(
235 aroscbase->acb_exec_args, &argssize, args, aroscbase->acb_exec_pool
237 if (!aroscbase->acb_exec_args)
239 errno = ENOMEM;
240 goto error;
243 /* Set file to execute as the script interpreter */
244 filename2 = AllocPooled(aroscbase->acb_exec_pool, strlen(inter) + 1);
245 strcpy(filename2, inter);
249 Close(fh);
251 else
253 /* Simply assume it doesn't exist */
254 errno = __arosc_ioerr2errno(IoErr());
255 goto error;
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)
262 errno = ENOMEM;
263 goto error;
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';
269 else
270 strcat(aroscbase->acb_exec_args, "\n");
272 /* let's make some sanity tests */
273 struct stat st;
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 */
279 errno = EACCES;
280 goto error;
282 if(st.st_mode & S_IFDIR)
284 /* it's a directory */
285 errno = EACCES;
286 goto error;
289 else
291 /* couldn't stat file */
292 goto error;
295 /* Set taskname */
296 aroscbase->acb_exec_taskname = AllocPooled(aroscbase->acb_exec_pool, strlen(filename2) + 1);
297 if (!aroscbase->acb_exec_taskname)
299 errno = ENOMEM;
300 goto error;
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)
308 errno = ENOEXEC;
309 goto error;
312 me = (struct Process *)FindTask(NULL);
314 if (envp && envp != environ)
316 struct MinList tempenv;
317 struct LocalVar *lv, *lv2;
318 char *const *envit;
319 int env_ok = 1;
321 /* Remember previous environment variables so they can be put back
322 * if something goes wrong
324 NEWLIST(&tempenv);
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);
332 environ = NULL;
334 for (envit = envp; *envit && env_ok; envit++)
335 env_ok = putenv(*envit) == 0;
337 if (env_ok)
338 /* Old vars may be freed */
339 ForeachNodeSafe(&tempenv, lv, lv2)
341 Remove((struct Node *)lv);
342 FreeMem(lv->lv_Value, lv->lv_Len);
343 FreeVec(lv);
345 else
347 /* Free new nodes */
348 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
350 Remove((struct Node *)lv);
351 FreeMem(lv->lv_Value, lv->lv_Len);
352 FreeVec(lv);
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);
362 errno = ENOMEM;
363 goto error;
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);
371 if(in)
372 aroscbase->acb_exec_oldin = SelectInput(in->fcb->fh);
373 if(out)
374 aroscbase->acb_exec_oldout = SelectOutput(out->fcb->fh);
375 if (err)
377 aroscbase->acb_exec_olderr = me->pr_CES;
378 me->pr_CES = err->fcb->fh;
381 /* Everything OK */
382 return (APTR)aroscbase;
384 error:
385 __exec_cleanup(aroscbase);
387 return (APTR)NULL;
391 void __exec_do(APTR id)
393 struct aroscbase *aroscbase = __GM_GetBase();
394 char *oldtaskname;
395 struct CommandLineInterface *cli = Cli();
396 struct Task *self = FindTask(NULL);
397 LONG returncode;
399 D(bug("[__exec_do] Entering, id(%x)\n", id));
401 /* id is unused */
402 (void)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 */
425 _exit(0);
427 assert(0); /* Should not be reached */
428 return;
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"));
459 _exit(returncode);
463 char *const *__exec_valist2array(const char *arg1, va_list list)
465 struct aroscbase *aroscbase = __GM_GetBase();
466 int argc, i;
467 static char *no_arg[] = {NULL};
468 va_list list2;
469 char *argit;
471 assert(aroscbase->acb_exec_tmparray == NULL);
473 va_copy(list2, list);
475 if (arg1 == NULL)
476 return no_arg;
478 for (argit = va_arg(list, char *), argc = 1;
479 argit != NULL;
480 argit = va_arg(list, char *)
482 argc++;
484 if (!(aroscbase->acb_exec_tmparray = malloc((argc+1)*(sizeof(char *)))))
486 D(bug("__exec_valist2array: Memory allocation failed\n"));
487 va_end(list2);
488 return NULL;
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;
501 va_end(list2);
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)
524 while(*str != '\0')
525 if(isspace(*str++)) return TRUE;
526 return FALSE;
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')
539 switch(*strptr++)
541 case '\n':
542 case '"':
543 case '*':
544 bufsize++;
547 escptr = escaped = (char*) malloc(bufsize);
548 if(!escaped)
549 return NULL;
550 *escptr++ = '"';
551 for(strptr = str; *strptr != '\0'; strptr++)
553 switch(*strptr)
555 case '\n':
556 case '"':
557 case '*':
558 *escptr++ = '*';
559 *escptr++ = (*strptr == '\n' ? 'N' : *strptr);
560 break;
561 default:
562 *escptr++ = *strptr;
563 break;
566 *escptr++ = '"';
567 *escptr = '\0';
568 return escaped;
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)
576 char *argptr2;
577 int argptrsize2 = 2*(*argptrsize);
579 argptr2 = AllocPooled(pool, argptrsize2);
580 if(!argptr2)
582 FreePooled(pool, argptr, *argptrsize);
583 return NULL;
585 strcpy(argptr2, argptr);
586 FreePooled(pool, argptr, *argptrsize);
587 argptr = argptr2;
588 *argptrsize = argptrsize2;
590 strcat(argptr, arg);
591 strcat(argptr, " ");
593 return argptr;
596 static char *appendargs(char *argptr, int *argssizeptr, char *const args[], APTR pool)
598 char *const *argsit;
600 for (argsit = args; *argsit && argptr; argsit++)
602 if(containswhite(*argsit))
604 char *escaped = escape(*argsit);
605 if(!escaped)
607 FreePooled(pool, argptr, *argssizeptr);
608 return NULL;
610 argptr = appendarg(argptr, argssizeptr, escaped, pool);
611 free(escaped);
613 else
614 argptr = appendarg(argptr, argssizeptr, *argsit, pool);
617 return argptr;
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;