1 | /* Copyright (C) 1991-2016 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <http://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <alloca.h> |
19 | #include <unistd.h> |
20 | #include <stdarg.h> |
21 | #include <stdbool.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <errno.h> |
25 | #include <paths.h> |
26 | |
27 | |
28 | /* The file is accessible but it is not an executable file. Invoke |
29 | the shell to interpret it as a script. */ |
30 | static void |
31 | internal_function |
32 | scripts_argv (const char *file, char *const argv[], int argc, char **new_argv) |
33 | { |
34 | /* Construct an argument list for the shell. */ |
35 | new_argv[0] = (char *) _PATH_BSHELL; |
36 | new_argv[1] = (char *) file; |
37 | while (argc > 1) |
38 | { |
39 | new_argv[argc] = argv[argc - 1]; |
40 | --argc; |
41 | } |
42 | } |
43 | |
44 | |
45 | /* Execute FILE, searching in the `PATH' environment variable if it contains |
46 | no slashes, with arguments ARGV and environment from ENVP. */ |
47 | int |
48 | __execvpe (const char *file, char *const argv[], char *const envp[]) |
49 | { |
50 | if (*file == '\0') |
51 | { |
52 | /* We check the simple case first. */ |
53 | __set_errno (ENOENT); |
54 | return -1; |
55 | } |
56 | |
57 | if (strchr (file, '/') != NULL) |
58 | { |
59 | /* Don't search when it contains a slash. */ |
60 | __execve (file, argv, envp); |
61 | |
62 | if (errno == ENOEXEC) |
63 | { |
64 | /* Count the arguments. */ |
65 | int argc = 0; |
66 | while (argv[argc++]) |
67 | ; |
68 | size_t len = (argc + 1) * sizeof (char *); |
69 | char **script_argv; |
70 | void *ptr = NULL; |
71 | if (__libc_use_alloca (len)) |
72 | script_argv = alloca (len); |
73 | else |
74 | script_argv = ptr = malloc (len); |
75 | |
76 | if (script_argv != NULL) |
77 | { |
78 | scripts_argv (file, argv, argc, script_argv); |
79 | __execve (script_argv[0], script_argv, envp); |
80 | |
81 | free (ptr); |
82 | } |
83 | } |
84 | } |
85 | else |
86 | { |
87 | size_t pathlen; |
88 | size_t alloclen = 0; |
89 | char *path = getenv ("PATH" ); |
90 | if (path == NULL) |
91 | { |
92 | pathlen = confstr (_CS_PATH, (char *) NULL, 0); |
93 | alloclen = pathlen + 1; |
94 | } |
95 | else |
96 | pathlen = strlen (path); |
97 | |
98 | size_t len = strlen (file) + 1; |
99 | alloclen += pathlen + len + 1; |
100 | |
101 | char *name; |
102 | char *path_malloc = NULL; |
103 | if (__libc_use_alloca (alloclen)) |
104 | name = alloca (alloclen); |
105 | else |
106 | { |
107 | path_malloc = name = malloc (alloclen); |
108 | if (name == NULL) |
109 | return -1; |
110 | } |
111 | |
112 | if (path == NULL) |
113 | { |
114 | /* There is no `PATH' in the environment. |
115 | The default search path is the current directory |
116 | followed by the path `confstr' returns for `_CS_PATH'. */ |
117 | path = name + pathlen + len + 1; |
118 | path[0] = ':'; |
119 | (void) confstr (_CS_PATH, path + 1, pathlen); |
120 | } |
121 | |
122 | /* Copy the file name at the top. */ |
123 | name = (char *) memcpy (name + pathlen + 1, file, len); |
124 | /* And add the slash. */ |
125 | *--name = '/'; |
126 | |
127 | char **script_argv = NULL; |
128 | void *script_argv_malloc = NULL; |
129 | bool got_eacces = false; |
130 | char *p = path; |
131 | do |
132 | { |
133 | char *startp; |
134 | |
135 | path = p; |
136 | p = __strchrnul (path, ':'); |
137 | |
138 | if (p == path) |
139 | /* Two adjacent colons, or a colon at the beginning or the end |
140 | of `PATH' means to search the current directory. */ |
141 | startp = name + 1; |
142 | else |
143 | startp = (char *) memcpy (name - (p - path), path, p - path); |
144 | |
145 | /* Try to execute this name. If it works, execve will not return. */ |
146 | __execve (startp, argv, envp); |
147 | |
148 | if (errno == ENOEXEC) |
149 | { |
150 | if (script_argv == NULL) |
151 | { |
152 | /* Count the arguments. */ |
153 | int argc = 0; |
154 | while (argv[argc++]) |
155 | ; |
156 | size_t arglen = (argc + 1) * sizeof (char *); |
157 | if (__libc_use_alloca (alloclen + arglen)) |
158 | script_argv = alloca (arglen); |
159 | else |
160 | script_argv = script_argv_malloc = malloc (arglen); |
161 | if (script_argv == NULL) |
162 | { |
163 | /* A possible EACCES error is not as important as |
164 | the ENOMEM. */ |
165 | got_eacces = false; |
166 | break; |
167 | } |
168 | scripts_argv (startp, argv, argc, script_argv); |
169 | } |
170 | |
171 | __execve (script_argv[0], script_argv, envp); |
172 | } |
173 | |
174 | switch (errno) |
175 | { |
176 | case EACCES: |
177 | /* Record the we got a `Permission denied' error. If we end |
178 | up finding no executable we can use, we want to diagnose |
179 | that we did find one but were denied access. */ |
180 | got_eacces = true; |
181 | case ENOENT: |
182 | case ESTALE: |
183 | case ENOTDIR: |
184 | /* Those errors indicate the file is missing or not executable |
185 | by us, in which case we want to just try the next path |
186 | directory. */ |
187 | case ENODEV: |
188 | case ETIMEDOUT: |
189 | /* Some strange filesystems like AFS return even |
190 | stranger error numbers. They cannot reasonably mean |
191 | anything else so ignore those, too. */ |
192 | break; |
193 | |
194 | default: |
195 | /* Some other error means we found an executable file, but |
196 | something went wrong executing it; return the error to our |
197 | caller. */ |
198 | return -1; |
199 | } |
200 | } |
201 | while (*p++ != '\0'); |
202 | |
203 | /* We tried every element and none of them worked. */ |
204 | if (got_eacces) |
205 | /* At least one failure was due to permissions, so report that |
206 | error. */ |
207 | __set_errno (EACCES); |
208 | |
209 | free (script_argv_malloc); |
210 | free (path_malloc); |
211 | } |
212 | |
213 | /* Return the error from the last attempt (probably ENOENT). */ |
214 | return -1; |
215 | } |
216 | weak_alias (__execvpe, execvpe) |
217 | |