Blame src/posix-io.c

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