xref: /openbsd/lib/libc/gen/exec.c (revision e512315e)
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.8 1998/08/14 21:39:23 deraadt Exp $";
36 #endif /* LIBC_SCCS and not lint */
37 
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <stdio.h>
45 #include <paths.h>
46 
47 #ifdef __STDC__
48 #include <stdarg.h>
49 #else
50 #include <varargs.h>
51 #endif
52 
53 extern char **environ;
54 
55 static char **
56 buildargv(ap, arg, envpp)
57 	va_list ap;
58 	const char *arg;
59 	char ***envpp;
60 {
61 	register char **argv, **nargv;
62 	register int memsize, off;
63 
64 	argv = NULL;
65 	for (off = memsize = 0;; ++off) {
66 		if (off >= memsize) {
67 			memsize += 50;	/* Starts out at 0. */
68 			memsize *= 2;	/* Ramp up fast. */
69 			nargv = realloc(argv, memsize * sizeof(char *));
70 			if (nargv == NULL) {
71 				if (argv)
72 					free(argv);
73 				return (NULL);
74 			}
75 			argv = nargv;
76 			if (off == 0) {
77 				argv[0] = (char *)arg;
78 				off = 1;
79 			}
80 		}
81 		if (!(argv[off] = va_arg(ap, char *)))
82 			break;
83 	}
84 	/* Get environment pointer if user supposed to provide one. */
85 	if (envpp)
86 		*envpp = va_arg(ap, char **);
87 	return (argv);
88 }
89 
90 int
91 #ifdef __STDC__
92 execl(const char *name, const char *arg, ...)
93 #else
94 execl(name, arg, va_alist)
95 	const char *name;
96 	const char *arg;
97 	va_dcl
98 #endif
99 {
100 	va_list ap;
101 	int sverrno;
102 	char **argv;
103 
104 #ifdef __STDC__
105 	va_start(ap, arg);
106 #else
107 	va_start(ap);
108 #endif
109 	if ((argv = buildargv(ap, arg, NULL)))
110 		(void)execve(name, argv, environ);
111 	va_end(ap);
112 	sverrno = errno;
113 	free(argv);
114 	errno = sverrno;
115 	return (-1);
116 }
117 
118 int
119 #ifdef __STDC__
120 execle(const char *name, const char *arg, ...)
121 #else
122 execle(name, arg, va_alist)
123 	const char *name;
124 	const char *arg;
125 	va_dcl
126 #endif
127 {
128 	va_list ap;
129 	char **argv, **envp;
130 	int i;
131 
132 #ifdef __STDC__
133 	va_start(ap, arg);
134 #else
135 	va_start(ap);
136 #endif
137 	for (i = 1; va_arg(ap, char *) != NULL; i++)
138 		;
139 	va_end(ap);
140 
141 	argv = alloca (i * sizeof (char *));
142 
143 #if __STDC__
144 	va_start(ap, arg);
145 #else
146 	va_start(ap);
147 #endif
148 	argv[0] = (char *) arg;
149 	for (i = 1; (argv[i] = (char *) va_arg(ap, char *)) != NULL; i++)
150 		;
151 	envp = (char **) va_arg(ap, char **);
152 	va_end(ap);
153 
154 	return execve(name, argv, envp);
155 }
156 
157 int
158 #ifdef __STDC__
159 execlp(const char *name, const char *arg, ...)
160 #else
161 execlp(name, arg, va_alist)
162 	const char *name;
163 	const char *arg;
164 	va_dcl
165 #endif
166 {
167 	va_list ap;
168 	int sverrno;
169 	char **argv;
170 
171 #ifdef __STDC__
172 	va_start(ap, arg);
173 #else
174 	va_start(ap);
175 #endif
176 	if ((argv = buildargv(ap, arg, NULL)))
177 		(void)execvp(name, argv);
178 	va_end(ap);
179 	sverrno = errno;
180 	free(argv);
181 	errno = sverrno;
182 	return (-1);
183 }
184 
185 int
186 execv(name, argv)
187 	const char *name;
188 	char * const *argv;
189 {
190 	(void)execve(name, argv, environ);
191 	return (-1);
192 }
193 
194 int
195 execvp(name, argv)
196 	const char *name;
197 	char * const *argv;
198 {
199 	char **memp;
200 	register int cnt, lp, ln;
201 	register char *p;
202 	int eacces = 0, etxtbsy = 0;
203 	char *bp, *cur, *path, buf[MAXPATHLEN];
204 
205 	/*
206 	 * Do not allow null name
207 	 */
208 	if (name == NULL || *name == '\0') {
209 		errno = ENOENT;
210 		return (-1);
211  	}
212 
213 	/* If it's an absolute or relative path name, it's easy. */
214 	if (strchr(name, '/')) {
215 		bp = (char *)name;
216 		cur = path = NULL;
217 		goto retry;
218 	}
219 	bp = buf;
220 
221 	/* Get the path we're searching. */
222 	if (!(path = getenv("PATH")))
223 		path = _PATH_DEFPATH;
224 	cur = path = strdup(path);
225 
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 			(void)write(STDERR_FILENO, "execvp: ", 8);
245 			(void)write(STDERR_FILENO, p, lp);
246 			(void)write(STDERR_FILENO, ": path too long\n", 16);
247 			continue;
248 		}
249 		bcopy(p, buf, lp);
250 		buf[lp] = '/';
251 		bcopy(name, buf + lp + 1, ln);
252 		buf[lp + ln + 1] = '\0';
253 
254 retry:		(void)execve(bp, argv, environ);
255 		switch(errno) {
256 		case EACCES:
257 			eacces = 1;
258 			break;
259 		case ENOENT:
260 			break;
261 		case ENOEXEC:
262 			for (cnt = 0; argv[cnt]; ++cnt)
263 				;
264 			memp = malloc((cnt + 2) * sizeof(char *));
265 			if (memp == NULL)
266 				goto done;
267 			memp[0] = "sh";
268 			memp[1] = bp;
269 			bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
270 			(void)execve(_PATH_BSHELL, memp, environ);
271 			free(memp);
272 			goto done;
273 		case ETXTBSY:
274 			if (etxtbsy < 3)
275 				(void)sleep(++etxtbsy);
276 			goto retry;
277 		default:
278 			goto done;
279 		}
280 	}
281 	if (eacces)
282 		errno = EACCES;
283 	else if (!errno)
284 		errno = ENOENT;
285 done:	if (path)
286 		free(path);
287 	return (-1);
288 }
289