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