Fixed compatibility of output.
[AROS.git] / compiler / posixc / __exec.c
blob2e6db82fc08cbd3910b4909cb8a70347ec71f3e7
1 /*
2 Copyright © 2008-2014, 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 static void __exec_do_regular(struct PosixCIntBase *PosixCBase);
39 static void __exec_do_pretend_child(struct PosixCIntBase *PosixCBase);
40 static char * assign_filename(const char *filename, int searchpath, char **environ, struct PosixCIntBase *PosixCBase);
41 static APTR __exec_prepare_pretend_child(char *filename2, char *const argv[], char *const envp[],
42 struct PosixCIntBase *PosixCBase);
43 static APTR __exec_prepare_regular(char * filename2, char *const argv[], char *const envp[],
44 char **environ, struct PosixCIntBase *PosixCBase);
46 /* Public functions */
47 /********************/
49 void __exec_do(APTR id)
51 struct PosixCIntBase *PosixCBase =
52 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
54 D(bug("[__exec_do] Entering, id(%x)\n", id));
56 /* id is unused */
57 (void)id;
59 if (PosixCBase->flags & PRETEND_CHILD)
61 /* forking parent executing child code prior to child taking over */
62 __exec_do_pretend_child(PosixCBase);
64 else
66 /* exec* without fork or forked child executing exec* */
67 __exec_do_regular(PosixCBase);
71 APTR __exec_prepare(const char *filename, int searchpath, char *const argv[], char *const envp[])
73 struct PosixCIntBase *PosixCBase =
74 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
75 char *filename2 = NULL;
76 char ***environptr = __posixc_get_environptr();
77 char **environ = (environptr != NULL) ? *environptr : NULL;
79 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
80 filename, searchpath, argv, envp
81 ));
82 /* Sanity check */
83 if (filename == NULL || filename[0] == '\0' || argv == NULL)
85 errno = EINVAL;
86 goto error;
89 /* Use own memory to allocate so that no stdc.library functions need to be called
90 exec_pool can also be allocated in __exec_valist2array
92 if (!PosixCBase->exec_pool)
93 PosixCBase->exec_pool = CreatePool(MEMF_PUBLIC, 1024, 512);
94 if (!PosixCBase->exec_pool)
96 errno = ENOMEM;
97 goto error;
100 /* Remember current stdcbase, child may overwrite it and mess up exiting */
101 PosixCBase->exec_oldstdcbase = PosixCBase->PosixCBase.StdCBase;
103 filename2 = assign_filename(filename, searchpath, environ, PosixCBase);
104 if (!filename2)
105 goto error;
107 if (PosixCBase->flags & PRETEND_CHILD)
109 /* forking parent executing child code prior to child taking over */
110 return __exec_prepare_pretend_child(filename2, argv, envp, PosixCBase);
112 else
114 /* exec* without fork or forked child executing exec* */
115 return __exec_prepare_regular(filename2, argv, envp, environ, PosixCBase);
118 error:
119 __exec_cleanup(PosixCBase);
121 return (APTR)NULL;
124 static char * assign_filename(const char *filename, int searchpath, char **environ, struct PosixCIntBase *PosixCBase)
126 /* Search path if asked and no directory separator is present in the file */
127 if (searchpath && index(filename, '/') == NULL && index(filename, ':') == NULL)
129 int i, len, size;
130 char *path = NULL, *path_ptr, *path_item, *filename2;
131 const char * filename2_dos;
132 BPTR lock;
134 if (environ)
136 for (i=0; environ[i]; i++)
138 if (strncmp(environ[i], "PATH=", 5) == 0)
140 path = &environ[i][5];
141 break;
146 if (!path)
147 path = getenv("PATH");
149 if (!path)
150 path = ":/bin:/usr/bin";
152 D(bug("assign_filename: PATH('%s')\n", path));
154 path_ptr = AllocPooled(PosixCBase->exec_pool, strlen(path) + 1);
155 strcpy(path_ptr, path);
156 path = path_ptr;
158 D(bug("assign_filename: PATH('%s')\n", path));
160 size = 128;
161 filename2 = AllocPooled(PosixCBase->exec_pool, size);
162 if (!filename2)
164 errno = ENOMEM;
165 return NULL;
168 for(path_ptr = path, lock = (BPTR)NULL, path_item = strsep(&path_ptr, ",:");
169 lock == (BPTR)NULL && path_item != NULL;
170 path_item = strsep(&path_ptr, ",:")
173 if(path_item[0] == '\0')
174 path_item = ".";
176 len = strlen(path_item) + strlen(filename) + 2;
178 if (len > size)
180 FreePooled(PosixCBase->exec_pool, filename2, size);
181 size = len;
182 filename2 = AllocPooled(PosixCBase->exec_pool, size);
183 if (!filename2)
185 errno = ENOMEM;
186 return NULL;
190 strcpy(filename2, path_item);
191 strcat(filename2, "/");
192 strcat(filename2, filename);
193 filename2_dos = __path_u2a(filename2);
194 lock = Lock(filename2_dos, SHARED_LOCK);
195 D(bug("assign_filename: Lock(\"%s\") == %x\n", filename2, (APTR)lock));
198 if(lock != (BPTR)NULL)
199 UnLock(lock);
200 else
202 errno = ENOENT;
203 return NULL;
206 return filename2;
208 else
209 return (char *)filename;
212 static APTR __exec_prepare_pretend_child(char *filename2, char *const argv[], char *const envp[],
213 struct PosixCIntBase *PosixCBase)
215 struct vfork_data *udata = PosixCBase->vfork_data;
217 udata->exec_filename = filename2;
218 udata->exec_argv = argv;
219 udata->exec_envp = envp;
221 /* Set this so the child knows that __exec_prepare was called */
222 udata->child_executed = 1;
224 D(bug("[__exec_prepare_pretend_child] Calling child\n"));
225 /* Now call child process, so it will call __exec_prepare */
226 SETPARENTSTATE(PARENT_STATE_EXEC_CALLED);
227 Signal(udata->child, 1 << udata->child_signal);
229 D(bug("[__exec_prepare_pretend_child] Waiting for child to finish __exec_prepare\n"));
230 /* __exec_prepare should be finished now on child */
231 Wait(1 << udata->parent_signal);
232 ASSERTCHILDSTATE(CHILD_STATE_EXEC_PREPARE_FINISHED);
233 PRINTSTATE;
235 if (!udata->exec_id)
237 D(bug("[__exec_prepare_pretend_child] Continue child immediately on error\n"));
238 SETPARENTSTATE(PARENT_STATE_EXEC_DO_FINISHED);
239 Signal(udata->child, 1 << udata->child_signal);
241 return NULL;
244 D(bug("[__exec_prepare_pretend_child] Exiting from forked __exec_prepare id=%x, errno=%d\n",
245 udata->exec_id, udata->child_errno
248 return (APTR)PosixCBase;
251 static APTR __exec_prepare_regular(char * filename2, char *const argv[], char *const envp[],
252 char **environ, struct PosixCIntBase *PosixCBase)
254 const char *filename2_dos = NULL;
255 int argssize = 512;
256 struct Process *me;
258 filename2_dos = __path_u2a(filename2);
260 PosixCBase->exec_args = AllocPooled(PosixCBase->exec_pool, argssize);
261 PosixCBase->exec_args[0] = '\0';
263 /* Let's check if it's a script */
264 BPTR fh = Open((CONST_STRPTR)filename2_dos, MODE_OLDFILE);
265 if(fh)
267 if(FGetC(fh) == '#' && FGetC(fh) == '!')
269 char firstline[128], *linebuf, *inter, *interargs = NULL;
271 /* It is a script, let's read the first line */
272 if(FGets(fh, (STRPTR)firstline, sizeof(firstline) - 1))
274 /* delete end of line if present */
275 if(firstline[0] && firstline[strlen(firstline)-1] == '\n')
276 firstline[strlen(firstline)-1] = '\0';
277 linebuf = firstline;
278 while(isblank(*linebuf)) linebuf++;
279 if(*linebuf != '\0')
281 /* Interpreter name is here */
282 inter = linebuf;
283 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
284 if(*linebuf != '\0')
286 *linebuf++ = '\0';
287 while(isblank(*linebuf)) linebuf++;
288 if(*linebuf != '\0')
289 /* Interpreter arguments are here */
290 interargs = linebuf;
293 /* Add interpeter args and the script name to command line args */
294 char *args[] = {NULL, NULL, NULL};
295 if (interargs)
297 args[0] = interargs;
298 args[1] = filename2;
300 else
302 args[0] = filename2;
304 PosixCBase->exec_args = appendargs(
305 PosixCBase->exec_args, &argssize, args, PosixCBase->exec_pool
307 if (!PosixCBase->exec_args)
309 errno = ENOMEM;
310 goto error;
313 /* Set file to execute as the script interpreter */
314 filename2 = AllocPooled(PosixCBase->exec_pool, strlen(inter) + 1);
315 strcpy(filename2, inter);
316 filename2_dos = __path_u2a(filename2);
320 Close(fh);
322 else
324 /* Simply assume it doesn't exist */
325 errno = __stdc_ioerr2errno(IoErr());
326 goto error;
329 /* Add arguments to command line args */
330 PosixCBase->exec_args = appendargs(PosixCBase->exec_args, &argssize, argv + 1, PosixCBase->exec_pool);
331 if (!PosixCBase->exec_args)
333 errno = ENOMEM;
334 goto error;
337 /* End command line args with '\n' */
338 if(strlen(PosixCBase->exec_args) > 0)
339 PosixCBase->exec_args[strlen(PosixCBase->exec_args) - 1] = '\n';
340 else
341 strcat(PosixCBase->exec_args, "\n");
343 /* let's make some sanity tests */
344 struct stat st;
345 if(stat(filename2, &st) == 0)
347 if(!(st.st_mode & S_IXUSR) && !(st.st_mode & S_IXGRP) && !(st.st_mode & S_IXOTH))
349 /* file is not executable */
350 errno = EACCES;
351 goto error;
353 if(st.st_mode & S_IFDIR)
355 /* it's a directory */
356 errno = EACCES;
357 goto error;
360 else
362 /* couldn't stat file */
363 goto error;
366 /* Set taskname */
367 PosixCBase->exec_taskname = AllocPooled(PosixCBase->exec_pool, strlen(filename2_dos) + 1);
368 if (!PosixCBase->exec_taskname)
370 errno = ENOMEM;
371 goto error;
373 strcpy(PosixCBase->exec_taskname, filename2_dos);
375 /* Load file to execute */
376 PosixCBase->exec_seglist = LoadSeg((CONST_STRPTR)filename2_dos);
377 if (!PosixCBase->exec_seglist)
379 errno = ENOEXEC;
380 goto error;
383 me = (struct Process *)FindTask(NULL);
385 if (envp && envp != environ)
387 struct MinList tempenv;
388 struct LocalVar *lv, *lv2;
389 char *const *envit;
390 int env_ok = 1;
392 /* Remember previous environment variables so they can be put back
393 * if something goes wrong
395 NEWLIST(&tempenv);
396 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
398 Remove((struct Node *)lv);
399 AddTail((struct List *)&tempenv, (struct Node *)lv);
402 NEWLIST(&me->pr_LocalVars);
403 environ = NULL;
405 for (envit = envp; *envit && env_ok; envit++)
406 env_ok = putenv(*envit) == 0;
408 if (env_ok)
409 /* Old vars may be freed */
410 ForeachNodeSafe(&tempenv, lv, lv2)
412 Remove((struct Node *)lv);
413 FreeMem(lv->lv_Value, lv->lv_Len);
414 FreeVec(lv);
416 else
418 /* Free new nodes */
419 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
421 Remove((struct Node *)lv);
422 FreeMem(lv->lv_Value, lv->lv_Len);
423 FreeVec(lv);
426 /* Put old ones back */
427 ForeachNodeSafe(&tempenv, lv, lv2)
429 Remove((struct Node *)lv);
430 AddTail((struct List *)&me->pr_LocalVars, (struct Node *)lv);
433 errno = ENOMEM;
434 goto error;
438 D(bug("[__exec_prepare_regular] Done, returning %p\n", PosixCBase));
440 /* Everything OK */
441 return (APTR)PosixCBase;
443 error:
444 __exec_cleanup(PosixCBase);
446 return (APTR)NULL;
449 static void __exec_do_pretend_child(struct PosixCIntBase *PosixCBase)
451 /* When exec is called under vfork condition, the PRETEND_CHILD flag is set
452 and we need to signal child that exec is called.
455 struct vfork_data *udata = PosixCBase->vfork_data;
457 D(bug("[__exec_do_pretend_child] PRETEND_CHILD\n"));
459 __close_on_exec_fdescs();
461 D(bug("[__exec_do_pretend_child] Notify child to call __exec_do\n"));
463 /* Signal child that __exec_do is called */
464 SETPARENTSTATE(PARENT_STATE_EXEC_DO_FINISHED);
465 Signal(udata->child, 1 << udata->child_signal);
467 /* Clean up in parent */
468 D(bug("[__exec_do_pretend_child] Cleaning up parent\n"));
469 __exec_cleanup(PosixCBase);
471 /* Continue as parent process */
472 D(bug("[__exec_do_pretend_child] Stop running as PRETEND_CHILD\n"));
473 _exit(0);
475 assert(0); /* Should not be reached */
478 static void __exec_do_regular(struct PosixCIntBase *PosixCBase)
480 char *oldtaskname;
481 struct CommandLineInterface *cli = Cli();
482 struct Task *self = FindTask(NULL);
483 LONG returncode;
485 PosixCBase->flags |= EXEC_PARENT;
487 oldtaskname = self->tc_Node.ln_Name;
488 self->tc_Node.ln_Name = PosixCBase->exec_taskname;
489 SetProgramName((STRPTR)PosixCBase->exec_taskname);
492 /* Set standard files to the standard files from arosc */
493 fdesc *in = __getfdesc(STDIN_FILENO), *out = __getfdesc(STDOUT_FILENO),
494 *err = __getfdesc(STDERR_FILENO);
495 BPTR oldin = BNULL, oldout = BNULL, olderr = BNULL;
496 char inchanged = 0, outchanged = 0, errchanged = 0;
497 struct Process *me = (struct Process *)FindTask(NULL);
499 if(in && in->fcb->handle != Input())
501 oldin = SelectInput(in->fcb->handle);
502 inchanged = 1;
504 if(out && in->fcb->handle != Output())
506 oldout = SelectOutput(out->fcb->handle);
507 outchanged = 1;
509 if (err)
511 if (me->pr_CES)
513 if (me->pr_CES != err->fcb->handle)
514 errchanged = 1;
516 else /* me->pr_CES */
518 /* Only replace if stdout != stderr */
519 if (out && out->fcb->handle != err->fcb->handle)
520 errchanged = 1;
522 if (errchanged)
524 olderr = me->pr_CES;
525 me->pr_CES = err->fcb->handle;
529 D(bug("[__exec_do_regular] Running program, PosixCBase=%x\n", PosixCBase));
530 returncode = RunCommand(
531 PosixCBase->exec_seglist,
532 cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT,
533 (STRPTR)PosixCBase->exec_args,
534 strlen(PosixCBase->exec_args)
537 /* RunCommand() does not Close() standard output so may not flush either.
538 So to be sure flush them here */
539 Flush(Output());
540 if (me->pr_CES)
541 Flush(me->pr_CES);
543 if(inchanged)
544 SelectInput(oldin);
545 if(outchanged)
546 SelectOutput(oldout);
547 if(errchanged)
548 me->pr_CES = olderr;
550 D(bug("[__exec_do_regular] Program ran, PosixCBase=%x, __aros_getbase_PosixCBase()=%x\n",
551 PosixCBase, __aros_getbase_PosixCBase()
555 self->tc_Node.ln_Name = oldtaskname;
556 SetProgramName((STRPTR)oldtaskname);
558 __exec_cleanup(PosixCBase);
560 D(bug("[__exec_do_regular] exiting from __exec()\n"));
561 _Exit(returncode);
565 char *const *__exec_valist2array(const char *arg1, va_list list)
567 struct PosixCIntBase *PosixCBase =
568 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
569 int argc, i;
570 static char *no_arg[] = {NULL};
571 va_list list2;
572 char *argit;
574 assert(PosixCBase->exec_tmparray == NULL);
576 va_copy(list2, list);
578 if (arg1 == NULL)
579 return no_arg;
581 for (argit = va_arg(list, char *), argc = 1;
582 argit != NULL;
583 argit = va_arg(list, char *)
585 argc++;
587 if (!(PosixCBase->exec_tmparray = malloc((argc+1)*(sizeof(char *)))))
589 D(bug("__exec_valist2array: Memory allocation failed\n"));
590 va_end(list2);
591 return NULL;
594 PosixCBase->exec_tmparray[0] = (char *)arg1;
595 for (argit = va_arg(list2, char *), i = 1;
596 i <= argc; /* i == argc will copy the NULL pointer */
597 argit = va_arg(list2, char *), i++
600 D(bug("arg %d: %x\n", i, argit));
601 PosixCBase->exec_tmparray[i] = argit;
604 va_end(list2);
606 return PosixCBase->exec_tmparray;
610 void __exec_cleanup_array()
612 struct PosixCIntBase *PosixCBase =
613 (struct PosixCIntBase *)__aros_getbase_PosixCBase();
615 if (PosixCBase->exec_tmparray)
617 free((void *)PosixCBase->exec_tmparray);
618 PosixCBase->exec_tmparray = NULL;
623 /* Support functions */
624 /*********************/
626 /* Return TRUE if there are any white spaces in the string */
627 static BOOL containswhite(const char *str)
629 while(*str != '\0')
630 if(isspace(*str++)) return TRUE;
631 return FALSE;
634 /* Escape the string and quote it */
635 static char *escape(const char *str)
637 const char *strptr = str;
638 char *escaped, *escptr;
639 /* Additional two characters for '"', and one for '\0' */
640 int bufsize = strlen(str) + 3;
641 /* Take into account characters to ecape */
642 while(*strptr != '\0')
644 switch(*strptr++)
646 case '\n':
647 case '"':
648 case '*':
649 bufsize++;
652 escptr = escaped = (char*) malloc(bufsize);
653 if(!escaped)
654 return NULL;
655 *escptr++ = '"';
656 for(strptr = str; *strptr != '\0'; strptr++)
658 switch(*strptr)
660 case '\n':
661 case '"':
662 case '*':
663 *escptr++ = '*';
664 *escptr++ = (*strptr == '\n' ? 'N' : *strptr);
665 break;
666 default:
667 *escptr++ = *strptr;
668 break;
671 *escptr++ = '"';
672 *escptr = '\0';
673 return escaped;
676 /* Append arg string to argptr increasing argptr if needed */
677 static char *appendarg(char *argptr, int *argptrsize, const char *arg, APTR pool)
679 while(strlen(argptr) + strlen(arg) + 2 > *argptrsize)
681 char *argptr2;
682 int argptrsize2 = 2*(*argptrsize);
684 argptr2 = AllocPooled(pool, argptrsize2);
685 if(!argptr2)
687 FreePooled(pool, argptr, *argptrsize);
688 return NULL;
690 strcpy(argptr2, argptr);
691 FreePooled(pool, argptr, *argptrsize);
692 argptr = argptr2;
693 *argptrsize = argptrsize2;
695 strcat(argptr, arg);
696 strcat(argptr, " ");
698 return argptr;
701 static char *appendargs(char *argptr, int *argssizeptr, char *const args[], APTR pool)
703 char *const *argsit;
705 for (argsit = args; *argsit && argptr; argsit++)
707 if(containswhite(*argsit))
709 char *escaped = escape(*argsit);
710 if(!escaped)
712 FreePooled(pool, argptr, *argssizeptr);
713 return NULL;
715 argptr = appendarg(argptr, argssizeptr, escaped, pool);
716 free(escaped);
718 else
719 argptr = appendarg(argptr, argssizeptr, *argsit, pool);
722 return argptr;
725 static void __exec_cleanup(struct PosixCIntBase *PosixCBase)
727 D(bug("[__exec_cleanup] me(%x)\n", FindTask(NULL)));
729 if (PosixCBase->exec_pool)
731 DeletePool(PosixCBase->exec_pool);
732 PosixCBase->exec_pool = NULL;
734 if (PosixCBase->exec_seglist)
736 UnLoadSeg(PosixCBase->exec_seglist);
737 PosixCBase->exec_seglist = (BPTR)NULL;
740 if (PosixCBase->exec_oldstdcbase)
742 PosixCBase->PosixCBase.StdCBase = PosixCBase->exec_oldstdcbase;
743 PosixCBase->exec_oldstdcbase = NULL;