Blame src/engine-spawn.c

Packit d7e8d0
/* engine-spawn.c - Run an arbitrary program
Packit d7e8d0
   Copyright (C) 2014 g10 Code GmbH
Packit d7e8d0
Packit d7e8d0
   This file is part of GPGME.
Packit d7e8d0
Packit d7e8d0
   GPGME is free software; you can redistribute it and/or modify it
Packit d7e8d0
   under the terms of the GNU Lesser General Public License as
Packit d7e8d0
   published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
   the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
   GPGME is distributed in the hope that it will be useful, but
Packit d7e8d0
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
   Lesser General Public License for more details.
Packit d7e8d0
Packit d7e8d0
   You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
   License along with this program; if not, see <https://www.gnu.org/licenses/>.
Packit d7e8d0
*/
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <stdio.h>
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#include <assert.h>
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
#ifdef HAVE_UNISTD_H
Packit d7e8d0
# include <unistd.h>
Packit d7e8d0
#endif
Packit d7e8d0
#ifdef HAVE_LOCALE_H
Packit d7e8d0
#include <locale.h>
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include "gpgme.h"
Packit d7e8d0
#include "util.h"
Packit d7e8d0
#include "ops.h"
Packit d7e8d0
#include "wait.h"
Packit d7e8d0
#include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
Packit d7e8d0
#include "priv-io.h"
Packit d7e8d0
#include "sema.h"
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
Packit d7e8d0
#include "engine-backend.h"
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* This type is used to build a list of data sources/sinks.  */
Packit d7e8d0
struct datalist_s
Packit d7e8d0
{
Packit d7e8d0
  struct datalist_s *next;
Packit d7e8d0
  gpgme_data_t data;  /* The data object. */
Packit d7e8d0
  int inbound;        /* True if this is used for reading from the peer.  */
Packit d7e8d0
  int dup_to;         /* The fd used by the peer.  */
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
struct fd_data_map_s
Packit d7e8d0
{
Packit d7e8d0
  gpgme_data_t data;
Packit d7e8d0
  int inbound;  /* True if this is used for reading from the peer. */
Packit d7e8d0
  int dup_to;   /* Dup the fd to that one.  */
Packit d7e8d0
  int fd;       /* The fd to use.  */
Packit d7e8d0
  int peer_fd;  /* The other side of the pipe. */
Packit d7e8d0
  void *tag;    /* Tag used by the I/O callback.  */
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
struct engine_spawn
Packit d7e8d0
{
Packit d7e8d0
  struct datalist_s *arglist;
Packit d7e8d0
  struct datalist_s **argtail;
Packit d7e8d0
Packit d7e8d0
  struct fd_data_map_s *fd_data_map;
Packit d7e8d0
Packit d7e8d0
  struct gpgme_io_cbs io_cbs;
Packit d7e8d0
};
Packit d7e8d0
typedef struct engine_spawn *engine_spawn_t;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void engspawn_io_event (void *engine,
Packit d7e8d0
                               gpgme_event_io_t type, void *type_data);
Packit d7e8d0
static gpgme_error_t engspawn_cancel (void *engine);
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static void
Packit d7e8d0
close_notify_handler (int fd, void *opaque)
Packit d7e8d0
{
Packit d7e8d0
  engine_spawn_t esp = opaque;
Packit d7e8d0
  int i;
Packit d7e8d0
Packit d7e8d0
  assert (fd != -1);
Packit d7e8d0
Packit d7e8d0
  if (esp->fd_data_map)
Packit d7e8d0
    {
Packit d7e8d0
      for (i = 0; esp->fd_data_map[i].data; i++)
Packit d7e8d0
	{
Packit d7e8d0
	  if (esp->fd_data_map[i].fd == fd)
Packit d7e8d0
	    {
Packit d7e8d0
	      if (esp->fd_data_map[i].tag)
Packit d7e8d0
		(*esp->io_cbs.remove) (esp->fd_data_map[i].tag);
Packit d7e8d0
	      esp->fd_data_map[i].fd = -1;
Packit d7e8d0
	      break;
Packit d7e8d0
            }
Packit d7e8d0
	  if (esp->fd_data_map[i].peer_fd == fd)
Packit d7e8d0
	    {
Packit d7e8d0
	      esp->fd_data_map[i].peer_fd = -1;
Packit d7e8d0
	      break;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
add_data (engine_spawn_t esp, gpgme_data_t data, int dup_to, int inbound)
Packit d7e8d0
{
Packit d7e8d0
  struct datalist_s *a;
Packit d7e8d0
Packit d7e8d0
  assert (esp);
Packit d7e8d0
  assert (data);
Packit d7e8d0
Packit d7e8d0
  a = malloc (sizeof *a);
Packit d7e8d0
  if (!a)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
  a->next = NULL;
Packit d7e8d0
  a->data = data;
Packit d7e8d0
  a->inbound = inbound;
Packit d7e8d0
  a->dup_to = dup_to;
Packit d7e8d0
  *esp->argtail = a;
Packit d7e8d0
  esp->argtail = &a->next;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static void
Packit d7e8d0
free_fd_data_map (struct fd_data_map_s *fd_data_map)
Packit d7e8d0
{
Packit d7e8d0
  int i;
Packit d7e8d0
Packit d7e8d0
  if (!fd_data_map)
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
  for (i = 0; fd_data_map[i].data; i++)
Packit d7e8d0
    {
Packit d7e8d0
      if (fd_data_map[i].fd != -1)
Packit d7e8d0
	_gpgme_io_close (fd_data_map[i].fd);
Packit d7e8d0
      if (fd_data_map[i].peer_fd != -1)
Packit d7e8d0
	_gpgme_io_close (fd_data_map[i].peer_fd);
Packit d7e8d0
      /* Don't release data because this is only a reference.  */
Packit d7e8d0
    }
Packit d7e8d0
  free (fd_data_map);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
build_fd_data_map (engine_spawn_t esp)
Packit d7e8d0
{
Packit d7e8d0
  struct datalist_s *a;
Packit d7e8d0
  size_t datac;
Packit d7e8d0
  int fds[2];
Packit d7e8d0
Packit d7e8d0
  for (datac = 0, a = esp->arglist; a; a = a->next)
Packit d7e8d0
    if (a->data)
Packit d7e8d0
      datac++;
Packit d7e8d0
Packit d7e8d0
  free_fd_data_map (esp->fd_data_map);
Packit d7e8d0
  esp->fd_data_map = calloc (datac + 1, sizeof *esp->fd_data_map);
Packit d7e8d0
  if (!esp->fd_data_map)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  for (datac = 0, a = esp->arglist; a; a = a->next)
Packit d7e8d0
    {
Packit d7e8d0
      assert (a->data);
Packit d7e8d0
Packit d7e8d0
      if (_gpgme_io_pipe (fds, a->inbound ? 1 : 0) == -1)
Packit d7e8d0
        {
Packit d7e8d0
          free (esp->fd_data_map);
Packit d7e8d0
          esp->fd_data_map = NULL;
Packit d7e8d0
          return gpg_error_from_syserror ();
Packit d7e8d0
        }
Packit d7e8d0
      if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp)
Packit d7e8d0
          || _gpgme_io_set_close_notify (fds[1], close_notify_handler, esp))
Packit d7e8d0
        {
Packit d7e8d0
          /* FIXME: Need error cleanup.  */
Packit d7e8d0
          return gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
        }
Packit d7e8d0
Packit d7e8d0
      esp->fd_data_map[datac].inbound = a->inbound;
Packit d7e8d0
      if (a->inbound)
Packit d7e8d0
        {
Packit d7e8d0
          esp->fd_data_map[datac].fd       = fds[0];
Packit d7e8d0
          esp->fd_data_map[datac].peer_fd  = fds[1];
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        {
Packit d7e8d0
          esp->fd_data_map[datac].fd       = fds[1];
Packit d7e8d0
          esp->fd_data_map[datac].peer_fd  = fds[0];
Packit d7e8d0
        }
Packit d7e8d0
      esp->fd_data_map[datac].data    = a->data;
Packit d7e8d0
      esp->fd_data_map[datac].dup_to  = a->dup_to;
Packit d7e8d0
      datac++;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
add_io_cb (engine_spawn_t esp, int fd, int dir, gpgme_io_cb_t handler,
Packit d7e8d0
           void *data, void **tag)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  err = (*esp->io_cbs.add) (esp->io_cbs.add_priv, fd, dir, handler, data, tag);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
  if (!dir) /* Fixme: Kludge around poll() problem.  */
Packit d7e8d0
    err = _gpgme_io_set_nonblocking (fd);
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
engspawn_start (engine_spawn_t esp, const char *file, const char *argv[],
Packit d7e8d0
                unsigned int flags)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  int i, n;
Packit d7e8d0
  int status;
Packit d7e8d0
  struct spawn_fd_item_s *fd_list;
Packit d7e8d0
  pid_t pid;
Packit d7e8d0
  unsigned int spflags;
Packit d7e8d0
  const char *save_argv0 = NULL;
Packit d7e8d0
Packit d7e8d0
  if (!esp || !file || !argv || !argv[0])
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
Packit d7e8d0
  spflags = 0;
Packit d7e8d0
  if ((flags & GPGME_SPAWN_DETACHED))
Packit d7e8d0
    spflags |= IOSPAWN_FLAG_DETACHED;
Packit d7e8d0
  if ((flags & GPGME_SPAWN_ALLOW_SET_FG))
Packit d7e8d0
    spflags |= IOSPAWN_FLAG_ALLOW_SET_FG;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
  err = build_fd_data_map (esp);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  n = 0;
Packit d7e8d0
  for (i = 0; esp->fd_data_map[i].data; i++)
Packit d7e8d0
    n++;
Packit d7e8d0
  fd_list = calloc (n+1, sizeof *fd_list);
Packit d7e8d0
  if (!fd_list)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  /* Build the fd list for the child.  */
Packit d7e8d0
  n = 0;
Packit d7e8d0
  for (i = 0; esp->fd_data_map[i].data; i++)
Packit d7e8d0
    {
Packit d7e8d0
      fd_list[n].fd = esp->fd_data_map[i].peer_fd;
Packit d7e8d0
      fd_list[n].dup_to = esp->fd_data_map[i].dup_to;
Packit d7e8d0
      n++;
Packit d7e8d0
    }
Packit d7e8d0
  fd_list[n].fd = -1;
Packit d7e8d0
  fd_list[n].dup_to = -1;
Packit d7e8d0
Packit d7e8d0
  if (argv[0] && !*argv[0])
Packit d7e8d0
    {
Packit d7e8d0
      save_argv0 = argv[0];
Packit d7e8d0
      argv[0] = _gpgme_get_basename (file);
Packit d7e8d0
    }
Packit d7e8d0
  status = _gpgme_io_spawn (file, (char * const *)argv, spflags,
Packit d7e8d0
                            fd_list, NULL, NULL, &pid;;
Packit d7e8d0
  if (save_argv0)
Packit d7e8d0
    argv[0] = save_argv0;
Packit d7e8d0
  free (fd_list);
Packit d7e8d0
  if (status == -1)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  for (i = 0; esp->fd_data_map[i].data; i++)
Packit d7e8d0
    {
Packit d7e8d0
      err = add_io_cb (esp, esp->fd_data_map[i].fd,
Packit d7e8d0
                       esp->fd_data_map[i].inbound,
Packit d7e8d0
                       esp->fd_data_map[i].inbound
Packit d7e8d0
                       ? _gpgme_data_inbound_handler
Packit d7e8d0
                       : _gpgme_data_outbound_handler,
Packit d7e8d0
                       esp->fd_data_map[i].data, &esp->fd_data_map[i].tag);
Packit d7e8d0
      if (err)
Packit d7e8d0
        return err;  /* FIXME: kill the child */
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  engspawn_io_event (esp, GPGME_EVENT_START, NULL);
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/*
Packit d7e8d0
    Public functions
Packit d7e8d0
 */
Packit d7e8d0
Packit d7e8d0
static const char *
Packit d7e8d0
engspawn_get_file_name (void)
Packit d7e8d0
{
Packit d7e8d0
  return "/nonexistent";
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static char *
Packit d7e8d0
engspawn_get_version (const char *file_name)
Packit d7e8d0
{
Packit d7e8d0
  (void)file_name;
Packit d7e8d0
  return NULL;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static const char *
Packit d7e8d0
engspawn_get_req_version (void)
Packit d7e8d0
{
Packit d7e8d0
  return NULL;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
engspawn_new (void **engine, const char *file_name, const char *home_dir,
Packit d7e8d0
              const char *version)
Packit d7e8d0
{
Packit d7e8d0
  engine_spawn_t esp;
Packit d7e8d0
Packit d7e8d0
  (void)file_name;
Packit d7e8d0
  (void)home_dir;
Packit d7e8d0
  (void)version;
Packit d7e8d0
Packit d7e8d0
  esp = calloc (1, sizeof *esp);
Packit d7e8d0
  if (!esp)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  esp->argtail = &esp->arglist;
Packit d7e8d0
  *engine = esp;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
engspawn_release (void *engine)
Packit d7e8d0
{
Packit d7e8d0
  engine_spawn_t esp = engine;
Packit d7e8d0
Packit d7e8d0
  if (!esp)
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
  engspawn_cancel (engine);
Packit d7e8d0
Packit d7e8d0
  while (esp->arglist)
Packit d7e8d0
    {
Packit d7e8d0
      struct datalist_s *next = esp->arglist->next;
Packit d7e8d0
Packit d7e8d0
      free (esp->arglist);
Packit d7e8d0
      esp->arglist = next;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  free (esp);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
engspawn_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
Packit d7e8d0
{
Packit d7e8d0
  engine_spawn_t esp = engine;
Packit d7e8d0
Packit d7e8d0
  esp->io_cbs = *io_cbs;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data)
Packit d7e8d0
{
Packit d7e8d0
  engine_spawn_t esp = engine;
Packit d7e8d0
Packit d7e8d0
  TRACE3 (DEBUG_ENGINE, "gpgme:engspawn_io_event", esp,
Packit d7e8d0
          "event %p, type %d, type_data %p",
Packit d7e8d0
          esp->io_cbs.event, type, type_data);
Packit d7e8d0
  if (esp->io_cbs.event)
Packit d7e8d0
    (*esp->io_cbs.event) (esp->io_cbs.event_priv, type, type_data);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
engspawn_cancel (void *engine)
Packit d7e8d0
{
Packit d7e8d0
  engine_spawn_t esp = engine;
Packit d7e8d0
Packit d7e8d0
  if (!esp)
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
Packit d7e8d0
  if (esp->fd_data_map)
Packit d7e8d0
    {
Packit d7e8d0
      free_fd_data_map (esp->fd_data_map);
Packit d7e8d0
      esp->fd_data_map = NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
engspawn_op_spawn (void *engine,
Packit d7e8d0
                   const char *file, const char *argv[],
Packit d7e8d0
                   gpgme_data_t datain,
Packit d7e8d0
                   gpgme_data_t dataout, gpgme_data_t dataerr,
Packit d7e8d0
                   unsigned int flags)
Packit d7e8d0
{
Packit d7e8d0
  engine_spawn_t esp = engine;
Packit d7e8d0
  gpgme_error_t err = 0;
Packit d7e8d0
Packit d7e8d0
  if (datain)
Packit d7e8d0
    err = add_data (esp, datain, 0, 0);
Packit d7e8d0
  if (!err && dataout)
Packit d7e8d0
    err = add_data (esp, dataout, 1, 1);
Packit d7e8d0
  if (!err && dataerr)
Packit d7e8d0
    err = add_data (esp, dataerr, 2, 1);
Packit d7e8d0
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = engspawn_start (esp, file, argv, flags);
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
struct engine_ops _gpgme_engine_ops_spawn =
Packit d7e8d0
  {
Packit d7e8d0
    /* Static functions.  */
Packit d7e8d0
    engspawn_get_file_name,
Packit d7e8d0
    NULL,               /* get_home_dir */
Packit d7e8d0
    engspawn_get_version,
Packit d7e8d0
    engspawn_get_req_version,
Packit d7e8d0
    engspawn_new,
Packit d7e8d0
Packit d7e8d0
    /* Member functions.  */
Packit d7e8d0
    engspawn_release,
Packit d7e8d0
    NULL,		/* reset */
Packit d7e8d0
    NULL,               /* set_status_cb */
Packit d7e8d0
    NULL,		/* set_status_handler */
Packit d7e8d0
    NULL,		/* set_command_handler */
Packit d7e8d0
    NULL,		/* set_colon_line_handler */
Packit d7e8d0
    NULL,		/* set_locale */
Packit d7e8d0
    NULL,		/* set_protocol */
Packit d7e8d0
    NULL,		/* decrypt */
Packit d7e8d0
    NULL,		/* delete */
Packit d7e8d0
    NULL,		/* edit */
Packit d7e8d0
    NULL,		/* encrypt */
Packit d7e8d0
    NULL,		/* encrypt_sign */
Packit d7e8d0
    NULL,		/* export */
Packit d7e8d0
    NULL,		/* export_ext */
Packit d7e8d0
    NULL,		/* genkey */
Packit d7e8d0
    NULL,		/* import */
Packit d7e8d0
    NULL,		/* keylist */
Packit d7e8d0
    NULL,		/* keylist_ext */
Packit d7e8d0
    NULL,               /* keylist_data */
Packit d7e8d0
    NULL,               /* keysign */
Packit d7e8d0
    NULL,               /* tofu_policy */
Packit d7e8d0
    NULL,		/* sign */
Packit d7e8d0
    NULL,		/* trustlist */
Packit d7e8d0
    NULL,		/* verify */
Packit d7e8d0
    NULL,		/* getauditlog */
Packit d7e8d0
    NULL,               /* opassuan_transact */
Packit d7e8d0
    NULL,		/* conf_load */
Packit d7e8d0
    NULL,		/* conf_save */
Packit d7e8d0
    NULL,		/* conf_dir */
Packit d7e8d0
    NULL,               /* query_swdb */
Packit d7e8d0
    engspawn_set_io_cbs,
Packit d7e8d0
    engspawn_io_event,	/* io_event */
Packit d7e8d0
    engspawn_cancel,	/* cancel */
Packit d7e8d0
    NULL,               /* cancel_op */
Packit d7e8d0
    NULL,               /* passwd */
Packit d7e8d0
    NULL,               /* set_pinentry_mode */
Packit d7e8d0
    engspawn_op_spawn   /* opspawn */
Packit d7e8d0
  };