xref: /original-bsd/lib/libc/gen/exec.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #if defined(LIBC_SCCS) && !defined(lint)
9 static char sccsid[] = "@(#)exec.c	8.1 (Berkeley) 06/04/93";
10 #endif /* LIBC_SCCS and not lint */
11 
12 #include <sys/param.h>
13 #include <sys/types.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <paths.h>
20 
21 #if __STDC__
22 #include <stdarg.h>
23 #else
24 #include <varargs.h>
25 #endif
26 
27 extern char **environ;
28 
29 static char **
30 buildargv(ap, arg, envpp)
31 	va_list ap;
32 	const char *arg;
33 	char ***envpp;
34 {
35 	static int memsize;
36 	static char **argv;
37 	register int off;
38 
39 	argv = NULL;
40 	for (off = 0;; ++off) {
41 		if (off >= memsize) {
42 			memsize += 50;	/* Starts out at 0. */
43 			memsize *= 2;	/* Ramp up fast. */
44 			if (!(argv = realloc(argv, memsize * sizeof(char *)))) {
45 				memsize = 0;
46 				return (NULL);
47 			}
48 			if (off == 0) {
49 				argv[0] = (char *)arg;
50 				off = 1;
51 			}
52 		}
53 		if (!(argv[off] = va_arg(ap, char *)))
54 			break;
55 	}
56 	/* Get environment pointer if user supposed to provide one. */
57 	if (envpp)
58 		*envpp = va_arg(ap, char **);
59 	return (argv);
60 }
61 
62 int
63 #if __STDC__
64 execl(const char *name, const char *arg, ...)
65 #else
66 execl(name, arg, va_alist)
67 	const char *name;
68 	const char *arg;
69 	va_dcl
70 #endif
71 {
72 	va_list ap;
73 	int sverrno;
74 	char **argv;
75 
76 #if __STDC__
77 	va_start(ap, arg);
78 #else
79 	va_start(ap);
80 #endif
81 	if (argv = buildargv(ap, arg, NULL))
82 		(void)execve(name, argv, environ);
83 	va_end(ap);
84 	sverrno = errno;
85 	free(argv);
86 	errno = sverrno;
87 	return (-1);
88 }
89 
90 int
91 #if __STDC__
92 execle(const char *name, const char *arg, ...)
93 #else
94 execle(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, **envp;
103 
104 #if __STDC__
105 	va_start(ap, arg);
106 #else
107 	va_start(ap);
108 #endif
109 	if (argv = buildargv(ap, arg, &envp))
110 		(void)execve(name, argv, envp);
111 	va_end(ap);
112 	sverrno = errno;
113 	free(argv);
114 	errno = sverrno;
115 	return (-1);
116 }
117 
118 int
119 #if __STDC__
120 execlp(const char *name, const char *arg, ...)
121 #else
122 execlp(name, arg, va_alist)
123 	const char *name;
124 	const char *arg;
125 	va_dcl
126 #endif
127 {
128 	va_list ap;
129 	int sverrno;
130 	char **argv;
131 
132 #if __STDC__
133 	va_start(ap, arg);
134 #else
135 	va_start(ap);
136 #endif
137 	if (argv = buildargv(ap, arg, NULL))
138 		(void)execvp(name, argv);
139 	va_end(ap);
140 	sverrno = errno;
141 	free(argv);
142 	errno = sverrno;
143 	return (-1);
144 }
145 
146 int
147 execv(name, argv)
148 	const char *name;
149 	char * const *argv;
150 {
151 	(void)execve(name, argv, environ);
152 	return (-1);
153 }
154 
155 int
156 execvp(name, argv)
157 	const char *name;
158 	char * const *argv;
159 {
160 	static int memsize;
161 	static char **memp;
162 	register int cnt, lp, ln;
163 	register char *p;
164 	int eacces, etxtbsy;
165 	char *bp, *cur, *path, buf[MAXPATHLEN];
166 
167 	/* If it's an absolute or relative path name, it's easy. */
168 	if (index(name, '/')) {
169 		bp = (char *)name;
170 		cur = path = NULL;
171 		goto retry;
172 	}
173 	bp = buf;
174 
175 	/* Get the path we're searching. */
176 	if (!(path = getenv("PATH")))
177 		path = _PATH_DEFPATH;
178 	cur = path = strdup(path);
179 
180 	eacces = etxtbsy = 0;
181 	while (p = strsep(&cur, ":")) {
182 		/*
183 		 * It's a SHELL path -- double, leading and trailing colons
184 		 * mean the current directory.
185 		 */
186 		if (!*p) {
187 			p = ".";
188 			lp = 1;
189 		} else
190 			lp = strlen(p);
191 		ln = strlen(name);
192 
193 		/*
194 		 * If the path is too long complain.  This is a possible
195 		 * security issue; given a way to make the path too long
196 		 * the user may execute the wrong program.
197 		 */
198 		if (lp + ln + 2 > sizeof(buf)) {
199 			(void)write(STDERR_FILENO, "execvp: ", 8);
200 			(void)write(STDERR_FILENO, p, lp);
201 			(void)write(STDERR_FILENO, ": path too long\n", 16);
202 			continue;
203 		}
204 		bcopy(p, buf, lp);
205 		buf[lp] = '/';
206 		bcopy(name, buf + lp + 1, ln);
207 		buf[lp + ln + 1] = '\0';
208 
209 retry:		(void)execve(bp, argv, environ);
210 		switch(errno) {
211 		case EACCES:
212 			eacces = 1;
213 			break;
214 		case ENOENT:
215 			break;
216 		case ENOEXEC:
217 			for (cnt = 0; argv[cnt]; ++cnt);
218 			if ((cnt + 2) * sizeof(char *) > memsize) {
219 				memsize = (cnt + 2) * sizeof(char *);
220 				if ((memp = realloc(memp, memsize)) == NULL) {
221 					memsize = 0;
222 					goto done;
223 				}
224 			}
225 			memp[0] = "sh";
226 			memp[1] = bp;
227 			bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
228 			(void)execve(_PATH_BSHELL, memp, environ);
229 			goto done;
230 		case ETXTBSY:
231 			if (etxtbsy < 3)
232 				(void)sleep(++etxtbsy);
233 			goto retry;
234 		default:
235 			goto done;
236 		}
237 	}
238 	if (eacces)
239 		errno = EACCES;
240 	else if (!errno)
241 		errno = ENOENT;
242 done:	if (path)
243 		free(path);
244 	return (-1);
245 }
246