diff --git a/src/engine-spawn.c b/src/engine-spawn.c
index bfcad3d9..8ffc6280 100644
--- a/src/engine-spawn.c
+++ b/src/engine-spawn.c
@@ -1,475 +1,475 @@
/* 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 .
*/
#if HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#ifdef HAVE_UNISTD_H
# include
#endif
#ifdef HAVE_LOCALE_H
#include
#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;
const char *save_argv0 = NULL;
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);
+ fd_list = calloc (n+1, 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;
if (argv[0] && !*argv[0])
{
save_argv0 = argv[0];
argv[0] = _gpgme_get_basename (file);
}
status = _gpgme_io_spawn (file, (char * const *)argv, spflags,
fd_list, NULL, NULL, &pid);
if (save_argv0)
argv[0] = save_argv0;
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/posix-util.c b/src/posix-util.c
index e78cd771..f7e0a171 100644
--- a/src/posix-util.c
+++ b/src/posix-util.c
@@ -1,147 +1,147 @@
/* posix-util.c - Utility functions for Posix
Copyright (C) 2001 Werner Koch (dd9jn)
Copyright (C) 2001, 2002, 2004 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include "util.h"
#include "sys-util.h"
#include "debug.h"
/* These variables store the malloced name of alternative default
binaries. The are set only once by gpgme_set_global_flag. */
static char *default_gpg_name;
static char *default_gpgconf_name;
/* Set the default name for the gpg binary. This function may only be
called by gpgme_set_global_flag. Returns 0 on success. Leading
directories are removed from NAME. */
int
_gpgme_set_default_gpg_name (const char *name)
{
const char *s;
s = strrchr (name, '/');
if (s)
name = s + 1;
if (!default_gpg_name)
default_gpg_name = strdup (name);
return !default_gpg_name;
}
/* Set the default name for the gpgconf binary. This function may
only be called by gpgme_set_global_flag. Returns 0 on success.
Leading directories are removed from NAME. */
int
_gpgme_set_default_gpgconf_name (const char *name)
{
const char *s;
s = strrchr (name, '/');
if (s)
name = s + 1;
if (!default_gpgconf_name)
default_gpgconf_name = strdup (name);
return !default_gpgconf_name;
}
/* Find an executable program PGM along the envvar PATH. */
static char *
walk_path (const char *pgm)
{
const char *orig_path, *path, *s;
char *fname, *p;
#ifdef FIXED_SEARCH_PATH
orig_path = FIXED_SEARCH_PATH;
#else
orig_path = getenv ("PATH");
if (!orig_path)
orig_path = "/bin:/usr/bin";
#endif
fname = malloc (strlen (orig_path) + 1 + strlen (pgm) + 1);
if (!fname)
return NULL;
path = orig_path;
for (;;)
{
for (s=path, p=fname; *s && *s != ':'; s++, p++)
*p = *s;
- if (*p != '/')
+ if (p != fname && p[-1] != '/')
*p++ = '/';
strcpy (p, pgm);
if (!access (fname, X_OK))
return fname;
if (!*s)
break;
path = s + 1;
}
_gpgme_debug (DEBUG_ENGINE, "gpgme-walk_path: '%s' not found in '%s'",
pgm, orig_path);
free (fname);
return NULL;
}
/* Return the full file name of the GPG binary. This function is used
if gpgconf was not found and thus it can be assumed that gpg2 is
not installed. This function is only called by get_gpgconf_item
and may not be called concurrently. */
char *
_gpgme_get_gpg_path (void)
{
return walk_path (default_gpg_name? default_gpg_name : "gpg");
}
/* This function is only called by get_gpgconf_item and may not be
called concurrently. */
char *
_gpgme_get_gpgconf_path (void)
{
return walk_path (default_gpgconf_name? default_gpgconf_name : "gpgconf");
}
/* See w32-util.c */
int
_gpgme_get_conf_int (const char *key, int *value)
{
return 0;
}
void
_gpgme_allow_set_foreground_window (pid_t pid)
{
(void)pid;
/* Not needed. */
}