crete an idle task to run when theres ... umm. .. nothing to do ..
[AROS.git] / compiler / clib / __exec.c
blob3c680007afa22947c138145e5ea5aac7ed43b0fa
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 = __aros_getbase_aroscbase();
44 char *filename2 = NULL;
45 int argssize = 512;
46 struct Process *me;
47 char ***environptr = __arosc_get_environptr();
48 char **environ = (environptr != NULL) ? *environptr : NULL;
50 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
51 filename, searchpath, argv, envp
52 ));
53 /* Sanity check */
54 if (filename == NULL || filename[0] == '\0' || argv == NULL)
56 errno = EINVAL;
57 goto error;
60 /* Use own memory to allocate so that no arosstdc.library functions need to be called
61 exec_pool can also be allocated in __exec_valist2array
63 if (!aroscbase->acb_exec_pool)
64 aroscbase->acb_exec_pool = CreatePool(MEMF_PUBLIC, 1024, 512);
65 if (!aroscbase->acb_exec_pool)
67 errno = ENOMEM;
68 goto error;
71 /* Search path if asked and no directory separator is present in the file */
72 if (searchpath && index(filename, '/') == NULL && index(filename, ':') == NULL)
74 int i, len, size;
75 char *path = NULL, *path_ptr, *path_item;
76 BPTR lock;
78 if (environ)
80 for (i=0; environ[i]; i++)
82 if (strncmp(environ[i], "PATH=", 5) == 0)
84 path = &environ[i][5];
85 break;
90 if (!path)
91 path = getenv("PATH");
93 if (!path)
94 path = ":/bin:/usr/bin";
96 D(bug("__exec_prepare: PATH('%s')\n", path));
98 path_ptr = AllocPooled(aroscbase->acb_exec_pool, strlen(path) + 1);
99 strcpy(path_ptr, path);
100 path = path_ptr;
102 D(bug("__exec_prepare: PATH('%s')\n", path));
104 size = 128;
105 filename2 = AllocPooled(aroscbase->acb_exec_pool, size);
106 if (!filename2)
108 errno = ENOMEM;
109 goto error;
112 for(path_ptr = path, lock = (BPTR)NULL, path_item = strsep(&path_ptr, ",:");
113 lock == (BPTR)NULL && path_item != NULL;
114 path_item = strsep(&path_ptr, ",:")
117 if(path_item[0] == '\0')
118 path_item = ".";
120 len = strlen(path_item) + strlen(filename) + 2;
122 if (len > size)
124 FreePooled(aroscbase->acb_exec_pool, filename2, size);
125 size = len;
126 filename2 = AllocPooled(aroscbase->acb_exec_pool, size);
127 if (!filename2)
129 errno = ENOMEM;
130 goto error;
134 strcpy(filename2, path_item);
135 strcat(filename2, "/");
136 strcat(filename2, filename);
137 lock = Lock(__path_u2a(filename2), SHARED_LOCK);
138 D(bug("__exec_prepare: Lock(\"%s\") == %x\n", filename2, (APTR)lock));
141 if(lock != (BPTR)NULL)
142 UnLock(lock);
143 else
145 errno = ENOENT;
146 goto error;
149 else
150 filename2 = (char *)filename;
153 if (aroscbase->acb_flags & PRETEND_CHILD)
155 struct vfork_data *udata = aroscbase->acb_vfork_data;
157 udata->exec_filename = filename2;
158 udata->exec_argv = argv;
159 udata->exec_envp = envp;
161 /* Set this so the child knows that __exec_prepare was called */
162 udata->child_executed = 1;
164 D(bug("__exec_prepare: Calling child\n"));
165 /* Now call child process, so it will call __exec_prepare */
166 Signal(udata->child, 1 << udata->child_signal);
168 D(bug("__exec_prepare: Waiting for child to finish __exec_prepare\n"));
169 /* __exec_prepare should be finished now on child */
170 Wait(1 << udata->parent_signal);
172 if (!udata->exec_id)
174 D(bug("__exec_prepare: Continue child immediately on error\n"));
175 Signal(udata->child, 1 << udata->child_signal);
177 return NULL;
180 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
181 udata->exec_id, udata->child_errno
184 return aroscbase;
187 D(bug("__exec_prepare: Not running as PRETEND_CHILD\n"));
188 aroscbase->acb_exec_args = AllocPooled(aroscbase->acb_exec_pool, argssize);
189 aroscbase->acb_exec_args[0] = '\0';
191 /* Let's check if it's a script */
192 BPTR fh = Open((CONST_STRPTR)__path_u2a(filename2), MODE_OLDFILE);
193 if(fh)
195 if(FGetC(fh) == '#' && FGetC(fh) == '!')
197 char firstline[128], *linebuf, *inter, *interargs = NULL;
199 /* It is a script, let's read the first line */
200 if(FGets(fh, (STRPTR)firstline, sizeof(firstline) - 1))
202 /* delete end of line if present */
203 if(firstline[0] && firstline[strlen(firstline)-1] == '\n')
204 firstline[strlen(firstline)-1] = '\0';
205 linebuf = firstline;
206 while(isblank(*linebuf)) linebuf++;
207 if(*linebuf != '\0')
209 /* Interpreter name is here */
210 inter = linebuf;
211 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
212 if(*linebuf != '\0')
214 *linebuf++ = '\0';
215 while(isblank(*linebuf)) linebuf++;
216 if(*linebuf != '\0')
217 /* Interpreter arguments are here */
218 interargs = linebuf;
221 /* Add interpeter args and the script name to command line args */
222 char *args[] = {NULL, NULL, NULL};
223 if (interargs)
225 args[0] = interargs;
226 args[1] = filename2;
228 else
230 args[0] = filename2;
232 aroscbase->acb_exec_args = appendargs(
233 aroscbase->acb_exec_args, &argssize, args, aroscbase->acb_exec_pool
235 if (!aroscbase->acb_exec_args)
237 errno = ENOMEM;
238 goto error;
241 /* Set file to execute as the script interpreter */
242 filename2 = AllocPooled(aroscbase->acb_exec_pool, strlen(inter) + 1);
243 strcpy(filename2, inter);
247 Close(fh);
249 else
251 /* Simply assume it doesn't exist */
252 errno = __arosc_ioerr2errno(IoErr());
253 goto error;
256 /* Add arguments to command line args */
257 aroscbase->acb_exec_args = appendargs(aroscbase->acb_exec_args, &argssize, argv + 1, aroscbase->acb_exec_pool);
258 if (!aroscbase->acb_exec_args)
260 errno = ENOMEM;
261 goto error;
264 /* End command line args with '\n' */
265 if(strlen(aroscbase->acb_exec_args) > 0)
266 aroscbase->acb_exec_args[strlen(aroscbase->acb_exec_args) - 1] = '\n';
267 else
268 strcat(aroscbase->acb_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 aroscbase->acb_exec_taskname = AllocPooled(aroscbase->acb_exec_pool, strlen(filename2) + 1);
295 if (!aroscbase->acb_exec_taskname)
297 errno = ENOMEM;
298 goto error;
300 strcpy(aroscbase->acb_exec_taskname, filename2);
302 /* Load file to execute */
303 aroscbase->acb_exec_seglist = LoadSeg((CONST_STRPTR)__path_u2a(filename2));
304 if (!aroscbase->acb_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 aroscbase->acb_exec_oldin = SelectInput(in->fcb->fh);
371 if(out)
372 aroscbase->acb_exec_oldout = SelectOutput(out->fcb->fh);
373 if (err)
375 aroscbase->acb_exec_olderr = me->pr_CES;
376 me->pr_CES = err->fcb->fh;
379 /* Everything OK */
380 return (APTR)aroscbase;
382 error:
383 __exec_cleanup(aroscbase);
385 return (APTR)NULL;
389 void __exec_do(APTR id)
391 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
392 char *oldtaskname;
393 struct CommandLineInterface *cli = Cli();
394 struct Task *self = FindTask(NULL);
395 LONG returncode;
397 D(bug("[__exec_do] Entering, id(%x)\n", id));
399 /* id is unused */
400 (void)id;
401 /* When exec is not called under vfork condition id == __aros_getbase_aroscbase()
402 When exec is called under vfork condition we need to use __aros_getbase_aroscbase() in the
403 parent to check for PRETEND_CHILD and find the udata for signaling the child
406 if (aroscbase->acb_flags & PRETEND_CHILD)
408 struct vfork_data *udata = aroscbase->acb_vfork_data;
410 D(bug("[__exec_do] PRETEND_CHILD\n"));
412 __close_on_exec_fdescs();
414 D(bug("Notify child to call __exec_do\n"));
416 /* Signal child that __exec_do is called */
417 Signal(udata->child, 1 << udata->child_signal);
419 /* Clean up in parent */
420 __exec_cleanup(aroscbase);
422 /* Continue as parent process */
423 _exit(0);
425 assert(0); /* Should not be reached */
426 return;
429 D(bug("[__exec_do] !PRETEND_CHILD\n"));
431 aroscbase->acb_flags |= EXEC_PARENT;
433 oldtaskname = self->tc_Node.ln_Name;
434 self->tc_Node.ln_Name = aroscbase->acb_exec_taskname;
435 SetProgramName((STRPTR)aroscbase->acb_exec_taskname);
437 D(bug("[__exec_do] Running program, aroscbase=%x\n", aroscbase));
439 returncode = RunCommand(
440 aroscbase->acb_exec_seglist,
441 cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT,
442 (STRPTR)aroscbase->acb_exec_args,
443 strlen(aroscbase->acb_exec_args)
446 D(bug("[__exec_do] Program ran, aroscbase=%x, __aros_getbase_aroscbase()=%x\n",
447 aroscbase, __aros_getbase_aroscbase()
451 self->tc_Node.ln_Name = oldtaskname;
452 SetProgramName((STRPTR)oldtaskname);
454 __exec_cleanup(aroscbase);
456 D(bug("[__exec_do] exiting from non-forked\n"));
457 _exit(returncode);
461 char *const *__exec_valist2array(const char *arg1, va_list list)
463 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
464 int argc, i;
465 static char *no_arg[] = {NULL};
466 va_list list2;
467 char *argit;
469 assert(aroscbase->acb_exec_tmparray == NULL);
471 va_copy(list2, list);
473 if (arg1 == NULL)
474 return no_arg;
476 for (argit = va_arg(list, char *), argc = 1;
477 argit != NULL;
478 argit = va_arg(list, char *)
480 argc++;
482 if (!(aroscbase->acb_exec_tmparray = malloc((argc+1)*(sizeof(char *)))))
484 D(bug("__exec_valist2array: Memory allocation failed\n"));
485 va_end(list2);
486 return NULL;
489 aroscbase->acb_exec_tmparray[0] = (char *)arg1;
490 for (argit = va_arg(list2, char *), i = 1;
491 i <= argc; /* i == argc will copy the NULL pointer */
492 argit = va_arg(list2, char *), i++
495 D(bug("arg %d: %x\n", i, argit));
496 aroscbase->acb_exec_tmparray[i] = argit;
499 va_end(list2);
501 return aroscbase->acb_exec_tmparray;
505 void __exec_cleanup_array()
507 struct aroscbase *aroscbase = __aros_getbase_aroscbase();
508 if (aroscbase->acb_exec_tmparray)
510 free((void *)aroscbase->acb_exec_tmparray);
511 aroscbase->acb_exec_tmparray = NULL;
516 /* Support functions */
517 /*********************/
519 /* Return TRUE if there are any white spaces in the string */
520 static BOOL containswhite(const char *str)
522 while(*str != '\0')
523 if(isspace(*str++)) return TRUE;
524 return FALSE;
527 /* Escape the string and quote it */
528 static char *escape(const char *str)
530 const char *strptr = str;
531 char *escaped, *escptr;
532 /* Additional two characters for '"', and one for '\0' */
533 int bufsize = strlen(str) + 3;
534 /* Take into account characters to ecape */
535 while(*strptr != '\0')
537 switch(*strptr++)
539 case '\n':
540 case '"':
541 case '*':
542 bufsize++;
545 escptr = escaped = (char*) malloc(bufsize);
546 if(!escaped)
547 return NULL;
548 *escptr++ = '"';
549 for(strptr = str; *strptr != '\0'; strptr++)
551 switch(*strptr)
553 case '\n':
554 case '"':
555 case '*':
556 *escptr++ = '*';
557 *escptr++ = (*strptr == '\n' ? 'N' : *strptr);
558 break;
559 default:
560 *escptr++ = *strptr;
561 break;
564 *escptr++ = '"';
565 *escptr = '\0';
566 return escaped;
569 /* Append arg string to argptr increasing argptr if needed */
570 static char *appendarg(char *argptr, int *argptrsize, const char *arg, APTR pool)
572 while(strlen(argptr) + strlen(arg) + 2 > *argptrsize)
574 char *argptr2;
575 int argptrsize2 = 2*(*argptrsize);
577 argptr2 = AllocPooled(pool, argptrsize2);
578 if(!argptr2)
580 FreePooled(pool, argptr, *argptrsize);
581 return NULL;
583 strcpy(argptr2, argptr);
584 FreePooled(pool, argptr, *argptrsize);
585 argptr = argptr2;
586 *argptrsize = argptrsize2;
588 strcat(argptr, arg);
589 strcat(argptr, " ");
591 return argptr;
594 static char *appendargs(char *argptr, int *argssizeptr, char *const args[], APTR pool)
596 char *const *argsit;
598 for (argsit = args; *argsit && argptr; argsit++)
600 if(containswhite(*argsit))
602 char *escaped = escape(*argsit);
603 if(!escaped)
605 FreePooled(pool, argptr, *argssizeptr);
606 return NULL;
608 argptr = appendarg(argptr, argssizeptr, escaped, pool);
609 free(escaped);
611 else
612 argptr = appendarg(argptr, argssizeptr, *argsit, pool);
615 return argptr;
618 static void __exec_cleanup(struct aroscbase *aroscbase)
620 D(bug("__exec_cleanup: me(%x)\n", FindTask(NULL)));
622 if(aroscbase->acb_exec_oldin)
624 SelectInput(aroscbase->acb_exec_oldin);
625 aroscbase->acb_exec_oldin = (BPTR)NULL;
627 if(aroscbase->acb_exec_oldout)
629 SelectOutput(aroscbase->acb_exec_oldout);
630 aroscbase->acb_exec_oldout = (BPTR)NULL;
632 if(aroscbase->acb_exec_olderr)
634 struct Process *me = (struct Process *)FindTask(NULL);
636 me->pr_CES = aroscbase->acb_exec_olderr;
637 aroscbase->acb_exec_olderr = BNULL;
640 if (aroscbase->acb_exec_pool)
642 DeletePool(aroscbase->acb_exec_pool);
643 aroscbase->acb_exec_pool = NULL;
645 if (aroscbase->acb_exec_seglist)
647 UnLoadSeg(aroscbase->acb_exec_seglist);
648 aroscbase->acb_exec_seglist = (BPTR)NULL;