Blame src/spawn-posix.c

Packit fc043f
/* exechelp.c - Fork and exec helpers for POSIX
Packit fc043f
 * Copyright (C) 2004, 2007-2009, 2010 Free Software Foundation, Inc.
Packit fc043f
 * Copyright (C) 2004, 2006-2012, 2014-2017 g10 Code GmbH
Packit fc043f
 *
Packit fc043f
 * This file is part of Libgpg-error.
Packit fc043f
 *
Packit fc043f
 * Libgpg-error is free software; you can redistribute it and/or
Packit fc043f
 * modify it under the terms of the GNU Lesser General Public License
Packit fc043f
 * as published by the Free Software Foundation; either version 2.1 of
Packit fc043f
 * the License, or (at your option) any later version.
Packit fc043f
 *
Packit fc043f
 * Libgpg-error is distributed in the hope that it will be useful, but
Packit fc043f
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit fc043f
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit fc043f
 * Lesser General Public License for more details.
Packit fc043f
 *
Packit fc043f
 * You should have received a copy of the GNU Lesser General Public
Packit fc043f
 * License along with this program; if not, see <https://www.gnu.org/licenses/>.
Packit fc043f
 * SPDX-License-Identifier: LGPL-2.1+
Packit fc043f
 *
Packit fc043f
 * This file was originally a part of GnuPG.
Packit fc043f
 */
Packit fc043f
Packit fc043f
#include <config.h>
Packit fc043f
Packit fc043f
#if defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM)
Packit fc043f
#error This code is only used on POSIX
Packit fc043f
#endif
Packit fc043f
Packit fc043f
#include <stdio.h>
Packit fc043f
#include <stdlib.h>
Packit fc043f
#include <stdint.h>
Packit fc043f
#include <string.h>
Packit fc043f
#include <errno.h>
Packit fc043f
#include <assert.h>
Packit fc043f
#ifdef HAVE_SIGNAL_H
Packit fc043f
# include <signal.h>
Packit fc043f
#endif
Packit fc043f
#include <unistd.h>
Packit fc043f
#include <fcntl.h>
Packit fc043f
Packit fc043f
#include <sys/wait.h>
Packit fc043f
Packit fc043f
#ifdef HAVE_GETRLIMIT
Packit fc043f
#include <sys/time.h>
Packit fc043f
#include <sys/resource.h>
Packit fc043f
#endif /*HAVE_GETRLIMIT*/
Packit fc043f
Packit fc043f
#ifdef HAVE_STAT
Packit fc043f
# include <sys/stat.h>
Packit fc043f
#endif
Packit fc043f
Packit fc043f
#if __linux__
Packit fc043f
# include <sys/types.h>
Packit fc043f
# include <dirent.h>
Packit fc043f
#endif /*__linux__ */
Packit fc043f
Packit fc043f
#include "gpgrt-int.h"
Packit fc043f
Packit fc043f
Packit fc043f
static void
Packit fc043f
out_of_core (int line)
Packit fc043f
{
Packit fc043f
  _gpgrt_log_fatal ("malloc failed at line %d: %s\n",
Packit fc043f
                    line, _gpg_strerror (_gpg_err_code_from_syserror ()));
Packit fc043f
  /*NOTREACHED*/
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Return the maximum number of currently allowed open file
Packit fc043f
 * descriptors.  Only useful on POSIX systems but returns a value on
Packit fc043f
 * other systems too.  */
Packit fc043f
static int
Packit fc043f
get_max_fds (void)
Packit fc043f
{
Packit fc043f
  int max_fds = -1;
Packit fc043f
#ifdef HAVE_GETRLIMIT
Packit fc043f
  struct rlimit rl;
Packit fc043f
Packit fc043f
  /* Under Linux we can figure out the highest used file descriptor by
Packit fc043f
   * reading /proc/PID/fd.  This is in the common cases much fast than
Packit fc043f
   * for example doing 4096 close calls where almost all of them will
Packit fc043f
   * fail.  On a system with a limit of 4096 files and only 8 files
Packit fc043f
   * open with the highest number being 10, we speedup close_all_fds
Packit fc043f
   * from 125ms to 0.4ms including readdir.
Packit fc043f
   *
Packit fc043f
   * Another option would be to close the file descriptors as returned
Packit fc043f
   * from reading that directory - however then we need to snapshot
Packit fc043f
   * that list before starting to close them.  */
Packit fc043f
#ifdef __linux__
Packit fc043f
  {
Packit fc043f
    DIR *dir = NULL;
Packit fc043f
    struct dirent *dir_entry;
Packit fc043f
    const char *s;
Packit fc043f
    int x;
Packit fc043f
Packit fc043f
    /* FIXME: Check gpgme on how to do this right on Linux.  */
Packit fc043f
    dir = opendir ("/proc/self/fd");
Packit fc043f
    if (dir)
Packit fc043f
      {
Packit fc043f
        while ((dir_entry = readdir (dir)))
Packit fc043f
          {
Packit fc043f
            s = dir_entry->d_name;
Packit fc043f
            if ( *s < '0' || *s > '9')
Packit fc043f
              continue;
Packit fc043f
            x = atoi (s);
Packit fc043f
            if (x > max_fds)
Packit fc043f
              max_fds = x;
Packit fc043f
          }
Packit fc043f
        closedir (dir);
Packit fc043f
      }
Packit fc043f
    if (max_fds != -1)
Packit fc043f
      return max_fds + 1;
Packit fc043f
    }
Packit fc043f
#endif /* __linux__ */
Packit fc043f
Packit fc043f
Packit fc043f
# ifdef RLIMIT_NOFILE
Packit fc043f
  if (!getrlimit (RLIMIT_NOFILE, &rl))
Packit fc043f
    max_fds = rl.rlim_max;
Packit fc043f
# endif
Packit fc043f
Packit fc043f
# ifdef RLIMIT_OFILE
Packit fc043f
  if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl))
