Improved fallback ModeID selection. Works on a wider variety of hardware. Now works...
[AROS.git] / compiler / clib / __exec.c
blobc8d46c68ef523ba15f6fa95b4178dde5d7896c11
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;
47 struct Process *me;
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 /* 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)
66 errno = ENOMEM;
67 goto error;
70 /* Search path if asked and no directory separator is present in the file */
71 if (searchpath && index(filename, '/') == NULL && index(filename, ':') == NULL)
73 int i, len, size;
74 char *path = NULL, *path_ptr, *path_item;
75 BPTR lock;
77 if (environ)
79 for (i=0; environ[i]; i++)
81 if (strncmp(environ[i], "PATH=", 5) == 0)
83 path = &environ[i][5];
84 break;
89 if (!path)
90 path = getenv("PATH");
92 if (!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);
99 path = path_ptr;
101 D(bug("__exec_prepare: PATH('%s')\n", path));
103 size = 128;
104 filename2 = AllocPooled(privdata->acpd_exec_pool, size);
105 if (!filename2)
107 errno = ENOMEM;
108 goto error;
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')
117 path_item = ".";
119 len = strlen(path_item) + strlen(filename) + 2;
121 if (len > size)
123 FreePooled(privdata->acpd_exec_pool, filename2, size);
124 size = len;
125 filename2 = AllocPooled(privdata->acpd_exec_pool, size);
126 if (!filename2)
128 errno = ENOMEM;
129 goto error;
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)
141 UnLock(lock);
142 else
144 errno = ENOENT;
145 goto error;
148 else
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);
171 if (!udata->exec_id)
173 D(bug("__exec_prepare: Continue child immediately on error\n"));
174 Signal(udata->child, 1 << udata->child_signal);
176 return NULL;
179 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
180 udata->exec_id, udata->child_errno
183 return privdata;
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);
192 if(fh)
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';
204 linebuf = firstline;
205 while(isblank(*linebuf)) linebuf++;
206 if(*linebuf != '\0')
208 /* Interpreter name is here */
209 inter = linebuf;
210 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
211 if(*linebuf != '\0')
213 *linebuf++ = '\0';
214 while(isblank(*linebuf)) linebuf++;
215 if(*linebuf != '\0')
216 /* Interpreter arguments are here */
217 interargs = linebuf;
220 /* Add interpeter args and the script name to command line args */
221 char *args[] = {NULL, NULL, NULL};
222 if (interargs)
224 args[0] = interargs;
225 args[1] = filename2;
227 else
229 args[0] = filename2;
231 privdata->acpd_exec_args = appendargs(
232 privdata->acpd_exec_args, &argssize, args, privdata->acpd_exec_pool
234 if (!privdata->acpd_exec_args)
236 errno = ENOMEM;
237 goto error;
240 /* Set file to execute as the script interpreter */
241 filename2 = AllocPooled(privdata->acpd_exec_pool, strlen(inter) + 1);
242 strcpy(filename2, inter);
246 Close(fh);
248 else
250 /* Simply assume it doesn't exist */
251 errno = IoErr2errno(IoErr());
252 goto error;
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)
259 errno = ENOMEM;
260 goto error;
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';
266 else
267 strcat(privdata->acpd_exec_args, "\n");
270 /* let's make some sanity tests */
271 struct stat st;
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 */
277 errno = EACCES;
278 goto error;
280 if(st.st_mode & S_IFDIR)
282 /* it's a directory */
283 errno = EACCES;
284 goto error;
287 else
289 /* couldn't stat file */
290 goto error;
293 /* Set taskname */
294 privdata->acpd_exec_taskname = AllocPooled(privdata->acpd_exec_pool, strlen(filename2) + 1);
295 if (!privdata->acpd_exec_taskname)
297 errno = ENOMEM;
298 goto error;
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)
306 errno = ENOEXEC;
307 goto error;
310 me = (struct Process *)FindTask(NULL);
312 if (envp && envp != environ)
314 struct MinList tempenv;
315 struct LocalVar *lv, *lv2;
316 char *const *envit;
317 int env_ok = 1;
319 /* Remember previous environment variables so they can be put back
320 * if something goes wrong
322 NEWLIST(&tempenv);
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);
330 environ = NULL;
332 for (envit = envp; *envit && env_ok; envit++)
333 env_ok = putenv(*envit) == 0;
335 if (env_ok)
336 /* Old vars may be freed */
337 ForeachNodeSafe(&tempenv, lv, lv2)
339 Remove((struct Node *)lv);
340 FreeMem(lv->lv_Value, lv->lv_Len);
341 FreeVec(lv);
343 else
345 /* Free new nodes */
346 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
348 Remove((struct Node *)lv);
349 FreeMem(lv->lv_Value, lv->lv_Len);
350 FreeVec(lv);
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);
360 errno = ENOMEM;
361 goto error;
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);
369 if(in)
370 privdata->acpd_exec_oldin = SelectInput(in->fcb->fh);
371 if(out)
372 privdata->acpd_exec_oldout = SelectOutput(out->fcb->fh);
373 if (err)
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)
385 errno = ENOMEM;
386 goto error;
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;
395 /* Everything OK */
396 return (APTR)privdata;
398 error:
399 __exec_cleanup(privdata);
401 return (APTR)NULL;
405 void __exec_do(APTR id)
407 struct arosc_privdata *privdata = id;
408 char *oldtaskname;
409 struct CommandLineInterface *cli = Cli();
410 struct Task *self = FindTask(NULL);
411 LONG returncode;
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"));
421 close_on_exec();
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 */
430 _exit(0);
432 assert(0); /* Should not be reached */
433 return;
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"));
455 _exit(returncode);
459 char *const *__exec_valist2array(const char *arg1, va_list list)
461 struct arosc_privdata *privdata = __get_arosc_privdata();
462 int argc, i;
463 static char *no_arg[] = {NULL};
464 va_list list2;
465 char *argit;
467 assert(privdata->acpd_exec_tmparray == NULL);
469 va_copy(list2, list);
471 if (arg1 == NULL)
472 return no_arg;
474 for (argit = va_arg(list, char *), argc = 1;
475 argit != NULL;
476 argit = va_arg(list, char *)
478 argc++;
480 if (!(privdata->acpd_exec_tmparray = malloc((argc+1)*(sizeof(char *)))))
482 D(bug("__exec_valist2array: Memory allocation failed\n"));
483 va_end(list2);
484 return NULL;
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;
497 va_end(list2);
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)
520 while(*str != '\0')
521 if(isspace(*str++)) return TRUE;
522 return FALSE;
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')
535 switch(*strptr++)
537 case '\n':
538 case '"':
539 case '*':
540 bufsize++;
543 escptr = escaped = (char*) malloc(bufsize);
544 if(!escaped)
545 return NULL;
546 *escptr++ = '"';
547 for(strptr = str; *strptr != '\0'; strptr++)
549 switch(*strptr)
551 case '\n':
552 case '"':
553 case '*':
554 *escptr++ = '*';
555 *escptr++ = (*strptr == '\n' ? 'N' : *strptr);
556 break;
557 default:
558 *escptr++ = *strptr;
559 break;
562 *escptr++ = '"';
563 *escptr = '\0';
564 return escaped;
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)
572 char *argptr2;
573 int argptrsize2 = 2*(*argptrsize);
575 argptr2 = AllocPooled(pool, argptrsize2);
576 if(!argptr2)
578 FreePooled(pool, argptr, *argptrsize);
579 return NULL;
581 strcpy(argptr2, argptr);
582 FreePooled(pool, argptr, *argptrsize);
583 argptr = argptr2;
584 *argptrsize = argptrsize2;
586 strcat(argptr, arg);
587 strcat(argptr, " ");
589 return argptr;
592 static char *appendargs(char *argptr, int *argssizeptr, char *const args[], APTR pool)
594 char *const *argsit;
596 for (argsit = args; *argsit && argptr; argsit++)
598 if(containswhite(*argsit))
600 char *escaped = escape(*argsit);
601 if(!escaped) {
602 return NULL;
604 argptr = appendarg(argptr, argssizeptr, escaped, pool);
605 free(escaped);
607 else
608 argptr = appendarg(argptr, argssizeptr, *argsit, pool);
611 return argptr;
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)
657 int i;
658 fdesc *fd;
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);