Blame src/engine-spawn.c

Packit d7e8d0
/* engine-spawn.c - Run an arbitrary program
Packit Service 30b792
 * Copyright (C) 2014 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 Service 30b792
 */
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 Service 30b792
  if ((flags & GPGME_SPAWN_SHOW_WINDOW))
Packit Service 30b792
    spflags |= IOSPAWN_FLAG_SHOW_WINDOW;
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 Service 30b792
  TRACE (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 Service 30b792
    NULL,               /* set_engine_flags */
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
  };