Blame src/posix-io.c

Packit Service 672cf4
/* posix-io.c - Posix I/O functions
Packit Service 6c01f9
   Copyright (C) 2000 Werner Koch (dd9jn)
Packit Service 6c01f9
   Copyright (C) 2001, 2002, 2004, 2005, 2007, 2010 g10 Code GmbH
Packit Service 6c01f9
Packit Service 6c01f9
   This file is part of GPGME.
Packit Service 6c01f9
Packit Service 6c01f9
   GPGME is free software; you can redistribute it and/or modify it
Packit Service 6c01f9
   under the terms of the GNU Lesser General Public License as
Packit Service 6c01f9
   published by the Free Software Foundation; either version 2.1 of
Packit Service 6c01f9
   the License, or (at your option) any later version.
Packit Service 6c01f9
Packit Service 6c01f9
   GPGME is distributed in the hope that it will be useful, but
Packit Service 6c01f9
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 6c01f9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 6c01f9
   Lesser General Public License for more details.
Packit Service 6c01f9
Packit Service 6c01f9
   You should have received a copy of the GNU Lesser General Public
Packit Service 6c01f9
   License along with this program; if not, see <https://www.gnu.org/licenses/>.
Packit Service 672cf4
 */
Packit Service 672cf4
Packit Service 672cf4
#ifdef HAVE_CONFIG_H
Packit Service 672cf4
#include <config.h>
Packit Service 672cf4
#endif
Packit Service 672cf4
#include <stdio.h>
Packit Service 672cf4
#include <stdlib.h>
Packit Service 672cf4
#ifdef HAVE_STDINT_H
Packit Service 672cf4
# include <stdint.h>
Packit Service 672cf4
#endif
Packit Service 672cf4
#include <string.h>
Packit Service 672cf4
#include <assert.h>
Packit Service 672cf4
#include <errno.h>
Packit Service 672cf4
#include <signal.h>
Packit Service 672cf4
#include <fcntl.h>
Packit Service 672cf4
#ifdef HAVE_UNISTD_H
Packit Service 672cf4
# include <unistd.h>
Packit Service 672cf4
#endif
Packit Service 672cf4
#ifdef HAVE_SYS_TIME_H
Packit Service 672cf4
# include <sys/time.h>
Packit Service 672cf4
#endif
Packit Service 672cf4
#ifdef HAVE_SYS_TYPES_H
Packit Service 672cf4
# include <sys/types.h>
Packit Service 672cf4
#endif
Packit Service 672cf4
#include <sys/wait.h>
Packit Service 672cf4
#ifdef HAVE_SYS_UIO_H
Packit Service 672cf4
# include <sys/uio.h>
Packit Service 672cf4
#endif
Packit Service 672cf4
#include <ctype.h>
Packit Service 672cf4
#include <sys/resource.h>
Packit Service 672cf4
Packit Service 672cf4
#ifdef USE_LINUX_GETDENTS
Packit Service 672cf4
# include <sys/syscall.h>
Packit Service 672cf4
# include <sys/types.h>
Packit Service 672cf4
# include <dirent.h>
Packit Service 672cf4
#endif /*USE_LINUX_GETDENTS*/
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
#include "util.h"
Packit Service 672cf4
#include "priv-io.h"
Packit Service 672cf4
#include "sema.h"
Packit Service 672cf4
#include "ath.h"
Packit Service 672cf4
#include "debug.h"
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4

