8421bd048f73a9d4ba84690a72ef56c7cbebeb2f
[platform/upstream/glibc.git] / posix / execvp.c
1 /* Copyright (C) 1991,92,1995-99,2002,2004,2005 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, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
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 char **
31 internal_function
32 allocate_scripts_argv (const char *file, char *const argv[])
33 {
34   /* Count the arguments.  */
35   int argc = 0;
36   while (argv[argc++])
37     ;
38
39   /* Construct an argument list for the shell.  */
40   char **new_argv = (char **) malloc ((argc + 1) * sizeof (char *));
41   if (new_argv != NULL)
42     {
43       new_argv[0] = (char *) _PATH_BSHELL;
44       new_argv[1] = (char *) file;
45       while (argc > 1)
46         {
47           new_argv[argc] = argv[argc - 1];
48           --argc;
49         }
50     }
51
52   return new_argv;
53 }
54
55
56 /* Execute FILE, searching in the `PATH' environment variable if it contains
57    no slashes, with arguments ARGV and environment from `environ'.  */
58 int
59 execvp (file, argv)
60      const char *file;
61      char *const argv[];
62 {
63   if (*file == '\0')
64     {
65       /* We check the simple case first. */
66       __set_errno (ENOENT);
67       return -1;
68     }
69
70   char **script_argv = NULL;
71
72   if (strchr (file, '/') != NULL)
73     {
74       /* Don't search when it contains a slash.  */
75       __execve (file, argv, __environ);
76
77       if (errno == ENOEXEC)
78         {
79           script_argv = allocate_scripts_argv (file, argv);
80           if (script_argv != NULL)
81             {
82               __execve (script_argv[0], script_argv, __environ);
83
84               free (script_argv);
85             }
86         }
87     }
88   else
89     {
90       char *path = getenv ("PATH");
91       char *path_malloc = NULL;
92       if (path == NULL)
93         {
94           /* There is no `PATH' in the environment.
95              The default search path is the current directory
96              followed by the path `confstr' returns for `_CS_PATH'.  */
97           size_t len = confstr (_CS_PATH, (char *) NULL, 0);
98           path = (char *) malloc (1 + len);
99           if (path == NULL)
100             return -1;
101           path[0] = ':';
102           (void) confstr (_CS_PATH, path + 1, len);
103           path_malloc = path;
104         }
105
106       size_t len = strlen (file) + 1;
107       size_t pathlen = strlen (path);
108       char *name = malloc (pathlen + len + 1);
109       if (name == NULL)
110         {
111           free (path_malloc);
112           return -1;
113         }
114       /* Copy the file name at the top.  */
115       name = (char *) memcpy (name + pathlen + 1, file, len);
116       /* And add the slash.  */
117       *--name = '/';
118
119       bool got_eacces = false;
120       char *p = path;
121       do
122         {
123           char *startp;
124
125           path = p;
126           p = __strchrnul (path, ':');
127
128           if (p == path)
129             /* Two adjacent colons, or a colon at the beginning or the end
130                of `PATH' means to search the current directory.  */
131             startp = name + 1;
132           else
133             startp = (char *) memcpy (name - (p - path), path, p - path);
134
135           /* Try to execute this name.  If it works, execve will not return. */
136           __execve (startp, argv, __environ);
137
138           if (errno == ENOEXEC)
139             {
140               if (script_argv == NULL)
141                 {
142                   script_argv = allocate_scripts_argv (startp, argv);
143                   if (script_argv == NULL)
144                     {
145                       /* A possible EACCES error is not as important as
146                          the ENOMEM.  */
147                       got_eacces = false;
148                       break;
149                     }
150                 }
151
152               __execve (script_argv[0], script_argv, __environ);
153             }
154
155           switch (errno)
156             {
157             case EACCES:
158               /* Record the we got a `Permission denied' error.  If we end
159                  up finding no executable we can use, we want to diagnose
160                  that we did find one but were denied access.  */
161               got_eacces = true;
162             case ENOENT:
163             case ESTALE:
164             case ENOTDIR:
165               /* Those errors indicate the file is missing or not executable
166                  by us, in which case we want to just try the next path
167                  directory.  */
168             case ENODEV:
169             case ETIMEDOUT:
170               /* Some strange filesystems like AFS return even
171                  stranger error numbers.  They cannot reasonably mean
172                  anything else so ignore those, too.  */
173               break;
174
175             default:
176               /* Some other error means we found an executable file, but
177                  something went wrong executing it; return the error to our
178                  caller.  */
179               return -1;
180             }
181         }
182       while (*p++ != '\0');
183
184       /* We tried every element and none of them worked.  */
185       if (got_eacces)
186         /* At least one failure was due to permissions, so report that
187            error.  */
188         __set_errno (EACCES);
189
190       free (script_argv);
191       free (name - pathlen);
192       free (path_malloc);
193     }
194
195   /* Return the error from the last attempt (probably ENOENT).  */
196   return -1;
197 }
198 libc_hidden_def (execvp)