Packit fc043f
    max_fds = rl.rlim_max;
Packit fc043f
Packit fc043f
# endif
Packit fc043f
#endif /*HAVE_GETRLIMIT*/
Packit fc043f
Packit fc043f
#ifdef _SC_OPEN_MAX
Packit fc043f
  if (max_fds == -1)
Packit fc043f
    {
Packit fc043f
      long int scres = sysconf (_SC_OPEN_MAX);
Packit fc043f
      if (scres >= 0)
Packit fc043f
        max_fds = scres;
Packit fc043f
    }
Packit fc043f
#endif
Packit fc043f
Packit fc043f
#ifdef _POSIX_OPEN_MAX
Packit fc043f
  if (max_fds == -1)
Packit fc043f
    max_fds = _POSIX_OPEN_MAX;
Packit fc043f
#endif
Packit fc043f
Packit fc043f
#ifdef OPEN_MAX
Packit fc043f
  if (max_fds == -1)
Packit fc043f
    max_fds = OPEN_MAX;
Packit fc043f
#endif
Packit fc043f
Packit fc043f
  if (max_fds == -1)
Packit fc043f
    max_fds = 256;  /* Arbitrary limit.  */
Packit fc043f
Packit fc043f
  /* AIX returns INT32_MAX instead of a proper value.  We assume that
Packit fc043f
     this is always an error and use an arbitrary limit.  */
Packit fc043f
#ifdef INT32_MAX
Packit fc043f
  if (max_fds == INT32_MAX)
Packit fc043f
    max_fds = 256;
Packit fc043f
#endif
Packit fc043f
Packit fc043f
  return max_fds;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Close all file descriptors starting with descriptor FIRST.  If
Packit fc043f
 * EXCEPT is not NULL, it is expected to be a list of file descriptors
Packit fc043f
 * which shall not be closed.  This list shall be sorted in ascending
Packit fc043f
 * order with the end marked by -1.  */
