OSDN Git Service

Last portion of libc_hidden_proto removal.
[uclinux-h8/uClibc.git] / libc / unistd / exec.c
1 /*  Copyright (C) 2004     Manuel Novoa III
2  *
3  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
4  */
5
6 /* Jan 1, 2004
7  *   Initial version of a SUSv3 compliant exec*() functions.
8  * Feb 17, 2004
9  *   Sigh... Fall back to alloca() if munmap() is broken on uClinux.
10  */
11
12 /* NOTE: Strictly speaking, there could be problems from accessing
13  * __environ in multithreaded programs.  The only way around this
14  * that I see is to essentially lock __environ access (modifying
15  * the setenv code), make a copy of the environment table (just the
16  * pointers since the strings themselves are never freed), and then
17  * unlock prior to the execve call.  If that fails, then we'd need
18  * to free the storage allocated for the copy.  Better ideas anyone?
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <limits.h>
27 #include <unistd.h>
28 #include <sys/mman.h>
29
30 /* libc_hidden_proto(execl) */
31 /* libc_hidden_proto(execle) */
32 /* libc_hidden_proto(execlp) */
33 /* libc_hidden_proto(execv) */
34 /* libc_hidden_proto(execvp) */
35
36 /* Experimentally off - libc_hidden_proto(memcpy) */
37 /* Experimentally off - libc_hidden_proto(strchr) */
38 /* Experimentally off - libc_hidden_proto(strlen) */
39 /* libc_hidden_proto(execve) */
40 /* libc_hidden_proto(mmap) */
41 /* libc_hidden_proto(munmap) */
42 /* libc_hidden_proto(getenv) */
43
44 /**********************************************************************/
45 #define EXEC_FUNC_COMMON 0
46 #define EXEC_FUNC_EXECVP 1
47 #if defined(__ARCH_USE_MMU__)
48
49 /* We have an MMU, so use alloca() to grab space for buffers and arg lists. */
50
51 # define EXEC_ALLOC_SIZE(VAR)      /* nothing to do */
52 # define EXEC_ALLOC(SIZE,VAR,FUNC) alloca((SIZE))
53 # define EXEC_FREE(PTR,VAR)        ((void)0)
54
55 #else
56
57 /* We do not have an MMU, so using alloca() is not an option (as this will
58  * easily overflow the stack in most setups).  Less obviously, using malloc()
59  * is not an option either since malloc()ed memory can leak in from a vfork()ed
60  * child into the parent as no one is around after the child calls exec*() to
61  * free() the memory.  Therefore, we must use mmap() and unmap() directly,
62  * caching the result as we go.  This way we minimize the leak by reusing the
63  * memory with every call to an exec*().
64  *
65  * To prevent recursive use of the same cached memory, we have to give execvp()
66  * its own cache.  Here are the nested exec calls (a/-: alloc/no-alloc):
67  *  execve(-) -> calls straight to kernel
68  *  execl(a)  -> execve(-)
69  *  execlp(a) -> execvp(a)      !! recursive usage !!
70  *  execle(a) -> execve(-)
71  *  execv(-)  -> execve(-)
72  *  execvp(a) -> execve(-)
73  */
74
75 # define EXEC_ALLOC_SIZE(VAR)      /* nothing to do */
76 # define EXEC_ALLOC(SIZE,VAR,FUNC) __exec_alloc((SIZE), FUNC)
77 # define EXEC_FREE(PTR,VAR)        ((void)0)
78
79 extern void *__exec_alloc(size_t size, int func) attribute_hidden;
80
81 # ifdef L___exec_alloc
82
83 void attribute_hidden *__exec_alloc(size_t size, int func)
84 {
85         static void *common_cache, *execvp_cache;
86         static size_t common_size, execvp_size;
87
88         void **cache = (func ? &execvp_cache : &common_cache);
89         size_t *cache_size = (func ? &execvp_size : &common_size);
90
91         if (*cache_size >= size)
92                 return *cache;
93         else if (*cache)
94                 munmap(*cache, *cache_size);
95
96         *cache_size = size;
97         return *cache = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
98
99         /* We don't actually handle OOM in the exec funcs ...
100         if (*cache != MAP_FAILED)
101                 return *cache;
102         else
103                 return (*cache = NULL);
104         */
105 }
106
107 # endif
108
109 #endif
110 /**********************************************************************/
111 #ifdef L_execl
112
113 int execl(const char *path, const char *arg, ...)
114 {
115         EXEC_ALLOC_SIZE(size)           /* Do NOT add a semicolon! */
116         int n;
117         char **argv;
118         char **p;
119         va_list args;
120
121         n = 0;
122         va_start(args, arg);
123         do {
124                 ++n;
125         } while (va_arg(args, char *));
126         va_end(args);
127
128         p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON);
129
130         p[0] = (char *)arg;
131
132         va_start(args, arg);
133         do {
134                 *++p = va_arg(args, char *);
135         } while (--n);
136         va_end(args);
137
138         n = execve(path, (char *const *) argv, __environ);
139
140         EXEC_FREE(argv, size);
141
142         return n;
143 }
144 libc_hidden_def(execl)
145
146 #endif
147 /**********************************************************************/
148 #ifdef L_execv
149
150 int execv(__const char *path, char *__const argv[])
151 {
152         return execve(path, argv, __environ);
153 }
154 libc_hidden_def(execv)
155
156 #endif
157 /**********************************************************************/
158 #ifdef L_execle
159
160 int execle(const char *path, const char *arg, ...)
161 {
162         EXEC_ALLOC_SIZE(size)           /* Do NOT add a semicolon! */
163         int n;
164         char **argv;
165         char **p;
166         char *const *envp;
167         va_list args;
168
169         n = 0;
170         va_start(args, arg);
171         do {
172                 ++n;
173         } while (va_arg(args, char *));
174         envp = va_arg(args, char *const *);     /* Varies from execl and execlp. */
175         va_end(args);
176
177         p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON);
178
179         p[0] = (char *)arg;
180
181         va_start(args, arg);
182         do {
183                 *++p = va_arg(args, char *);
184         } while (--n);
185         va_end(args);
186
187         n = execve(path, (char *const *) argv, envp);
188
189         EXEC_FREE(argv, size);
190
191         return n;
192 }
193 libc_hidden_def(execle)
194
195 #endif
196 /**********************************************************************/
197 #ifdef L_execlp
198
199 int execlp(const char *file, const char *arg, ...)
200 {
201         EXEC_ALLOC_SIZE(size)           /* Do NOT add a semicolon! */
202         int n;
203         char **argv;
204         char **p;
205         va_list args;
206
207         n = 0;
208         va_start(args, arg);
209         do {
210                 ++n;
211         } while (va_arg(args, char *));
212         va_end(args);
213
214         p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON);
215
216         p[0] = (char *)arg;
217
218         va_start(args, arg);
219         do {
220                 *++p = va_arg(args, char *);
221         } while (--n);
222         va_end(args);
223
224         n = execvp(file, (char *const *) argv);
225
226         EXEC_FREE(argv, size);
227
228         return n;
229 }
230 libc_hidden_def(execlp)
231
232 #endif
233 /**********************************************************************/
234 #ifdef L_execvp
235
236 /* Experimentally off - libc_hidden_proto(strchrnul) */
237
238 /* Use a default path that matches glibc behavior, since SUSv3 says
239  * this is implementation-defined.  The default is current working dir,
240  * /bin, and then /usr/bin. */
241 static const char default_path[] = ":/bin:/usr/bin";
242
243 int execvp(const char *path, char *const argv[])
244 {
245         char *buf = NULL;
246         char *p;
247         char *e;
248         char *s0;
249         char *s;
250         EXEC_ALLOC_SIZE(size = 0)       /* Do NOT add a semicolon! */
251         size_t len;
252         size_t plen;
253
254         if (!path || !*path) {          /* Comply with SUSv3. */
255         BAD:
256                 __set_errno(ENOENT);
257                 return -1;
258         }
259
260         if (strchr(path, '/')) {
261                 execve(path, argv, __environ);
262                 if (errno == ENOEXEC) {
263                         char **nargv;
264                         EXEC_ALLOC_SIZE(size2) /* Do NOT add a semicolon! */
265                         size_t n;
266         RUN_BIN_SH:
267                         /* Need the dimension - 1.  We omit counting the trailing
268                          * NULL but we actually omit the first entry. */
269                         for (n=0 ; argv[n] ; n++) {}
270                         nargv = (char **) EXEC_ALLOC((n+2) * sizeof(char *), size2, EXEC_FUNC_EXECVP);
271                         nargv[0] = argv[0];
272                         nargv[1] = (char *)path;
273                         memcpy(nargv+2, argv+1, n*sizeof(char *));
274                         execve("/bin/sh", nargv, __environ);
275                         EXEC_FREE(nargv, size2);
276                 }
277         } else {
278                 if ((p = getenv("PATH")) != NULL) {
279                         if (!*p) {
280                                 goto BAD;
281                         }
282                 } else {
283                         p = (char *) default_path;
284                 }
285
286                 plen = strlen(path);
287                 if (plen > (FILENAME_MAX - 1)) {
288                 ALL_TOO_LONG:
289                         __set_errno(ENAMETOOLONG);
290                         return -1;
291                 }
292                 len = (FILENAME_MAX - 1) - plen;
293
294                 buf = EXEC_ALLOC(FILENAME_MAX, size, EXEC_FUNC_EXECVP);
295                 {
296                         int seen_small = 0;
297                         s0 = buf + len;
298                         memcpy(s0, path, plen+1);
299
300                         do {
301                                 s = s0;
302                                 e = strchrnul(p, ':');
303                                 if (e > p) {
304                                         plen = e - p;
305                                         if (e[-1] != '/') {
306                                                 ++plen;
307                                         }
308                                         if (plen > len) {
309                                                 goto NEXT;
310                                         }
311                                         s -= plen;
312                                         memcpy(s, p, plen);
313                                         s[plen-1] = '/';
314                                 }
315
316                                 execve(s, argv, __environ);
317
318                                 seen_small = 1;
319
320                                 if (errno == ENOEXEC) {
321                                         path = s;
322                                         goto RUN_BIN_SH;
323                                 }
324
325                         NEXT:
326                                 if (!*e) {
327                                         if (!seen_small) {
328                                                 goto ALL_TOO_LONG;
329                                         }
330                                         break;
331                                 }
332                                 p = e + 1;
333                         } while (1);
334                 }
335         }
336
337         EXEC_FREE(buf, size);
338
339         return -1;
340 }
341 libc_hidden_def(execvp)
342
343 #endif
344 /**********************************************************************/