Packit Service 672cf4
void
Packit Service 672cf4
_gpgme_io_subsystem_init (void)
Packit Service 672cf4
{
Packit Service 672cf4
  struct sigaction act;
Packit Service 672cf4
Packit Service 672cf4
  sigaction (SIGPIPE, NULL, &act;;
Packit Service 672cf4
  if (act.sa_handler == SIG_DFL)
Packit Service 672cf4
    {
Packit Service 672cf4
      act.sa_handler = SIG_IGN;
Packit Service 672cf4
      sigemptyset (&act.sa_mask);
Packit Service 672cf4
      act.sa_flags = 0;
Packit Service 672cf4
      sigaction (SIGPIPE, &act, NULL);
Packit Service 672cf4
    }
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
/* Write the printable version of FD to the buffer BUF of length
Packit Service 672cf4
   BUFLEN.  The printable version is the representation on the command
Packit Service 672cf4
   line that the child process expects.  */
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_fd2str (char *buf, int buflen, int fd)
Packit Service 672cf4
{
Packit Service 672cf4
  return snprintf (buf, buflen, "%d", fd);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4

Packit Service 672cf4
/* The table to hold notification handlers.  We use a linear search
Packit Service 672cf4
   and extend the table as needed.  */
Packit Service 672cf4
struct notify_table_item_s
Packit Service 672cf4
{
Packit Service 672cf4
  int fd;  /* -1 indicates an unused entry.  */
Packit Service 672cf4
  _gpgme_close_notify_handler_t handler;
Packit Service 672cf4
  void *value;
Packit Service 672cf4
};
Packit Service 672cf4
typedef struct notify_table_item_s *notify_table_item_t;
Packit Service 672cf4
Packit Service 672cf4
static notify_table_item_t notify_table;
Packit Service 672cf4
static size_t notify_table_size;
Packit Service 672cf4
DEFINE_STATIC_LOCK (notify_table_lock);
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4

Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_read (int fd, void *buffer, size_t count)
Packit Service 672cf4
{
Packit Service 672cf4
  int nread;
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
Packit Service 6c01f9
	      "buffer=%p, count=%u", buffer, count);
Packit Service 672cf4
Packit Service 672cf4
  do
Packit Service 672cf4
    {
Packit Service 672cf4
      nread = _gpgme_ath_read (fd, buffer, count);
Packit Service 672cf4
    }
Packit Service 672cf4
  while (nread == -1 && errno == EINTR);
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_LOGBUF (buffer, nread);
Packit Service 672cf4
  return TRACE_SYSRES (nread);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_write (int fd, const void *buffer, size_t count)
Packit Service 672cf4
{
Packit Service 672cf4
  int nwritten;
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
Packit Service 6c01f9
	      "buffer=%p, count=%u", buffer, count);
Packit Service 6c01f9
  TRACE_LOGBUF (buffer, count);
Packit Service 672cf4
Packit Service 672cf4
  do
Packit Service 672cf4
    {
Packit Service 672cf4
      nwritten = _gpgme_ath_write (fd, buffer, count);
Packit Service 672cf4
    }
Packit Service 672cf4
  while (nwritten == -1 && errno == EINTR);
Packit Service 672cf4
Packit Service 672cf4
  return TRACE_SYSRES (nwritten);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_pipe (int filedes[2], int inherit_idx)
Packit Service 672cf4
{
Packit Service 672cf4
  int saved_errno;
Packit Service 672cf4
  int err;
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
Packit Service 672cf4
	      "inherit_idx=%i (GPGME uses it for %s)",
Packit Service 672cf4
	      inherit_idx, inherit_idx ? "reading" : "writing");
Packit Service 672cf4
Packit Service 672cf4
  err = pipe (filedes);
Packit Service 672cf4
  if (err < 0)
Packit Service 672cf4
    return TRACE_SYSRES (err);
Packit Service 672cf4
Packit Service 672cf4
  /* FIXME: Should get the old flags first.  */
Packit Service 672cf4
  err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
Packit Service 672cf4
  saved_errno = errno;
Packit Service 672cf4
  if (err < 0)
Packit Service 672cf4
    {
Packit Service 672cf4
      close (filedes[0]);
Packit Service 672cf4
      close (filedes[1]);
Packit Service 672cf4
    }
Packit Service 672cf4
  errno = saved_errno;
Packit Service 672cf4
  if (err)
Packit Service 672cf4
    return TRACE_SYSRES (err);
Packit Service 672cf4
Packit Service 6c01f9
  return TRACE_SUC2 ("read=0x%x, write=0x%x", filedes[0], filedes[1]);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_close (int fd)
Packit Service 672cf4
{
Packit Service 672cf4
  int res;
Packit Service 672cf4
  _gpgme_close_notify_handler_t handler = NULL;
Packit Service 672cf4
  void *handler_value;
Packit Service 672cf4
  int idx;
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
Packit Service 672cf4
Packit Service 672cf4
  if (fd == -1)
Packit Service 672cf4
    {
Packit Service 672cf4
      errno = EINVAL;
Packit Service 672cf4
      return TRACE_SYSRES (-1);
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 672cf4
  /* First call the notify handler.  */
Packit Service 672cf4
  LOCK (notify_table_lock);
Packit Service 672cf4
  for (idx=0; idx < notify_table_size; idx++)
Packit Service 672cf4
    {
Packit Service 672cf4
      if (notify_table[idx].fd == fd)
Packit Service 672cf4
        {
Packit Service 672cf4
	  handler       = notify_table[idx].handler;
Packit Service 672cf4
	  handler_value = notify_table[idx].value;
Packit Service 672cf4
	  notify_table[idx].handler = NULL;
Packit Service 672cf4
	  notify_table[idx].value = NULL;
Packit Service 672cf4
	  notify_table[idx].fd = -1; /* Mark slot as free.  */
Packit Service 672cf4
          break;
Packit Service 672cf4
        }
Packit Service 672cf4
    }
Packit Service 672cf4
  UNLOCK (notify_table_lock);
Packit Service 672cf4
  if (handler)
Packit Service 672cf4
    {
Packit Service 6c01f9
      TRACE_LOG2 ("invoking close handler %p/%p", handler, handler_value);
Packit Service 672cf4
      handler (fd, handler_value);
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 672cf4
  /* Then do the close.  */
Packit Service 672cf4
  res = close (fd);
Packit Service 672cf4
  return TRACE_SYSRES (res);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
Packit Service 672cf4
			    void *value)
Packit Service 672cf4
{
Packit Service 672cf4
  int res = 0;
Packit Service 672cf4
  int idx;
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
Packit Service 6c01f9
	      "close_handler=%p/%p", handler, value);
Packit Service 672cf4
Packit Service 672cf4
  assert (fd != -1);
Packit Service 672cf4
Packit Service 672cf4
  LOCK (notify_table_lock);
Packit Service 672cf4
  for (idx=0; idx < notify_table_size; idx++)
Packit Service 672cf4
    if (notify_table[idx].fd == -1)
Packit Service 672cf4
      break;
Packit Service 672cf4
  if (idx == notify_table_size)
Packit Service 672cf4
    {
Packit Service 672cf4
      /* We need to increase the size of the table.  The approach we
Packit Service 672cf4
         take is straightforward to minimize the risk of bugs.  */
Packit Service 672cf4
      notify_table_item_t newtbl;
Packit Service 672cf4
      size_t newsize = notify_table_size + 64;
Packit Service 672cf4
Packit Service 672cf4
      newtbl = calloc (newsize, sizeof *newtbl);
Packit Service 672cf4
      if (!newtbl)
Packit Service 672cf4
        {
Packit Service 672cf4
          res = -1;
Packit Service 672cf4
          goto leave;
Packit Service 672cf4
        }
Packit Service 672cf4
      for (idx=0; idx < notify_table_size; idx++)
Packit Service 672cf4
        newtbl[idx] = notify_table[idx];
Packit Service 672cf4
      for (; idx < newsize; idx++)
Packit Service 672cf4
        {
Packit Service 672cf4
          newtbl[idx].fd = -1;
Packit Service 672cf4
          newtbl[idx].handler = NULL;
Packit Service 672cf4
          newtbl[idx].value = NULL;
Packit Service 672cf4
        }
Packit Service 672cf4
      free (notify_table);
Packit Service 672cf4
      notify_table = newtbl;
Packit Service 672cf4
      idx = notify_table_size;
Packit Service 672cf4
      notify_table_size = newsize;
Packit Service 672cf4
    }
Packit Service 672cf4
  notify_table[idx].fd = fd;
Packit Service 672cf4
  notify_table[idx].handler = handler;
Packit Service 672cf4
  notify_table[idx].value = value;
Packit Service 672cf4
Packit Service 672cf4
 leave:
Packit Service 672cf4
  UNLOCK (notify_table_lock);
Packit Service 672cf4
Packit Service 672cf4
  return TRACE_SYSRES (res);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_set_nonblocking (int fd)
Packit Service 672cf4
{
Packit Service 672cf4
  int flags;
Packit Service 672cf4
  int res;
Packit Service 6c01f9
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
Packit Service 672cf4
Packit Service 672cf4
  flags = fcntl (fd, F_GETFL, 0);
Packit Service 672cf4
  if (flags == -1)
Packit Service 672cf4
    return TRACE_SYSRES (-1);
Packit Service 672cf4
  flags |= O_NONBLOCK;
Packit Service 672cf4
  res = fcntl (fd, F_SETFL, flags);
Packit Service 672cf4
  return TRACE_SYSRES (res);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 6c01f9
#ifdef USE_LINUX_GETDENTS
Packit Service 6c01f9
/* This is not declared in public headers; getdents64(2) says that we must
Packit Service 6c01f9
 * define it ourselves.  */
Packit Service 6c01f9
struct linux_dirent64
Packit Service 6c01f9
{
Packit Service 6c01f9
  ino64_t d_ino;
Packit Service 6c01f9
  off64_t d_off;
Packit Service 6c01f9
  unsigned short d_reclen;
Packit Service 6c01f9
  unsigned char d_type;
Packit Service 6c01f9
  char d_name[];
Packit Service 6c01f9
};
Packit Service 6c01f9
Packit Service 6c01f9
# define DIR_BUF_SIZE 1024
Packit Service 6c01f9
#endif /*USE_LINUX_GETDENTS*/
Packit Service 6c01f9
Packit Service 6c01f9
Packit Service 672cf4
static long int
Packit Service 672cf4
get_max_fds (void)
Packit Service 672cf4
{
Packit Service 672cf4
  const char *source = NULL;
Packit Service 672cf4
  long int fds = -1;
Packit Service 672cf4
  int rc;
Packit Service 672cf4
Packit Service 672cf4
  /* Under Linux we can figure out the highest used file descriptor by
Packit Service 672cf4
   * reading /proc/self/fd.  This is in the common cases much faster
Packit Service 672cf4
   * than for example doing 4096 close calls where almost all of them
Packit Service 672cf4
   * will fail.
Packit Service 672cf4
   *
Packit Service 672cf4
   * We can't use the normal opendir/readdir/closedir interface between
Packit Service 672cf4
   * fork and exec in a multi-threaded process because opendir uses
Packit Service 672cf4
   * malloc and thus a mutex which may deadlock with a malloc in another
Packit Service 672cf4
   * thread.  However, the underlying getdents system call is safe.  */
Packit Service 672cf4
#ifdef USE_LINUX_GETDENTS
Packit Service 672cf4
  {
Packit Service 672cf4
    int dir_fd;
Packit Service 672cf4
    char dir_buf[DIR_BUF_SIZE];
Packit Service 672cf4
    struct linux_dirent64 *dir_entry;
Packit Service 672cf4
    int r, pos;
Packit Service 672cf4
    const char *s;
Packit Service 672cf4
    int x;
Packit Service 672cf4
Packit Service 672cf4
    dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
Packit Service 672cf4
    if (dir_fd != -1)
Packit Service 672cf4
      {
Packit Service 672cf4
        for (;;)
Packit Service 672cf4
          {
Packit Service 672cf4
            r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE);
Packit Service 672cf4
            if (r == -1)
Packit Service 672cf4
              {
Packit Service 672cf4
                /* Fall back to other methods.  */
Packit Service 672cf4
                fds = -1;
Packit Service 672cf4
                break;
Packit Service 672cf4
              }
Packit Service 672cf4
            if (r == 0)
Packit Service 672cf4
              break;
Packit Service 672cf4
Packit Service 672cf4
            for (pos = 0; pos < r; pos += dir_entry->d_reclen)
Packit Service 672cf4
              {
Packit Service 672cf4
                dir_entry = (struct linux_dirent64 *) (dir_buf + pos);
Packit Service 672cf4
                s = dir_entry->d_name;
Packit Service 672cf4
                if (*s < '0' || *s > '9')
Packit Service 672cf4
                  continue;
Packit Service 672cf4
                /* atoi is not guaranteed to be async-signal-safe.  */
Packit Service 672cf4
                for (x = 0; *s >= '0' && *s <= '9'; s++)
Packit Service 672cf4
                  x = x * 10 + (*s - '0');
Packit Service 672cf4
                if (!*s && x > fds && x != dir_fd)
Packit Service 672cf4
                  fds = x;
Packit Service 672cf4
              }
Packit Service 672cf4
          }
Packit Service 672cf4
Packit Service 672cf4
        close (dir_fd);
Packit Service 672cf4
      }
Packit Service 672cf4
    if (fds != -1)
Packit Service 672cf4
      {
Packit Service 672cf4
        fds++;
Packit Service 672cf4
        source = "/proc";
Packit Service 672cf4
      }
Packit Service 672cf4
    }
Packit Service 672cf4
#endif /*USE_LINUX_GETDENTS*/
Packit Service 672cf4
Packit Service 672cf4
#ifdef RLIMIT_NOFILE
Packit Service 672cf4
  if (fds == -1)
Packit Service 672cf4
    {
Packit Service 672cf4
      struct rlimit rl;
Packit Service 672cf4
      rc = getrlimit (RLIMIT_NOFILE, &rl);
Packit Service 672cf4
      if (rc == 0)
Packit Service 672cf4
        {
Packit Service 672cf4
          source = "RLIMIT_NOFILE";
Packit Service 672cf4
          fds = rl.rlim_max;
Packit Service 672cf4
        }
Packit Service 672cf4
    }
Packit Service 672cf4
#endif
Packit Service 672cf4
#ifdef RLIMIT_OFILE
Packit Service 672cf4
  if (fds == -1)
Packit Service 672cf4
    {
Packit Service 672cf4
      struct rlimit rl;
Packit Service 672cf4
      rc = getrlimit (RLIMIT_OFILE, &rl);
Packit Service 672cf4
      if (rc == 0)
Packit Service 672cf4
	{
Packit Service 672cf4
	  source = "RLIMIT_OFILE";
Packit Service 672cf4
	  fds = rl.rlim_max;
Packit Service 672cf4
	}
Packit Service 672cf4
    }
Packit Service 672cf4
#endif
Packit Service 672cf4
#ifdef _SC_OPEN_MAX
Packit Service 672cf4
  if (fds == -1)
Packit Service 672cf4
    {
Packit Service 672cf4
      long int scres;
Packit Service 672cf4
      scres = sysconf (_SC_OPEN_MAX);
Packit Service 672cf4
      if (scres >= 0)
Packit Service 672cf4
	{
Packit Service 672cf4
	  source = "_SC_OPEN_MAX";
Packit Service 672cf4
	  return scres;
Packit Service 672cf4
	}
Packit Service 672cf4
    }
Packit Service 672cf4
#endif
Packit Service 672cf4
#ifdef OPEN_MAX
Packit Service 672cf4
  if (fds == -1)
Packit Service 672cf4
    {
Packit Service 672cf4
      source = "OPEN_MAX";
Packit Service 672cf4
      fds = OPEN_MAX;
Packit Service 672cf4
    }
Packit Service 672cf4
#endif
Packit Service 672cf4
Packit Service 672cf4
#if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \
Packit Service 672cf4
  && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX)
Packit Service 672cf4
#warning "No known way to get the maximum number of file descriptors."
Packit Service 672cf4
#endif
Packit Service 672cf4
  if (fds == -1)
Packit Service 672cf4
    {
Packit Service 672cf4
      source = "arbitrary";
Packit Service 672cf4
      /* Arbitrary limit.  */
Packit Service 672cf4
      fds = 1024;
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 672cf4
  /* AIX returns INT32_MAX instead of a proper value.  We assume that
Packit Service 672cf4
   * this is always an error and use a more reasonable limit.  */
Packit Service 672cf4
#ifdef INT32_MAX
Packit Service 672cf4
  if (fds == INT32_MAX)
Packit Service 672cf4
    {
Packit Service 672cf4
      source = "aix-fix";
Packit Service 672cf4
      fds = 1024;
Packit Service 672cf4
    }
Packit Service 672cf4
#endif
Packit Service 672cf4
Packit Service 6c01f9
  TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%i (%s)", fds, source);
Packit Service 672cf4
  return fds;
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
Packit Service 672cf4
{
Packit Service 672cf4
  int status;
Packit Service 672cf4
  pid_t ret;
Packit Service 672cf4
Packit Service 672cf4
  *r_status = 0;
Packit Service 672cf4
  *r_signal = 0;
Packit Service 672cf4
  do
Packit Service 672cf4
    ret = _gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG);
Packit Service 672cf4
  while (ret == (pid_t)(-1) && errno == EINTR);
Packit Service 672cf4
Packit Service 672cf4
  if (ret == pid)
Packit Service 672cf4
    {
Packit Service 672cf4
      if (WIFSIGNALED (status))
Packit Service 672cf4
	{
Packit Service 672cf4
	  *r_status = 4; /* Need some value here.  */
Packit Service 672cf4
	  *r_signal = WTERMSIG (status);
Packit Service 672cf4
	}
Packit Service 672cf4
      else if (WIFEXITED (status))
Packit Service 672cf4
	*r_status = WEXITSTATUS (status);
Packit Service 672cf4
      else
Packit Service 672cf4
	*r_status = 4; /* Oops.  */
Packit Service 672cf4
      return 1;
Packit Service 672cf4
    }
Packit Service 672cf4
  return 0;
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
/* Returns 0 on success, -1 on error.  */
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
Packit Service 672cf4
		 struct spawn_fd_item_s *fd_list,
Packit Service 672cf4
		 void (*atfork) (void *opaque, int reserved),
Packit Service 672cf4
		 void *atforkvalue, pid_t *r_pid)
Packit Service 672cf4
{
Packit Service 672cf4
  pid_t pid;
Packit Service 672cf4
  int i;
Packit Service 672cf4
  int status;
Packit Service 672cf4
  int signo;
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
Packit Service 672cf4
	      "path=%s", path);
Packit Service 672cf4
  i = 0;
Packit Service 672cf4
  while (argv[i])
Packit Service 672cf4
    {
Packit Service 6c01f9
      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
Packit Service 672cf4
      i++;
Packit Service 672cf4
    }
Packit Service 672cf4
  for (i = 0; fd_list[i].fd != -1; i++)
Packit Service 6c01f9
    if (fd_list[i].dup_to == -1)
Packit Service 6c01f9
      TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd);
Packit Service 6c01f9
    else
Packit Service 6c01f9
      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
Packit Service 672cf4
Packit Service 672cf4
  pid = fork ();
Packit Service 672cf4
  if (pid == -1)
Packit Service 672cf4
    return TRACE_SYSRES (-1);
Packit Service 672cf4
Packit Service 672cf4
  if (!pid)
Packit Service 672cf4
    {
Packit Service 672cf4
      /* Intermediate child to prevent zombie processes.  */
Packit Service 672cf4
      if ((pid = fork ()) == 0)
Packit Service 672cf4
	{
Packit Service 672cf4
	  /* Child.  */
Packit Service 672cf4
          int max_fds = -1;
Packit Service 672cf4
          int fd;
Packit Service 672cf4
	  int seen_stdin = 0;
Packit Service 672cf4
	  int seen_stdout = 0;
Packit Service 672cf4
	  int seen_stderr = 0;
Packit Service 672cf4
Packit Service 672cf4
	  if (atfork)
Packit Service 672cf4
	    atfork (atforkvalue, 0);
Packit Service 672cf4
Packit Service 672cf4
          /* First close all fds which will not be inherited.  If we
Packit Service 672cf4
           * have closefrom(2) we first figure out the highest fd we
Packit Service 672cf4
           * do not want to close, then call closefrom, and on success
Packit Service 672cf4
           * use the regular code to close all fds up to the start
Packit Service 672cf4
           * point of closefrom.  Note that Solaris' and FreeBSD's closefrom do
Packit Service 672cf4
           * not return errors.  */
Packit Service 672cf4
#ifdef HAVE_CLOSEFROM
Packit Service 672cf4
          {
Packit Service 672cf4
            fd = -1;
Packit Service 672cf4
            for (i = 0; fd_list[i].fd != -1; i++)
Packit Service 672cf4
              if (fd_list[i].fd > fd)
Packit Service 672cf4
                fd = fd_list[i].fd;
Packit Service 672cf4
            fd++;
Packit Service 672cf4
#if defined(__sun) || defined(__FreeBSD__)
Packit Service 672cf4
            closefrom (fd);
Packit Service 672cf4
            max_fds = fd;
Packit Service 672cf4
#else /*!__sun */
Packit Service 672cf4
            while ((i = closefrom (fd)) && errno == EINTR)
Packit Service 672cf4
              ;
Packit Service 672cf4
            if (!i || errno == EBADF)
Packit Service 672cf4
              max_fds = fd;
Packit Service 672cf4
#endif /*!__sun*/
Packit Service 672cf4
          }
Packit Service 672cf4
#endif /*HAVE_CLOSEFROM*/
Packit Service 672cf4
          if (max_fds == -1)
Packit Service 672cf4
            max_fds = get_max_fds ();
Packit Service 672cf4
          for (fd = 0; fd < max_fds; fd++)
Packit Service 672cf4
            {
Packit Service 672cf4
              for (i = 0; fd_list[i].fd != -1; i++)
Packit Service 672cf4
                if (fd_list[i].fd == fd)
Packit Service 672cf4
                  break;
Packit Service 672cf4
              if (fd_list[i].fd == -1)
Packit Service 672cf4
                close (fd);
Packit Service 672cf4
            }
Packit Service 672cf4
Packit Service 672cf4
	  /* And now dup and close those to be duplicated.  */
Packit Service 672cf4
	  for (i = 0; fd_list[i].fd != -1; i++)
Packit Service 672cf4
	    {
Packit Service 672cf4
	      int child_fd;
Packit Service 672cf4
	      int res;
Packit Service 672cf4
Packit Service 672cf4
	      if (fd_list[i].dup_to != -1)
Packit Service 672cf4
		child_fd = fd_list[i].dup_to;
Packit Service 672cf4
	      else
Packit Service 672cf4
		child_fd = fd_list[i].fd;
Packit Service 672cf4
Packit Service 672cf4
	      if (child_fd == 0)
Packit Service 672cf4
		seen_stdin = 1;
Packit Service 672cf4
	      else if (child_fd == 1)
Packit Service 672cf4
		seen_stdout = 1;
Packit Service 672cf4
	      else if (child_fd == 2)
Packit Service 672cf4
		seen_stderr = 1;
Packit Service 672cf4
Packit Service 672cf4
	      if (fd_list[i].dup_to == -1)
Packit Service 672cf4
		continue;
Packit Service 672cf4
Packit Service 672cf4
	      res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
Packit Service 672cf4
	      if (res < 0)
Packit Service 672cf4
		{
Packit Service 672cf4
#if 0
Packit Service 672cf4
		  /* FIXME: The debug file descriptor is not
Packit Service 672cf4
		     dup'ed anyway, so we can't see this.  */
Packit Service 6c01f9
		  TRACE_LOG1 ("dup2 failed in child: %s\n",
Packit Service 672cf4
			      strerror (errno));
Packit Service 672cf4
#endif
Packit Service 672cf4
		  _exit (8);
Packit Service 672cf4
		}
Packit Service 672cf4
Packit Service 672cf4
	      close (fd_list[i].fd);
Packit Service 672cf4
	    }
Packit Service 672cf4
Packit Service 672cf4
	  if (! seen_stdin || ! seen_stdout || !seen_stderr)
Packit Service 672cf4
	    {
Packit Service 672cf4
	      fd = open ("/dev/null", O_RDWR);
Packit Service 672cf4
	      if (fd == -1)
Packit Service 672cf4
		{
Packit Service 672cf4
		  /* The debug file descriptor is not dup'ed, so we
Packit Service 672cf4
		     can't do a trace output.  */
Packit Service 672cf4
		  _exit (8);
Packit Service 672cf4
		}
Packit Service 672cf4
	      /* Make sure that the process has connected stdin.  */
Packit Service 672cf4
	      if (! seen_stdin && fd != 0)
Packit Service 672cf4
		{
Packit Service 672cf4
		  if (dup2 (fd, 0) == -1)
Packit Service 672cf4
                    _exit (8);
Packit Service 672cf4
		}
Packit Service 672cf4
	      if (! seen_stdout && fd != 1)
Packit Service 672cf4
                {
Packit Service 672cf4
                  if (dup2 (fd, 1) == -1)
Packit Service 672cf4
                    _exit (8);
Packit Service 672cf4
                }
Packit Service 672cf4
	      if (! seen_stderr && fd != 2)
Packit Service 672cf4
                {
Packit Service 672cf4
                  if (dup2 (fd, 2) == -1)
Packit Service 672cf4
                    _exit (8);
Packit Service 672cf4
                }
Packit Service 672cf4
	      if (fd != 0 && fd != 1 && fd != 2)
Packit Service 672cf4
		close (fd);
Packit Service 672cf4
	    }
Packit Service 672cf4
Packit Service 672cf4
	  execv (path, (char *const *) argv);
Packit Service 672cf4
	  /* Hmm: in that case we could write a special status code to the
Packit Service 672cf4
	     status-pipe.  */
Packit Service 672cf4
	  _exit (8);
Packit Service 672cf4
	  /* End child.  */
Packit Service 672cf4
	}
Packit Service 672cf4
      if (pid == -1)
Packit Service 672cf4
	_exit (1);
Packit Service 672cf4
      else
Packit Service 672cf4
	_exit (0);
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_LOG1 ("waiting for child process pid=%i", pid);
Packit Service 672cf4
  _gpgme_io_waitpid (pid, 1, &status, &signo);
Packit Service 672cf4
  if (status)
Packit Service 672cf4
    return TRACE_SYSRES (-1);
Packit Service 672cf4
Packit Service 672cf4
  for (i = 0; fd_list[i].fd != -1; i++)
Packit Service 672cf4
    {
Packit Service 672cf4
      if (! (flags & IOSPAWN_FLAG_NOCLOSE))
Packit Service 672cf4
	_gpgme_io_close (fd_list[i].fd);
Packit Service 672cf4
      /* No handle translation.  */
Packit Service 672cf4
      fd_list[i].peer_name = fd_list[i].fd;
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 672cf4
  if (r_pid)
Packit Service 672cf4
    *r_pid = pid;
Packit Service 672cf4
Packit Service 672cf4
  return TRACE_SYSRES (0);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
/* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
Packit Service 672cf4
   nothing to select, > 0 = number of signaled fds.  */
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
Packit Service 672cf4
{
Packit Service 672cf4
  fd_set readfds;
Packit Service 672cf4
  fd_set writefds;
Packit Service 672cf4
  unsigned int i;
Packit Service 672cf4
  int any;
Packit Service 672cf4
  int max_fd;
Packit Service 672cf4
  int n;
Packit Service 672cf4
  int count;
Packit Service 672cf4
  /* Use a 1s timeout.  */
Packit Service 672cf4
  struct timeval timeout = { 1, 0 };
Packit Service 672cf4
  void *dbg_help = NULL;
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
Packit Service 6c01f9
	      "nfds=%u, nonblock=%u", nfds, nonblock);
Packit Service 672cf4
Packit Service 672cf4
  FD_ZERO (&readfds);
Packit Service 672cf4
  FD_ZERO (&writefds);
Packit Service 672cf4
  max_fd = 0;
Packit Service 672cf4
  if (nonblock)
Packit Service 672cf4
    timeout.tv_sec = 0;
Packit Service 672cf4
Packit Service 672cf4
  TRACE_SEQ (dbg_help, "select on [ ");
Packit Service 672cf4
Packit Service 672cf4
  any = 0;
Packit Service 672cf4
  for (i = 0; i < nfds; i++)
Packit Service 672cf4
    {
Packit Service 672cf4
      if (fds[i].fd == -1)
Packit Service 672cf4
	continue;
Packit Service 672cf4
      if (fds[i].for_read)
Packit Service 672cf4
	{
Packit Service 672cf4
          if (fds[i].fd >= FD_SETSIZE)
Packit Service 672cf4
            {
Packit Service 672cf4
              TRACE_END (dbg_help, " -BAD- ]");
Packit Service 672cf4
              gpg_err_set_errno (EMFILE);
Packit Service 672cf4
              return TRACE_SYSRES (-1);
Packit Service 672cf4
            }
Packit Service 672cf4
	  assert (!FD_ISSET (fds[i].fd, &readfds));
Packit Service 672cf4
	  FD_SET (fds[i].fd, &readfds);
Packit Service 672cf4
	  if (fds[i].fd > max_fd)
Packit Service 672cf4
	    max_fd = fds[i].fd;
Packit Service 6c01f9
	  TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
Packit Service 6c01f9
	  any = 1;
Packit Service 672cf4
        }
Packit Service 672cf4
      else if (fds[i].for_write)
Packit Service 672cf4
	{
Packit Service 672cf4
          if (fds[i].fd >= FD_SETSIZE)
Packit Service 672cf4
            {
Packit Service 672cf4
              TRACE_END (dbg_help, " -BAD- ]");
Packit Service 672cf4
              gpg_err_set_errno (EMFILE);
Packit Service 672cf4
              return TRACE_SYSRES (-1);
Packit Service 672cf4
            }
Packit Service 672cf4
	  assert (!FD_ISSET (fds[i].fd, &writefds));
Packit Service 672cf4
	  FD_SET (fds[i].fd, &writefds);
Packit Service 672cf4
	  if (fds[i].fd > max_fd)
Packit Service 672cf4
	    max_fd = fds[i].fd;
Packit Service 6c01f9
	  TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
Packit Service 672cf4
	  any = 1;
Packit Service 672cf4
        }
Packit Service 672cf4
      fds[i].signaled = 0;
Packit Service 672cf4
    }
Packit Service 672cf4
  TRACE_END (dbg_help, "]");
Packit Service 672cf4
  if (!any)
Packit Service 672cf4
    return TRACE_SYSRES (0);
Packit Service 672cf4
Packit Service 672cf4
  do
Packit Service 672cf4
    {
Packit Service 672cf4
      count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
Packit Service 672cf4
				 &timeout);
Packit Service 672cf4
    }
Packit Service 672cf4
  while (count < 0 && errno == EINTR);
Packit Service 672cf4
  if (count < 0)
Packit Service 672cf4
    return TRACE_SYSRES (-1);
Packit Service 672cf4
Packit Service 672cf4
  TRACE_SEQ (dbg_help, "select OK [ ");
Packit Service 672cf4
  if (TRACE_ENABLED (dbg_help))
Packit Service 672cf4
    {
Packit Service 672cf4
      for (i = 0; i <= max_fd; i++)
Packit Service 672cf4
	{
Packit Service 672cf4
	  if (FD_ISSET (i, &readfds))
Packit Service 6c01f9
	    TRACE_ADD1 (dbg_help, "r0x%x ", i);
Packit Service 672cf4
	  if (FD_ISSET (i, &writefds))
Packit Service 6c01f9
	    TRACE_ADD1 (dbg_help, "w0x%x ", i);
Packit Service 672cf4
        }
Packit Service 672cf4
      TRACE_END (dbg_help, "]");
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 672cf4
  /* The variable N is used to optimize it a little bit.  */
Packit Service 672cf4
  for (n = count, i = 0; i < nfds && n; i++)
Packit Service 672cf4
    {
Packit Service 672cf4
      if (fds[i].fd == -1)
Packit Service 672cf4
	;
Packit Service 672cf4
      else if (fds[i].for_read)
Packit Service 672cf4
	{
Packit Service 672cf4
	  if (FD_ISSET (fds[i].fd, &readfds))
Packit Service 672cf4
	    {
Packit Service 672cf4
	      fds[i].signaled = 1;
Packit Service 672cf4
	      n--;
Packit Service 672cf4
            }
Packit Service 672cf4
        }
Packit Service 672cf4
      else if (fds[i].for_write)
Packit Service 672cf4
	{
Packit Service 672cf4
	  if (FD_ISSET (fds[i].fd, &writefds))
Packit Service 672cf4
	    {
Packit Service 672cf4
	      fds[i].signaled = 1;
Packit Service 672cf4
	      n--;
Packit Service 672cf4
            }
Packit Service 672cf4
        }
Packit Service 672cf4
    }
Packit Service 672cf4
  return TRACE_SYSRES (count);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4

Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
Packit Service 672cf4
{
Packit Service 672cf4
  int nread;
Packit Service 672cf4
  int saved_errno;
Packit Service 672cf4
  struct iovec *iov;
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
Packit Service 6c01f9
	      "msg=%p, flags=%i", msg, flags);
Packit Service 672cf4
Packit Service 672cf4
  nread = 0;
Packit Service 672cf4
  iov = msg->msg_iov;
Packit Service 672cf4
  while (iov < msg->msg_iov + msg->msg_iovlen)
Packit Service 672cf4
    {
Packit Service 672cf4
      nread += iov->iov_len;
Packit Service 672cf4
      iov++;
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_LOG1 ("about to receive %d bytes", nread);
Packit Service 672cf4
Packit Service 672cf4
  do
Packit Service 672cf4
    {
Packit Service 672cf4
      nread = _gpgme_ath_recvmsg (fd, msg, flags);
Packit Service 672cf4
    }
Packit Service 672cf4
  while (nread == -1 && errno == EINTR);
Packit Service 672cf4
  saved_errno = errno;
Packit Service 672cf4
  if (nread > 0)
Packit Service 672cf4
    {
Packit Service 672cf4
      int nr = nread;
Packit Service 672cf4
Packit Service 672cf4
      iov = msg->msg_iov;
Packit Service 672cf4
      while (nr > 0)
Packit Service 672cf4
	{
Packit Service 672cf4
	  int len = nr > iov->iov_len ? iov->iov_len : nr;
Packit Service 6c01f9
	  TRACE_LOGBUF (msg->msg_iov->iov_base, len);
Packit Service 672cf4
	  iov++;
Packit Service 672cf4
	  nr -= len;
Packit Service 672cf4
	}
Packit Service 672cf4
    }
Packit Service 672cf4
  errno = saved_errno;
Packit Service 672cf4
  return TRACE_SYSRES (nread);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
Packit Service 672cf4
{
Packit Service 672cf4
  int nwritten;
Packit Service 672cf4
  struct iovec *iov;
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
Packit Service 6c01f9
	      "msg=%p, flags=%i", msg, flags);
Packit Service 672cf4
Packit Service 672cf4
  nwritten = 0;
Packit Service 672cf4
  iov = msg->msg_iov;
Packit Service 672cf4
  while (iov < msg->msg_iov + msg->msg_iovlen)
Packit Service 672cf4
    {
Packit Service 672cf4
      nwritten += iov->iov_len;
Packit Service 672cf4
      iov++;
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_LOG1 ("about to receive %d bytes", nwritten);
Packit Service 672cf4
  iov = msg->msg_iov;
Packit Service 672cf4
  while (nwritten > 0)
Packit Service 672cf4
    {
Packit Service 672cf4
      int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
Packit Service 6c01f9
      TRACE_LOGBUF (msg->msg_iov->iov_base, len);
Packit Service 672cf4
      iov++;
Packit Service 672cf4
      nwritten -= len;
Packit Service 672cf4
    }
Packit Service 672cf4
Packit Service 672cf4
  do
Packit Service 672cf4
    {
Packit Service 672cf4
      nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
Packit Service 672cf4
    }
Packit Service 672cf4
  while (nwritten == -1 && errno == EINTR);
Packit Service 672cf4
  return TRACE_SYSRES (nwritten);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_dup (int fd)
Packit Service 672cf4
{
Packit Service 672cf4
  int new_fd;
Packit Service 672cf4
Packit Service 672cf4
  do
Packit Service 672cf4
    new_fd = dup (fd);
Packit Service 672cf4
  while (new_fd == -1 && errno == EINTR);
Packit Service 672cf4
Packit Service 6c01f9
  TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
Packit Service 672cf4
Packit Service 672cf4
  return new_fd;
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4

Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_socket (int domain, int type, int proto)
Packit Service 672cf4
{
Packit Service 672cf4
  int res;
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
Packit Service 6c01f9
	      "type=%i, proto=%i", type, proto);
Packit Service 672cf4
Packit Service 672cf4
  res = socket (domain, type, proto);
Packit Service 672cf4
Packit Service 672cf4
  return TRACE_SYSRES (res);
Packit Service 672cf4
}
Packit Service 672cf4
Packit Service 672cf4
Packit Service 672cf4
int
Packit Service 672cf4
_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
Packit Service 672cf4
{
Packit Service 672cf4
  int res;
Packit Service 672cf4
Packit Service 6c01f9
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
Packit Service 6c01f9
	      "addr=%p, addrlen=%i", addr, addrlen);
Packit Service 672cf4
Packit Service 672cf4
  do
Packit Service 672cf4
    res = ath_connect (fd, addr, addrlen);
Packit Service 672cf4
  while (res == -1 && errno == EINTR);
Packit Service 672cf4
Packit Service 672cf4
  return TRACE_SYSRES (res);
Packit Service 672cf4
}