Use intern GENMF function.
[AROS.git] / compiler / posixc / __exec.c
blob04299f1cd87a86aa42988c19a193fb25b526a063
1 /*
2 Copyright © 2008-2013, 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 "__posixc_intbase.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 PosixCIntBase *PosixCBase);
38 /* Public functions */
39 /********************/
41 APTR __exec_prepare(const char *filename, int searchpath, char *const argv[], char *const envp[])
43 struct PosixCIntBase *PosixCBase =
44 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
45 char *filename2 = NULL;
46 const char *filename2_dos = NULL;
47 int argssize = 512;
48 struct Process *me;
49 char ***environptr = __posixc_get_environptr();
50 char **environ = (environptr != NULL) ? *environptr : NULL;
52 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
53 filename, searchpath, argv, envp
54 ));
55 /* Sanity check */
56 if (filename == NULL || filename[0] == '\0' || argv == NULL)
58 errno = EINVAL;
59 goto error;
62 /* Use own memory to allocate so that no stdc.library functions need to be called
63 exec_pool can also be allocated in __exec_valist2array
65 if (!PosixCBase->exec_pool)
66 PosixCBase->exec_pool = CreatePool(MEMF_PUBLIC, 1024, 512);
67 if (!PosixCBase->exec_pool)
69 errno = ENOMEM;
70 goto error;
73 /* Remember current stdcbase, child may overwrite it and mess up exiting */
74 PosixCBase->exec_oldstdcbase = PosixCBase->PosixCBase.StdCBase;
76 /* Search path if asked and no directory separator is present in the file */
77 if (searchpath && index(filename, '/') == NULL && index(filename, ':') == NULL)
79 int i, len, size;
80 char *path = NULL, *path_ptr, *path_item;
81 BPTR lock;
83 if (environ)
85 for (i=0; environ[i]; i++)
87 if (strncmp(environ[i], "PATH=", 5) == 0)
89 path = &environ[i][5];
90 break;
95 if (!path)
96 path = getenv("PATH");
98 if (!path)
99 path = ":/bin:/usr/bin";
101 D(bug("__exec_prepare: PATH('%s')\n", path));
103 path_ptr = AllocPooled(PosixCBase->exec_pool, strlen(path) + 1);
104 strcpy(path_ptr, path);
105 path = path_ptr;
107 D(bug("__exec_prepare: PATH('%s')\n", path));
109 size = 128;
110 filename2 = AllocPooled(PosixCBase->exec_pool, size);
111 if (!filename2)
113 errno = ENOMEM;
114 goto error;
117 for(path_ptr = path, lock = (BPTR)NULL, path_item = strsep(&path_ptr, ",:");
118 lock == (BPTR)NULL && path_item != NULL;
119 path_item = strsep(&path_ptr, ",:")
122 if(path_item[0] == '\0')
123 path_item = ".";
125 len = strlen(path_item) + strlen(filename) + 2;
127 if (len > size)
129 FreePooled(PosixCBase->exec_pool, filename2, size);
130 size = len;
131 filename2 = AllocPooled(PosixCBase->exec_pool, size);
132 if (!filename2)
134 errno = ENOMEM;
135 goto error;
139 strcpy(filename2, path_item);
140 strcat(filename2, "/");
141 strcat(filename2, filename);
142 filename2_dos = __path_u2a(filename2);
143 lock = Lock(filename2_dos, SHARED_LOCK);
144 D(bug("__exec_prepare: Lock(\"%s\") == %x\n", filename2, (APTR)lock));
147 if(lock != (BPTR)NULL)
148 UnLock(lock);
149 else
151 errno = ENOENT;
152 goto error;
155 else
157 filename2 = (char *)filename;
158 filename2_dos = __path_u2a(filename2);
161 if (PosixCBase->flags & PRETEND_CHILD)
163 struct vfork_data *udata = PosixCBase->vfork_data;
165 udata->exec_filename = filename2;
166 udata->exec_argv = argv;
167 udata->exec_envp = envp;
169 /* Set this so the child knows that __exec_prepare was called */
170 udata->child_executed = 1;
172 D(bug("__exec_prepare: Calling child\n"));
173 /* Now call child process, so it will call __exec_prepare */
174 Signal(udata->child, 1 << udata->child_signal);
176 D(bug("__exec_prepare: Waiting for child to finish __exec_prepare\n"));
177 /* __exec_prepare should be finished now on child */
178 Wait(1 << udata->parent_signal);
180 if (!udata->exec_id)
182 D(bug("__exec_prepare: Continue child immediately on error\n"));
183 Signal(udata->child, 1 << udata->child_signal);
185 return NULL;
188 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
189 udata->exec_id, udata->child_errno
192 return (APTR)PosixCBase;
195 D(bug("__exec_prepare: Not running as PRETEND_CHILD\n"));
196 PosixCBase->exec_args = AllocPooled(PosixCBase->exec_pool, argssize);
197 PosixCBase->exec_args[0] = '\0';
199 /* Let's check if it's a script */
200 BPTR fh = Open((CONST_STRPTR)filename2_dos, MODE_OLDFILE);
201 if(fh)
203 if(FGetC(fh) == '#' && FGetC(fh) == '!')
205 char firstline[128], *linebuf, *inter, *interargs = NULL;
207 /* It is a script, let's read the first line */
208 if(FGets(fh, (STRPTR)firstline, sizeof(firstline) - 1))
210 /* delete end of line if present */
211 if(firstline[0] && firstline[strlen(firstline)-1] == '\n')
212 firstline[strlen(firstline)-1] = '\0';
213 linebuf = firstline;
214 while(isblank(*linebuf)) linebuf++;
215 if(*linebuf != '\0')
217 /* Interpreter name is here */
218 inter = linebuf;
219 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
220 if(*linebuf != '\0')
222 *linebuf++ = '\0';
223 while(isblank(*linebuf)) linebuf++;
224 if(*linebuf != '\0')
225 /* Interpreter arguments are here */
226 interargs = linebuf;
229 /* Add interpeter args and the script name to command line args */
230 char *args[] = {NULL, NULL, NULL};
231 if (interargs)
233 args[0] = interargs;
234 args[1] = filename2;
236 else
238 args[0] = filename2;
240 PosixCBase->exec_args = appendargs(
241 PosixCBase->exec_args, &argssize, args, PosixCBase->exec_pool
243 if (!PosixCBase->exec_args)
245 errno = ENOMEM;
246 goto error;
249 /* Set file to execute as the script interpreter */
250 filename2 = AllocPooled(PosixCBase->exec_pool, strlen(inter) + 1);
251 strcpy(filename2, inter);
252 filename2_dos = __path_u2a(filename2);
256 Close(fh);
258 else
260 /* Simply assume it doesn't exist */
261 errno = __stdc_ioerr2errno(IoErr());
262 goto error;
265 /* Add arguments to command line args */
266 PosixCBase->exec_args = appendargs(PosixCBase->exec_args, &argssize, argv + 1, PosixCBase->exec_pool);
267 if (!PosixCBase->exec_args)
269 errno = ENOMEM;
270 goto error;
273 /* End command line args with '\n' */
274 if(strlen(PosixCBase->exec_args) > 0)
275 PosixCBase->exec_args[strlen(PosixCBase->exec_args) - 1] = '\n';
276 else
277 strcat(PosixCBase->exec_args, "\n");
279 /* let's make some sanity tests */
280 struct stat st;
281 if(stat(filename2, &st) == 0)
283 if(!(st.st_mode & S_IXUSR) && !(st.st_mode & S_IXGRP) && !(st.st_mode & S_IXOTH))
285 /* file is not executable */
286 errno = EACCES;
287 goto error;
289 if(st.st_mode & S_IFDIR)
291 /* it's a directory */
292 errno = EACCES;
293 goto error;
296 else
298 /* couldn't stat file */
299 goto error;
302 /* Set taskname */
303 PosixCBase->exec_taskname = AllocPooled(PosixCBase->exec_pool, strlen(filename2_dos) + 1);
304 if (!PosixCBase->exec_taskname)
306 errno = ENOMEM;
307 goto error;
309 strcpy(PosixCBase->exec_taskname, filename2_dos);
311 /* Load file to execute */
312 PosixCBase->exec_seglist = LoadSeg((CONST_STRPTR)filename2_dos);
313 if (!PosixCBase->exec_seglist)
315 errno = ENOEXEC;
316 goto error;
319 me = (struct Process *)FindTask(NULL);
321 if (envp && envp != environ)
323 struct MinList tempenv;
324 struct LocalVar *lv, *lv2;
325 char *const *envit;
326 int env_ok = 1;
328 /* Remember previous environment variables so they can be put back
329 * if something goes wrong
331 NEWLIST(&tempenv);
332 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
334 Remove((struct Node *)lv);
335 AddTail((struct List *)&tempenv, (struct Node *)lv);
338 NEWLIST(&me->pr_LocalVars);
339 environ = NULL;
341 for (envit = envp; *envit && env_ok; envit++)
342 env_ok = putenv(*envit) == 0;
344 if (env_ok)
345 /* Old vars may be freed */
346 ForeachNodeSafe(&tempenv, lv, lv2)
348 Remove((struct Node *)lv);
349 FreeMem(lv->lv_Value, lv->lv_Len);
350 FreeVec(lv);
352 else
354 /* Free new nodes */
355 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
357 Remove((struct Node *)lv);
358 FreeMem(lv->lv_Value, lv->lv_Len);
359 FreeVec(lv);
362 /* Put old ones back */
363 ForeachNodeSafe(&tempenv, lv, lv2)
365 Remove((struct Node *)lv);
366 AddTail((struct List *)&me->pr_LocalVars, (struct Node *)lv);
369 errno = ENOMEM;
370 goto error;
374 D(bug("__exec_prepare: Done, returning %p\n", aroscbase));
376 /* Everything OK */
377 return (APTR)PosixCBase;
379 error:
380 __exec_cleanup(PosixCBase);
382 return (APTR)NULL;
386 void __exec_do(APTR id)
388 struct PosixCIntBase *PosixCBase =
389 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
390 char *oldtaskname;
391 struct CommandLineInterface *cli = Cli();
392 struct Task *self = FindTask(NULL);
393 LONG returncode;
395 D(bug("[__exec_do] Entering, id(%x)\n", id));
397 /* id is unused */
398 (void)id;
400 /* When exec is called under vfork condition, the PRETEND_CHILD flag is set
401 and we need to signal child that exec is called.
403 if (PosixCBase->flags & PRETEND_CHILD)
405 struct vfork_data *udata = PosixCBase->vfork_data;
407 D(bug("[__exec_do] PRETEND_CHILD\n"));
409 __close_on_exec_fdescs();
411 D(bug("[__exec_do] Notify child to call __exec_do\n"));
413 /* Signal child that __exec_do is called */
414 Signal(udata->child, 1 << udata->child_signal);
416 /* Clean up in parent */
417 D(bug("[__exec_do] Cleaning up parent\n"));
418 __exec_cleanup(PosixCBase);
420 /* Continue as parent process */
421 D(bug("[__exec_do] Stop running as PRETEND_CHILD\n"));
422 _exit(0);
424 assert(0); /* Should not be reached */
425 return;
428 D(bug("[__exec_do] !PRETEND_CHILD\n"));
430 PosixCBase->flags |= EXEC_PARENT;
432 oldtaskname = self->tc_Node.ln_Name;
433 self->tc_Node.ln_Name = PosixCBase->exec_taskname;
434 SetProgramName((STRPTR)PosixCBase->exec_taskname);
437 /* Set standard files to the standard files from arosc */
438 fdesc *in = __getfdesc(STDIN_FILENO), *out = __getfdesc(STDOUT_FILENO),
439 *err = __getfdesc(STDERR_FILENO);
440 BPTR oldin = BNULL, oldout = BNULL, olderr = BNULL;
441 char inchanged = 0, outchanged = 0, errchanged = 0;
442 struct Process *me = (struct Process *)FindTask(NULL);
444 if(in && in->fcb->handle != Input())
446 oldin = SelectInput(in->fcb->handle);
447 inchanged = 1;
449 if(out && in->fcb->handle != Output())
451 oldout = SelectOutput(out->fcb->handle);
452 outchanged = 1;
454 if (err)
456 if (me->pr_CES)
458 if (me->pr_CES != err->fcb->handle)
459 errchanged = 1;
461 else /* me->pr_CES */
463 /* Only replace if stdout != stderr */
464 if (out && out->fcb->handle != err->fcb->handle)
465 errchanged = 1;
467 if (errchanged)
469 olderr = me->pr_CES;
470 me->pr_CES = err->fcb->handle;
474 D(bug("[__exec_do] Running program, PosixCBase=%x\n", PosixCBase));
475 returncode = RunCommand(
476 PosixCBase->exec_seglist,
477 cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT,
478 (STRPTR)PosixCBase->exec_args,
479 strlen(PosixCBase->exec_args)
482 /* RunCommand() does not Close() standard output so may not flush either.
483 So to be sure flush them here */
484 Flush(Output());
485 if (me->pr_CES)
486 Flush(me->pr_CES);
488 if(inchanged)
489 SelectInput(oldin);
490 if(outchanged)
491 SelectOutput(oldout);
492 if(errchanged)
493 me->pr_CES = olderr;
495 D(bug("[__exec_do] Program ran, PosixCBase=%x, __aros_getbase_PosixCBase()=%x\n",
496 PosixCBase, __aros_getbase_PosixCBase()
500 self->tc_Node.ln_Name = oldtaskname;
501 SetProgramName((STRPTR)oldtaskname);
503 __exec_cleanup(PosixCBase);
505 D(bug("[__exec_do] exiting from non-forked __exec()\n"));
506 _Exit(returncode);
510 char *const *__exec_valist2array(const char *arg1, va_list list)
512 struct PosixCIntBase *PosixCBase =
513 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
514 int argc, i;
515 static char *no_arg[] = {NULL};
516 va_list list2;
517 char *argit;
519 assert(PosixCBase->exec_tmparray == NULL);
521 va_copy(list2, list);
523 if (arg1 == NULL)
524 return no_arg;
526 for (argit = va_arg(list, char *), argc = 1;
527 argit != NULL;
528 argit = va_arg(list, char *)
530 argc++;
532 if (!(PosixCBase->exec_tmparray = malloc((argc+1)*(sizeof(char *)))))
534 D(bug("__exec_valist2array: Memory allocation failed\n"));
535 va_end(list2);
536 return NULL;
539 PosixCBase->exec_tmparray[0] = (char *)arg1;
540 for (argit = va_arg(list2, char *), i = 1;
541 i <= argc; /* i == argc will copy the NULL pointer */
542 argit = va_arg(list2, char *), i++
545 D(bug("arg %d: %x\n", i, argit));
546 PosixCBase->exec_tmparray[i] = argit;
549 va_end(list2);
551 return PosixCBase->exec_tmparray;
555 void __exec_cleanup_array()
557 struct PosixCIntBase *PosixCBase =
558 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
560 if (PosixCBase->exec_tmparray)
562 free((void *)PosixCBase->exec_tmparray);
563 PosixCBase->exec_tmparray = NULL;
568 /* Support functions */
569 /*********************/
571 /* Return TRUE if there are any white spaces in the string */
572 static BOOL containswhite(const char *str)
574 while(*str != '\0')
575 if(isspace(*str++)) return TRUE;
576 return FALSE;
579 /* Escape the string and quote it */
580 static char *escape(const char *str)
582 const char *strptr = str;
583 char *escaped, *escptr;
584 /* Additional two characters for '"', and one for '\0' */
585 int bufsize = strlen(str) + 3;
586 /* Take into account characters to ecape */
587 while(*strptr != '\0')
589 switch(*strptr++)
591 case '\n':
592 case '"':
593 case '*':
594 bufsize++;
597 escptr = escaped = (char*) malloc(bufsize);
598 if(!escaped)
599 return NULL;
600 *escptr++ = '"';
601 for(strptr = str; *strptr != '\0'; strptr++)
603 switch(*strptr)
605 case '\n':
606 case '"':
607 case '*':
608 *escptr++ = '*';
609 *escptr++ = (*strptr == '\n' ? 'N' : *strptr);
610 break;
611 default:
612 *escptr++ = *strptr;
613 break;
616 *escptr++ = '"';
617 *escptr = '\0';
618 return escaped;
621 /* Append arg string to argptr increasing argptr if needed */
622 static char *appendarg(char *argptr, int *argptrsize, const char *arg, APTR pool)
624 while(strlen(argptr) + strlen(arg) + 2 > *argptrsize)
626 char *argptr2;
627 int argptrsize2 = 2*(*argptrsize);
629 argptr2 = AllocPooled(pool, argptrsize2);
630 if(!argptr2)
632 FreePooled(pool, argptr, *argptrsize);
633 return NULL;
635 strcpy(argptr2, argptr);
636 FreePooled(pool, argptr, *argptrsize);
637 argptr = argptr2;
638 *argptrsize = argptrsize2;
640 strcat(argptr, arg);
641 strcat(argptr, " ");
643 return argptr;
646 static char *appendargs(char *argptr, int *argssizeptr, char *const args[], APTR pool)
648 char *const *argsit;
650 for (argsit = args; *argsit && argptr; argsit++)
652 if(containswhite(*argsit))
654 char *escaped = escape(*argsit);
655 if(!escaped)
657 FreePooled(pool, argptr, *argssizeptr);
658 return NULL;
660 argptr = appendarg(argptr, argssizeptr, escaped, pool);
661 free(escaped);
663 else
664 argptr = appendarg(argptr, argssizeptr, *argsit, pool);
667 return argptr;
670 static void __exec_cleanup(struct PosixCIntBase *PosixCBase)
672 D(bug("__exec_cleanup: me(%x)\n", FindTask(NULL)));
674 if (PosixCBase->exec_pool)
676 DeletePool(PosixCBase->exec_pool);
677 PosixCBase->exec_pool = NULL;
679 if (PosixCBase->exec_seglist)
681 UnLoadSeg(PosixCBase->exec_seglist);
682 PosixCBase->exec_seglist = (BPTR)NULL;
685 if (PosixCBase->exec_oldstdcbase)
687 PosixCBase->PosixCBase.StdCBase = PosixCBase->exec_oldstdcbase;
688 PosixCBase->exec_oldstdcbase = NULL;