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. */
30static void
31internal_function
32scripts_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. */
47int
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}
216weak_alias (__execvpe, execvpe)
217