Back to index

glibc  2.9
Defines | Functions
spawni.c File Reference
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <spawn.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "spawn_int.h"
#include <not-cancel.h>
#include <local-setxid.h>

Go to the source code of this file.

Defines

#define SPAWN_ERROR   127

Functions

static void internal_function script_execute (const char *file, char *const argv[], char *const envp[])
int __spawni (pid_t *pid, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], int use_path)

Define Documentation

#define SPAWN_ERROR   127

Definition at line 37 of file spawni.c.


Function Documentation

int __spawni ( pid_t pid,
const char *  file,
const posix_spawn_file_actions_t file_actions,
const posix_spawnattr_t attrp,
char *const  argv[],
char *const  envp[],
int  use_path 
)

Definition at line 71 of file spawni.c.

{
  pid_t new_pid;
  char *path, *p, *name;
  size_t len;
  size_t pathlen;

  /* Do this once.  */
  short int flags = attrp == NULL ? 0 : attrp->__flags;

  /* Generate the new process.  */
  if ((flags & POSIX_SPAWN_USEVFORK) != 0
      /* If no major work is done, allow using vfork.  Note that we
        might perform the path searching.  But this would be done by
        a call to execvp(), too, and such a call must be OK according
        to POSIX.  */
      || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF
                  | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER
                  | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0
         && file_actions == NULL))
    new_pid = __vfork ();
  else
    new_pid = __fork ();

  if (new_pid != 0)
    {
      if (new_pid < 0)
       return errno;

      /* The call was successful.  Store the PID if necessary.  */
      if (pid != NULL)
       *pid = new_pid;

      return 0;
    }

  /* Set signal mask.  */
  if ((flags & POSIX_SPAWN_SETSIGMASK) != 0
      && __sigprocmask (SIG_SETMASK, &attrp->__ss, NULL) != 0)
    _exit (SPAWN_ERROR);

  /* Set signal default action.  */
  if ((flags & POSIX_SPAWN_SETSIGDEF) != 0)
    {
      /* We have to iterate over all signals.  This could possibly be
        done better but it requires system specific solutions since
        the sigset_t data type can be very different on different
        architectures.  */
      int sig;
      struct sigaction sa;

      memset (&sa, '\0', sizeof (sa));
      sa.sa_handler = SIG_DFL;

      for (sig = 1; sig <= _NSIG; ++sig)
       if (__sigismember (&attrp->__sd, sig) != 0
           && __sigaction (sig, &sa, NULL) != 0)
         _exit (SPAWN_ERROR);

    }

