diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 3 | ||||
| -rw-r--r-- | src/engine-assuan.c | 3 | ||||
| -rw-r--r-- | src/engine-backend.h | 9 | ||||
| -rw-r--r-- | src/engine-g13.c | 3 | ||||
| -rw-r--r-- | src/engine-gpg.c | 3 | ||||
| -rw-r--r-- | src/engine-gpgconf.c | 3 | ||||
| -rw-r--r-- | src/engine-gpgsm.c | 3 | ||||
| -rw-r--r-- | src/engine-spawn.c | 467 | ||||
| -rw-r--r-- | src/engine-uiserver.c | 3 | ||||
| -rw-r--r-- | src/engine.c | 25 | ||||
| -rw-r--r-- | src/engine.h | 6 | ||||
| -rw-r--r-- | src/gpgme-tool.c | 64 | ||||
| -rw-r--r-- | src/gpgme.c | 14 | ||||
| -rw-r--r-- | src/gpgme.def | 2 | ||||
| -rw-r--r-- | src/gpgme.h.in | 23 | ||||
| -rw-r--r-- | src/libgpgme.vers | 3 | ||||
| -rw-r--r-- | src/spawn.c | 105 | 
17 files changed, 723 insertions, 16 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index abc014c7..82f5327a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,11 +89,12 @@ main_sources =								\  	sign.c passphrase.c progress.c					\  	key.c keylist.c trust-item.c trustlist.c			\  	import.c export.c genkey.c delete.c edit.c getauditlog.c        \ -	opassuan.c passwd.c assuan-support.c                            \ +	opassuan.c passwd.c spawn.c assuan-support.c                    \  	engine.h engine-backend.h engine.c engine-gpg.c status-table.c	\  	engine-gpgsm.c engine-assuan.c engine-gpgconf.c                 \  	$(uiserver_components)						\  	engine-g13.c vfs-mount.c vfs-create.c			        \ +	engine-spawn.c 	                                                \  	gpgconf.c							\  	sema.h priv-io.h $(system_components) sys-util.h dirinfo.c	\  	debug.c debug.h gpgme.c version.c error.c diff --git a/src/engine-assuan.c b/src/engine-assuan.c index 5ef30479..663b2eab 100644 --- a/src/engine-assuan.c +++ b/src/engine-assuan.c @@ -783,5 +783,6 @@ struct engine_ops _gpgme_engine_ops_assuan =      llass_cancel,      llass_cancel_op,      NULL,               /* passwd */ -    NULL                /* set_pinentry_mode */ +    NULL,               /* set_pinentry_mode */ +    NULL                /* opspawn */    }; diff --git a/src/engine-backend.h b/src/engine-backend.h index 7e6c0c12..dbb9e932 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -124,6 +124,14 @@ struct engine_ops    /* Set the pinentry mode.  */    gpgme_error_t (*set_pinentry_mode) (void *engine, gpgme_pinentry_mode_t mode); + +  /* The spawn command.  */ +  gpgme_error_t (*opspawn) (void * engine, +                            const char *file, const char *argv[], +                            gpgme_data_t datain, +                            gpgme_data_t dataout, +                            gpgme_data_t dataerr); +  }; @@ -135,6 +143,7 @@ extern struct engine_ops _gpgme_engine_ops_g13;         /* Crypto VFS. */  #ifdef ENABLE_UISERVER  extern struct engine_ops _gpgme_engine_ops_uiserver;  #endif +extern struct engine_ops _gpgme_engine_ops_spawn;       /* Spawn engine. */  /* Prototypes for extra functions in engine-gpgconf.c  */ diff --git a/src/engine-g13.c b/src/engine-g13.c index 75154ca0..a9717eec 100644 --- a/src/engine-g13.c +++ b/src/engine-g13.c @@ -799,5 +799,6 @@ struct engine_ops _gpgme_engine_ops_g13 =      g13_cancel,      g13_cancel_op,      NULL,               /* passwd */ -    NULL                /* set_pinentry_mode */ +    NULL,               /* set_pinentry_mode */ +    NULL                /* opspawn */    }; diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 9037dd7c..a8eab3a4 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -2445,5 +2445,6 @@ struct engine_ops _gpgme_engine_ops_gpg =      gpg_cancel,      NULL,		/* cancel_op */      gpg_passwd, -    gpg_set_pinentry_mode +    gpg_set_pinentry_mode, +    NULL                /* opspawn */    }; diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c index 811ad9d7..a2407ac7 100644 --- a/src/engine-gpgconf.c +++ b/src/engine-gpgconf.c @@ -963,5 +963,6 @@ struct engine_ops _gpgme_engine_ops_gpgconf =      NULL,		/* cancel */      NULL,               /* cancel_op */      NULL,               /* passwd */ -    NULL                /* set_pinentry_mode */ +    NULL,               /* set_pinentry_mode */ +    NULL                /* opspawn */    }; diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 6bcc0952..710bf14a 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -1988,5 +1988,6 @@ struct engine_ops _gpgme_engine_ops_gpgsm =      gpgsm_cancel,      NULL,		/* cancel_op */      gpgsm_passwd, -    NULL                /* set_pinentry_mode */ +    NULL,               /* set_pinentry_mode */ +    NULL                /* opspawn */    }; diff --git a/src/engine-spawn.c b/src/engine-spawn.c new file mode 100644 index 00000000..1e71c1cc --- /dev/null +++ b/src/engine-spawn.c @@ -0,0 +1,467 @@ +/* engine-spawn.c - Run an arbitrary program +   Copyright (C) 2014 g10 Code GmbH + +   This file is part of GPGME. + +   GPGME is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as +   published by the Free Software Foundation; either version 2.1 of +   the License, or (at your option) any later version. + +   GPGME is distributed in the hope that it will be useful, but +   WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +#include "gpgme.h" +#include "util.h" +#include "ops.h" +#include "wait.h" +#include "context.h"  /*temp hack until we have GpmeData methods to do I/O */ +#include "priv-io.h" +#include "sema.h" +#include "debug.h" + +#include "engine-backend.h" + + +/* This type is used to build a list of data sources/sinks.  */ +struct datalist_s +{ +  struct datalist_s *next; +  gpgme_data_t data;  /* The data object. */ +  int inbound;        /* True if this is used for reading from the peer.  */ +  int dup_to;         /* The fd used by the peer.  */ +}; + + +struct fd_data_map_s +{ +  gpgme_data_t data; +  int inbound;  /* True if this is used for reading from the peer. */ +  int dup_to;   /* Dup the fd to that one.  */ +  int fd;       /* The fd to use.  */ +  int peer_fd;  /* The other side of the pipe. */ +  void *tag;    /* Tag used by the I/O callback.  */ +}; + + +struct engine_spawn +{ +  struct datalist_s *arglist; +  struct datalist_s **argtail; + +  struct fd_data_map_s *fd_data_map; + +  struct gpgme_io_cbs io_cbs; +}; +typedef struct engine_spawn *engine_spawn_t; + + +static void engspawn_io_event (void *engine, +                               gpgme_event_io_t type, void *type_data); +static gpgme_error_t engspawn_cancel (void *engine); + + + +static void +close_notify_handler (int fd, void *opaque) +{ +  engine_spawn_t esp = opaque; +  int i; + +  assert (fd != -1); + +  if (esp->fd_data_map) +    { +      for (i = 0; esp->fd_data_map[i].data; i++) +	{ +	  if (esp->fd_data_map[i].fd == fd) +	    { +	      if (esp->fd_data_map[i].tag) +		(*esp->io_cbs.remove) (esp->fd_data_map[i].tag); +	      esp->fd_data_map[i].fd = -1; +	      break; +            } +	  if (esp->fd_data_map[i].peer_fd == fd) +	    { +	      esp->fd_data_map[i].peer_fd = -1; +	      break; +            } +        } +    } +} + + +static gpgme_error_t +add_data (engine_spawn_t esp, gpgme_data_t data, int dup_to, int inbound) +{ +  struct datalist_s *a; + +  assert (esp); +  assert (data); + +  a = malloc (sizeof *a - 1); +  if (!a) +    return gpg_error_from_syserror (); +  a->next = NULL; +  a->data = data; +  a->inbound = inbound; +  a->dup_to = dup_to; +  *esp->argtail = a; +  esp->argtail = &a->next; +  return 0; +} + + +static void +free_fd_data_map (struct fd_data_map_s *fd_data_map) +{ +  int i; + +  if (!fd_data_map) +    return; + +  for (i = 0; fd_data_map[i].data; i++) +    { +      if (fd_data_map[i].fd != -1) +	_gpgme_io_close (fd_data_map[i].fd); +      if (fd_data_map[i].peer_fd != -1) +	_gpgme_io_close (fd_data_map[i].peer_fd); +      /* Don't release data because this is only a reference.  */ +    } +  free (fd_data_map); +} + + +static gpgme_error_t +build_fd_data_map (engine_spawn_t esp) +{ +  struct datalist_s *a; +  size_t datac; +  int fds[2]; + +  for (datac = 0, a = esp->arglist; a; a = a->next) +    if (a->data) +      datac++; + +  free_fd_data_map (esp->fd_data_map); +  esp->fd_data_map = calloc (datac + 1, sizeof *esp->fd_data_map); +  if (!esp->fd_data_map) +    return gpg_error_from_syserror (); + +  for (datac = 0, a = esp->arglist; a; a = a->next) +    { +      assert (a->data); + +      if (_gpgme_io_pipe (fds, a->inbound ? 1 : 0) == -1) +        { +          free (esp->fd_data_map); +          esp->fd_data_map = NULL; +          return gpg_error_from_syserror (); +        } +      if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp) +          || _gpgme_io_set_close_notify (fds[1], close_notify_handler, esp)) +        { +          /* FIXME: Need error cleanup.  */ +          return gpg_error (GPG_ERR_GENERAL); +        } + +      esp->fd_data_map[datac].inbound = a->inbound; +      if (a->inbound) +        { +          esp->fd_data_map[datac].fd       = fds[0]; +          esp->fd_data_map[datac].peer_fd  = fds[1]; +        } +      else +        { +          esp->fd_data_map[datac].fd       = fds[1]; +          esp->fd_data_map[datac].peer_fd  = fds[0]; +        } +      esp->fd_data_map[datac].data    = a->data; +      esp->fd_data_map[datac].dup_to  = a->dup_to; +      datac++; +    } + +  return 0; +} + + +static gpgme_error_t +add_io_cb (engine_spawn_t esp, int fd, int dir, gpgme_io_cb_t handler, +           void *data, void **tag) +{ +  gpgme_error_t err; + +  err = (*esp->io_cbs.add) (esp->io_cbs.add_priv, fd, dir, handler, data, tag); +  if (err) +    return err; +  if (!dir) /* Fixme: Kludge around poll() problem.  */ +    err = _gpgme_io_set_nonblocking (fd); +  return err; +} + + +static gpgme_error_t +engspawn_start (engine_spawn_t esp, const char *file, const char *argv[], +                unsigned int flags) +{ +  gpgme_error_t err; +  int i, n; +  int status; +  struct spawn_fd_item_s *fd_list; +  pid_t pid; +  unsigned int spflags; + +  if (!esp || !file || !argv || !argv[0]) +    return gpg_error (GPG_ERR_INV_VALUE); + +  spflags = 0; +  if ((flags & GPGME_SPAWN_DETACHED)) +    spflags |= IOSPAWN_FLAG_DETACHED; +  if ((flags & GPGME_SPAWN_ALLOW_SET_FG)) +    spflags |= IOSPAWN_FLAG_ALLOW_SET_FG; + + +  err = build_fd_data_map (esp); +  if (err) +    return err; + +  n = 0; +  for (i = 0; esp->fd_data_map[i].data; i++) +    n++; +  fd_list = calloc (n, sizeof *fd_list); +  if (!fd_list) +    return gpg_error_from_syserror (); + +  /* Build the fd list for the child.  */ +  n = 0; +  for (i = 0; esp->fd_data_map[i].data; i++) +    { +      fd_list[n].fd = esp->fd_data_map[i].peer_fd; +      fd_list[n].dup_to = esp->fd_data_map[i].dup_to; +      n++; +    } +  fd_list[n].fd = -1; +  fd_list[n].dup_to = -1; + +  status = _gpgme_io_spawn (file, (char * const *)argv, spflags, +                            fd_list, NULL, NULL, &pid); +  free (fd_list); +  if (status == -1) +    return gpg_error_from_syserror (); + +  for (i = 0; esp->fd_data_map[i].data; i++) +    { +      err = add_io_cb (esp, esp->fd_data_map[i].fd, +                       esp->fd_data_map[i].inbound, +                       esp->fd_data_map[i].inbound +                       ? _gpgme_data_inbound_handler +                       : _gpgme_data_outbound_handler, +                       esp->fd_data_map[i].data, &esp->fd_data_map[i].tag); +      if (err) +        return err;  /* FIXME: kill the child */ +    } + +  engspawn_io_event (esp, GPGME_EVENT_START, NULL); + +  return 0; +} + + + +/* +    Public functions + */ + +static const char * +engspawn_get_file_name (void) +{ +  return "/nonexistent"; +} + + +static char * +engspawn_get_version (const char *file_name) +{ +  (void)file_name; +  return strdup ("1.0"); +} + + +static const char * +engspawn_get_req_version (void) +{ +  return "1.0"; +} + + +static gpgme_error_t +engspawn_new (void **engine, const char *file_name, const char *home_dir) +{ +  engine_spawn_t esp; + +  (void)file_name; +  (void)home_dir; + +  esp = calloc (1, sizeof *esp); +  if (!esp) +    return gpg_error_from_syserror (); + +  esp->argtail = &esp->arglist; +  *engine = esp; +  return 0; +} + + +static void +engspawn_release (void *engine) +{ +  engine_spawn_t esp = engine; + +  if (!esp) +    return; + +  engspawn_cancel (engine); + +  while (esp->arglist) +    { +      struct datalist_s *next = esp->arglist->next; + +      if (esp->arglist) +	free (esp->arglist); +      esp->arglist = next; +    } + +  free (esp); +} + + +static void +engspawn_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) +{ +  engine_spawn_t esp = engine; + +  esp->io_cbs = *io_cbs; +} + + +static void +engspawn_io_event (void *engine, gpgme_event_io_t type, void *type_data) +{ +  engine_spawn_t esp = engine; + +  TRACE3 (DEBUG_ENGINE, "gpgme:engspawn_io_event", esp, +          "event %p, type %d, type_data %p", +          esp->io_cbs.event, type, type_data); +  if (esp->io_cbs.event) +    (*esp->io_cbs.event) (esp->io_cbs.event_priv, type, type_data); +} + + +static gpgme_error_t +engspawn_cancel (void *engine) +{ +  engine_spawn_t esp = engine; + +  if (!esp) +    return gpg_error (GPG_ERR_INV_VALUE); + +  if (esp->fd_data_map) +    { +      free_fd_data_map (esp->fd_data_map); +      esp->fd_data_map = NULL; +    } + +  return 0; +} + + +static gpgme_error_t +engspawn_op_spawn (void *engine, +                   const char *file, const char *argv[], +                   gpgme_data_t datain, +                   gpgme_data_t dataout, gpgme_data_t dataerr, +                   unsigned int flags) +{ +  engine_spawn_t esp = engine; +  gpgme_error_t err = 0; + +  if (datain) +    err = add_data (esp, datain, 0, 0); +  if (!err && dataout) +    err = add_data (esp, dataout, 1, 1); +  if (!err && dataerr) +    err = add_data (esp, dataerr, 2, 1); + +  if (!err) +    err = engspawn_start (esp, file, argv, flags); + +  return err; +} + + + +struct engine_ops _gpgme_engine_ops_spawn = +  { +    /* Static functions.  */ +    engspawn_get_file_name, +    NULL,               /* get_home_dir */ +    engspawn_get_version, +    engspawn_get_req_version, +    engspawn_new, + +    /* Member functions.  */ +    engspawn_release, +    NULL,		/* reset */ +    NULL,		/* set_status_handler */ +    NULL,		/* set_command_handler */ +    NULL,		/* set_colon_line_handler */ +    NULL,		/* set_locale */ +    NULL,		/* set_protocol */ +    NULL,		/* decrypt */ +    NULL,		/* decrypt_verify */ +    NULL,		/* delete */ +    NULL,		/* edit */ +    NULL,		/* encrypt */ +    NULL,		/* encrypt_sign */ +    NULL,		/* export */ +    NULL,		/* export_ext */ +    NULL,		/* genkey */ +    NULL,		/* import */ +    NULL,		/* keylist */ +    NULL,		/* keylist_ext */ +    NULL,		/* sign */ +    NULL,		/* trustlist */ +    NULL,		/* verify */ +    NULL,		/* getauditlog */ +    NULL,               /* opassuan_transact */ +    NULL,		/* conf_load */ +    NULL,		/* conf_save */ +    engspawn_set_io_cbs, +    engspawn_io_event,	/* io_event */ +    engspawn_cancel,	/* cancel */ +    NULL,               /* cancel_op */ +    NULL,               /* passwd */ +    NULL,               /* set_pinentry_mode */ +    engspawn_op_spawn   /* opspawn */ +  }; diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index bd140f90..2738c366 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1340,5 +1340,6 @@ struct engine_ops _gpgme_engine_ops_uiserver =      uiserver_cancel,      NULL,		/* cancel_op */      NULL,               /* passwd */ -    NULL                /* set_pinentry_mode */ +    NULL,                /* set_pinentry_mode */ +    NULL                /* opspawn */    }; diff --git a/src/engine.c b/src/engine.c index 4f2000c2..f5034306 100644 --- a/src/engine.c +++ b/src/engine.c @@ -51,10 +51,11 @@ static struct engine_ops *engine_ops[] =      &_gpgme_engine_ops_assuan,		/* Low-Level Assuan.  */      &_gpgme_engine_ops_g13,		/* Crypto VFS.  */  #ifdef ENABLE_UISERVER -    &_gpgme_engine_ops_uiserver		/* UI-Server.  */ +    &_gpgme_engine_ops_uiserver,	/* UI-Server.  */  #else -    NULL +    NULL,  #endif +    &_gpgme_engine_ops_spawn    }; @@ -193,7 +194,8 @@ gpgme_get_engine_info (gpgme_engine_info_t *info)  					GPGME_PROTOCOL_GPGCONF,  					GPGME_PROTOCOL_ASSUAN,  					GPGME_PROTOCOL_G13, -					GPGME_PROTOCOL_UISERVER }; +					GPGME_PROTOCOL_UISERVER, +                                        GPGME_PROTOCOL_SPAWN    };        unsigned int proto;        err = 0; @@ -936,3 +938,20 @@ _gpgme_engine_set_pinentry_mode (engine_t engine, gpgme_pinentry_mode_t mode)    return (*engine->ops->set_pinentry_mode) (engine->engine, mode);  } + + +gpgme_error_t +_gpgme_engine_op_spawn (engine_t engine, +                        const char *file, const char *argv[], +                        gpgme_data_t datain, +                        gpgme_data_t dataout, gpgme_data_t dataerr) +{ +  if (!engine) +    return gpg_error (GPG_ERR_INV_VALUE); + +  if (!engine->ops->opspawn) +    return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + +  return (*engine->ops->opspawn) (engine->engine, file, argv, +                                  datain, dataout, dataerr); +} diff --git a/src/engine.h b/src/engine.h index a0287add..ade7de15 100644 --- a/src/engine.h +++ b/src/engine.h @@ -163,5 +163,11 @@ gpgme_error_t _gpgme_engine_op_passwd (engine_t engine, gpgme_key_t key,  gpgme_error_t _gpgme_engine_set_pinentry_mode (engine_t engine,                                                 gpgme_pinentry_mode_t mode); +gpgme_error_t _gpgme_engine_op_spawn (engine_t engine, +                                      const char *file, const char *argv[], +                                      gpgme_data_t datain, +                                      gpgme_data_t dataout, +                                      gpgme_data_t dataerr); +  #endif /* ENGINE_H */ diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c index 2bf7654a..a980347c 100644 --- a/src/gpgme-tool.c +++ b/src/gpgme-tool.c @@ -1742,6 +1742,8 @@ gt_protocol_from_name (const char *name)      return GPGME_PROTOCOL_G13;    if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_UISERVER)))      return GPGME_PROTOCOL_UISERVER; +  if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_SPAWN))) +    return GPGME_PROTOCOL_SPAWN;    if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_DEFAULT)))      return GPGME_PROTOCOL_DEFAULT;    return GPGME_PROTOCOL_UNKNOWN; @@ -2106,6 +2108,18 @@ gt_identify (gpgme_tool_t gt, gpgme_data_t data)  } +gpg_error_t +gt_spawn (gpgme_tool_t gt, const char *pgm, +          gpgme_data_t inp, gpgme_data_t outp) +{ +  gpg_error_t err; + +  err = gpgme_op_spawn (gt->ctx, pgm, NULL, inp, outp, outp, 0); + +  return err; +} + +  #define GT_RESULT_ENCRYPT 0x1  #define GT_RESULT_DECRYPT 0x2  #define GT_RESULT_SIGN 0x4 @@ -3487,6 +3501,55 @@ cmd_identify (assuan_context_t ctx, char *line)  } +static const char hlp_spawn[] = +  "SPAWN PGM [args]\n" +  "\n" +  "Run program PGM with stdin connected to the INPUT source;\n" +  "stdout and stderr to the OUTPUT source."; +static gpg_error_t +cmd_spawn (assuan_context_t ctx, char *line) +{ +  struct server *server = assuan_get_pointer (ctx); +  gpg_error_t err; +  assuan_fd_t inp_fd; +  char *inp_fn; +  assuan_fd_t out_fd; +  char *out_fn; +  gpgme_data_t inp_data = NULL; +  gpgme_data_t out_data = NULL; + +  inp_fd = server->input_fd; +  inp_fn = server->input_filename; +  out_fd = server->output_fd; +  out_fn = server->output_filename; +  if (inp_fd != ASSUAN_INVALID_FD || inp_fn) +    { +      err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data, +			     &server->input_stream); +      if (err) +	return err; +    } +  if (out_fd != ASSUAN_INVALID_FD || out_fn) +    { +      err = server_data_obj (out_fd, out_fn, 1, server->output_enc, &out_data, +			     &server->output_stream); +      if (err) +	{ +	  gpgme_data_release (inp_data); +	  return err; +	} +    } + +  err = gt_spawn (server->gt, line, inp_data, out_data); + +  gpgme_data_release (inp_data); +  gpgme_data_release (out_data); + +  server_reset_fds (server); + +  return err; +} +  /* Tell the assuan library about our commands.  */  static gpg_error_t @@ -3547,6 +3610,7 @@ register_commands (assuan_context_t ctx)      { "HASH_ALGO_NAME", cmd_hash_algo_name },      { "PASSWD", cmd_passwd, hlp_passwd },      { "IDENTIFY", cmd_identify, hlp_identify }, +    { "SPAWN", cmd_spawn, hlp_spawn },      { NULL }    };    int idx; diff --git a/src/gpgme.c b/src/gpgme.c index 438fef21..24b04fc8 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -1,6 +1,7 @@  /* gpgme.c - GnuPG Made Easy.     Copyright (C) 2000 Werner Koch (dd9jn) -   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012 g10 Code GmbH +   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012, +                 2014 g10 Code GmbH     This file is part of GPGME. @@ -15,9 +16,8 @@     Lesser General Public License for more details.     You should have received a copy of the GNU Lesser General Public -   License along with this program; if not, write to the Free Software -   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -   02111-1307, USA.  */ +   License along with this program; if not, see <http://www.gnu.org/licenses/>. + */  #if HAVE_CONFIG_H  #include <config.h> @@ -321,7 +321,8 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)        && protocol != GPGME_PROTOCOL_GPGCONF        && protocol != GPGME_PROTOCOL_ASSUAN        && protocol != GPGME_PROTOCOL_G13 -      && protocol != GPGME_PROTOCOL_UISERVER) +      && protocol != GPGME_PROTOCOL_UISERVER +      && protocol != GPGME_PROTOCOL_SPAWN)      return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));    if (!ctx) @@ -405,6 +406,9 @@ gpgme_get_protocol_name (gpgme_protocol_t protocol)      case GPGME_PROTOCOL_UISERVER:        return "UIServer"; +    case GPGME_PROTOCOL_SPAWN: +      return "Spawn"; +      case GPGME_PROTOCOL_DEFAULT:        return "default"; diff --git a/src/gpgme.def b/src/gpgme.def index ee0c42e2..dc189484 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -215,5 +215,7 @@ EXPORTS      gpgme_get_dirinfo                     @162 +    gpgme_op_spawn_start                  @163 +    gpgme_op_spawn                        @164  ; END diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 050051a4..655602df 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1,7 +1,7 @@  /* gpgme.h - Public interface to GnuPG Made Easy.                   -*- c -*-     Copyright (C) 2000 Werner Koch (dd9jn)     Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 -                 2010, 2011, 2012, 2013 g10 Code GmbH +                 2010, 2011, 2012, 2013, 2014 g10 Code GmbH     This file is part of GPGME. @@ -354,6 +354,7 @@ typedef enum      GPGME_PROTOCOL_ASSUAN  = 3,  /* Low-level access to an Assuan server.  */      GPGME_PROTOCOL_G13     = 4,      GPGME_PROTOCOL_UISERVER= 5, +    GPGME_PROTOCOL_SPAWN   = 6,  /* Direct access to any program.  */      GPGME_PROTOCOL_DEFAULT = 254,      GPGME_PROTOCOL_UNKNOWN = 255    } @@ -1694,6 +1695,26 @@ gpgme_error_t gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key,  				  gpgme_edit_cb_t fnc, void *fnc_value,  				  gpgme_data_t out); + +/* Flags for the spawn operations.  */ +#define GPGME_SPAWN_DETACHED      1 +#define GPGME_SPAWN_ALLOW_SET_FG  2 + + +/* Run the command FILE with the arguments in ARGV.  Connect stdin to +   DATAIN, stdout to DATAOUT, and STDERR to DATAERR.  If one the data +   streams is NULL, connect to /dev/null instead.  */ +gpgme_error_t gpgme_op_spawn_start (gpgme_ctx_t ctx, +                                    const char *file, const char *argv[], +                                    gpgme_data_t datain, +                                    gpgme_data_t dataout, gpgme_data_t dataerr, +                                    unsigned int flags); +gpgme_error_t gpgme_op_spawn (gpgme_ctx_t ctx, +                              const char *file, const char *argv[], +                              gpgme_data_t datain, +                              gpgme_data_t dataout, gpgme_data_t dataerr, +                              unsigned int flags); +  /* Key management functions.  */  struct _gpgme_op_keylist_result diff --git a/src/libgpgme.vers b/src/libgpgme.vers index 4db1d408..39663c1c 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -89,6 +89,9 @@ GPGME_1.1 {      gpgme_get_pinentry_mode;      gpgme_get_dirinfo; + +    gpgme_op_spawn_start; +    gpgme_op_spawn;  }; diff --git a/src/spawn.c b/src/spawn.c new file mode 100644 index 00000000..e3454f3b --- /dev/null +++ b/src/spawn.c @@ -0,0 +1,105 @@ +/* spawn.c - Run an arbitrary command with callbacks. +   Copyright (C) 2014 Code GmbH + +   This file is part of GPGME. + +   GPGME is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as +   published by the Free Software Foundation; either version 2.1 of +   the License, or (at your option) any later version. + +   GPGME is distributed in the hope that it will be useful, but +   WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with this program; if not, write to the Free Software +   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +   02111-1307, USA.  */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdlib.h> + +#include "gpgme.h" +#include "debug.h" +#include "context.h" +#include "util.h" +#include "ops.h" + + +static gpgme_error_t +spawn_start (gpgme_ctx_t ctx, int synchronous, +             const char *file, const char *argv[], +             gpgme_data_t datain, +             gpgme_data_t dataout, gpgme_data_t dataerr) +{ +  gpgme_error_t err; +  const char *tmp_argv[2]; + +  if (ctx->protocol != GPGME_PROTOCOL_SPAWN) +    return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + +  err = _gpgme_op_reset (ctx, synchronous); +  if (err) +    return err; + +  if (!argv) +    { +      tmp_argv[0] = _gpgme_get_basename (file); +      tmp_argv[1] = NULL; +      argv = tmp_argv; +    } + +  return _gpgme_engine_op_spawn (ctx->engine, file, argv, +                                 datain, dataout, dataerr); +} + + +/* Run the command FILE with the arguments in ARGV.  Connect stdin to +   DATAIN, stdout to DATAOUT, and STDERR to DATAERR.  If one the data +   streams is NULL, connect to /dev/null instead.  */ +gpgme_error_t +gpgme_op_spawn_start (gpgme_ctx_t ctx, const char *file, const char *argv[], +                      gpgme_data_t datain, +                      gpgme_data_t dataout, gpgme_data_t dataerr, +                      unsigned int flags) +{ +  gpgme_error_t err; + +  TRACE_BEG2 (DEBUG_CTX, "gpgme_op_spawn_start", ctx, "file=(%s) flaggs=%x", +              file, flags); + +  if (!ctx) +    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); + +  err = spawn_start (ctx, 0, file, argv, datain, dataout, dataerr); +  return err; +} + + +/* Run the command FILE with the arguments in ARGV.  Connect stdin to +   DATAIN, stdout to DATAOUT, and STDERR to DATAERR.  If one the data +   streams is NULL, connect to /dev/null instead.  Synchronous +   variant. */ +gpgme_error_t +gpgme_op_spawn (gpgme_ctx_t ctx, const char *file, const char *argv[], +	        gpgme_data_t datain, +                gpgme_data_t dataout, gpgme_data_t dataerr, +                unsigned int flags) +{ +  gpgme_error_t err; + +  TRACE_BEG2 (DEBUG_CTX, "gpgme_op_spawn", ctx, "file=(%s) flags=%x", +              file, flags); +  if (!ctx) +    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); + +  err = spawn_start (ctx, 1, file, argv, datain, dataout, dataerr); + +  if (!err) +    err = _gpgme_wait_one (ctx); +  return TRACE_ERR (err); +} | 
