| /*  Copyright (C) 2004     Manuel Novoa III | 
 |  * | 
 |  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. | 
 |  */ | 
 |  | 
 | /* Jan 1, 2004 | 
 |  *   Initial version of a SUSv3 compliant exec*() functions. | 
 |  * Feb 17, 2004 | 
 |  *   Sigh... Fall back to alloca() if munmap() is broken on uClinux. | 
 |  */ | 
 |  | 
 | /* NOTE: Strictly speaking, there could be problems from accessing | 
 |  * __environ in multithreaded programs.  The only way around this | 
 |  * that I see is to essentially lock __environ access (modifying | 
 |  * the setenv code), make a copy of the environment table (just the | 
 |  * pointers since the strings themselves are never freed), and then | 
 |  * unlock prior to the execve call.  If that fails, then we'd need | 
 |  * to free the storage allocated for the copy.  Better ideas anyone? | 
 |  */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <errno.h> | 
 | #include <stdarg.h> | 
 | #include <limits.h> | 
 | #include <unistd.h> | 
 | #include <sys/mman.h> | 
 |  | 
 |  | 
 |  | 
 | /**********************************************************************/ | 
 | #define EXEC_FUNC_COMMON 0 | 
 | #define EXEC_FUNC_EXECVP 1 | 
 | #if defined(__ARCH_USE_MMU__) | 
 |  | 
 | /* We have an MMU, so use alloca() to grab space for buffers and arg lists. */ | 
 |  | 
 | # define EXEC_ALLOC_SIZE(VAR)      /* nothing to do */ | 
 | # define EXEC_ALLOC(SIZE,VAR,FUNC) alloca((SIZE)) | 
 | # define EXEC_FREE(PTR,VAR)        ((void)0) | 
 |  | 
 | #else | 
 |  | 
 | /* We do not have an MMU, so using alloca() is not an option (as this will | 
 |  * easily overflow the stack in most setups).  Less obviously, using malloc() | 
 |  * is not an option either since malloc()ed memory can leak in from a vfork()ed | 
 |  * child into the parent as no one is around after the child calls exec*() to | 
 |  * free() the memory.  Therefore, we must use mmap() and unmap() directly, | 
 |  * caching the result as we go.  This way we minimize the leak by reusing the | 
 |  * memory with every call to an exec*(). | 
 |  * | 
 |  * To prevent recursive use of the same cached memory, we have to give execvp() | 
 |  * its own cache.  Here are the nested exec calls (a/-: alloc/no-alloc): | 
 |  *  execve(-) -> calls straight to kernel | 
 |  *  execl(a)  -> execve(-) | 
 |  *  execlp(a) -> execvp(a)	!! recursive usage !! | 
 |  *  execle(a) -> execve(-) | 
 |  *  execv(-)  -> execve(-) | 
 |  *  execvp(a) -> execve(-) | 
 |  */ | 
 |  | 
 | # define EXEC_ALLOC_SIZE(VAR)      /* nothing to do */ | 
 | # define EXEC_ALLOC(SIZE,VAR,FUNC) __exec_alloc((SIZE), FUNC) | 
 | # define EXEC_FREE(PTR,VAR)        ((void)0) | 
 |  | 
 | extern void *__exec_alloc(size_t size, int func) attribute_hidden; | 
 |  | 
 | # ifdef L___exec_alloc | 
 |  | 
 | void attribute_hidden *__exec_alloc(size_t size, int func) | 
 | { | 
 | 	static void *common_cache, *execvp_cache; | 
 | 	static size_t common_size, execvp_size; | 
 |  | 
 | 	void **cache = (func ? &execvp_cache : &common_cache); | 
 | 	size_t *cache_size = (func ? &execvp_size : &common_size); | 
 |  | 
 | 	if (*cache_size >= size) | 
 | 		return *cache; | 
 | 	else if (*cache) | 
 | 		munmap(*cache, *cache_size); | 
 |  | 
 | 	*cache_size = size; | 
 | 	return *cache = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); | 
 |  | 
 | 	/* We don't actually handle OOM in the exec funcs ... | 
 | 	if (*cache != MAP_FAILED) | 
 | 		return *cache; | 
 | 	else | 
 | 		return (*cache = NULL); | 
 | 	*/ | 
 | } | 
 |  | 
 | # endif | 
 |  | 
 | #endif | 
 | /**********************************************************************/ | 
 | #ifdef L_execl | 
 |  | 
 | int execl(const char *path, const char *arg, ...) | 
 | { | 
 | 	EXEC_ALLOC_SIZE(size)		/* Do NOT add a semicolon! */ | 
 | 	int n; | 
 | 	char **argv; | 
 | 	char **p; | 
 | 	va_list args; | 
 |  | 
 | 	n = 0; | 
 | 	va_start(args, arg); | 
 | 	do { | 
 | 		++n; | 
 | 	} while (va_arg(args, char *)); | 
 | 	va_end(args); | 
 |  | 
 | 	p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON); | 
 |  | 
 | 	p[0] = (char *)arg; | 
 |  | 
 | 	va_start(args, arg); | 
 | 	do { | 
 | 		*++p = va_arg(args, char *); | 
 | 	} while (--n); | 
 | 	va_end(args); | 
 |  | 
 | 	n = execve(path, (char *const *) argv, __environ); | 
 |  | 
 | 	EXEC_FREE(argv, size); | 
 |  | 
 | 	return n; | 
 | } | 
 | libc_hidden_def(execl) | 
 |  | 
 | #endif | 
 | /**********************************************************************/ | 
 | #ifdef L_execv | 
 |  | 
 | int execv(__const char *path, char *__const argv[]) | 
 | { | 
 | 	return execve(path, argv, __environ); | 
 | } | 
 | libc_hidden_def(execv) | 
 |  | 
 | #endif | 
 | /**********************************************************************/ | 
 | #ifdef L_execle | 
 |  | 
 | int execle(const char *path, const char *arg, ...) | 
 | { | 
 | 	EXEC_ALLOC_SIZE(size)		/* Do NOT add a semicolon! */ | 
 | 	int n; | 
 | 	char **argv; | 
 | 	char **p; | 
 | 	char *const *envp; | 
 | 	va_list args; | 
 |  | 
 | 	n = 0; | 
 | 	va_start(args, arg); | 
 | 	do { | 
 | 		++n; | 
 | 	} while (va_arg(args, char *)); | 
 | 	envp = va_arg(args, char *const *);	/* Varies from execl and execlp. */ | 
 | 	va_end(args); | 
 |  | 
 | 	p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON); | 
 |  | 
 | 	p[0] = (char *)arg; | 
 |  | 
 | 	va_start(args, arg); | 
 | 	do { | 
 | 		*++p = va_arg(args, char *); | 
 | 	} while (--n); | 
 | 	va_end(args); | 
 |  | 
 | 	n = execve(path, (char *const *) argv, envp); | 
 |  | 
 | 	EXEC_FREE(argv, size); | 
 |  | 
 | 	return n; | 
 | } | 
 | libc_hidden_def(execle) | 
 |  | 
 | #endif | 
 | /**********************************************************************/ | 
 | #ifdef L_execlp | 
 |  | 
 | int execlp(const char *file, const char *arg, ...) | 
 | { | 
 | 	EXEC_ALLOC_SIZE(size)		/* Do NOT add a semicolon! */ | 
 | 	int n; | 
 | 	char **argv; | 
 | 	char **p; | 
 | 	va_list args; | 
 |  | 
 | 	n = 0; | 
 | 	va_start(args, arg); | 
 | 	do { | 
 | 		++n; | 
 | 	} while (va_arg(args, char *)); | 
 | 	va_end(args); | 
 |  | 
 | 	p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON); | 
 |  | 
 | 	p[0] = (char *)arg; | 
 |  | 
 | 	va_start(args, arg); | 
 | 	do { | 
 | 		*++p = va_arg(args, char *); | 
 | 	} while (--n); | 
 | 	va_end(args); | 
 |  | 
 | 	n = execvp(file, (char *const *) argv); | 
 |  | 
 | 	EXEC_FREE(argv, size); | 
 |  | 
 | 	return n; | 
 | } | 
 | libc_hidden_def(execlp) | 
 |  | 
 | #endif | 
 | /**********************************************************************/ | 
 | #ifdef L_execvp | 
 |  | 
 |  | 
 | /* Use a default path that matches glibc behavior, since SUSv3 says | 
 |  * this is implementation-defined.  The default is current working dir, | 
 |  * /bin, and then /usr/bin. */ | 
 | static const char default_path[] = ":/bin:/usr/bin"; | 
 |  | 
 | int execvp(const char *path, char *const argv[]) | 
 | { | 
 | 	char *buf = NULL; | 
 | 	char *p; | 
 | 	char *e; | 
 | 	char *s0; | 
 | 	char *s; | 
 | 	EXEC_ALLOC_SIZE(size = 0)	/* Do NOT add a semicolon! */ | 
 | 	size_t len; | 
 | 	size_t plen; | 
 |  | 
 | 	if (!path || !*path) {		/* Comply with SUSv3. */ | 
 | 	BAD: | 
 | 		__set_errno(ENOENT); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	if (strchr(path, '/')) { | 
 | 		execve(path, argv, __environ); | 
 | 		if (errno == ENOEXEC) { | 
 | 			char **nargv; | 
 | 			EXEC_ALLOC_SIZE(size2) /* Do NOT add a semicolon! */ | 
 | 			size_t n; | 
 | 	RUN_BIN_SH: | 
 | 			/* Need the dimension - 1.  We omit counting the trailing | 
 | 			 * NULL but we actually omit the first entry. */ | 
 | 			for (n=0 ; argv[n] ; n++) {} | 
 | 			nargv = (char **) EXEC_ALLOC((n+2) * sizeof(char *), size2, EXEC_FUNC_EXECVP); | 
 | 			nargv[0] = argv[0]; | 
 | 			nargv[1] = (char *)path; | 
 | 			memcpy(nargv+2, argv+1, n*sizeof(char *)); | 
 | 			execve("/bin/sh", nargv, __environ); | 
 | 			EXEC_FREE(nargv, size2); | 
 | 		} | 
 | 	} else { | 
 | 		if ((p = getenv("PATH")) != NULL) { | 
 | 			if (!*p) { | 
 | 				goto BAD; | 
 | 			} | 
 | 		} else { | 
 | 			p = (char *) default_path; | 
 | 		} | 
 |  | 
 | 		plen = strlen(path); | 
 | 		if (plen > (FILENAME_MAX - 1)) { | 
 | 		ALL_TOO_LONG: | 
 | 			__set_errno(ENAMETOOLONG); | 
 | 			return -1; | 
 | 		} | 
 | 		len = (FILENAME_MAX - 1) - plen; | 
 |  | 
 | 		buf = EXEC_ALLOC(FILENAME_MAX, size, EXEC_FUNC_EXECVP); | 
 | 		{ | 
 | 			int seen_small = 0; | 
 | 			s0 = buf + len; | 
 | 			memcpy(s0, path, plen+1); | 
 |  | 
 | 			do { | 
 | 				s = s0; | 
 | 				e = strchrnul(p, ':'); | 
 | 				if (e > p) { | 
 | 					plen = e - p; | 
 | 					if (e[-1] != '/') { | 
 | 						++plen; | 
 | 					} | 
 | 					if (plen > len) { | 
 | 						goto NEXT; | 
 | 					} | 
 | 					s -= plen; | 
 | 					memcpy(s, p, plen); | 
 | 					s[plen-1] = '/'; | 
 | 				} | 
 |  | 
 | 				execve(s, argv, __environ); | 
 |  | 
 | 				seen_small = 1; | 
 |  | 
 | 				if (errno == ENOEXEC) { | 
 | 					path = s; | 
 | 					goto RUN_BIN_SH; | 
 | 				} | 
 |  | 
 | 			NEXT: | 
 | 				if (!*e) { | 
 | 					if (!seen_small) { | 
 | 						goto ALL_TOO_LONG; | 
 | 					} | 
 | 					break; | 
 | 				} | 
 | 				p = e + 1; | 
 | 			} while (1); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	EXEC_FREE(buf, size); | 
 |  | 
 | 	return -1; | 
 | } | 
 | libc_hidden_def(execvp) | 
 |  | 
 | #endif | 
 | /**********************************************************************/ |