diff options
-rw-r--r-- | libc/unistd/Makefile | 17 | ||||
-rw-r--r-- | libc/unistd/exec.c | 319 | ||||
-rw-r--r-- | libc/unistd/execl.c | 46 | ||||
-rw-r--r-- | libc/unistd/execle.c | 50 | ||||
-rw-r--r-- | libc/unistd/execlp.c | 48 | ||||
-rw-r--r-- | libc/unistd/execv.c | 6 | ||||
-rw-r--r-- | libc/unistd/execvep.c | 46 | ||||
-rw-r--r-- | libc/unistd/execvp.c | 8 |
8 files changed, 332 insertions, 208 deletions
diff --git a/libc/unistd/Makefile b/libc/unistd/Makefile index 2ab770cab..3bd44d778 100644 --- a/libc/unistd/Makefile +++ b/libc/unistd/Makefile @@ -20,13 +20,18 @@ TOPDIR=../../ include $(TOPDIR)Rules.mak DIRS:= -CSRC=execl.c execlp.c execv.c execvep.c execvp.c execle.c \ - sleep.c usleep.c getpass.c sysconf.c getlogin.c \ +MSRC1 = exec.c +MOBJ1 = execl.o execv.o execle.o execlp.o execvp.o + +CSRC= sleep.c usleep.c getpass.c sysconf.c getlogin.c \ fpathconf.c confstr.c pathconf.c swab.c usershell.c \ getsubopt.c + ifeq ($(strip $(UCLIBC_HAS_MMU)),y) - CSRC+=daemon.c + CSRC += daemon.c + MOBJ1 += __exec_alloc.o endif + ifeq ($(strip $(UCLIBC_HAS_GNU_GETOPT)),y) CSRC += getopt.c else @@ -34,7 +39,7 @@ else endif COBJS=$(patsubst %.c,%.o, $(CSRC)) -OBJS=$(COBJS) +OBJS=$(COBJS) $(MOBJ1) all: $(SYSCONF) $(OBJS) $(LIBC) @@ -43,6 +48,10 @@ $(LIBC): ar-target subdirs ar-target: $(OBJS) $(AR) $(ARFLAGS) $(LIBC) $(OBJS) +$(MOBJ1): $(MSRC1) + $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + $(COBJS): %.o : %.c $(CC) $(CFLAGS) -c $< -o $@ $(STRIPTOOL) -x -R .note -R .comment $*.o diff --git a/libc/unistd/exec.c b/libc/unistd/exec.c new file mode 100644 index 000000000..fa1eda2b9 --- /dev/null +++ b/libc/unistd/exec.c @@ -0,0 +1,319 @@ +/* Copyright (C) 2004 Manuel Novoa III + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Jan 1, 2004 + * Initial version of a SUSv3 compliant exec*() functions. + */ + +/* 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? + */ + +#define _GNU_SOURCE +#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> + +extern char *__strchrnul(const char *s, int c); + +/**********************************************************************/ +#ifdef __UCLIBC_HAS_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) alloca((SIZE)) +# define EXEC_FREE(PTR,VAR) ((void)0) + +#else + +/* We do not have an MMU, so using alloca() is not an option. + * Less obviously, using malloc() is not an option either since + * malloc()ed memory can leak in a vfork() and exec*() situation. + * Therefore, we must use mmap() and unmap() directly. + */ + +# define EXEC_ALLOC_SIZE(VAR) size_t VAR; /* Semicolon included! */ +# define EXEC_ALLOC(SIZE,VAR) __exec_alloc((VAR = (SIZE))) +# define EXEC_FREE(PTR,VAR) __exec_free((PTR),(VAR)) + +extern void *__exec_alloc(size_t size); +extern void __exec_free(void *ptr, size_t size); + +#endif +/**********************************************************************/ +#ifdef L___exec_alloc + +#ifndef __UCLIBC_HAS_MMU__ + +void *__exec_alloc(size_t size) +{ + void *p; + + p = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + + return (p != MAP_FAILED) ? p : NULL; +} + +void __exec_free(void *ptr, size_t size) +{ + if (ptr) { + munmap(ptr, size); + } +} + +#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); + + 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; +} + +#endif +/**********************************************************************/ +#ifdef L_execv + +int execv(__const char *path, char *__const argv[]) +{ + return execve(path, argv, __environ); +} + +#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); + + 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; +} + +#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); + + 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; +} + +#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); + CHECK_ENOEXEC: + if (errno == ENOEXEC) { + char **nargv; + EXEC_ALLOC_SIZE(size2) /* Do NOT add a semicolon! */ + size_t n; + /* 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); + 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; + + if ((buf = EXEC_ALLOC(FILENAME_MAX, size)) != NULL) { + 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 != ENOENT) { + path = s; + goto CHECK_ENOEXEC; + } + + NEXT: + if (!*e) { + if (!seen_small) { + goto ALL_TOO_LONG; + } + break; + } + p = e + 1; + } while (1); + } + } + + EXEC_FREE(buf, size); + + return -1; +} + +#endif +/**********************************************************************/ diff --git a/libc/unistd/execl.c b/libc/unistd/execl.c deleted file mode 100644 index b2c6dd5a0..000000000 --- a/libc/unistd/execl.c +++ /dev/null @@ -1,46 +0,0 @@ - -#include <stdlib.h> -#include <unistd.h> -#include <stdarg.h> - -int execl(__const char *path, __const char *arg, ...) -{ - const char *shortargv[16]; - const char **argv; - const char *c; - int i; - va_list args; - - i = 1; - - va_start(args, arg); - - do { - c = va_arg(args, const char *); - - i++; - } while (c); - - va_end(args); - - if (i <= 16) - argv = shortargv; - else { - argv = (const char **) alloca(sizeof(char *) * i); - } - - argv[0] = arg; - i = 1; - - va_start(args, arg); - - do { - argv[i] = va_arg(args, const char *); - } while (argv[i++]); - - va_end(args); - - i = execve(path, (char *const *) argv, __environ); - - return i; -} diff --git a/libc/unistd/execle.c b/libc/unistd/execle.c deleted file mode 100644 index 4003ee6be..000000000 --- a/libc/unistd/execle.c +++ /dev/null @@ -1,50 +0,0 @@ - -#include <stdlib.h> -#include <unistd.h> -#include <stdarg.h> - -extern int execvep(const char *path, char *const argv[], char *const envp[]); - -int execle(const char *file, const char *arg, ...) -{ - const char *shortargv[16]; - const char **argv; - const char *c; - int i; - va_list args; - const char *const *envp; - - i = 1; - - va_start(args, arg); - - do { - c = va_arg(args, const char *); - - i++; - } while (c); - - va_end(args); - - if (i <= 16) - argv = shortargv; - else { - argv = (const char **) alloca(sizeof(char *) * i); - } - - argv[0] = arg; - i = 1; - - va_start(args, arg); - - do { - argv[i] = va_arg(args, const char *); - } while (argv[i++]); - - envp = va_arg (args, const char *const *); - va_end(args); - - i = execvep(file, (char *const *) argv, (char *const *) envp); - - return i; -} diff --git a/libc/unistd/execlp.c b/libc/unistd/execlp.c deleted file mode 100644 index 923a2e22a..000000000 --- a/libc/unistd/execlp.c +++ /dev/null @@ -1,48 +0,0 @@ - -#include <stdlib.h> -#include <unistd.h> -#include <stdarg.h> - -extern int execvep(const char *path, char *__const argv[], char *__const envp[]); - -int execlp(__const char *file, __const char *arg, ...) -{ - const char *shortargv[16]; - const char **argv; - const char *c; - int i; - va_list args; - - i = 1; - - va_start(args, arg); - - do { - c = va_arg(args, const char *); - - i++; - } while (c); - - va_end(args); - - if (i <= 16) - argv = shortargv; - else { - argv = (const char **) alloca(sizeof(char *) * i); - } - - argv[0] = arg; - i = 1; - - va_start(args, arg); - - do { - argv[i] = va_arg(args, const char *); - } while (argv[i++]); - - va_end(args); - - i = execvep(file, (char *const *) argv, __environ); - - return i; -} diff --git a/libc/unistd/execv.c b/libc/unistd/execv.c deleted file mode 100644 index 486f53745..000000000 --- a/libc/unistd/execv.c +++ /dev/null @@ -1,6 +0,0 @@ -#include <unistd.h> - -int execv(__const char *path, char *__const argv[]) -{ - return execve(path, argv, __environ); -} diff --git a/libc/unistd/execvep.c b/libc/unistd/execvep.c deleted file mode 100644 index 6cd8cbaf9..000000000 --- a/libc/unistd/execvep.c +++ /dev/null @@ -1,46 +0,0 @@ -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -int execvep(const char *path, char *__const argv[], char *__const envp[]) -{ - if (!strchr(path, '/')) { - char *p = getenv("PATH"); - - if (!p) - p = "/bin:/usr/bin"; - - for (; p && *p;) { - char partial[FILENAME_MAX]; - char *p2; - - p2 = strchr(p, ':'); - if (p2) { - size_t len = p2 - p; - strncpy(partial, p, len); - partial[len] = 0; - } else { - strcpy(partial, p); - } - - if (strlen(partial)) - strcat(partial, "/"); - strcat(partial, path); - - execve(partial, argv, envp); - - if (errno != ENOENT) - return -1; - - if (p2) { - p = p2 + 1; - } else { - p = 0; - } - } - return -1; - } else - return execve(path, argv, envp); -} diff --git a/libc/unistd/execvp.c b/libc/unistd/execvp.c deleted file mode 100644 index e7e496063..000000000 --- a/libc/unistd/execvp.c +++ /dev/null @@ -1,8 +0,0 @@ -#include <unistd.h> - -extern int execvep(const char *path, char *__const argv[], char *__const envp[]); - -int execvp(__const char *path, char *__const argv[]) -{ - return execvep(path, argv, __environ); -} |