Packit fc043f
static void
Packit fc043f
close_all_fds (int first, int *except)
Packit fc043f
{
Packit fc043f
  int max_fd = get_max_fds ();
Packit fc043f
  int fd, i, except_start;
Packit fc043f
Packit fc043f
  if (except)
Packit fc043f
    {
Packit fc043f
      except_start = 0;
Packit fc043f
      for (fd=first; fd < max_fd; fd++)
Packit fc043f
        {
Packit fc043f
          for (i=except_start; except[i] != -1; i++)
Packit fc043f
            {
Packit fc043f
              if (except[i] == fd)
Packit fc043f
                {
Packit fc043f
                  /* If we found the descriptor in the exception list
Packit fc043f
                     we can start the next compare run at the next
Packit fc043f
                     index because the exception list is ordered.  */
Packit fc043f
                except_start = i + 1;
Packit fc043f
                break;
Packit fc043f
                }
Packit fc043f
            }
Packit fc043f
          if (except[i] == -1)
Packit fc043f
            close (fd);
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
  else
Packit fc043f
    {
Packit fc043f
      for (fd=first; fd < max_fd; fd++)
Packit fc043f
        close (fd);
Packit fc043f
    }
Packit fc043f
Packit fc043f
  _gpg_err_set_errno (0);
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Returns an array with all currently open file descriptors.  The end
Packit fc043f
 * of the array is marked by -1.  The caller needs to release this
Packit fc043f
 * array using the *standard free* and not with xfree.  This allow the
Packit fc043f
 * use of this function right at startup even before libgcrypt has
Packit fc043f
 * been initialized.  Returns NULL on error and sets ERRNO
Packit fc043f
 * accordingly.
Packit fc043f
 *
Packit fc043f
 * FIXME: Needs to be adjusted for use here.
Packit fc043f
 */
Packit fc043f
#if 0
Packit fc043f
int *
Packit fc043f
get_all_open_fds (void)
Packit fc043f
{
Packit fc043f
  int *array;
Packit fc043f
  size_t narray;
Packit fc043f
  int fd, max_fd, idx;
Packit fc043f
#ifndef HAVE_STAT
Packit fc043f
  array = calloc (1, sizeof *array);
Packit fc043f
  if (array)
Packit fc043f
    array[0] = -1;
Packit fc043f
#else /*HAVE_STAT*/
Packit fc043f
  struct stat statbuf;
Packit fc043f
Packit fc043f
  max_fd = get_max_fds ();
Packit fc043f
  narray = 32;  /* If you change this change also t-exechelp.c.  */
Packit fc043f
  array = calloc (narray, sizeof *array);
Packit fc043f
  if (!array)
Packit fc043f
    return NULL;
Packit fc043f
Packit fc043f
  /* Note:  The list we return is ordered.  */
Packit fc043f
  for (idx=0, fd=0; fd < max_fd; fd++)
Packit fc043f
    if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
Packit fc043f
      {
Packit fc043f
        if (idx+1 >= narray)
Packit fc043f
          {
Packit fc043f
            int *tmp;
Packit fc043f
Packit fc043f
            narray += (narray < 256)? 32:256;
Packit fc043f
            tmp = realloc (array, narray * sizeof *array);
Packit fc043f
            if (!tmp)
Packit fc043f
              {
Packit fc043f
                free (array);
Packit fc043f
                return NULL;
Packit fc043f
              }
Packit fc043f
            array = tmp;
Packit fc043f
          }
Packit fc043f
        array[idx++] = fd;
Packit fc043f
      }
Packit fc043f
  array[idx] = -1;
Packit fc043f
#endif /*HAVE_STAT*/
Packit fc043f
  return array;
Packit fc043f
}
Packit fc043f
#endif /*0*/
Packit fc043f
Packit fc043f
Packit fc043f
/* The exec core used right after the fork.  This will never return.  */
Packit fc043f
static void
Packit fc043f
do_exec (const char *pgmname, const char *argv[],
Packit fc043f
         int fd_in, int fd_out, int fd_err,
Packit fc043f
         int *except, void (*preexec)(void) )
Packit fc043f
{
Packit fc043f
  char **arg_list;
Packit fc043f
  int i, j;
Packit fc043f
  int fds[3];
Packit fc043f
Packit fc043f
  fds[0] = fd_in;
Packit fc043f
  fds[1] = fd_out;
Packit fc043f
  fds[2] = fd_err;
Packit fc043f
Packit fc043f
  /* Create the command line argument array.  */
Packit fc043f
  i = 0;
Packit fc043f
  if (argv)
Packit fc043f
    while (argv[i])
Packit fc043f
      i++;
Packit fc043f
  arg_list = xtrycalloc (i+2, sizeof *arg_list);
Packit fc043f
  if (!arg_list)
Packit fc043f
    out_of_core (__LINE__);
Packit fc043f
  arg_list[0] = strrchr (pgmname, '/');
Packit fc043f
  if (arg_list[0])
Packit fc043f
    arg_list[0]++;
Packit fc043f
  else
Packit fc043f
    {
Packit fc043f
      arg_list[0] = xtrystrdup (pgmname);
Packit fc043f
      if (!arg_list[0])
Packit fc043f
        out_of_core (__LINE__);
Packit fc043f
    }
Packit fc043f
  if (argv)
Packit fc043f
    for (i=0,j=1; argv[i]; i++, j++)
Packit fc043f
      arg_list[j] = (char*)argv[i];
Packit fc043f
Packit fc043f
  /* Assign /dev/null to unused FDs. */
Packit fc043f
  for (i=0; i <= 2; i++)
Packit fc043f
    {
Packit fc043f
      if (fds[i] == -1 )
Packit fc043f
        {
Packit fc043f
          fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY);
Packit fc043f
          if (fds[i] == -1)
Packit fc043f
            _gpgrt_log_fatal ("failed to open '%s': %s\n",
Packit fc043f
                              "/dev/null", strerror (errno));
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
Packit fc043f
  /* Connect the standard files.  */
Packit fc043f
  for (i=0; i <= 2; i++)
Packit fc043f
    {
Packit fc043f
      if (fds[i] != i && dup2 (fds[i], i) == -1)
Packit fc043f
        _gpgrt_log_fatal ("dup2 std%s failed: %s\n",
Packit fc043f
                          i==0?"in":i==1?"out":"err", strerror (errno));
Packit fc043f
    }
Packit fc043f
Packit fc043f
  /* Close all other files. */
Packit fc043f
  close_all_fds (3, except);
Packit fc043f
Packit fc043f
  if (preexec)
Packit fc043f
    preexec ();
Packit fc043f
  execv (pgmname, arg_list);
Packit fc043f
  /* No way to print anything, as we have may have closed all streams. */
Packit fc043f
  _exit (127);
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Helper for _gpgrt_make_pipe.  */
Packit fc043f
static gpg_err_code_t
Packit fc043f
do_create_pipe (int filedes[2])
Packit fc043f
{
Packit fc043f
  gpg_error_t err = 0;
Packit fc043f
Packit fc043f
  _gpgrt_pre_syscall ();
Packit fc043f
  if (pipe (filedes) == -1)
Packit fc043f
    {
Packit fc043f
      err = _gpg_err_code_from_syserror ();
Packit fc043f
      filedes[0] = filedes[1] = -1;
Packit fc043f
    }
Packit fc043f
  _gpgrt_post_syscall ();
Packit fc043f
Packit fc043f
  return err;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Helper for _gpgrt_make_pipe.  */
Packit fc043f
static gpg_err_code_t
Packit fc043f
do_create_pipe_and_estream (int filedes[2], estream_t *r_fp,
Packit fc043f
                            int outbound, int nonblock)
Packit fc043f
{
Packit fc043f
  gpg_err_code_t err;
Packit fc043f
Packit fc043f
  _gpgrt_pre_syscall ();
Packit fc043f
  if (pipe (filedes) == -1)
Packit fc043f
    {
Packit fc043f
      err = _gpg_err_code_from_syserror ();
Packit fc043f
      _gpgrt_log_error (_("error creating a pipe: %s\n"), _gpg_strerror (err));
Packit fc043f
      filedes[0] = filedes[1] = -1;
Packit fc043f
      *r_fp = NULL;
Packit fc043f
      return err;
Packit fc043f
    }
Packit fc043f
  _gpgrt_post_syscall ();
Packit fc043f
Packit fc043f
  if (!outbound)
Packit fc043f
    *r_fp = _gpgrt_fdopen (filedes[0], nonblock? "r,nonblock" : "r");
Packit fc043f
  else
Packit fc043f
    *r_fp = _gpgrt_fdopen (filedes[1], nonblock? "w,nonblock" : "w");
Packit fc043f
  if (!*r_fp)
Packit fc043f
    {
Packit fc043f
      err = _gpg_err_code_from_syserror ();
Packit fc043f
      _gpgrt_log_error (_("error creating a stream for a pipe: %s\n"),
Packit fc043f
                        _gpg_strerror (err));
Packit fc043f
      close (filedes[0]);
Packit fc043f
      close (filedes[1]);
Packit fc043f
      filedes[0] = filedes[1] = -1;
Packit fc043f
      return err;
Packit fc043f
    }
Packit fc043f
  return 0;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Create a pipe.  The DIRECTION parameter gives the type of the created pipe:
Packit fc043f
 *   DIRECTION < 0 := Inbound pipe: On Windows the write end is inheritable.
Packit fc043f
 *   DIRECTION > 0 := Outbound pipe: On Windows the read end is inheritable.
Packit fc043f
 * If R_FP is NULL a standard pipe and no stream is created, DIRECTION
Packit fc043f
 * should then be 0.  */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_make_pipe (int filedes[2], estream_t *r_fp, int direction,
Packit fc043f
                  int nonblock)
Packit fc043f
{
Packit fc043f
  if (r_fp && direction)
Packit fc043f
    return do_create_pipe_and_estream (filedes, r_fp,
Packit fc043f
                                       (direction > 0), nonblock);
Packit fc043f
  else
Packit fc043f
    return do_create_pipe (filedes);
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Fork and exec the PGMNAME, see gpgrt-int.h for details.  */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_spawn_process (const char *pgmname, const char *argv[],
Packit fc043f
                      int *except, void (*preexec)(void), unsigned int flags,
Packit fc043f
                      estream_t *r_infp,
Packit fc043f
                      estream_t *r_outfp,
Packit fc043f
                      estream_t *r_errfp,
Packit fc043f
                      pid_t *pid)
Packit fc043f
{
Packit fc043f
  gpg_error_t err;
Packit fc043f
  int inpipe[2] = {-1, -1};
Packit fc043f
  int outpipe[2] = {-1, -1};
Packit fc043f
  int errpipe[2] = {-1, -1};
Packit fc043f
  estream_t infp = NULL;
Packit fc043f
  estream_t outfp = NULL;
Packit fc043f
  estream_t errfp = NULL;
Packit fc043f
  int nonblock = !!(flags & GPGRT_SPAWN_NONBLOCK);
Packit fc043f
Packit fc043f
  if (r_infp)
Packit fc043f
    *r_infp = NULL;
Packit fc043f
  if (r_outfp)
Packit fc043f
    *r_outfp = NULL;
Packit fc043f
  if (r_errfp)
Packit fc043f
    *r_errfp = NULL;
Packit fc043f
  *pid = (pid_t)(-1); /* Always required.  */
Packit fc043f
Packit fc043f
  if (r_infp)
Packit fc043f
    {
Packit fc043f
      err = _gpgrt_create_outbound_pipe (inpipe, &infp, nonblock);
Packit fc043f
      if (err)
Packit fc043f
        return err;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (r_outfp)
Packit fc043f
    {
Packit fc043f
      err = _gpgrt_create_inbound_pipe (outpipe, &outfp, nonblock);
Packit fc043f
      if (err)
Packit fc043f
        {
Packit fc043f
          if (infp)
Packit fc043f
            _gpgrt_fclose (infp);
Packit fc043f
          else if (inpipe[1] != -1)
Packit fc043f
            close (inpipe[1]);
Packit fc043f
          if (inpipe[0] != -1)
Packit fc043f
            close (inpipe[0]);
Packit fc043f
Packit fc043f
          return err;
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (r_errfp)
Packit fc043f
    {
Packit fc043f
      err = _gpgrt_create_inbound_pipe (errpipe, &errfp, nonblock);
Packit fc043f
      if (err)
Packit fc043f
        {
Packit fc043f
          if (infp)
Packit fc043f
            _gpgrt_fclose (infp);
Packit fc043f
          else if (inpipe[1] != -1)
Packit fc043f
            close (inpipe[1]);
Packit fc043f
          if (inpipe[0] != -1)
Packit fc043f
            close (inpipe[0]);
Packit fc043f
Packit fc043f
          if (outfp)
Packit fc043f
            _gpgrt_fclose (outfp);
Packit fc043f
          else if (outpipe[0] != -1)
Packit fc043f
            close (outpipe[0]);
Packit fc043f
          if (outpipe[1] != -1)
Packit fc043f
            close (outpipe[1]);
Packit fc043f
Packit fc043f
          return err;
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
Packit fc043f
  _gpgrt_pre_syscall ();
Packit fc043f
  *pid = fork ();
Packit fc043f
  _gpgrt_post_syscall ();
Packit fc043f
  if (*pid == (pid_t)(-1))
Packit fc043f
    {
Packit fc043f
      err = _gpg_err_code_from_syserror ();
Packit fc043f
      _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (err));
Packit fc043f
Packit fc043f
      if (infp)
Packit fc043f
        _gpgrt_fclose (infp);
Packit fc043f
      else if (inpipe[1] != -1)
Packit fc043f
        close (inpipe[1]);
Packit fc043f
      if (inpipe[0] != -1)
Packit fc043f
        close (inpipe[0]);
Packit fc043f
Packit fc043f
      if (outfp)
Packit fc043f
        _gpgrt_fclose (outfp);
Packit fc043f
      else if (outpipe[0] != -1)
Packit fc043f
        close (outpipe[0]);
Packit fc043f
      if (outpipe[1] != -1)
Packit fc043f
        close (outpipe[1]);
Packit fc043f
Packit fc043f
      if (errfp)
Packit fc043f
        _gpgrt_fclose (errfp);
Packit fc043f
      else if (errpipe[0] != -1)
Packit fc043f
        close (errpipe[0]);
Packit fc043f
      if (errpipe[1] != -1)
Packit fc043f
        close (errpipe[1]);
Packit fc043f
      return err;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (!*pid)
Packit fc043f
    {
Packit fc043f
      /* This is the child. */
Packit fc043f
      /* FIXME: Needs to be done by preexec:
Packit fc043f
         gcry_control (GCRYCTL_TERM_SECMEM); */
Packit fc043f
      _gpgrt_fclose (infp);
Packit fc043f
      _gpgrt_fclose (outfp);
Packit fc043f
      _gpgrt_fclose (errfp);
Packit fc043f
      do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1],
Packit fc043f
               except, preexec);
Packit fc043f
      /*NOTREACHED*/
Packit fc043f
    }
Packit fc043f
Packit fc043f
  /* This is the parent. */
Packit fc043f
  if (inpipe[0] != -1)
Packit fc043f
    close (inpipe[0]);
Packit fc043f
  if (outpipe[1] != -1)
Packit fc043f
    close (outpipe[1]);
Packit fc043f
  if (errpipe[1] != -1)
Packit fc043f
    close (errpipe[1]);
Packit fc043f
Packit fc043f
  if (r_infp)
Packit fc043f
    *r_infp = infp;
Packit fc043f
  if (r_outfp)
Packit fc043f
    *r_outfp = outfp;
Packit fc043f
  if (r_errfp)
Packit fc043f
    *r_errfp = errfp;
Packit fc043f
Packit fc043f
  return 0;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Fork and exec the PGMNAME using FDs, see gpgrt-int.h for details.  */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_spawn_process_fd (const char *pgmname, const char *argv[],
Packit fc043f
                         int infd, int outfd, int errfd, pid_t *pid)
Packit fc043f
{
Packit fc043f
  gpg_error_t err;
Packit fc043f
Packit fc043f
  _gpgrt_pre_syscall ();
Packit fc043f
  *pid = fork ();
Packit fc043f
  _gpgrt_post_syscall ();
Packit fc043f
  if (*pid == (pid_t)(-1))
Packit fc043f
    {
Packit fc043f
      err = _gpg_err_code_from_syserror ();
Packit fc043f
      _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (err));
Packit fc043f
      return err;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (!*pid)
Packit fc043f
    {
Packit fc043f
      /* FIXME: We need to add a preexec so that a
Packit fc043f
           gcry_control (GCRYCTL_TERM_SECMEM);
Packit fc043f
         can be done.  */
Packit fc043f
      /* Run child. */
Packit fc043f
      do_exec (pgmname, argv, infd, outfd, errfd, NULL, NULL);
Packit fc043f
      /*NOTREACHED*/
Packit fc043f
    }
Packit fc043f
Packit fc043f
  return 0;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Waiting for child processes.
Packit fc043f
 *
Packit fc043f
 * waitpid(2) may return information about terminated children that we
Packit fc043f
 * did not yet request, and there is no portable way to wait for a
Packit fc043f
 * specific set of children.
Packit fc043f
 *
Packit fc043f
 * As a workaround, we store the results of children for later use.
Packit fc043f
 *
Packit fc043f
 * XXX: This assumes that PIDs are not reused too quickly.
Packit fc043f
 * FIXME: This is not thread-safe.
Packit fc043f
 */
Packit fc043f
Packit fc043f
struct terminated_child
Packit fc043f
{
Packit fc043f
  pid_t pid;
Packit fc043f
  int exitcode;
Packit fc043f
  struct terminated_child *next;
Packit fc043f
};
Packit fc043f
Packit fc043f
static struct terminated_child *terminated_children;
Packit fc043f
Packit fc043f
Packit fc043f
static gpg_err_code_t
Packit fc043f
store_result (pid_t pid, int exitcode)
Packit fc043f
{
Packit fc043f
  struct terminated_child *c;
Packit fc043f
Packit fc043f
  c = xtrymalloc (sizeof *c);
Packit fc043f
  if (c == NULL)
Packit fc043f
    return _gpg_err_code_from_syserror ();
Packit fc043f
Packit fc043f
  c->pid = pid;
Packit fc043f
  c->exitcode = exitcode;
Packit fc043f
  c->next = terminated_children;
Packit fc043f
  terminated_children = c;
Packit fc043f
Packit fc043f
  return 0;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
static int
Packit fc043f
get_result (pid_t pid, int *r_exitcode)
Packit fc043f
{
Packit fc043f
  struct terminated_child *c, **prevp;
Packit fc043f
Packit fc043f
  for (prevp = &terminated_children, c = terminated_children;
Packit fc043f
       c;
Packit fc043f
       prevp = &c->next, c = c->next)
Packit fc043f
    if (c->pid == pid)
Packit fc043f
      {
Packit fc043f
        *prevp = c->next;
Packit fc043f
        *r_exitcode = c->exitcode;
Packit fc043f
        xfree (c);
Packit fc043f
        return 1;
Packit fc043f
      }
Packit fc043f
Packit fc043f
  return 0;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* See gpgrt-int.h for a description.  */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
Packit fc043f
{
Packit fc043f
  gpg_err_code_t ec;
Packit fc043f
  int i, status;
Packit fc043f
Packit fc043f
  if (r_exitcode)
Packit fc043f
    *r_exitcode = -1;
Packit fc043f
Packit fc043f
  if (pid == (pid_t)(-1))
Packit fc043f
    return GPG_ERR_INV_VALUE;
Packit fc043f
Packit fc043f
  _gpgrt_pre_syscall ();
Packit fc043f
  while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1)
Packit fc043f
	 && errno == EINTR);
Packit fc043f
  _gpgrt_post_syscall ();
Packit fc043f
Packit fc043f
  if (i == (pid_t)(-1))
Packit fc043f
    {
Packit fc043f
      ec = _gpg_err_code_from_syserror ();
Packit fc043f
      _gpgrt_log_error (_("waiting for process %d to terminate failed: %s\n"),
Packit fc043f
                        (int)pid, _gpg_strerror (ec));
Packit fc043f
    }
Packit fc043f
  else if (!i)
Packit fc043f
    {
Packit fc043f
      ec = GPG_ERR_TIMEOUT; /* Still running.  */
Packit fc043f
    }
Packit fc043f
  else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
Packit fc043f
    {
Packit fc043f
      /* FIXME: This is GnuPG specific.  */
Packit fc043f
      _gpgrt_log_error (_("error running '%s': probably not installed\n"),
Packit fc043f
                        pgmname);
Packit fc043f
      ec = GPG_ERR_CONFIGURATION;
Packit fc043f
    }
Packit fc043f
  else if (WIFEXITED (status) && WEXITSTATUS (status))
Packit fc043f
    {
Packit fc043f
      if (!r_exitcode)
Packit fc043f
        _gpgrt_log_error (_("error running '%s': exit status %d\n"), pgmname,
Packit fc043f
                          WEXITSTATUS (status));
Packit fc043f
      else
Packit fc043f
        *r_exitcode = WEXITSTATUS (status);
Packit fc043f
      ec = GPG_ERR_GENERAL;
Packit fc043f
    }
Packit fc043f
  else if (!WIFEXITED (status))
Packit fc043f
    {
Packit fc043f
      _gpgrt_log_error (_("error running '%s': terminated\n"), pgmname);
Packit fc043f
      ec = GPG_ERR_GENERAL;
Packit fc043f
    }
Packit fc043f
  else
Packit fc043f
    {
Packit fc043f
      if (r_exitcode)
Packit fc043f
        *r_exitcode = 0;
Packit fc043f
      ec = 0;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  return ec;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* See gpgrt-int.h for a description.
Packit fc043f
 *
Packit fc043f
 * FIXME: What about using a poll like data structure for the pids and
Packit fc043f
 * their exit codes?  The whole thing is anyway problematic in a
Packit fc043f
 * threaded processs because waitpid has no association between PIDS
Packit fc043f
 * and threads.
Packit fc043f
 */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
Packit fc043f
                       int hang, int *r_exitcodes)
Packit fc043f
{
Packit fc043f
  gpg_err_code_t ec = 0;
Packit fc043f
  size_t i, left;
Packit fc043f
  int *dummy = NULL;
Packit fc043f
Packit fc043f
  if (!r_exitcodes)
Packit fc043f
    {
Packit fc043f
      dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count);
Packit fc043f
      if (!dummy)
Packit fc043f
        return _gpg_err_code_from_syserror ();
Packit fc043f
    }
Packit fc043f
Packit fc043f
  for (i = 0, left = count; i < count; i++)
Packit fc043f
    {
Packit fc043f
      int status = -1;
Packit fc043f
Packit fc043f
      if (pids[i] == (pid_t)(-1))
Packit fc043f
        return GPG_ERR_INV_VALUE;
Packit fc043f
Packit fc043f
      /* See if there was a previously stored result for this pid.  */
Packit fc043f
      if (get_result (pids[i], &status))
Packit fc043f
        left -= 1;
Packit fc043f
Packit fc043f
      r_exitcodes[i] = status;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  while (left > 0)
Packit fc043f
    {
Packit fc043f
      pid_t pid;
Packit fc043f
      int status;
Packit fc043f
Packit fc043f
      _gpgrt_pre_syscall ();
Packit fc043f
      while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1)
Packit fc043f
             && errno == EINTR);
Packit fc043f
      _gpgrt_post_syscall ();
Packit fc043f
Packit fc043f
      if (pid == (pid_t)(-1))
Packit fc043f
        {
Packit fc043f
          ec = _gpg_err_code_from_syserror ();
Packit fc043f
          _gpgrt_log_error (_("waiting for processes to terminate"
Packit fc043f
                              " failed: %s\n"), _gpg_strerror (ec));
Packit fc043f
          break;
Packit fc043f
        }
Packit fc043f
      else if (!pid)
Packit fc043f
        {
Packit fc043f
          ec = GPG_ERR_TIMEOUT; /* Still running.  */
Packit fc043f
          break;
Packit fc043f
        }
Packit fc043f
      else
Packit fc043f
        {
Packit fc043f
          for (i = 0; i < count; i++)
Packit fc043f
            if (pid == pids[i])
Packit fc043f
              break;
Packit fc043f
Packit fc043f
          if (i == count)
Packit fc043f
            {
Packit fc043f
              /* No match, store this result.  */
Packit fc043f
              ec = store_result (pid, status);
Packit fc043f
              if (ec)
Packit fc043f
                break;
Packit fc043f
              continue;
Packit fc043f
            }
Packit fc043f
Packit fc043f
          /* Process PIDS[i] died.  */
Packit fc043f
          if (r_exitcodes[i] != (pid_t) -1)
Packit fc043f
            {
Packit fc043f
              _gpgrt_log_error ("PID %d was reused", pid);
Packit fc043f
              ec = GPG_ERR_GENERAL;
Packit fc043f
              break;
Packit fc043f
            }
Packit fc043f
Packit fc043f
          left -= 1;
Packit fc043f
          r_exitcodes[i] = status;
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
Packit fc043f
  for (i = 0; i < count; i++)
Packit fc043f
    {
Packit fc043f
      if (r_exitcodes[i] == -1)
Packit fc043f
        continue;
Packit fc043f
Packit fc043f
      if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127)
Packit fc043f
        {
Packit fc043f
          _gpgrt_log_error (_("error running '%s': probably not installed\n"),
Packit fc043f
                            pgmnames[i]);
Packit fc043f
          ec = GPG_ERR_CONFIGURATION;
Packit fc043f
        }
Packit fc043f
      else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]))
Packit fc043f
        {
Packit fc043f
          if (dummy)
Packit fc043f
            _gpgrt_log_error (_("error running '%s': exit status %d\n"),
Packit fc043f
                              pgmnames[i], WEXITSTATUS (r_exitcodes[i]));
Packit fc043f
          else
Packit fc043f
            r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]);
Packit fc043f
          ec = GPG_ERR_GENERAL;
Packit fc043f
        }
Packit fc043f
      else if (!WIFEXITED (r_exitcodes[i]))
Packit fc043f
        {
Packit fc043f
          _gpgrt_log_error (_("error running '%s': terminated\n"), pgmnames[i]);
Packit fc043f
          ec = GPG_ERR_GENERAL;
Packit fc043f
        }
Packit fc043f
    }
Packit fc043f
Packit fc043f
  xfree (dummy);
Packit fc043f
  return ec;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* See gpgrt-int.h for a description.  FIXME: We should add a prexec
Packit fc043f
 * callback. */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_spawn_process_detached (const char *pgmname, const char *argv[],
Packit fc043f
                               const char *envp[] )
Packit fc043f
{
Packit fc043f
  gpg_err_code_t ec;
Packit fc043f
  pid_t pid;
Packit fc043f
  int i;
Packit fc043f
Packit fc043f
  /* FIXME: Is this GnuPG specific or should we keep it.  */
Packit fc043f
  if (getuid() != geteuid())
Packit fc043f
    return GPG_ERR_BUG;
Packit fc043f
Packit fc043f
  if (access (pgmname, X_OK))
Packit fc043f
    return _gpg_err_code_from_syserror ();
Packit fc043f
Packit fc043f
  _gpgrt_pre_syscall ();
Packit fc043f
  pid = fork ();
Packit fc043f
  _gpgrt_post_syscall ();
Packit fc043f
  if (pid == (pid_t)(-1))
Packit fc043f
    {
Packit fc043f
      ec = _gpg_err_code_from_syserror ();
Packit fc043f
      _gpgrt_log_error (_("error forking process: %s\n"), _gpg_strerror (ec));
Packit fc043f
      return ec;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  if (!pid)
Packit fc043f
    {
Packit fc043f
      pid_t pid2;
Packit fc043f
Packit fc043f
      /* gcry_control (GCRYCTL_TERM_SECMEM); */
Packit fc043f
      if (setsid() == -1 || chdir ("/"))
Packit fc043f
        _exit (1);
Packit fc043f
Packit fc043f
      pid2 = fork (); /* Double fork to let init take over the new child. */
Packit fc043f
      if (pid2 == (pid_t)(-1))
Packit fc043f
        _exit (1);
Packit fc043f
      if (pid2)
Packit fc043f
        _exit (0);  /* Let the parent exit immediately. */
Packit fc043f
Packit fc043f
      for (i=0; envp && envp[i]; i++)
Packit fc043f
        {
Packit fc043f
          char *p = xtrystrdup (envp[i]);
Packit fc043f
          if (!p)
Packit fc043f
            out_of_core (__LINE__);
Packit fc043f
          putenv (p);
Packit fc043f
        }
Packit fc043f
Packit fc043f
      do_exec (pgmname, argv, -1, -1, -1, NULL, NULL);
Packit fc043f
      /*NOTREACHED*/
Packit fc043f
    }
Packit fc043f
Packit fc043f
  _gpgrt_pre_syscall ();
Packit fc043f
  if (waitpid (pid, NULL, 0) == -1)
Packit fc043f
    {
Packit fc043f
      _gpgrt_post_syscall ();
Packit fc043f
      ec = _gpg_err_code_from_syserror ();
Packit fc043f
      _gpgrt_log_error ("waitpid failed in gpgrt_spawn_process_detached: %s",
Packit fc043f
                        _gpg_strerror (ec));
Packit fc043f
      return ec;
Packit fc043f
    }
Packit fc043f
  else
Packit fc043f
    _gpgrt_post_syscall ();
Packit fc043f
Packit fc043f
  return 0;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Kill a process; that is send an appropriate signal to the process.
Packit fc043f
 * gnupg_wait_process must be called to actually remove the process
Packit fc043f
   from the system.  An invalid PID is ignored.  */
Packit fc043f
void
Packit fc043f
_gpgrt_kill_process (pid_t pid)
Packit fc043f
{
Packit fc043f
  if (pid != (pid_t)(-1))
Packit fc043f
    {
Packit fc043f
      _gpgrt_pre_syscall ();
Packit fc043f
      kill (pid, SIGTERM);
Packit fc043f
      _gpgrt_post_syscall ();
Packit fc043f
    }
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
void
Packit fc043f
_gpgrt_release_process (pid_t pid)
Packit fc043f
{
Packit fc043f
  (void)pid;
Packit fc043f
}