xref: /freebsd/lib/libc/gen/exec.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD$
34  */
35 
36 #if defined(LIBC_SCCS) && !defined(lint)
37 #if 0
38 static char sccsid[] = "@(#)exec.c	8.1 (Berkeley) 6/4/93";
39 #endif
40 static const char rcsid[] =
41   "$FreeBSD$";
42 #endif /* LIBC_SCCS and not lint */
43 
44 #include "namespace.h"
45 #include <sys/param.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <errno.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <stdio.h>
53 #include <paths.h>
54 
55 #if __STDC__
56 #include <stdarg.h>
57 #else
58 #include <varargs.h>
59 #endif
60 #include "un-namespace.h"
61 
62 extern char **environ;
63 
64 int
65 #if __STDC__
66 execl(const char *name, const char *arg, ...)
67 #else
68 execl(name, arg, va_alist)
69 	const char *name;
70 	const char *arg;
71 	va_dcl
72 #endif
73 {
74 	va_list ap;
75 	char **argv;
76 	int n;
77 
78 #if __STDC__
79 	va_start(ap, arg);
80 #else
81 	va_start(ap);
82 #endif
83 	n = 1;
84 	while (va_arg(ap, char *) != NULL)
85 		n++;
86 	va_end(ap);
87 	argv = alloca((n + 1) * sizeof(*argv));
88 	if (argv == NULL) {
89 		errno = ENOMEM;
90 		return (-1);
91 	}
92 #if __STDC__
93 	va_start(ap, arg);
94 #else
95 	va_start(ap);
96 #endif
97 	n = 1;
98 	argv[0] = (char *)arg;
99 	while ((argv[n] = va_arg(ap, char *)) != NULL)
100 		n++;
101 	va_end(ap);
102 	return (_execve(name, argv, environ));
103 }
104 
105 int
106 #if __STDC__
107 execle(const char *name, const char *arg, ...)
108 #else
109 execle(name, arg, va_alist)
110 	const char *name;
111 	const char *arg;
112 	va_dcl
113 #endif
114 {
115 	va_list ap;
116 	char **argv, **envp;
117 	int n;
118 
119 #if __STDC__
120 	va_start(ap, arg);
121 #else
122 	va_start(ap);
123 #endif
124 	n = 1;
125 	while (va_arg(ap, char *) != NULL)
126 		n++;
127 	va_end(ap);
128 	argv = alloca((n + 1) * sizeof(*argv));
129 	if (argv == NULL) {
130 		errno = ENOMEM;
131 		return (-1);
132 	}
133 #if __STDC__
134 	va_start(ap, arg);
135 #else
136 	va_start(ap);
137 #endif
138 	n = 1;
139 	argv[0] = (char *)arg;
140 	while ((argv[n] = va_arg(ap, char *)) != NULL)
141 		n++;
142 	envp = va_arg(ap, char **);
143 	va_end(ap);
144 	return (_execve(name, argv, envp));
145 }
146 
147 int
148 #if __STDC__
149 execlp(const char *name, const char *arg, ...)
150 #else
151 execlp(name, arg, va_alist)
152 	const char *name;
153 	const char *arg;
154 	va_dcl
155 #endif
156 {
157 	va_list ap;
158 	int sverrno;
159 	char **argv;
160 	int n;
161 
162 #if __STDC__
163 	va_start(ap, arg);
164 #else
165 	va_start(ap);
166 #endif
167 	n = 1;
168 	while (va_arg(ap, char *) != NULL)
169 		n++;
170 	va_end(ap);
171 	argv = alloca((n + 1) * sizeof(*argv));
172 	if (argv == NULL) {
173 		errno = ENOMEM;
174 		return (-1);
175 	}
176 #if __STDC__
177 	va_start(ap, arg);
178 #else
179 	va_start(ap);
180 #endif
181 	n = 1;
182 	argv[0] = (char *)arg;
183 	while ((argv[n] = va_arg(ap, char *)) != NULL)
184 		n++;
185 	va_end(ap);
186 	return (execvp(name, argv));
187 }
188 
189 int
190 execv(name, argv)
191 	const char *name;
192 	char * const *argv;
193 {
194 	(void)_execve(name, argv, environ);
195 	return (-1);
196 }
197 
198 int
199 execvp(name, argv)
200 	const char *name;
201 	char * const *argv;
202 {
203 	char **memp;
204 	register int cnt, lp, ln;
205 	register char *p;
206 	int eacces, save_errno;
207 	char *bp, *cur, *path, buf[MAXPATHLEN];
208 	struct stat sb;
209 
210 	eacces = 0;
211 
212 	/* If it's an absolute or relative path name, it's easy. */
213 	if (index(name, '/')) {
214 		bp = (char *)name;
215 		cur = path = NULL;
216 		goto retry;
217 	}
218 	bp = buf;
219 
220 	/* If it's an empty path name, fail in the usual POSIX way. */
221 	if (*name == '\0') {
222 		errno = ENOENT;
223 		return (-1);
224 	}
225 
226 	/* Get the path we're searching. */
227 	if (!(path = getenv("PATH")))
228 		path = _PATH_DEFPATH;
229 	cur = alloca(strlen(path) + 1);
230 	if (cur == NULL) {
231 		errno = ENOMEM;
232 		return (-1);
233 	}
234 	strcpy(cur, path);
235 	path = cur;
236 	while ( (p = strsep(&cur, ":")) ) {
237 		/*
238 		 * It's a SHELL path -- double, leading and trailing colons
239 		 * mean the current directory.
240 		 */
241 		if (!*p) {
242 			p = ".";
243 			lp = 1;
244 		} else
245 			lp = strlen(p);
246 		ln = strlen(name);
247 
248 		/*
249 		 * If the path is too long complain.  This is a possible
250 		 * security issue; given a way to make the path too long
251 		 * the user may execute the wrong program.
252 		 */
253 		if (lp + ln + 2 > sizeof(buf)) {
254 			(void)_write(STDERR_FILENO, "execvp: ", 8);
255 			(void)_write(STDERR_FILENO, p, lp);
256 			(void)_write(STDERR_FILENO, ": path too long\n",
257 			    16);
258 			continue;
259 		}
260 		bcopy(p, buf, lp);
261 		buf[lp] = '/';
262 		bcopy(name, buf + lp + 1, ln);
263 		buf[lp + ln + 1] = '\0';
264 
265 retry:		(void)_execve(bp, argv, environ);
266 		switch(errno) {
267 		case E2BIG:
268 			goto done;
269 		case ELOOP:
270 		case ENAMETOOLONG:
271 		case ENOENT:
272 			break;
273 		case ENOEXEC:
274 			for (cnt = 0; argv[cnt]; ++cnt)
275 				;
276 			memp = alloca((cnt + 2) * sizeof(char *));
277 			if (memp == NULL) {
278 				/* errno = ENOMEM; XXX override ENOEXEC? */
279 				goto done;
280 			}
281 			memp[0] = "sh";
282 			memp[1] = bp;
283 			bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
284 			(void)_execve(_PATH_BSHELL, memp, environ);
285 			goto done;
286 		case ENOMEM:
287 			goto done;
288 		case ENOTDIR:
289 			break;
290 		case ETXTBSY:
291 			/*
292 			 * We used to retry here, but sh(1) doesn't.
293 			 */
294 			goto done;
295 		default:
296 			/*
297 			 * EACCES may be for an inaccessible directory or
298 			 * a non-executable file.  Call stat() to decide
299 			 * which.  This also handles ambiguities for EFAULT
300 			 * and EIO, and undocumented errors like ESTALE.
301 			 * We hope that the race for a stat() is unimportant.
302 			 */
303 			save_errno = errno;
304 			if (stat(bp, &sb) != 0)
305 				break;
306 			if (save_errno == EACCES) {
307 				eacces = 1;
308 				continue;
309 			}
310 			errno = save_errno;
311 			goto done;
312 		}
313 	}
314 	if (eacces)
315 		errno = EACCES;
316 	else
317 		errno = ENOENT;
318 done:
319 	return (-1);
320 }
321