| Elliott Hughes | 1b40aaf | 2016-08-18 10:11:36 -0700 | [diff] [blame] | 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. Neither the name of the University nor the names of its contributors | 
|  | 14 | *    may be used to endorse or promote products derived from this software | 
|  | 15 | *    without specific prior written permission. | 
|  | 16 | * | 
|  | 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
|  | 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | 20 | * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
|  | 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | 27 | * SUCH DAMAGE. | 
|  | 28 | */ | 
|  | 29 |  | 
|  | 30 | #include <sys/types.h> | 
|  | 31 | #include <sys/uio.h> | 
|  | 32 |  | 
|  | 33 | #include <errno.h> | 
|  | 34 | #include <limits.h> | 
|  | 35 | #include <paths.h> | 
|  | 36 | #include <stdarg.h> | 
|  | 37 | #include <stdio.h> | 
|  | 38 | #include <stdlib.h> | 
|  | 39 | #include <string.h> | 
|  | 40 | #include <unistd.h> | 
|  | 41 |  | 
|  | 42 | extern "C" char** environ; | 
|  | 43 |  | 
|  | 44 | enum ExecVariant { kIsExecL, kIsExecLE, kIsExecLP }; | 
|  | 45 |  | 
|  | 46 | static int __execl(const char* name, const char* argv0, ExecVariant variant, va_list ap) { | 
|  | 47 | // Count the arguments. | 
|  | 48 | va_list count_ap; | 
|  | 49 | va_copy(count_ap, ap); | 
|  | 50 | size_t n = 1; | 
|  | 51 | while (va_arg(count_ap, char*) != nullptr) { | 
|  | 52 | ++n; | 
|  | 53 | } | 
|  | 54 | va_end(count_ap); | 
|  | 55 |  | 
|  | 56 | // Construct the new argv. | 
|  | 57 | char* argv[n + 1]; | 
|  | 58 | argv[0] = const_cast<char*>(argv0); | 
|  | 59 | n = 1; | 
|  | 60 | while ((argv[n] = va_arg(ap, char*)) != nullptr) { | 
|  | 61 | ++n; | 
|  | 62 | } | 
|  | 63 |  | 
|  | 64 | // Collect the argp too. | 
|  | 65 | char** argp = (variant == kIsExecLE) ? va_arg(ap, char**) : environ; | 
|  | 66 |  | 
|  | 67 | va_end(ap); | 
|  | 68 |  | 
|  | 69 | return (variant == kIsExecLP) ? execvp(name, argv) : execve(name, argv, argp); | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | int execl(const char* name, const char* arg, ...) { | 
|  | 73 | va_list ap; | 
|  | 74 | va_start(ap, arg); | 
|  | 75 | return __execl(name, arg, kIsExecL, ap); | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | int execle(const char* name, const char* arg, ...) { | 
|  | 79 | va_list ap; | 
|  | 80 | va_start(ap, arg); | 
|  | 81 | return __execl(name, arg, kIsExecLE, ap); | 
|  | 82 | } | 
|  | 83 |  | 
|  | 84 | int execlp(const char* name, const char* arg, ...) { | 
|  | 85 | va_list ap; | 
|  | 86 | va_start(ap, arg); | 
|  | 87 | return __execl(name, arg, kIsExecLP, ap); | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | int execv(const char* name, char* const* argv) { | 
|  | 91 | return execve(name, argv, environ); | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | int execvp(const char* name, char* const* argv) { | 
|  | 95 | return execvpe(name, argv, environ); | 
|  | 96 | } | 
|  | 97 |  | 
| Elliott Hughes | 3c11590 | 2016-08-24 19:27:04 -0700 | [diff] [blame] | 98 | static int __exec_as_script(const char* buf, char* const* argv, char* const* envp) { | 
|  | 99 | size_t arg_count = 1; | 
| Elliott Hughes | 1b40aaf | 2016-08-18 10:11:36 -0700 | [diff] [blame] | 100 | while (argv[arg_count] != nullptr) ++arg_count; | 
|  | 101 |  | 
| Elliott Hughes | 3c11590 | 2016-08-24 19:27:04 -0700 | [diff] [blame] | 102 | const char* script_argv[arg_count + 2]; | 
|  | 103 | script_argv[0] = "sh"; | 
| Elliott Hughes | 1b40aaf | 2016-08-18 10:11:36 -0700 | [diff] [blame] | 104 | script_argv[1] = buf; | 
| Elliott Hughes | 3c11590 | 2016-08-24 19:27:04 -0700 | [diff] [blame] | 105 | memcpy(script_argv + 2, argv + 1, arg_count * sizeof(char*)); | 
|  | 106 | return execve(_PATH_BSHELL, const_cast<char**>(script_argv), envp); | 
| Elliott Hughes | 1b40aaf | 2016-08-18 10:11:36 -0700 | [diff] [blame] | 107 | } | 
|  | 108 |  | 
|  | 109 | int execvpe(const char* name, char* const* argv, char* const* envp) { | 
|  | 110 | // Do not allow null name. | 
|  | 111 | if (name == nullptr || *name == '\0') { | 
|  | 112 | errno = ENOENT; | 
|  | 113 | return -1; | 
|  | 114 | } | 
|  | 115 |  | 
|  | 116 | // If it's an absolute or relative path name, it's easy. | 
| Elliott Hughes | 3c11590 | 2016-08-24 19:27:04 -0700 | [diff] [blame] | 117 | if (strchr(name, '/') && execve(name, argv, envp) == -1) { | 
| Elliott Hughes | 6361506 | 2016-08-25 17:40:27 -0700 | [diff] [blame] | 118 | if (errno == ENOEXEC) return __exec_as_script(name, argv, envp); | 
|  | 119 | return -1; | 
| Elliott Hughes | 3c11590 | 2016-08-24 19:27:04 -0700 | [diff] [blame] | 120 | } | 
| Elliott Hughes | 1b40aaf | 2016-08-18 10:11:36 -0700 | [diff] [blame] | 121 |  | 
|  | 122 | // Get the path we're searching. | 
|  | 123 | const char* path = getenv("PATH"); | 
|  | 124 | if (path == nullptr) path = _PATH_DEFPATH; | 
|  | 125 |  | 
|  | 126 | // Make a writable copy. | 
|  | 127 | size_t len = strlen(path) + 1; | 
|  | 128 | char writable_path[len]; | 
|  | 129 | memcpy(writable_path, path, len); | 
|  | 130 |  | 
|  | 131 | bool saw_EACCES = false; | 
|  | 132 |  | 
|  | 133 | // Try each element of $PATH in turn... | 
|  | 134 | char* strsep_buf = writable_path; | 
|  | 135 | const char* dir; | 
|  | 136 | while ((dir = strsep(&strsep_buf, ":"))) { | 
|  | 137 | // It's a shell path: double, leading and trailing colons | 
|  | 138 | // mean the current directory. | 
|  | 139 | if (*dir == '\0') dir = const_cast<char*>("."); | 
|  | 140 |  | 
|  | 141 | size_t dir_len = strlen(dir); | 
|  | 142 | size_t name_len = strlen(name); | 
|  | 143 |  | 
|  | 144 | char buf[dir_len + 1 + name_len + 1]; | 
|  | 145 | mempcpy(mempcpy(mempcpy(buf, dir, dir_len), "/", 1), name, name_len + 1); | 
|  | 146 |  | 
|  | 147 | execve(buf, argv, envp); | 
|  | 148 | switch (errno) { | 
|  | 149 | case EISDIR: | 
|  | 150 | case ELOOP: | 
|  | 151 | case ENAMETOOLONG: | 
|  | 152 | case ENOENT: | 
|  | 153 | case ENOTDIR: | 
|  | 154 | break; | 
|  | 155 | case ENOEXEC: | 
|  | 156 | return __exec_as_script(buf, argv, envp); | 
|  | 157 | case EACCES: | 
|  | 158 | saw_EACCES = true; | 
|  | 159 | break; | 
|  | 160 | default: | 
|  | 161 | return -1; | 
|  | 162 | } | 
|  | 163 | } | 
|  | 164 | if (saw_EACCES) errno = EACCES; | 
|  | 165 | return -1; | 
|  | 166 | } |