1 /* @(#)fexec.c 1.51 15/07/06 Copyright 1985, 1995-2015 J. Schilling */
2 /*
3 * Execute a program with stdio redirection
4 *
5 * Copyright (c) 1985, 1995-2015 J. Schilling
6 *
7 * This is an interface that exists in the public since 1982.
8 * The POSIX.1-2008 standard did ignore POSIX rules not to
9 * redefine existing public interfaces and redefined the interfaces
10 * forcing us to add a js_*() prefix to the original functions.
11 */
12 /*
13 * The contents of this file are subject to the terms of the
14 * Common Development and Distribution License, Version 1.0 only
15 * (the "License"). You may not use this file except in compliance
16 * with the License.
17 *
18 * See the file CDDL.Schily.txt in this distribution for details.
19 * A copy of the CDDL is also available via the Internet at
20 * http://www.opensource.org/licenses/cddl1.txt
21 *
22 * When distributing Covered Code, include this CDDL HEADER in each
23 * file and include the License file CDDL.Schily.txt from this distribution.
24 */
25
26 #ifndef __DO__FEXEC__
27
28 #define fexecl __no__fexecl__
29 #define fexecle __no__fexecle__
30 #define fexecv __no__fexecv__
31 #define fexecve __no__fexecve__
32
33 #include <schily/mconfig.h>
34 #include <schily/stdio.h>
35 #include <schily/standard.h>
36 #include <schily/unistd.h>
37 #include <schily/stdlib.h>
38 #include <schily/string.h>
39 #include <schily/varargs.h>
40 #include <schily/errno.h>
41 #include <schily/fcntl.h>
42 #include <schily/dirent.h>
43 #include <schily/maxpath.h>
44 #include <schily/schily.h>
45 #define VMS_VFORK_OK
46 #include <schily/vfork.h>
47 #endif /* __DO__FEXEC__ */
48
49 #if defined(IS_MACOS_X) && defined(HAVE_CRT_EXTERNS_H)
50 /*
51 * The MAC OS X linker does not grok "common" varaibles.
52 * We need to fetch the address of "environ" using a hack.
53 */
54 #include <crt_externs.h>
55 #define environ *_NSGetEnviron()
56 #else
57 extern char **environ;
58 #endif
59
60 /*
61 * Check whether fexec may be implemented...
62 */
63 #if defined(HAVE_DUP) && (defined(HAVE_DUP2) || defined(F_DUPFD))
64
65 #define MAX_F_ARGS 16
66
67 #ifndef __DO__FEXEC__
68 #ifndef NO_FEXEC_COMPAT /* Define to disable backward compatibility */
69 #undef fexecl
70 #undef fexecle
71 #undef fexecv
72 #undef fexecve
73
74 /*
75 * The Cygwin compile environment incorrectly implements #pragma weak.
76 * The weak symbols are only defined as local symbols making it impossible
77 * to use them from outside the scope of this source file.
78 * A platform that allows linking with global symbols has HAVE_LINK_WEAK
79 * defined.
80 */
81 #if defined(HAVE_PRAGMA_WEAK) && defined(HAVE_LINK_WEAK)
82 #pragma weak fexecl = js_fexecl
83 #pragma weak fexecle = js_fexecle
84 #pragma weak fexecv = js_fexecv
85 #pragma weak fexecve = js_fexecve
86 #else
87
88 EXPORT int fexecl __PR((const char *, FILE *, FILE *, FILE *,
89 const char *, ...));
90 EXPORT int fexecle __PR((const char *, FILE *, FILE *, FILE *,
91 const char *, ...));
92 /* 6th arg not const, fexecv forces av[ac] = NULL */
93 EXPORT int fexecv __PR((const char *, FILE *, FILE *, FILE *, int,
94 char **));
95 EXPORT int fexecve __PR((const char *, FILE *, FILE *, FILE *,
96 char * const *, char * const *));
97
98 #define __DO__FEXEC__
99 #define js_fexecl fexecl
100 #define js_fexecle fexecle
101 #include "fexec.c"
102 #undef js_fexecl
103 #undef js_fexecle
104 #undef __DO__FEXEC__
105
106 /*
107 * Use ac == -1 together with a NULL terminated arg vector.
108 */
109 EXPORT int
fexecv(name,in,out,err,ac,av)110 fexecv(name, in, out, err, ac, av)
111 const char *name;
112 FILE *in, *out, *err;
113 int ac;
114 char *av[];
115 {
116 if (ac >= 0)
117 av[ac] = NULL; /* force list to be null terminated */
118 return (js_fexecve(name, in, out, err, av, environ));
119 }
120
121 EXPORT int
fexecve(name,in,out,err,av,env)122 fexecve(name, in, out, err, av, env)
123 const char *name;
124 FILE *in, *out, *err;
125 char * const av[], * const env[];
126 {
127 return (js_fexecve(name, in, out, err, av, env));
128 }
129 #endif /* HAVE_PRAGMA_WEAK && HAVE_LINK_WEAK */
130 #endif /* NO_FEXEC_COMPAT */
131
132
133 #ifdef JOS
134 #define enofile(t) ((t) == EMISSDIR || \
135 (t) == ENOFILE || \
136 (t) == EISADIR || \
137 (t) == EIOERR)
138 #else
139 #define enofile(t) ((t) == ENOENT || \
140 (t) == ENOTDIR || \
141 (t) == EISDIR || \
142 (t) == EIO)
143 #endif
144
145 #ifndef set_child_standard_fds
146 LOCAL void fdcopy __PR((int, int));
147 LOCAL void fdmove __PR((int, int));
148 #endif
149 LOCAL const char *chkname __PR((const char *, const char *));
150 LOCAL const char *getpath __PR((char * const *));
151
152 #ifdef F_GETFD
153 #define fd_getfd(fd) fcntl((fd), F_GETFD, 0)
154 #else
155 #define fd_getfd(fd) (0)
156 #endif
157 #ifdef F_SETFD
158 #define fd_setfd(fd, val) fcntl((fd), F_SETFD, (val));
159 #else
160 #define fd_setfd(fd, val) (0)
161 #endif
162 #endif /* __DO__FEXEC__ */
163
164 #ifdef PROTOTYPES
165 EXPORT int
js_fexecl(const char * name,FILE * in,FILE * out,FILE * err,const char * arg0,...)166 js_fexecl(const char *name, FILE *in, FILE *out, FILE *err, const char *arg0,
167 ...)
168 #else
169 EXPORT int
170 js_fexecl(name, in, out, err, arg0, va_alist)
171 char *name;
172 FILE *in;
173 FILE *out;
174 FILE *err;
175 const char *arg0;
176 va_dcl
177 #endif
178 {
179 va_list args;
180 int ac = 0;
181 char *xav[MAX_F_ARGS+1];
182 char **av;
183 const char **pav;
184 char *p;
185 int ret;
186
187 #ifdef PROTOTYPES
188 va_start(args, arg0);
189 #else
190 va_start(args);
191 #endif
192 if (arg0) {
193 ac++;
194 while (va_arg(args, char *) != NULL)
195 ac++;
196 }
197 va_end(args);
198
199 if (ac <= MAX_F_ARGS) {
200 av = xav;
201 } else {
202 av = (char **)malloc((ac+1)*sizeof (char *));
203 if (av == 0)
204 return (-1);
205 }
206 pav = (const char **)av;
207
208 #ifdef PROTOTYPES
209 va_start(args, arg0);
210 #else
211 va_start(args);
212 #endif
213 *pav++ = arg0;
214 if (arg0) do {
215 p = va_arg(args, char *);
216 *pav++ = p;
217 } while (p != NULL);
218 va_end(args);
219
220 ret = js_fexecv(name, in, out, err, ac, av);
221 if (av != xav)
222 free(av);
223 return (ret);
224 }
225
226 #ifdef PROTOTYPES
227 EXPORT int
js_fexecle(const char * name,FILE * in,FILE * out,FILE * err,const char * arg0,...)228 js_fexecle(const char *name, FILE *in, FILE *out, FILE *err, const char *arg0,
229 ...)
230 #else
231 EXPORT int
232 js_fexecle(name, in, out, err, arg0, va_alist)
233 char *name;
234 FILE *in;
235 FILE *out;
236 FILE *err;
237 const char *arg0;
238 va_dcl
239 #endif
240 {
241 va_list args;
242 int ac = 0;
243 char *xav[MAX_F_ARGS+1];
244 char **av;
245 const char **pav;
246 char *p;
247 char **env;
248 int ret;
249
250 #ifdef PROTOTYPES
251 va_start(args, arg0);
252 #else
253 va_start(args);
254 #endif
255 if (arg0) {
256 ac++;
257 while (va_arg(args, char *) != NULL)
258 ac++;
259 }
260 env = va_arg(args, char **);
261 va_end(args);
262
263 if (ac <= MAX_F_ARGS) {
264 av = xav;
265 } else {
266 av = (char **)malloc((ac+1)*sizeof (char *));
267 if (av == 0)
268 return (-1);
269 }
270 pav = (const char **)av;
271
272 #ifdef PROTOTYPES
273 va_start(args, arg0);
274 #else
275 va_start(args);
276 #endif
277 *pav++ = arg0;
278 if (arg0) do {
279 p = va_arg(args, char *);
280 *pav++ = p;
281 } while (p != NULL);
282 va_end(args);
283
284 ret = js_fexecve(name, in, out, err, av, env);
285 if (av != xav)
286 free(av);
287 return (ret);
288 }
289
290 #ifndef __DO__FEXEC__
291 /*
292 * Use ac == -1 together with a NULL terminated arg vector.
293 */
294 EXPORT int
js_fexecv(name,in,out,err,ac,av)295 js_fexecv(name, in, out, err, ac, av)
296 const char *name;
297 FILE *in, *out, *err;
298 int ac;
299 char *av[];
300 {
301 if (ac >= 0)
302 av[ac] = NULL; /* force list to be null terminated */
303 return (js_fexecve(name, in, out, err, av, environ));
304 }
305
306 EXPORT int
js_fexecve(name,in,out,err,av,env)307 js_fexecve(name, in, out, err, av, env)
308 const char *name;
309 FILE *in, *out, *err;
310 char * const av[], * const env[];
311 {
312 char nbuf[MAXPATHNAME+1];
313 char *np;
314 const char *path;
315 #if defined(__BEOS__) || defined(__HAIKU__)
316 char *av0 = av[0];
317 #endif
318 int ret;
319 int fin;
320 int fout;
321 int ferr;
322 #ifndef JOS
323 int o[3]; /* Old fd's for stdin/stdout/stderr */
324 int f[3]; /* Old close on exec flags for above */
325 int errsav = 0;
326
327 o[0] = o[1] = o[2] = -1;
328 f[0] = f[1] = f[2] = 0;
329 #endif
330
331 fflush(out);
332 fflush(err);
333 fin = fdown(in);
334 fout = fdown(out);
335 ferr = fdown(err);
336 #ifdef JOS
337
338 /*
339 * If name contains a pathdelimiter ('/' on unix)
340 * or name is too long ...
341 * try exec without path search.
342 */
343 if (find('/', name) || strlen(name) > MAXFILENAME) {
344 ret = exec_env(name, fin, fout, ferr, av, env);
345
346 } else if ((path = getpath(env)) == NULL) {
347 ret = exec_env(name, fin, fout, ferr, av, env);
348 if ((ret == ENOFILE) && strlen(name) <= (sizeof (nbuf) - 6)) {
349 strcatl(nbuf, "/bin/", name, (char *)NULL);
350 ret = exec_env(nbuf, fin, fout, ferr, av, env);
351 if (ret == EMISSDIR)
352 ret = ENOFILE;
353 }
354 } else {
355 int nlen = strlen(name);
356
357 for (;;) {
358 np = nbuf;
359 /*
360 * JOS always uses ':' as PATH Environ separator
361 */
362 while (*path != ':' && *path != '\0' &&
363 np < &nbuf[MAXPATHNAME-nlen-2]) {
364
365 *np++ = *path++;
366 }
367 *np = '\0';
368 if (*nbuf == '\0')
369 strcatl(nbuf, name, (char *)NULL);
370 else
371 strcatl(nbuf, nbuf, "/", name, (char *)NULL);
372 ret = exec_env(nbuf, fin, fout, ferr, av, env);
373 if (ret == EMISSDIR)
374 ret = ENOFILE;
375 if (ret != ENOFILE || *path == '\0')
376 break;
377 path++;
378 }
379 }
380 return (ret);
381
382 #else /* JOS */
383
384 #ifdef set_child_standard_fds
385 set_child_standard_fds(fin, fout, ferr);
386 #else
387 if (fin != STDIN_FILENO) {
388 f[0] = fd_getfd(STDIN_FILENO);
389 o[0] = dup(STDIN_FILENO);
390 fd_setfd(o[0], FD_CLOEXEC);
391 fdmove(fin, STDIN_FILENO);
392 }
393 if (fout != STDOUT_FILENO) {
394 f[1] = fd_getfd(STDOUT_FILENO);
395 o[1] = dup(STDOUT_FILENO);
396 fd_setfd(o[1], FD_CLOEXEC);
397 fdmove(fout, STDOUT_FILENO);
398 }
399 if (ferr != STDERR_FILENO) {
400 f[2] = fd_getfd(STDERR_FILENO);
401 o[2] = dup(STDERR_FILENO);
402 fd_setfd(o[2], FD_CLOEXEC);
403 fdmove(ferr, STDERR_FILENO);
404 }
405 #endif
406
407 /*
408 * If name contains a pathdelimiter ('/' on unix)
409 * or name is too long ...
410 * try exec without path search.
411 */
412 #ifdef FOUND_MAXFILENAME
413 if (strchr(name, '/') || strlen(name) > (unsigned)MAXFILENAME) {
414 #else
415 if (strchr(name, '/')) {
416 #endif
417 ret = execve(name, av, env);
418
419 } else if ((path = getpath(env)) == NULL) {
420 ret = execve(name, av, env);
421 if ((geterrno() == ENOENT) && strlen(name) <= (sizeof (nbuf) - 6)) {
422 strcatl(nbuf, "/bin/", name, (char *)NULL);
423 ret = execve(nbuf, av, env);
424 #if defined(__BEOS__) || defined(__HAIKU__)
425 ((char **)av)[0] = av0; /* BeOS destroys things ... */
426 #endif
427 }
428 } else {
429 int nlen = strlen(name);
430
431 for (;;) {
432 int xerr;
433
434 np = nbuf;
435 while (*path != PATH_ENV_DELIM && *path != '\0' &&
436 np < &nbuf[MAXPATHNAME-nlen-2]) {
437
438 *np++ = *path++;
439 }
440 *np = '\0';
441 if (*nbuf == '\0')
442 strcatl(nbuf, name, (char *)NULL);
443 else
444 strcatl(nbuf, nbuf, "/", name, (char *)NULL);
445 ret = execve(nbuf, av, env);
446 #if defined(__BEOS__) || defined(__HAIKU__)
447 ((char **)av)[0] = av0; /* BeOS destroys things ... */
448 #endif
449 xerr = geterrno();
450 if (errsav == 0 && !enofile(xerr))
451 errsav = xerr;
452 if ((!enofile(xerr) && !(xerr == EACCES)) || *path == '\0')
453 break;
454 path++;
455 }
456 }
457 if (errsav == 0)
458 errsav = geterrno();
459
460 #ifndef set_child_standard_fds
461 /* reestablish old files */
462 if (ferr != STDERR_FILENO) {
463 fdmove(STDERR_FILENO, ferr);
464 fdmove(o[2], STDERR_FILENO);
465 if (f[2] == 0)
466 fd_setfd(STDERR_FILENO, 0);
467 }
468 if (fout != STDOUT_FILENO) {
469 fdmove(STDOUT_FILENO, fout);
470 fdmove(o[1], STDOUT_FILENO);
471 if (f[1] == 0)
472 fd_setfd(STDOUT_FILENO, 0);
473 }
474 if (fin != STDIN_FILENO) {
475 fdmove(STDIN_FILENO, fin);
476 fdmove(o[0], STDIN_FILENO);
477 if (f[0] == 0)
478 fd_setfd(STDIN_FILENO, 0);
479 }
480 #endif
481 seterrno(errsav);
482 return (ret);
483
484 #endif /* JOS */
485 }
486
487 #ifndef JOS
488 #ifndef set_child_standard_fds
489
490 LOCAL void
fdcopy(fd1,fd2)491 fdcopy(fd1, fd2)
492 int fd1;
493 int fd2;
494 {
495 close(fd2);
496 #ifdef F_DUPFD
497 fcntl(fd1, F_DUPFD, fd2);
498 #else
499 #ifdef HAVE_DUP2
500 dup2(fd1, fd2);
501 #endif
502 #endif
503 }
504
505 LOCAL void
fdmove(fd1,fd2)506 fdmove(fd1, fd2)
507 int fd1;
508 int fd2;
509 {
510 fdcopy(fd1, fd2);
511 close(fd1);
512 }
513
514 #endif
515 #endif
516
517 /*
518 * get PATH from env
519 */
520 LOCAL const char *
getpath(env)521 getpath(env)
522 char * const *env;
523 {
524 char * const *p = env;
525 const char *p2;
526
527 if (p != NULL) {
528 while (*p != NULL) {
529 if ((p2 = chkname("PATH", *p)) != NULL)
530 return (p2);
531 p++;
532 }
533 }
534 return (NULL);
535 }
536
537
538 /*
539 * Check if name is in environment.
540 * Return pointer to value name is found.
541 */
542 LOCAL const char *
chkname(name,ev)543 chkname(name, ev)
544 const char *name;
545 const char *ev;
546 {
547 for (;;) {
548 if (*name != *ev) {
549 if (*ev == '=' && *name == '\0')
550 return (++ev);
551 return (NULL);
552 }
553 name++;
554 ev++;
555 }
556 }
557 #endif /* __DO__FEXEC__ */
558
559 #endif /* defined(HAVE_DUP) && (defined(HAVE_DUP2) || defined(F_DUPFD)) */
560