1/* Guts of POSIX spawn interface.  Generic POSIX.1 version.
2   Copyright (C) 2000-2006, 2008-2012 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#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
93
94/* Native Windows API.  */
95int
96__spawni (pid_t *pid, const char *file,
97          const posix_spawn_file_actions_t *file_actions,
98          const posix_spawnattr_t *attrp, char *const argv[],
99          char *const envp[], int use_path)
100{
101  /* Not yet implemented.  */
102  return ENOSYS;
103}
104
105#else
106
107
108/* The file is accessible but it is not an executable file.  Invoke
109   the shell to interpret it as a script.  */
110static void
111internal_function
112script_execute (const char *file, char *const argv[], char *const envp[])
113{
114  /* Count the arguments.  */
115  int argc = 0;
116  while (argv[argc++])
117    ;
118
119  /* Construct an argument list for the shell.  */
120  {
121    char **new_argv = (char **) alloca ((argc + 1) * sizeof (char *));
122    new_argv[0] = (char *) _PATH_BSHELL;
123    new_argv[1] = (char *) file;
124    while (argc > 1)
125      {
126        new_argv[argc] = argv[argc - 1];
127        --argc;
128      }
129
130    /* Execute the shell.  */
131    execve (new_argv[0], new_argv, envp);
132  }
133}
134
135
136/* Spawn a new process executing PATH with the attributes describes in *ATTRP.
137   Before running the process perform the actions described in FILE-ACTIONS. */
138int
139__spawni (pid_t *pid, const char *file,
140          const posix_spawn_file_actions_t *file_actions,
141          const posix_spawnattr_t *attrp, char *const argv[],
142          char *const envp[], int use_path)
143{
144  pid_t new_pid;
145  char *path, *p, *name;
146  size_t len;
147  size_t pathlen;
148
149  /* Do this once.  */
150  short int flags = attrp == NULL ? 0 : attrp->_flags;
151
152  /* Avoid gcc warning
153       "variable 'flags' might be clobbered by 'longjmp' or 'vfork'"  */
154  (void) &flags;
155
156  /* Generate the new process.  */
157#if HAVE_VFORK
158  if ((flags & POSIX_SPAWN_USEVFORK) != 0
159      /* If no major work is done, allow using vfork.  Note that we
160         might perform the path searching.  But this would be done by
161         a call to execvp(), too, and such a call must be OK according
162         to POSIX.  */
163      || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF
164                    | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER
165                    | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0
166          && file_actions == NULL))
167    new_pid = vfork ();
168  else
169#endif
170    new_pid = fork ();
171
172  if (new_pid != 0)
173    {
174      if (new_pid < 0)
175        return errno;
176
177      /* The call was successful.  Store the PID if necessary.  */
178      if (pid != NULL)
179        *pid = new_pid;
180
181      return 0;
182    }
183
184  /* Set signal mask.  */
185  if ((flags & POSIX_SPAWN_SETSIGMASK) != 0
186      && sigprocmask (SIG_SETMASK, &attrp->_ss, NULL) != 0)
187    _exit (SPAWN_ERROR);
188
189  /* Set signal default action.  */
190  if ((flags & POSIX_SPAWN_SETSIGDEF) != 0)
191    {
192      /* We have to iterate over all signals.  This could possibly be
193         done better but it requires system specific solutions since
194         the sigset_t data type can be very different on different
195         architectures.  */
196      int sig;
197      struct sigaction sa;
198
199      memset (&sa, '\0', sizeof (sa));
200      sa.sa_handler = SIG_DFL;
201
202      for (sig = 1; sig <= NSIG; ++sig)
203        if (sigismember (&attrp->_sd, sig) != 0
204            && sigaction (sig, &sa, NULL) != 0)
205          _exit (SPAWN_ERROR);
206
207    }
208
209#if (_LIBC ? defined _POSIX_PRIORITY_SCHEDULING : HAVE_SCHED_SETPARAM && HAVE_SCHED_SETSCHEDULER)
210  /* Set the scheduling algorithm and parameters.  */
211  if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
212      == POSIX_SPAWN_SETSCHEDPARAM)
213    {
214      if (sched_setparam (0, &attrp->_sp) == -1)
215        _exit (SPAWN_ERROR);
216    }
217  else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
218    {
219      if (sched_setscheduler (0, attrp->_policy,
220                              (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
221                              ? &attrp->_sp : NULL) == -1)
222        _exit (SPAWN_ERROR);
223    }
224#endif
225
226  /* Set the process group ID.  */
227  if ((flags & POSIX_SPAWN_SETPGROUP) != 0
228      && setpgid (0, attrp->_pgrp) != 0)
229    _exit (SPAWN_ERROR);
230
231  /* Set the effective user and group IDs.  */
232  if ((flags & POSIX_SPAWN_RESETIDS) != 0
233      && (local_seteuid (getuid ()) != 0
234          || local_setegid (getgid ()) != 0))
235    _exit (SPAWN_ERROR);
236
237  /* Execute the file actions.  */
238  if (file_actions != NULL)
239    {
240      int cnt;
241
242      for (cnt = 0; cnt < file_actions->_used; ++cnt)
243        {
244          struct __spawn_action *action = &file_actions->_actions[cnt];
245
246          switch (action->tag)
247            {
248            case spawn_do_close:
249              if (close_not_cancel (action->action.close_action.fd) != 0)
250                /* Signal the error.  */
251                _exit (SPAWN_ERROR);
252              break;
253
254            case spawn_do_open:
255              {
256                int new_fd = open_not_cancel (action->action.open_action.path,
257                                              action->action.open_action.oflag
258                                              | O_LARGEFILE,
259                                              action->action.open_action.mode);
260
261                if (new_fd == -1)
262                  /* The 'open' call failed.  */
263                  _exit (SPAWN_ERROR);
264
265                /* Make sure the desired file descriptor is used.  */
266                if (new_fd != action->action.open_action.fd)
267                  {
268                    if (dup2 (new_fd, action->action.open_action.fd)
269                        != action->action.open_action.fd)
270                      /* The 'dup2' call failed.  */
271                      _exit (SPAWN_ERROR);
272
273                    if (close_not_cancel (new_fd) != 0)
274                      /* The 'close' call failed.  */
275                      _exit (SPAWN_ERROR);
276                  }
277              }
278              break;
279
280            case spawn_do_dup2:
281              if (dup2 (action->action.dup2_action.fd,
282                        action->action.dup2_action.newfd)
283                  != action->action.dup2_action.newfd)
284                /* The 'dup2' call failed.  */
285                _exit (SPAWN_ERROR);
286              break;
287            }
288        }
289    }
290
291  if (! use_path || strchr (file, '/') != NULL)
292    {
293      /* The FILE parameter is actually a path.  */
294      execve (file, argv, envp);
295
296      if (errno == ENOEXEC)
297        script_execute (file, argv, envp);
298
299      /* Oh, oh.  'execve' returns.  This is bad.  */
300      _exit (SPAWN_ERROR);
301    }
302
303  /* We have to search for FILE on the path.  */
304  path = getenv ("PATH");
305  if (path == NULL)
306    {
307#if HAVE_CONFSTR
308      /* There is no 'PATH' in the environment.
309         The default search path is the current directory
310         followed by the path 'confstr' returns for '_CS_PATH'.  */
311      len = confstr (_CS_PATH, (char *) NULL, 0);
312      path = (char *) alloca (1 + len);
313      path[0] = ':';
314      (void) confstr (_CS_PATH, path + 1, len);
315#else
316      /* Pretend that the PATH contains only the current directory.  */
317      path = "";
318#endif
319    }
320
321  len = strlen (file) + 1;
322  pathlen = strlen (path);
323  name = alloca (pathlen + len + 1);
324  /* Copy the file name at the top.  */
325  name = (char *) memcpy (name + pathlen + 1, file, len);
326  /* And add the slash.  */
327  *--name = '/';
328
329  p = path;
330  do
331    {
332      char *startp;
333
334      path = p;
335      p = strchrnul (path, ':');
336
337      if (p == path)
338        /* Two adjacent colons, or a colon at the beginning or the end
339           of 'PATH' means to search the current directory.  */
340        startp = name + 1;
341      else
342        startp = (char *) memcpy (name - (p - path), path, p - path);
343
344      /* Try to execute this name.  If it works, execv will not return.  */
345      execve (startp, argv, envp);
346
347      if (errno == ENOEXEC)
348        script_execute (startp, argv, envp);
349
350      switch (errno)
351        {
352        case EACCES:
353        case ENOENT:
354        case ESTALE:
355        case ENOTDIR:
356          /* Those errors indicate the file is missing or not executable
357             by us, in which case we want to just try the next path
358             directory.  */
359          break;
360
361        default:
362          /* Some other error means we found an executable file, but
363             something went wrong executing it; return the error to our
364             caller.  */
365          _exit (SPAWN_ERROR);
366        }
367    }
368  while (*p++ != '\0');
369
370  /* Return with an error.  */
371  _exit (SPAWN_ERROR);
372}
373
374#endif
375