xref: /openbsd/lib/libc/gen/exec.c (revision 789acbf7)
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 
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char rcsid[] = "$OpenBSD: exec.c,v 1.11 2000/08/22 18:46:04 deraadt Exp $";
36 #endif /* LIBC_SCCS and not lint */
37 
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/uio.h>
41 #include <errno.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <stdio.h>
46 #include <paths.h>
47 
48 #ifdef __STDC__
49 #include <stdarg.h>
50 #else
51 #include <varargs.h>
52 #endif
53 
54 extern char **environ;
55 
56 int
57 #ifdef __STDC__
58 execl(const char *name, const char *arg, ...)
59 #else
60 execl(name, arg, va_alist)
61 	const char *name;
62 	const char *arg;
63 	va_dcl
64 #endif
65 {
66 	va_list ap;
67 	char **argv;
68 	int n;
69 
70 #ifdef __STDC__
71 	va_start(ap, arg);
72 #else
73 	va_start(ap);
74 #endif
75 	n = 1;
76 	while (va_arg(ap, char *) != NULL)
77 		n++;
78 	va_end(ap);
79 	argv = alloca((n + 1) * sizeof(*argv));
80 	if (argv == NULL) {
81 		errno = ENOMEM;
82 		return (-1);
83 	}
84 #ifdef __STDC__
85 	va_start(ap, arg);
86 #else
87 	va_start(ap);
88 #endif
89 	n = 1;
90 	argv[0] = (char *)arg;
91 	while ((argv[n] = va_arg(ap, char *)) != NULL)
92 		n++;
93 	va_end(ap);
94 	return (execve(name, argv, environ));
95 }
96 
97 int
98 #ifdef __STDC__
99 execle(const char *name, const char *arg, ...)
100 #else
101 execle(name, arg, va_alist)
102 	const char *name;
103 	const char *arg;
104 	va_dcl
105 #endif
106 {
107 	va_list ap;
108 	char **argv, **envp;
109 	int n;
110 
111 #ifdef __STDC__
112 	va_start(ap, arg);
113 #else
114 	va_start(ap);
115 #endif
116 	n = 1;
117 	while (va_arg(ap, char *) != NULL)
118 		n++;
119 	va_end(ap);
120 	argv = alloca((n + 1) * sizeof(*argv));
121 	if (argv == NULL) {
122 		errno = ENOMEM;
123 		return (-1);
124 	}
125 #ifdef __STDC__
126 	va_start(ap, arg);
127 #else
128 	va_start(ap);
129 #endif
130 	n = 1;
131 	argv[0] = (char *)arg;
132 	while ((argv[n] = va_arg(ap, char *)) != NULL)
133 		n++;
134 	envp = va_arg(ap, char **);
135 	va_end(ap);
136 	return (execve(name, argv, envp));
137 }
138 
139 int
140 #ifdef __STDC__
141 execlp(const char *name, const char *arg, ...)
142 #else
143 execlp(name, arg, va_alist)
144 	const char *name;
145 	const char *arg;
146 	va_dcl
147 #endif
148 {
149 	va_list ap;
150 	char **argv;
151 	int n;
152 
153 #ifdef __STDC__
154 	va_start(ap, arg);
155 #else
156 	va_start(ap);
157 #endif
158 	n = 1;
159 	while (va_arg(ap, char *) != NULL)
160 		n++;
161 	va_end(ap);
162 	argv = alloca((n + 1) * sizeof(*argv));
163 	if (argv == NULL) {
164 		errno = ENOMEM;
165 		return (-1);
166 	}
167 #ifdef __STDC__
168 	va_start(ap, arg);
169 #else
170 	va_start(ap);
171 #endif
172 	n = 1;
173 	argv[0] = (char *)arg;
174 	while ((argv[n] = va_arg(ap, char *)) != NULL)
175 		n++;
176 	va_end(ap);
177 	return (execvp(name, argv));
178 }
179 
180 int
181 execv(name, argv)
182 	const char *name;
183 	char * const *argv;
184 {
185 	(void)execve(name, argv, environ);
186 	return (-1);
187 }
188 
189 int
190 execvp(name, argv)
191 	const char *name;
192 	char * const *argv;
193 {
194 	char **memp;
195 	register int cnt, lp, ln;
196 	register char *p;
197 	int eacces = 0;
198 	char *bp, *cur, *path, buf[MAXPATHLEN];
199 
200 	/*
201 	 * Do not allow null name
202 	 */
203 	if (name == NULL || *name == '\0') {
204 		errno = ENOENT;
205 		return (-1);
206  	}
207 
208 	/* If it's an absolute or relative path name, it's easy. */
209 	if (strchr(name, '/')) {
210 		bp = (char *)name;
211 		cur = path = NULL;
212 		goto retry;
213 	}
214 	bp = buf;
215 
216 	/* Get the path we're searching. */
217 	if (!(path = getenv("PATH")))
218 		path = _PATH_DEFPATH;
219 	cur = alloca(strlen(path) + 1);
220 	if (cur == NULL) {
221 		errno = ENOMEM;
222 		return (-1);
223 	}
224 	strcpy(cur, path);
225 	path = cur;
226 	while ((p = strsep(&cur, ":"))) {
227 		/*
228 		 * It's a SHELL path -- double, leading and trailing colons
229 		 * mean the current directory.
230 		 */
231 		if (!*p) {
232 			p = ".";
233 			lp = 1;
234 		} else
235 			lp = strlen(p);
236 		ln = strlen(name);
237 
238 		/*
239 		 * If the path is too long complain.  This is a possible
240 		 * security issue; given a way to make the path too long
241 		 * the user may execute the wrong program.
242 		 */
243 		if (lp + ln + 2 > sizeof(buf)) {
244 			struct iovec iov[3];
245 
246 			iov[0].iov_base = "execvp: ";
247 			iov[0].iov_len = 8;
248 			iov[1].iov_base = p;
249 			iov[1].iov_len = lp;
250 			iov[2].iov_base = ": path too long\n";
251 			iov[2].iov_len = 16;
252 			(void)writev(STDERR_FILENO, iov, 3);
253 			continue;
254 		}
255 		bcopy(p, buf, lp);
256 		buf[lp] = '/';
257 		bcopy(name, buf + lp + 1, ln);
258 		buf[lp + ln + 1] = '\0';
259 
260 retry:		(void)execve(bp, argv, environ);
261 		switch(errno) {
262 		case E2BIG:
263 			goto done;
264 		case ELOOP:
265 		case ENAMETOOLONG:
266 		case ENOENT:
267 			break;
268 		case ENOEXEC:
269 			for (cnt = 0; argv[cnt]; ++cnt)
270 				;
271 			memp = alloca((cnt + 2) * sizeof(char *));
272 			if (memp == NULL)
273 				goto done;
274 			memp[0] = "sh";
275 			memp[1] = bp;
276 			bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
277 			(void)execve(_PATH_BSHELL, memp, environ);
278 			goto done;
279 		case ENOMEM:
280 			goto done;
281 		case ENOTDIR:
282 			break;
283 		case ETXTBSY:
284 			/*
285 			 * We used to retry here, but sh(1) doesn't.
286 			 */
287 			goto done;
288 		case EACCES:
289 			eacces = 1;
290 			break;
291 		default:
292 			goto done;
293 		}
294 	}
295 	if (eacces)
296 		errno = EACCES;
297 	else if (!errno)
298 		errno = ENOENT;
299 done:
300 	return (-1);
301 }
302