Add GPGME_PROTOCOL_SPAWN and gpgme_op_spawn.

* src/gpgme.h.in (GPGME_PROTOCOL_SPAWN): New.
(GPGME_SPAWN_DETACHED, GPGME_SPAWN_ALLOW_SET_FG): New.
* src/gpgme.c (gpgme_set_protocol): Add new protocol.
(gpgme_get_protocol_name): Ditto.
* src/spawn.c: New.
* src/libgpgme.vers, src/gpgme.def: Add new public functions.
* src/engine-spawn.c: New.
* src/Makefile.am: Add new files.
* src/engine-backend.h (struct engine_ops): Add OPSPAWN.
* src/engine.c (engine_ops): Add _gpgme_engine_ops_spawn.
(gpgme_get_engine_info): Add Spawn to the list of protocols.
(_gpgme_engine_op_spawn): New.

* src/gpgme-tool.c (gt_protocol_from_name): Add new protocol.
(gt_spawn, cmd_spawn): New.
This commit is contained in:
Werner Koch 2014-04-10 13:01:00 +02:00
parent 77931a9a14
commit 4f2d652e60
19 changed files with 804 additions and 19 deletions

8
NEWS
View File

@ -9,9 +9,17 @@ Noteworthy changes in version 1.5.0 (unreleased)
whatever gpgconf tells as name for the OpenPGP engine. If gpgconf
is not found, GPGME looks for an engine named "gpg".
* Add feature to use the gpgme I/O subsystem to run arbitrary
commands.
* Interface changes relative to the 1.4.3 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgme_get_dirinfo NEW.
gpgme_op_spawn_start NEW.
gpgme_op_spawn NEW.
GPGME_PROTOCOL_SPAWN NEW.
GPGME_SPAWN_DETACHED NEW.
GPGME_SPAWN_ALLOW_SET_FG NEW.
Noteworthy changes in version 1.4.3 (2013-08-12)

View File

