- Give PCI controllers lower unit numbers than legacy controllers.
[cake.git] / compiler / clib / __exec.c
blob4a502d73575110b90a3792edef50d0008b88b058
1 /*
2 Copyright © 2008-2009, 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>
22 #include "__arosc_privdata.h"
23 #include "__exec.h"
24 #include "__upath.h"
25 #include "__errno.h"
26 #include "__open.h"
28 #define DEBUG 0
29 #include <aros/debug.h>
31 static BOOL containswhite(const char *str);
32 static char *escape(const char *str);
33 static char *appendarg(char *argptr, int *argptrsize, const char *arg);
34 static char *appendargs(char *argptr, int *argptrsize, char *const args[]);
35 static void __exec_cleanup(struct arosc_privdata *privdata);
37 /* Public functions */
38 /********************/
40 APTR __exec_prepare(const char *filename, int searchpath, char *const argv[], char *const envp[])
42 struct arosc_privdata *privdata = __get_arosc_privdata();
43 char *filename2 = NULL, *filenamefree = NULL;
44 int argssize = 1024;
46 D(bug("Entering __exec_prepare(\"%s\", %d, %x, %x)\n",
47 filename, searchpath, argv, envp
48 ));
49 /* Sanity check */
50 if (filename == NULL || filename[0] == '\0' || argv == NULL)
52 errno = EINVAL;
53 goto error;
56 if (privdata->acpd_flags & PRETEND_CHILD)
58 struct vfork_data *udata = privdata->acpd_vfork_data;
60 udata->exec_filename = filename;
61 udata->exec_searchpath = searchpath;
62 udata->exec_argv = argv;
63 udata->exec_envp = envp;
65 /* Set this so the child knows that __exec_prepare was called */
66 udata->child_executed = 1;
68 D(bug("__exec_prepare: Calling child\n"));
69 /* Now call child process, so it will call __exec_prepare */
70 Signal(udata->child, 1 << udata->child_signal);
72 D(bug("__exec_prepare: Waiting for child to finish __exec_prepare\n"));
73 /* __exec_prepare should be finished now on child */
74 Wait(1 << udata->parent_signal);
76 if (!udata->exec_id)
78 D(bug("__exec_prepare: Continue child immediately on error\n"));
79 Signal(udata->child, 1 << udata->child_signal);
82 D(bug("__exec_prepare: Exiting from forked __exec_prepare id=%x, errno=%d\n",
83 udata->exec_id, udata->child_errno
84 ));
86 return udata->exec_id;
88 privdata->acpd_exec_args = malloc(argssize);
89 privdata->acpd_exec_args[0] = '\0';
91 /* Search path if asked and no directory separator is present in the file */
92 if (searchpath && index(filename, '/') == NULL && index(filename, ':') == NULL)
94 int i;
95 char *path = NULL, *path_ptr, *path_item;
96 BPTR lock;
98 if (environ)
100 for (i=0; environ[i]; i++)
102 if (strncmp(environ[i], "PATH=", 5) == 0)
104 path = &environ[i][5];
105 break;
110 if (!path)
111 path = getenv("PATH");
113 path = strdup(path ? path : ":/bin:/usr/bin");
115 for(path_ptr = path, lock = (BPTR)NULL, path_item = strsep(&path_ptr, ",:");
116 lock == (BPTR)NULL && path_item != NULL;
117 path_item = strsep(&path_ptr, ",:")
120 if(filenamefree)
121 free(filenamefree);
123 if(path_item[0] == '\0')
124 path_item = ".";
126 filenamefree = filename2 = malloc(strlen(path_item) + strlen(filename) + 2);
127 if(filename2)
129 filename2[0] = '\0';
130 strcat(filename2, path_item);
131 strcat(filename2, "/");
132 strcat(filename2, filename);
133 lock = Lock(__path_u2a(filename2), SHARED_LOCK);
134 D(bug("__exec_prepare: Lock(\"%s\") == %x\n", filename2, (APTR)lock));
136 else
138 errno = ENOMEM;
139 goto error;
142 if(lock != (BPTR)NULL)
143 UnLock(lock);
144 else
146 errno = ENOENT;
147 goto error;
150 else
151 filename2 = (char *)filename;
154 /* Let's check if it's a script */
155 BPTR fh = Open((CONST_STRPTR)__path_u2a(filename2), MODE_OLDFILE);
156 if(fh)
158 if(FGetC(fh) == '#' && FGetC(fh) == '!')
160 char firstline[128], *linebuf, *inter, *interargs = NULL;
162 /* It is a script, let's read the first line */
163 if(FGets(fh, (STRPTR)firstline, sizeof(firstline) - 1))
165 /* delete end of line if present */
166 if(firstline[0] && firstline[strlen(firstline)-1] == '\n')
167 firstline[strlen(firstline)-1] = '\0';
168 linebuf = firstline;
169 while(isblank(*linebuf)) linebuf++;
170 if(*linebuf != '\0')
172 /* Interpreter name is here */
173 inter = linebuf;
174 while(*linebuf != '\0' && !isblank(*linebuf)) linebuf++;
175 if(*linebuf != '\0')
177 *linebuf++ = '\0';
178 while(isblank(*linebuf)) linebuf++;
179 if(*linebuf != '\0')
180 /* Interpreter arguments are here */
181 interargs = linebuf;
184 /* Add interpeter args and the script name to command line args */
185 char *args[] = {NULL, NULL, NULL};
186 if (interargs)
188 args[0] = interargs;
189 args[1] = filename2;
191 else
193 args[0] = filename2;
195 privdata->acpd_exec_args = appendargs(
196 privdata->acpd_exec_args, &argssize, args
198 if (!privdata->acpd_exec_args)
200 errno = ENOMEM;
201 goto error;
204 /* Set file to execute as the script interpreter */
205 if (filenamefree)
206 free(filenamefree);
207 filenamefree = filename2 = strdup(inter);
211 Close(fh);
213 else
215 /* Simply assume it doesn't exist */
216 errno = IoErr2errno(IoErr());
217 goto error;
220 /* Add arguments to command line args */
221 privdata->acpd_exec_args = appendargs(privdata->acpd_exec_args, &argssize, argv + 1);
222 if (!privdata->acpd_exec_args)
224 errno = ENOMEM;
225 goto error;
228 /* End command line args with '\n' */
229 if(strlen(privdata->acpd_exec_args) > 0)
230 privdata->acpd_exec_args[strlen(privdata->acpd_exec_args) - 1] = '\n';
231 else
232 strcat(privdata->acpd_exec_args, "\n");
235 /* let's make some sanity tests */
236 struct stat st;
237 if(stat(filename2, &st) == 0)
239 if(!(st.st_mode & S_IXUSR) && !(st.st_mode & S_IXGRP) && !(st.st_mode & S_IXOTH))
241 /* file is not executable */
242 errno = EACCES;
243 goto error;
245 if(st.st_mode & S_IFDIR)
247 /* it's a directory */
248 errno = EACCES;
249 goto error;
252 else
254 /* couldn't stat file */
255 goto error;
258 /* Set taskname */
259 if (!(privdata->acpd_exec_taskname = strdup(filename2)))
261 errno = ENOMEM;
262 goto error;
265 /* Load file to execute */
266 privdata->acpd_exec_seglist = LoadSeg((CONST_STRPTR)__path_u2a(filename2));
267 if (!privdata->acpd_exec_seglist)
269 errno = ENOEXEC;
270 goto error;
273 if (envp && envp != environ)
275 struct MinList tempenv;
276 struct LocalVar *lv, *lv2;
277 struct Process *me = (struct Process *)FindTask(NULL);
278 char *const *envit;
279 int env_ok = 1;
281 /* Remember previous environment variables so they can be put back
282 * if something goes wrong
284 NEWLIST(&tempenv);
285 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
287 Remove((struct Node *)lv);
288 AddTail((struct List *)&tempenv, (struct Node *)lv);
291 NEWLIST(&me->pr_LocalVars);
292 environ = NULL;
294 for (envit = envp; *envit && env_ok; envit++)
295 env_ok = putenv(*envit) == 0;
297 if (env_ok)
298 /* Old vars may be freed */
299 ForeachNodeSafe(&tempenv, lv, lv2)
301 Remove((struct Node *)lv);
302 FreeMem(lv->lv_Value, lv->lv_Len);
303 FreeVec(lv);
305 else
307 /* Free new nodes */
308 ForeachNodeSafe(&me->pr_LocalVars, lv, lv2)
310 Remove((struct Node *)lv);
311 FreeMem(lv->lv_Value, lv->lv_Len);
312 FreeVec(lv);
315 /* Put old ones back */
316 ForeachNodeSafe(&tempenv, lv, lv2)
318 Remove((struct Node *)lv);
319 AddTail((struct List *)&me->pr_LocalVars, (struct Node *)lv);
322 errno = ENOMEM;
323 goto error;
327 /* Set standard files to the standard files from arosc */
328 fdesc *in = privdata->acpd_fd_array[STDIN_FILENO],
329 *out = privdata->acpd_fd_array[STDOUT_FILENO],
330 *err = privdata->acpd_fd_array[STDERR_FILENO];
332 if(in)
333 privdata->acpd_exec_oldin = SelectInput(in->fcb->fh);
334 if(out)
335 privdata->acpd_exec_oldout = SelectOutput(out->fcb->fh);
336 if(err)
337 privdata->acpd_exec_olderr = SelectError(err->fcb->fh);
339 /* Clean up data */
340 if (privdata->acpd_exec_tmparray);
342 free((void *)privdata->acpd_exec_tmparray);
343 privdata->acpd_exec_tmparray = NULL;
345 if (filenamefree)
346 free(filenamefree);
348 /* Generate new privdata for the exec */
349 assert(!(privdata->acpd_flags & KEEP_OLD_ACPD));
350 privdata->acpd_flags |= CREATE_NEW_ACPD;
351 privdata->acpd_exec_aroscbase = OpenLibrary("arosc.library", 0);
352 if(!privdata->acpd_exec_aroscbase)
354 errno = ENOMEM;
355 goto error;
358 /* Initialize some data of the new generated privdata */
359 struct arosc_privdata *newprivdata = __get_arosc_privdata();
360 assert(!(newprivdata->acpd_flags & CREATE_NEW_ACPD));
361 newprivdata->acpd_flags |= KEEP_OLD_ACPD;
362 newprivdata->acpd_parent_does_upath = privdata->acpd_doupath;
364 /* Everything OK */
365 return (APTR)privdata;
367 error:
368 __exec_cleanup(privdata);
370 if (privdata->acpd_exec_tmparray);
372 free((void *)privdata->acpd_exec_tmparray);
373 privdata->acpd_exec_tmparray = NULL;
375 if (filenamefree)
376 free(filenamefree);
378 return (APTR)NULL;
382 void __exec_do(APTR id)
384 struct arosc_privdata *privdata = id;
385 char *oldtaskname;
386 struct CommandLineInterface *cli = Cli();
387 struct Task *self = FindTask(NULL);
388 LONG returncode;
390 if (__get_arosc_privdata()->acpd_flags & PRETEND_CHILD)
392 struct vfork_data *udata = __get_arosc_privdata()->acpd_vfork_data;
394 /* Signal child that __exec_do is called */
395 Signal(udata->child, 1 << udata->child_signal);
397 /* Continue as parent process */
398 _exit(0);
400 assert(0); /* Should not be reached */
401 return;
404 oldtaskname = self->tc_Node.ln_Name;
405 self->tc_Node.ln_Name = privdata->acpd_exec_taskname;
406 SetProgramName((STRPTR)privdata->acpd_exec_taskname);
408 returncode = RunCommand(
409 privdata->acpd_exec_seglist,
410 cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT,
411 (STRPTR)privdata->acpd_exec_args,
412 strlen(privdata->acpd_exec_args)
415 self->tc_Node.ln_Name = oldtaskname;
416 SetProgramName((STRPTR)oldtaskname);
418 __exec_cleanup(privdata);
420 D(bug("exiting from non-forked execve()\n"));
421 _exit(returncode);
425 char *const *__exec_valist2array(const char *arg1, va_list list)
427 struct arosc_privdata *privdata = __get_arosc_privdata();
428 int argc, i;
429 static char *no_arg[] = {NULL};
430 va_list list2;
431 char *argit;
433 assert(privdata->acpd_exec_tmparray == NULL);
435 va_copy(list2, list);
437 if (arg1 == NULL)
438 return no_arg;
440 for (argit = va_arg(list, char *), argc = 1;
441 argit != NULL;
442 argit = va_arg(list, char *)
444 argc++;
446 if (!(privdata->acpd_exec_tmparray = malloc((argc+1)*(sizeof(char *)))))
448 D(bug("__exec_valist2array: Memory allocation failed\n"));
449 va_end(list2);
450 return NULL;
453 privdata->acpd_exec_tmparray[0] = (char *)arg1;
454 for (argit = va_arg(list2, char *), i = 1;
455 i <= argc; /* i == argc will copy the NULL pointer */
456 argit = va_arg(list2, char *), i++
459 D(bug("arg %d: %x\n", i, argit));
460 privdata->acpd_exec_tmparray[i] = argit;
463 va_end(list2);
465 return privdata->acpd_exec_tmparray;
469 /* Support functions */
470 /*********************/
472 /* Return TRUE if there are any white spaces in the string */
473 static BOOL containswhite(const char *str)
475 while(*str != '\0')
476 if(isspace(*str++)) return TRUE;
477 return FALSE;
480 /* Escape the string and quote it */
481 static char *escape(const char *str)
483 const char *strptr = str;
484 char *escaped, *escptr;
485 /* Additional two characters for '"', and one for '\0' */
486 int bufsize = strlen(str) + 3;
487 /* Take into account characters to ecape */
488 while(*strptr != '\0')
490 switch(*strptr++)
492 case '\n':
493 case '"':
494 case '*':
495 bufsize++;
498 escptr = escaped = (char*) malloc(bufsize);
499 if(!escaped)
500 return NULL;
501 *escptr++ = '"';
502 for(strptr = str; *strptr != '\0'; strptr++)
504 switch(*strptr)
506 case '\n':
507 case '"':
508 case '*':
509 *escptr++ = '*';
510 *escptr++ = (*strptr == '\n' ? 'N' : *strptr);
511 break;
512 default:
513 *escptr++ = *strptr;
514 break;
517 *escptr++ = '"';
518 *escptr = '\0';
519 return escaped;
522 /* Append arg string to argptr increasing argptr if needed */
523 static char *appendarg(char *argptr, int *argptrsize, const char *arg)
525 while(strlen(argptr) + strlen(arg) + 2 > *argptrsize)
527 *argptrsize *= 2;
528 argptr = realloc(argptr, *argptrsize);
529 if(!argptr)
530 return NULL;
532 strcat(argptr, arg);
533 strcat(argptr, " ");
535 return argptr;
538 static char *appendargs(char *argptr, int *argssizeptr, char *const args[])
540 char *const *argsit;
542 for (argsit = args; *argsit && argptr; argsit++)
544 if(containswhite(*argsit))
546 char *escaped = escape(*argsit);
547 if(!escaped) {
548 free(argptr);
549 return NULL;
551 argptr = appendarg(argptr, argssizeptr, escaped);
552 free(escaped);
554 else
555 argptr = appendarg(argptr, argssizeptr, *argsit);
558 return argptr;
561 void __exec_cleanup(struct arosc_privdata *privdata)
564 /* Delete old private data */
565 if (privdata->acpd_exec_aroscbase)
567 CloseLibrary(privdata->acpd_exec_aroscbase);
568 privdata->acpd_exec_aroscbase = NULL;
571 if(privdata->acpd_exec_oldin)
573 SelectInput(privdata->acpd_exec_oldin);
574 privdata->acpd_exec_oldin = (BPTR)NULL;
576 if(privdata->acpd_exec_oldout)
578 SelectOutput(privdata->acpd_exec_oldout);
579 privdata->acpd_exec_oldout = (BPTR)NULL;
581 if(privdata->acpd_exec_olderr)
583 SelectError(privdata->acpd_exec_olderr);
584 privdata->acpd_exec_olderr = (BPTR)NULL;
587 if (privdata->acpd_exec_args)
589 free(privdata->acpd_exec_args);
590 privdata->acpd_exec_args = NULL;
592 if (privdata->acpd_exec_seglist)
594 UnLoadSeg(privdata->acpd_exec_seglist);
595 privdata->acpd_exec_seglist = (BPTR)NULL;
597 if (privdata->acpd_exec_taskname)
599 free(privdata->acpd_exec_taskname);
600 privdata->acpd_exec_taskname = NULL;