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