@ -113,7 +113,6 @@ Indices
* Concept Index:: Index of concepts and programs.
* Function and Data Index:: Index of functions, variables and data types.
@detailmenu
--- The Detailed Node Listing ---
@ -170,6 +169,7 @@ Manipulating Data Buffers
* Data Buffer I/O Operations:: I/O operations on data buffers.
* Data Buffer Meta-Data:: Meta-data manipulation of data buffers.
* Data Buffer Convenience:: Convenience function for data buffers.
Contexts
@ -180,6 +180,7 @@ Contexts
* Key Management:: Managing keys with @acronym{GPGME}.
* Trust Item Management:: Managing trust items with @acronym{GPGME}.
* Crypto Operations:: Using a context for cryptography.
* Miscellaneous:: Miscellaneous operations.
* Run Control:: Controlling how operations are run.
Context Attributes
@ -204,6 +205,7 @@ Key Management
* Exporting Keys:: Retrieving key data from the key ring.
* Importing Keys:: Adding keys to the key ring.
* Deleting Keys:: Removing keys from the key ring.
* Changing Passphrases:: Change the passphrase of a key.
* Advanced Key Editing:: Advanced key edit operation.
Trust Item Management
@ -230,6 +232,10 @@ Encrypt
* Encrypting a Plaintext:: How to encrypt a plaintext.
Miscellaneous
* Running other Programs:: Running other Programs
Run Control
* Waiting For Completion:: Waiting until an operation is completed.
@ -850,6 +856,9 @@ Under development. Please ask on @email{gnupg-devel@@gnupg.org} for help.
@item GPGME_PROTOCOL_UISERVER
Under development. Please ask on @email{gnupg-devel@@gnupg.org} for help.
@item GPGME_PROTOCOL_SPAWN
Special protocol for use with @code{gpgme_op_spawn}.
@item GPGME_PROTOCOL_UNKNOWN
Reserved for future extension. You may use this to indicate that the
used protocol is not known to the application. Currently,
@ -1946,7 +1955,7 @@ be used to manipulate both.
@menu
* Data Buffer I/O Operations:: I/O operations on data buffers.
* Data Buffer Meta-Data:: Meta-data manipulation of data buffers.
* Data Buffer Convenience:: Convenience fucntion for data buffers.
* Data Buffer Convenience:: Convenience function for data buffers.
@end menu
@ -2187,6 +2196,7 @@ cryptographic operations.
* Key Management:: Managing keys with @acronym{GPGME}.
* Trust Item Management:: Managing trust items with @acronym{GPGME}.
* Crypto Operations:: Using a context for cryptography.
* Miscellaneous:: Miscellaneous operations
* Run Control:: Controlling how operations are run.
@end menu
@ -2261,7 +2271,7 @@ started. In fact, these references are accessed through the
* Crypto Engine:: Configuring the crypto engine.
* ASCII Armor:: Requesting @acronym{ASCII} armored output.
* Text Mode:: Choosing canonical text mode.
* Included Certificates:: Including a number of certificates.
* Included Certificates:: Including a number of certificates.
* Key Listing Mode:: Selecting key listing mode.
* Passphrase Callback:: Getting the passphrase from the user.
* Progress Meter Callback:: Being informed about the progress.
@ -5222,6 +5232,66 @@ pointer.
@end deftypefun
@node Miscellaneous
@section Miscellaneous operations
Here are some support functions which are sometimes useful.
@menu
* Running other Programs:: Running other Programs
@end menu
@node Running other Programs
@subsection Running other Programs
GPGME features an internal subsystem to run the actual backend
engines. Along with data abstraction object this subsystem can be
used to run arbitrary simple programs which even need not be related
to cryptographic features. It may for example be used to run tools
which are part of the GnuPG system but are not directly accessible
with the GPGME API.
@deftypefun gpgme_error_t gpgme_op_spawn
(@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{file}}, @
@w{const char *@var{argv}[]}, @w{gpgme_data_t @var{datain}}, @
@w{gpgme_data_t @var{dataout}}, @w{gpgme_data_t @var{dataerr}}, @
@w{unsigned int @var{flags}})
The function @code{gpgme_op_spawn} runs the program @var{file} with
the arguments taken from the NULL terminated array @var{argv}. If no
arguments are required @var{argv} may be given as @code{NULL} (in that
case GPGME uses the basename of @var{file} for @code{argv[0]}). The
file descriptors @code{stdin}, @code{stdout}, and @code{stderr} are
connected to the data objects @var{datain}, @var{dataout}, and
@var{dataerr}. If NULL is passed for one of these data objects the
corresponding file descriptor is connected to @file{/dev/null}.
The value in @var{flags} is a bitwise-or combination of one or
multiple of the following bit values:
@table @code
@item GPGME_SPAWN_DETACHED
Under Windows this flag inhibits the allocation of a new console for
the program. This is useful for a GUI application which needs to call
a command line helper tool.
@item GPGME_SPAWN_ALLOW_SET_FG
Under Windows this flag allows the called program to put itself into
the foreground.
@end table
@end deftypefun
@deftypefun gpgme_error_t gpgme_op_spawn_start
(@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{file}}, @
@w{const char *@var{argv}[]}, @w{gpgme_data_t @var{datain}}, @
@w{gpgme_data_t @var{dataout}}, @w{gpgme_data_t @var{dataerr}}, @
@w{unsigned int @var{flags}})
This is the asynchronous variant of @code{gpgme_op_spawn}.
@end deftypefun
@node Run Control
@section Run Control
@cindex run control

View File

@ -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

View File

@ -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 */
};

View File

@ -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 */

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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 */
};

467
src/engine-spawn.c Normal file
View File

@ -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 */
};

View File

@ -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 */
};

View File

@ -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);
}

View File

@ -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 */

View File

@ -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;

View File

@ -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";

View File

@ -215,5 +215,7 @@ EXPORTS
gpgme_get_dirinfo @162
gpgme_op_spawn_start @163
gpgme_op_spawn @164
; END

View File

@ -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

View File

@ -89,6 +89,9 @@ GPGME_1.1 {
gpgme_get_pinentry_mode;
gpgme_get_dirinfo;
gpgme_op_spawn_start;
gpgme_op_spawn;
};

105
src/spawn.c Normal file
View File

@ -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);
}