Home | History | Annotate | Download | only in lib
      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.  */
     95 int
     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.  */
    110 static void
    111 internal_function
    112 script_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. */
    138 int
    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