#ifdef _POSIX_PRIORITY_SCHEDULING
  /* Set the scheduling algorithm and parameters.  */
  if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
      == POSIX_SPAWN_SETSCHEDPARAM)
    {
      if (__sched_setparam (0, &attrp->__sp) == -1)
       _exit (SPAWN_ERROR);
    }
  else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
    {
      if (__sched_setscheduler (0, attrp->__policy,
                            (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
                            ? &attrp->__sp : NULL) == -1)
       _exit (SPAWN_ERROR);
    }
#endif

  /* Set the process group ID.  */
  if ((flags & POSIX_SPAWN_SETPGROUP) != 0
      && __setpgid (0, attrp->__pgrp) != 0)
    _exit (SPAWN_ERROR);

  /* Set the effective user and group IDs.  */
  if ((flags & POSIX_SPAWN_RESETIDS) != 0
      && (local_seteuid (__getuid ()) != 0
         || local_setegid (__getgid ()) != 0))
    _exit (SPAWN_ERROR);

  /* Execute the file actions.  */
  if (file_actions != NULL)
    {
      int cnt;

      for (cnt = 0; cnt < file_actions->__used; ++cnt)
       {
         struct __spawn_action *action = &file_actions->__actions[cnt];

         switch (action->tag)
           {
           case spawn_do_close:
             if (close_not_cancel (action->action.close_action.fd) != 0)
              /* Signal the error.  */
              _exit (SPAWN_ERROR);
             break;

           case spawn_do_open:
             {
              int new_fd = open_not_cancel (action->action.open_action.path,
                                         action->action.open_action.oflag
                                         | O_LARGEFILE,
                                         action->action.open_action.mode);

              if (new_fd == -1)
                /* The `open' call failed.  */
                _exit (SPAWN_ERROR);

              /* Make sure the desired file descriptor is used.  */
              if (new_fd != action->action.open_action.fd)
                {
                  if (__dup2 (new_fd, action->action.open_action.fd)
                     != action->action.open_action.fd)
                    /* The `dup2' call failed.  */
                    _exit (SPAWN_ERROR);

                  if (close_not_cancel (new_fd) != 0)
                    /* The `close' call failed.  */
                    _exit (SPAWN_ERROR);
                }
             }
             break;

           case spawn_do_dup2:
             if (__dup2 (action->action.dup2_action.fd,
                       action->action.dup2_action.newfd)
                != action->action.dup2_action.newfd)
              /* The `dup2' call failed.  */
              _exit (SPAWN_ERROR);
             break;
           }
       }
    }

  if (! use_path || strchr (file, '/') != NULL)
    {
      /* The FILE parameter is actually a path.  */
      __execve (file, argv, envp);

      if (errno == ENOEXEC)
       script_execute (file, argv, envp);

      /* Oh, oh.  `execve' returns.  This is bad.  */
      _exit (SPAWN_ERROR);
    }

  /* We have to search for FILE on the path.  */
  path = getenv ("PATH");
  if (path == NULL)
    {
      /* There is no `PATH' in the environment.
        The default search path is the current directory
        followed by the path `confstr' returns for `_CS_PATH'.  */
      len = confstr (_CS_PATH, (char *) NULL, 0);
      path = (char *) __alloca (1 + len);
      path[0] = ':';
      (void) confstr (_CS_PATH, path + 1, len);
    }

  len = strlen (file) + 1;
  pathlen = strlen (path);
  name = __alloca (pathlen + len + 1);
  /* Copy the file name at the top.  */
  name = (char *) memcpy (name + pathlen + 1, file, len);
  /* And add the slash.  */
  *--name = '/';

  p = path;
  do
    {
      char *startp;

      path = p;
      p = __strchrnul (path, ':');

      if (p == path)
       /* Two adjacent colons, or a colon at the beginning or the end
          of `PATH' means to search the current directory.  */
       startp = name + 1;
      else
       startp = (char *) memcpy (name - (p - path), path, p - path);

      /* Try to execute this name.  If it works, execv will not return.  */
      __execve (startp, argv, envp);

      if (errno == ENOEXEC)
       script_execute (startp, argv, envp);

      switch (errno)
       {
       case EACCES:
       case ENOENT:
       case ESTALE:
       case ENOTDIR:
         /* Those errors indicate the file is missing or not executable
            by us, in which case we want to just try the next path
            directory.  */
         break;

       default:
         /* Some other error means we found an executable file, but
            something went wrong executing it; return the error to our
            caller.  */
         _exit (SPAWN_ERROR);
           }
    }
  while (*p++ != '\0');

  /* Return with an error.  */
  _exit (SPAWN_ERROR);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void internal_function script_execute ( const char *  file,
char *const  argv[],
char *const  envp[] 
) [static]

Definition at line 44 of file spawni.c.

{
  /* Count the arguments.  */
  int argc = 0;
  while (argv[argc++])
    ;

  /* Construct an argument list for the shell.  */
  {
    char *new_argv[argc + 1];
    new_argv[0] = (char *) _PATH_BSHELL;
    new_argv[1] = (char *) file;
    while (argc > 1)
      {
       new_argv[argc] = argv[argc - 1];
       --argc;
      }

    /* Execute the shell.  */
    __execve (new_argv[0], new_argv, envp);
  }
}

Here is the call graph for this function:

Here is the caller graph for this function: