New module 'posix_spawn-internal'.
[gnulib.git] / lib / spawni.c
1 /* Guts of POSIX spawn interface.  Generic POSIX.1 version.
2    Copyright (C) 2000-2005, 2006, 2008 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program 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
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include <spawn.h>
22 #include "spawn_int.h"
23
24 #include <alloca.h>
25 #include <errno.h>
26
27 #include <fcntl.h>
28 #ifndef O_LARGEFILE
29 # define O_LARGEFILE 0
30 #endif
31
32 #if _LIBC || HAVE_PATHS_H
33 # include <paths.h>
34 #else
35 # define _PATH_BSHELL "/bin/sh"
36 #endif
37
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #if _LIBC
44 # include <not-cancel.h>
45 #else
46 # define close_not_cancel close
47 # define open_not_cancel open
48 #endif
49
50 #if _LIBC
51 # include <local-setxid.h>
52 #else
53 # if !HAVE_SETEUID
54 #  define seteuid(id) setresuid (-1, id, -1)
55 # endif
56 # if !HAVE_SETEGID
57 #  define setegid(id) setresgid (-1, id, -1)
58 # endif
59 # define local_seteuid(id) seteuid (id)
60 # define local_setegid(id) setegid (id)
61 #endif
62
63 #if _LIBC
64 # define alloca __alloca
65 # define execve __execve
66 # define dup2 __dup2
67 # define fork __fork
68 # define getgid __getgid
69 # define getuid __getuid
70 # define sched_setparam __sched_setparam
71 # define sched_setscheduler __sched_setscheduler
72 # define setpgid __setpgid
73 # define sigaction __sigaction
74 # define sigismember __sigismember
75 # define sigprocmask __sigprocmask
76 # define strchrnul __strchrnul
77 # define vfork __vfork
78 #else
79 # undef internal_function
80 # define internal_function /* empty */
81 #endif
82
83
84 /* The Unix standard contains a long explanation of the way to signal
85    an error after the fork() was successful.  Since no new wait status
86    was wanted there is no way to signal an error using one of the
87    available methods.  The committee chose to signal an error by a
88    normal program exit with the exit code 127.  */
89 #define SPAWN_ERROR     127
90
91
92 /* The file is accessible but it is not an executable file.  Invoke
93    the shell to interpret it as a script.  */
94 static void
95 internal_function
96 script_execute (const char *file, char *const argv[], char *const envp[])
97 {
98   /* Count the arguments.  */
99   int argc = 0;
100   while (argv[argc++])
101     ;
102
103   /* Construct an argument list for the shell.  */
104   {
105     char **new_argv = (char **) alloca ((argc + 1) * sizeof (char *));
106     new_argv[0] = (char *) _PATH_BSHELL;
107     new_argv[1] = (char *) file;
108     while (argc > 1)
109       {
110         new_argv[argc] = argv[argc - 1];
111         --argc;
112       }
113
114     /* Execute the shell.  */
115     execve (new_argv[0], new_argv, envp);
116   }
117 }
118
119
120 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
121    Before running the process perform the actions described in FILE-ACTIONS. */
122 int
123 __spawni (pid_t *pid, const char *file,
124           const posix_spawn_file_actions_t *file_actions,
125           const posix_spawnattr_t *attrp, char *const argv[],
126           char *const envp[], int use_path)
127 {
128   pid_t new_pid;
129   char *path, *p, *name;
130   size_t len;
131   size_t pathlen;
132
133   /* Do this once.  */
134   short int flags = attrp == NULL ? 0 : attrp->_flags;
135
136   /* Generate the new process.  */
137 #if HAVE_VFORK
138   if ((flags & POSIX_SPAWN_USEVFORK) != 0
139       /* If no major work is done, allow using vfork.  Note that we
140          might perform the path searching.  But this would be done by
141          a call to execvp(), too, and such a call must be OK according
142          to POSIX.  */
143       || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF
144                     | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER
145                     | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0
146           && file_actions == NULL))
147     new_pid = vfork ();
148   else
149 #endif
150     new_pid = fork ();
151
152   if (new_pid != 0)
153     {
154       if (new_pid < 0)
155         return errno;
156
157       /* The call was successful.  Store the PID if necessary.  */
158       if (pid != NULL)
159         *pid = new_pid;
160
161       return 0;
162     }
163
164   /* Set signal mask.  */
165   if ((flags & POSIX_SPAWN_SETSIGMASK) != 0
166       && sigprocmask (SIG_SETMASK, &attrp->_ss, NULL) != 0)
167     _exit (SPAWN_ERROR);
168
169   /* Set signal default action.  */
170   if ((flags & POSIX_SPAWN_SETSIGDEF) != 0)
171     {
172       /* We have to iterate over all signals.  This could possibly be
173          done better but it requires system specific solutions since
174          the sigset_t data type can be very different on different
175          architectures.  */
176       int sig;
177       struct sigaction sa;
178
179       memset (&sa, '\0', sizeof (sa));
180       sa.sa_handler = SIG_DFL;
181
182       for (sig = 1; sig <= NSIG; ++sig)
183         if (sigismember (&attrp->_sd, sig) != 0
184             && sigaction (sig, &sa, NULL) != 0)
185           _exit (SPAWN_ERROR);
186
187     }
188
189 #if (_LIBC ? defined _POSIX_PRIORITY_SCHEDULING : HAVE_SCHED_SETPARAM && HAVE_SCHED_SETSCHEDULER)
190   /* Set the scheduling algorithm and parameters.  */
191   if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
192       == POSIX_SPAWN_SETSCHEDPARAM)
193     {
194       if (sched_setparam (0, &attrp->_sp) == -1)
195         _exit (SPAWN_ERROR);
196     }
197   else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
198     {
199       if (sched_setscheduler (0, attrp->_policy,
200                               (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
201                               ? &attrp->_sp : NULL) == -1)
202         _exit (SPAWN_ERROR);
203     }
204 #endif
205
206   /* Set the process group ID.  */
207   if ((flags & POSIX_SPAWN_SETPGROUP) != 0
208       && setpgid (0, attrp->_pgrp) != 0)
209     _exit (SPAWN_ERROR);
210
211   /* Set the effective user and group IDs.  */
212   if ((flags & POSIX_SPAWN_RESETIDS) != 0
213       && (local_seteuid (getuid ()) != 0
214           || local_setegid (getgid ()) != 0))
215     _exit (SPAWN_ERROR);
216
217   /* Execute the file actions.  */
218   if (file_actions != NULL)
219     {
220       int cnt;
221
222       for (cnt = 0; cnt < file_actions->_used; ++cnt)
223         {
224           struct __spawn_action *action = &file_actions->_actions[cnt];
225
226           switch (action->tag)
227             {
228             case spawn_do_close:
229               if (close_not_cancel (action->action.close_action.fd) != 0)
230                 /* Signal the error.  */
231                 _exit (SPAWN_ERROR);
232               break;
233
234             case spawn_do_open:
235               {
236                 int new_fd = open_not_cancel (action->action.open_action.path,
237                                               action->action.open_action.oflag
238                                               | O_LARGEFILE,
239                                               action->action.open_action.mode);
240
241                 if (new_fd == -1)
242                   /* The `open' call failed.  */
243                   _exit (SPAWN_ERROR);
244
245                 /* Make sure the desired file descriptor is used.  */
246                 if (new_fd != action->action.open_action.fd)
247                   {
248                     if (dup2 (new_fd, action->action.open_action.fd)
249                         != action->action.open_action.fd)
250                       /* The `dup2' call failed.  */
251                       _exit (SPAWN_ERROR);
252
253                     if (close_not_cancel (new_fd) != 0)
254                       /* The `close' call failed.  */
255                       _exit (SPAWN_ERROR);
256                   }
257               }
258               break;
259
260             case spawn_do_dup2:
261               if (dup2 (action->action.dup2_action.fd,
262                         action->action.dup2_action.newfd)
263                   != action->action.dup2_action.newfd)
264                 /* The `dup2' call failed.  */
265                 _exit (SPAWN_ERROR);
266               break;
267             }
268         }
269     }
270
271   if (! use_path || strchr (file, '/') != NULL)
272     {
273       /* The FILE parameter is actually a path.  */
274       execve (file, argv, envp);
275
276       if (errno == ENOEXEC)
277         script_execute (file, argv, envp);
278
279       /* Oh, oh.  `execve' returns.  This is bad.  */
280       _exit (SPAWN_ERROR);
281     }
282
283   /* We have to search for FILE on the path.  */
284   path = getenv ("PATH");
285   if (path == NULL)
286     {
287 #if HAVE_CONFSTR
288       /* There is no `PATH' in the environment.
289          The default search path is the current directory
290          followed by the path `confstr' returns for `_CS_PATH'.  */
291       len = confstr (_CS_PATH, (char *) NULL, 0);
292       path = (char *) alloca (1 + len);
293       path[0] = ':';
294       (void) confstr (_CS_PATH, path + 1, len);
295 #else
296       /* Pretend that the PATH contains only the current directory.  */
297       path = "";
298 #endif
299     }
300
301   len = strlen (file) + 1;
302   pathlen = strlen (path);
303   name = alloca (pathlen + len + 1);
304   /* Copy the file name at the top.  */
305   name = (char *) memcpy (name + pathlen + 1, file, len);
306   /* And add the slash.  */
307   *--name = '/';
308
309   p = path;
310   do
311     {
312       char *startp;
313
314       path = p;
315       p = strchrnul (path, ':');
316
317       if (p == path)
318         /* Two adjacent colons, or a colon at the beginning or the end
319            of `PATH' means to search the current directory.  */
320         startp = name + 1;
321       else
322         startp = (char *) memcpy (name - (p - path), path, p - path);
323
324       /* Try to execute this name.  If it works, execv will not return.  */
325       execve (startp, argv, envp);
326
327       if (errno == ENOEXEC)
328         script_execute (startp, argv, envp);
329
330       switch (errno)
331         {
332         case EACCES:
333         case ENOENT:
334         case ESTALE:
335         case ENOTDIR:
336           /* Those errors indicate the file is missing or not executable
337              by us, in which case we want to just try the next path
338              directory.  */
339           break;
340
341         default:
342           /* Some other error means we found an executable file, but
343              something went wrong executing it; return the error to our
344              caller.  */
345           _exit (SPAWN_ERROR);
346         }
347     }
348   while (*p++ != '\0');
349
350   /* Return with an error.  */
351   _exit (SPAWN_ERROR);
352 }