Unified __exec_do debug output. Avoid double allocation.
[AROS.git] / compiler / clib / __exec.c
blob0c04a55c6331fdb0e1989aa964390cae232e1cae
1 /*
2 Copyright © 2008-2011, 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 <strings.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 "__errno.h"
27 #include "__fdesc.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 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;
46 int argssize = 512;
48 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
49 filename, searchpath, argv, envp
50 ));
51 /* Sanity check */
52 if (filename == NULL || filename[0] == '\0' || argv == NULL)
54 errno = EINVAL;
55 goto error;
58 /* Use own memory to allocate so that no arosstdc.library functions need to be called
59 exec_pool can also be allocated in __exec_valist2array
61 if (!privdata->acpd_exec_pool)
62 privdata->acpd_exec_pool = CreatePool(MEMF_PUBLIC, 1024, 512);
63 if (!privdata->acpd_exec_pool)
65 errno = ENOMEM;
66 goto error;
69 /* Search path if asked and no directory separator is present in the file */
70 if (searchpath && index(filename, '/') == NULL && index(filename, ':') == NULL)
72 int i, len, size;
73 char *path = NULL, *path_ptr, *path_item;
74 BPTR lock;
76 if (environ)
78 for (i=0; environ[i]; i++)
80 if (strncmp(environ[i], "PATH=", 5) == 0)
82 path = &environ[i][5];
83 break;
88 if (!path)
89 path = getenv("PATH");
91 if (!path)
92 path = ":/bin:/usr/bin";
94 D(bug("__exec_prepare: PATH('%s')\n", path));
96 path_ptr = AllocPooled(privdata->acpd_exec_pool, strlen(path) + 1);
97 strcpy(path_ptr, path);
98 path = path_ptr;
100 D(bug("__exec_prepare: PATH('%s')\n", path));
102 size = 128;
103 filename2 = AllocPooled(privdata->acpd_exec_pool, size);
104 if (!filename2)
106 errno = ENOMEM;
107 goto error;
110 for(path_ptr = path, lock = (BPTR)NULL, path_item = strsep(&path_ptr, ",:");
111 lock == (BPTR)NULL && path_item != NULL;
112 path_item = strsep(&path_ptr, ",:")
115 if(path_item[0] == '\0')
116 path_item = ".";
118 len = strlen(path_item) + strlen(filename) + 2;
120 if (len > size)
122 FreePooled(privdata->acpd_exec_pool, filename2, size);
123 size = len;
124 filename2 = AllocPooled(privdata->acpd_exec_pool, size);
125 if (!filename2)
127 errno = ENOMEM;
128 goto error;
132 strcpy(filename2, path_item);
133 strcat(filename2, "/");
134 strcat(filename2, filename);
135 lock = Lock(__path_u2a(filename2), SHARED_LOCK);
136 D(bug("__exec_prepare: Lock(\"%s\") == %x\n", filename2, (APTR)lock));
139 if(lock != (BPTR)NULL)
140 UnLock(lock);
141 else
143 errno = ENOENT;
144 goto error;
147 else
148 filename2 = (char *)filename;
151 if (privdata->acpd_flags & PRETEND_CHILD)
153 struct vfork_data *udata = privdata->acpd_vfork_data;
155 udata->exec_filename = filename2;
156 udata->exec_argv = argv;
157 udata->exec_envp = envp;
159 /* Set this so the child knows that __exec_prepare was called */
160 udata->child_executed = 1;
162 D(bug("__exec_prepare: Calling child\n"));
163 /* Now call child process, so it will call __exec_prepare */
164 Signal(udata->child, 1 << udata->child_signal);
166 D(bug("__exec_prepare: Waiting for child to finish __exec_prepare\n"));
167 /* __exec_prepare should be finished now on child */
168 Wait(1 << udata->parent_signal);
170 if (!udata->exec_id)
172 D(bug("__exec_prepare: Continue child immediately on error\n"));
173 Signal(udata->child, 1 << udata->child_signal);
175 return NULL;
178 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
179 udata->exec_id, udata->child_errno
182 return privdata;
185 D(bug("__exec_prepare: Not running as PRETEND_CHILD\n"));
186 privdata->acpd_exec_args = AllocPooled(privdata->acpd_exec_pool, argssize);
187 privdata->acpd_exec_args[0] = '\0';
189 /* Let's check if it's a script */
190 BPTR fh = Open((CONST_STRPTR)__path_u2a(filename2), MODE_OLDFILE);
191 if(fh)
193 if(FGetC(fh) == '#' && FGetC(fh) == '!')
195 char firstline[128], *linebuf, *inter, *interargs = NULL;
197 /* It is a script, let's read the first line */
198 if(FGets(fh, (STRPTR)firstline, sizeof(firstline) - 1))
200 /* delete end of line if present */
201 if(firstline[0] && firstline[strlen(firstline)-1] == '\n')
202 firstline[strlen(firstline)-1] = '\0';
203 linebuf = firstline;
204 while(isblank(*linebuf)) linebuf++;
205 if(*linebuf != '\0')
207 /* Interpreter name is here */
208 inter = linebuf;
209 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
210 if(*linebuf != '\0')
212 *linebuf++ = '\0';
213 while(isblank(*linebuf)) linebuf++;
214 if(*linebuf != '\0')
215 /* Interpreter arguments are here */
216 interargs = linebuf;
219 /* Add interpeter args and the script name to command line args */
220 char *args[] = {NULL, NULL, NULL};
221 if (interargs)
223 args[0] = interargs;
224 args[1] = filename2;
226 else
228 args[0] = filename2;
230 privdata->acpd_exec_args = appendargs(
231 privdata->acpd_exec_args, &argssize, args, privdata->acpd_exec_pool
233 if (!privdata->acpd_exec_args)
235 errno = ENOMEM;
236 goto error;
239 /* Set file to execute as the script interpreter */
240 filename2 = AllocPooled(privdata->acpd_exec_pool, strlen(inter) + 1);
241 strcpy(filename2, inter);
245 Close(fh);
247 else
249 /* Simply assume it doesn't exist */
250 errno = IoErr2errno(IoErr());
251 goto error;
254 /* Add arguments to command line args */
255 privdata->acpd_exec_args = appendargs(privdata->acpd_exec_args, &argssize, argv + 1, privdata->acpd_exec_pool);
256 if (!privdata->acpd_exec_args)
258 errno = ENOMEM;
259 goto error;
262 /* End command line args with '\n' */
263 if(strlen(privdata->acpd_exec_args) > 0)
264 privdata->acpd_exec_args[strlen(privdata->acpd_exec_args) - 1] = '\n';
265 else
266 strcat(privdata->acpd_exec_args, "\n");
269 /* let's make some sanity tests */
270 struct stat st;
271 if(stat(filename2, &st) == 0)
273 if(!(st.st_mode & S_IXUSR) && !(st.st_mode & S_IXGRP) && !(st.st_mode & S_IXOTH))
275 /* file is not executable */
276 errno = EACCES;
277 goto error;
279 if(st.st_mode & S_IFDIR)
281 /* it's a directory */
282 errno = EACCES;
283 goto error;
286 else
288 /* couldn't stat file */
289 goto error;
292 /* Set taskname */
293 privdata->acpd_exec_taskname = AllocPooled(privdata->acpd_exec_pool, strlen(filename2) + 1);
294 if (!privdata->acpd_exec_taskname)
296 errno = ENOMEM;
297 goto error;
299 strcpy(privdata->acpd_exec_taskname, filename2);
301 /* Load file to execute */
302 privdata->acpd_exec_seglist = LoadSeg((CONST_STRPTR)__path_u2a(filename2));
303 if (!privdata->acpd_exec_seglist)
305 errno = ENOEXEC;
306 goto error;
309 if (envp && envp != environ)
311 struct MinList tempenv;
312 struct LocalVar *lv, *lv2;
313 struct Process *me = (struct Process *)FindTask(NULL);
314 char *const *envit;
315 int env_ok = 1;
317 /* Remember previous environment variables so they can be put back
318 * if something goes wrong
320 NEWLIST(&tempenv);
321 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
323 Remove((struct Node *)lv);
324 AddTail((struct List *)&tempenv, (struct Node *)lv);
327 NEWLIST(&me->pr_LocalVars);
328 environ = NULL;
330 for (envit = envp; *envit && env_ok; envit++)
331 env_ok = putenv(*envit) == 0;
333 if (env_ok)
334 /* Old vars may be freed */
335 ForeachNodeSafe(&tempenv, lv, lv2)
337 Remove((struct Node *)lv);
338 FreeMem(lv->lv_Value, lv->lv_Len);
339 FreeVec(lv);
341 else
343 /* Free new nodes */
344 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
346 Remove((struct Node *)lv);
347 FreeMem(lv->lv_Value, lv->lv_Len);
348 FreeVec(lv);
351 /* Put old ones back */
352 ForeachNodeSafe(&tempenv, lv, lv2)
354 Remove((struct Node *)lv);
355 AddTail((struct List *)&me->pr_LocalVars, (struct Node *)lv);
358 errno = ENOMEM;
359 goto error;
363 /* Set standard files to the standard files from arosc */
364 fdesc *in = __getfdesc(STDIN_FILENO), *out = __getfdesc(STDOUT_FILENO),
365 *err = __getfdesc(STDERR_FILENO);
367 if(in)
368 privdata->acpd_exec_oldin = SelectInput(in->fcb->fh);
369 if(out)
370 privdata->acpd_exec_oldout = SelectOutput(out->fcb->fh);
371 if(err)
372 privdata->acpd_exec_olderr = SelectError(err->fcb->fh);
374 /* Generate new privdata for the exec */
375 assert(!(privdata->acpd_flags & KEEP_OLD_ACPD));
376 privdata->acpd_flags |= CREATE_NEW_ACPD;
377 privdata->acpd_exec_aroscbase = OpenLibrary("arosc.library", 0);
378 if(!privdata->acpd_exec_aroscbase)
380 errno = ENOMEM;
381 goto error;
384 /* Initialize some data of the new generated privdata */
385 struct arosc_privdata *newprivdata = __get_arosc_privdata();
386 assert(!(newprivdata->acpd_flags & CREATE_NEW_ACPD));
387 newprivdata->acpd_flags |= KEEP_OLD_ACPD;
388 newprivdata->acpd_parent_does_upath = privdata->acpd_doupath;
390 /* Everything OK */
391 return (APTR)privdata;
393 error:
394 __exec_cleanup(privdata);
396 return (APTR)NULL;
400 void __exec_do(APTR id)
402 struct arosc_privdata *privdata = id;
403 char *oldtaskname;
404 struct CommandLineInterface *cli = Cli();
405 struct Task *self = FindTask(NULL);
406 LONG returncode;
408 D(bug("[__exec_do] Entering, id(%x)\n", id));
410 if (__get_arosc_privdata()->acpd_flags & PRETEND_CHILD)
412 struct vfork_data *udata = __get_arosc_privdata()->acpd_vfork_data;
414 D(bug("[__exec_do] PRETEND_CHILD\n"));
416 close_on_exec();
418 /* Signal child that __exec_do is called */
419 Signal(udata->child, 1 << udata->child_signal);
421 /* Clean up in parent */
422 __exec_cleanup(privdata);
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 oldtaskname = self->tc_Node.ln_Name;
434 self->tc_Node.ln_Name = privdata->acpd_exec_taskname;
435 SetProgramName((STRPTR)privdata->acpd_exec_taskname);
437 returncode = RunCommand(
438 privdata->acpd_exec_seglist,
439 cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT,
440 (STRPTR)privdata->acpd_exec_args,
441 strlen(privdata->acpd_exec_args)
444 self->tc_Node.ln_Name = oldtaskname;
445 SetProgramName((STRPTR)oldtaskname);
447 __exec_cleanup(privdata);
449 D(bug("[__exec_do] exiting from non-forked\n"));
450 _exit(returncode);
454 char *const *__exec_valist2array(const char *arg1, va_list list)
456 struct arosc_privdata *privdata = __get_arosc_privdata();
457 int argc, i;
458 static char *no_arg[] = {NULL};
459 va_list list2;
460 char *argit;
462 assert(privdata->acpd_exec_tmparray == NULL);
464 va_copy(list2, list);
466 if (arg1 == NULL)
467 return no_arg;
469 for (argit = va_arg(list, char *), argc = 1;
470 argit != NULL;
471 argit = va_arg(list, char *)
473 argc++;
475 if (!(privdata->acpd_exec_tmparray = malloc((argc+1)*(sizeof(char *)))))
477 D(bug("__exec_valist2array: Memory allocation failed\n"));
478 va_end(list2);
479 return NULL;
482 privdata->acpd_exec_tmparray[0] = (char *)arg1;
483 for (argit = va_arg(list2, char *), i = 1;
484 i <= argc; /* i == argc will copy the NULL pointer */
485 argit = va_arg(list2, char *), i++
488 D(bug("arg %d: %x\n", i, argit));
489 privdata->acpd_exec_tmparray[i] = argit;
492 va_end(list2);
494 return privdata->acpd_exec_tmparray;
498 void __exec_cleanup_array()
500 struct arosc_privdata *privdata = __get_arosc_privdata();
501 if (privdata->acpd_exec_tmparray)
503 free((void *)privdata->acpd_exec_tmparray);
504 privdata->acpd_exec_tmparray = NULL;
509 /* Support functions */
510 /*********************/
512 /* Return TRUE if there are any white spaces in the string */
513 static BOOL containswhite(const char *str)
515 while(*str != '\0')
516 if(isspace(*str++)) return TRUE;
517 return FALSE;
520 /* Escape the string and quote it */
521 static char *escape(const char *str)
523 const char *strptr = str;
524 char *escaped, *escptr;
525 /* Additional two characters for '"', and one for '\0' */
526 int bufsize = strlen(str) + 3;
527 /* Take into account characters to ecape */
528 while(*strptr != '\0')
530 switch(*strptr++)
532 case '\n':
533 case '"':
534 case '*':
535 bufsize++;
538 escptr = escaped = (char*) malloc(bufsize);
539 if(!escaped)
540 return NULL;
541 *escptr++ = '"';
542 for(strptr = str; *strptr != '\0'; strptr++)
544 switch(*strptr)
546 case '\n':
547 case '"':
548 case '*':
549 *escptr++ = '*';
550 *escptr++ = (*strptr == '\n' ? 'N' : *strptr);
551 break;
552 default:
553 *escptr++ = *strptr;
554 break;
557 *escptr++ = '"';
558 *escptr = '\0';
559 return escaped;
562 /* Append arg string to argptr increasing argptr if needed */
563 static char *appendarg(char *argptr, int *argptrsize, const char *arg, APTR pool)
565 while(strlen(argptr) + strlen(arg) + 2 > *argptrsize)
567 char *argptr2;
568 int argptrsize2 = 2*(*argptrsize);
570 argptr2 = AllocPooled(pool, argptrsize2);
571 if(!argptr2)
573 FreePooled(pool, argptr, *argptrsize);
574 return NULL;
576 strcpy(argptr2, argptr);
577 FreePooled(pool, argptr, *argptrsize);
578 argptr = argptr2;
579 *argptrsize = argptrsize2;
581 strcat(argptr, arg);
582 strcat(argptr, " ");
584 return argptr;
587 static char *appendargs(char *argptr, int *argssizeptr, char *const args[], APTR pool)
589 char *const *argsit;
591 for (argsit = args; *argsit && argptr; argsit++)
593 if(containswhite(*argsit))
595 char *escaped = escape(*argsit);
596 if(!escaped) {
597 return NULL;
599 argptr = appendarg(argptr, argssizeptr, escaped, pool);
600 free(escaped);
602 else
603 argptr = appendarg(argptr, argssizeptr, *argsit, pool);
606 return argptr;
609 static void __exec_cleanup(struct arosc_privdata *privdata)
611 D(bug("__exec_cleanup: me(%x)\n", FindTask(NULL)));
613 /* Delete old private data */
614 if (privdata->acpd_exec_aroscbase)
616 CloseLibrary(privdata->acpd_exec_aroscbase);
617 privdata->acpd_exec_aroscbase = NULL;
620 if(privdata->acpd_exec_oldin)
622 SelectInput(privdata->acpd_exec_oldin);
623 privdata->acpd_exec_oldin = (BPTR)NULL;
625 if(privdata->acpd_exec_oldout)
627 SelectOutput(privdata->acpd_exec_oldout);
628 privdata->acpd_exec_oldout = (BPTR)NULL;
630 if(privdata->acpd_exec_olderr)
632 SelectError(privdata->acpd_exec_olderr);
633 privdata->acpd_exec_olderr = (BPTR)NULL;
636 if (privdata->acpd_exec_pool)
638 DeletePool(privdata->acpd_exec_pool);
639 privdata->acpd_exec_pool = NULL;
641 if (privdata->acpd_exec_seglist)
643 UnLoadSeg(privdata->acpd_exec_seglist);
644 privdata->acpd_exec_seglist = (BPTR)NULL;
648 static void close_on_exec(void)
650 int i;
651 fdesc *fd;
653 for (i = __getfdslots() - 1; i >= 0; i--)
655 if ((fd = __getfdesc(i)) != NULL)
657 D(bug("close_on_exec: checking fd %d\n", i));
658 if (fd->fdflags & FD_CLOEXEC)
660 D(bug("close_on_exec: closing fd %d\n", i));
661 assert(close(i) == 0);