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