dirmngr: Use the LDAP wrapper process also for Windows.

* dirmngr/ldap-wrapper.c: Revamp module to make use of es_poll for
portability.
* configure.ac: Always use the ldap wrapper.
--

Since the migration from GNU Pth to nPth the ldap wrapper never worked
reliable on Windows.  Our long term use of the old Window CE wrapper
thing didn't fixed this either.  The new code uses the portable
es_poll function and thus code which is tested at several other
places.  It Should(tm) fix the Windows issues.

GnuPG-bug-id: 3937
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2018-04-27 12:03:41 +02:00
parent d22506a343
commit f9fbfc64e4
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 239 additions and 189 deletions

View File

@ -653,7 +653,6 @@ case "${host}" in
have_dosish_system=yes have_dosish_system=yes
have_w32_system=yes have_w32_system=yes
require_iconv=no require_iconv=no
use_ldapwrapper=no # Fixme: Do this only for CE.
require_pipe_to_unblock_pselect=no require_pipe_to_unblock_pselect=no
case "${host}" in case "${host}" in
*-mingw32ce*) *-mingw32ce*)

View File

@ -45,6 +45,7 @@
#ifdef USE_LDAPWRAPPER #ifdef USE_LDAPWRAPPER
# error This module is not expected to be build. # error This module is not expected to be build.
#endif #endif
#error This module might not anymore work.

View File

@ -1,5 +1,5 @@
/* ldap-wrapper.c - LDAP access via a wrapper process /* ldap-wrapper.c - LDAP access via a wrapper process
* Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH * Copyright (C) 2004, 2005, 2007, 2008, 2018 g10 Code GmbH
* Copyright (C) 2010 Free Software Foundation, Inc. * Copyright (C) 2010 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
@ -19,31 +19,34 @@
*/ */
/* /*
We can't use LDAP directly for these reasons: * We can't use LDAP directly for these reasons:
*
1. On some systems the LDAP library uses (indirectly) pthreads and * 1. On some systems the LDAP library uses (indirectly) pthreads and
that is not compatible with PTh. * that is not compatible with GNU Pth. Since 2.1 we use nPth
* instead of GNU Pth which does not have this problem anymore
2. It is huge library in particular if TLS comes into play. So * because it will use pthreads if the platform supports it. Thus
problems with unfreed memory might turn up and we don't want * this was a historical reasons.
this in a long running daemon. *
* 2. It is huge library in particular if TLS comes into play. So
3. There is no easy way for timeouts. In particular the timeout * problems with unfreed memory might turn up and we don't want
value does not work for DNS lookups (well, this is usual) and it * this in a long running daemon.
seems not to work while loading a large attribute like a *
CRL. Having a separate process allows us to either tell the * 3. There is no easy way for timeouts. In particular the timeout
process to commit suicide or have our own housekepping function * value does not work for DNS lookups (well, this is usual) and it
kill it after some time. The latter also allows proper * seems not to work while loading a large attribute like a
cancellation of a query at any point of time. * CRL. Having a separate process allows us to either tell the
* process to commit suicide or have our own housekepping function
4. Given that we are going out to the network and usually get back * kill it after some time. The latter also allows proper
a long response, the fork/exec overhead is acceptable. * cancellation of a query at any point of time.
*
Note that under WindowsCE the number of processes is strongly * 4. Given that we are going out to the network and usually get back
limited (32 processes including the kernel processes) and thus we * a long response, the fork/exec overhead is acceptable.
don't use the process approach but implement a different wrapper in *
ldap-wrapper-ce.c. * Note that under WindowsCE the number of processes is strongly
*/ * limited (32 processes including the kernel processes) and thus we
* don't use the process approach but implement a different wrapper in
* ldap-wrapper-ce.c.
*/
#include <config.h> #include <config.h>
@ -91,18 +94,19 @@ struct wrapper_context_s
pid_t pid; /* The pid of the wrapper process. */ pid_t pid; /* The pid of the wrapper process. */
int printable_pid; /* Helper to print diagnostics after the process has int printable_pid; /* Helper to print diagnostics after the process has
been cleaned up. */ * been cleaned up. */
int fd; /* Connected with stdout of the ldap wrapper. */ estream_t fp; /* Connected with stdout of the ldap wrapper. */
gpg_error_t fd_error; /* Set to the gpg_error of the last read error gpg_error_t fp_err; /* Set to the gpg_error of the last read error
if any. */ * if any. */
int log_fd; /* Connected with stderr of the ldap wrapper. */ estream_t log_fp; /* Connected with stderr of the ldap wrapper. */
ctrl_t ctrl; /* Connection data. */ ctrl_t ctrl; /* Connection data. */
int ready; /* Internally used to mark to be removed contexts. */ int ready; /* Internally used to mark to be removed contexts. */
ksba_reader_t reader; /* The ksba reader object or NULL. */ ksba_reader_t reader;/* The ksba reader object or NULL. */
char *line; /* Used to print the log lines (malloced). */ char *line; /* Used to print the log lines (malloced). */
size_t linesize;/* Allocated size of LINE. */ size_t linesize; /* Allocated size of LINE. */
size_t linelen; /* Use size of LINE. */ size_t linelen; /* Use size of LINE. */
time_t stamp; /* The last time we noticed ativity. */ time_t stamp; /* The last time we noticed ativity. */
int reaper_idx; /* Private to ldap_wrapper_thread. */
}; };
@ -115,9 +119,9 @@ static struct wrapper_context_s *wrapper_list;
/* We need to know whether we are shutting down the process. */ /* We need to know whether we are shutting down the process. */
static int shutting_down; static int shutting_down;
/* Close the pth file descriptor FD and set it to -1. */ /* Close the estream fp and set it to NULL. */
#define SAFE_CLOSE(fd) \ #define SAFE_CLOSE(fp) \
do { int _fd = fd; if (_fd != -1) { close (_fd); fd = -1;} } while (0) do { estream_t _fp = fp; es_fclose (_fp); fp = NULL; } while (0)
@ -151,8 +155,8 @@ destroy_wrapper (struct wrapper_context_s *ctx)
gnupg_release_process (ctx->pid); gnupg_release_process (ctx->pid);
} }
ksba_reader_release (ctx->reader); ksba_reader_release (ctx->reader);
SAFE_CLOSE (ctx->fd); SAFE_CLOSE (ctx->fp);
SAFE_CLOSE (ctx->log_fd); SAFE_CLOSE (ctx->log_fp);
xfree (ctx->line); xfree (ctx->line);
xfree (ctx); xfree (ctx);
} }
@ -218,25 +222,22 @@ print_log_line (struct wrapper_context_s *ctx, char *line)
/* Read data from the log stream. Returns true if the log stream /* Read data from the log stream. Returns true if the log stream
indicated EOF or error. */ * indicated EOF or error. */
static int static int
read_log_data (struct wrapper_context_s *ctx) read_log_data (struct wrapper_context_s *ctx)
{ {
int n; int rc;
size_t n;
char line[256]; char line[256];
/* We must use the npth_read function for pipes, always. */ rc = es_read (ctx->log_fp, line, sizeof line - 1, &n);
do if (rc || !n) /* Error or EOF. */
n = npth_read (ctx->log_fd, line, sizeof line - 1);
while (n < 0 && errno == EINTR);
if (n <= 0) /* EOF or error. */
{ {
if (n < 0) if (rc)
log_error (_("error reading log from ldap wrapper %d: %s\n"), log_error (_("error reading log from ldap wrapper %d: %s\n"),
(int)ctx->pid, strerror (errno)); (int)ctx->pid, strerror (errno));
print_log_line (ctx, NULL); print_log_line (ctx, NULL); /* Flush. */
SAFE_CLOSE (ctx->log_fd); SAFE_CLOSE (ctx->log_fp);
return 1; return 1;
} }
@ -253,13 +254,16 @@ read_log_data (struct wrapper_context_s *ctx)
void * void *
ldap_wrapper_thread (void *dummy) ldap_wrapper_thread (void *dummy)
{ {
int nfds; gpg_error_t err;
struct wrapper_context_s *ctx; struct wrapper_context_s *ctx;
struct wrapper_context_s *ctx_prev; struct wrapper_context_s *ctx_prev;
struct timespec abstime; struct timespec abstime;
struct timespec curtime; struct timespec curtime;
struct timespec timeout; struct timespec timeout;
fd_set fdset; int millisecs;
gpgrt_poll_t *fparray = NULL;
int fparraysize = 0;
int count;
int ret; int ret;
time_t exptime; time_t exptime;
@ -268,6 +272,11 @@ ldap_wrapper_thread (void *dummy)
npth_clock_gettime (&abstime); npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL; abstime.tv_sec += TIMERTICK_INTERVAL;
/* FIXME: When we are idle (i.e. !COUNT) we should not use the
* TIMERTICK_INTERVAL but wait on a mutex to avoid unnecessary
* wakeups. */
restart:
for (;;) for (;;)
{ {
int any_action = 0; int any_action = 0;
@ -280,33 +289,88 @@ ldap_wrapper_thread (void *dummy)
abstime.tv_sec += TIMERTICK_INTERVAL; abstime.tv_sec += TIMERTICK_INTERVAL;
} }
npth_timersub (&abstime, &curtime, &timeout); npth_timersub (&abstime, &curtime, &timeout);
millisecs = timeout.tv_sec * 1000;
millisecs += timeout.tv_nsec / 1000000;
if (millisecs < 0)
millisecs = 1;
FD_ZERO (&fdset); /* Setup FPARRAY. */
nfds = -1; for (count = 0, ctx = wrapper_list; ctx; ctx = ctx->next)
for (ctx = wrapper_list; ctx; ctx = ctx->next) if (ctx->log_fp)
count++;
if (DBG_EXTPROG)
log_debug ("ldap-reaper: next run (count=%d size=%d, timeout=%d)\n",
count, fparraysize, millisecs);
if (count > fparraysize || !fparray)
{ {
if (ctx->log_fd != -1) /* Need to realloc the array. We simply discard it and
* replace it by a new one. */
xfree (fparray);
fparray = xtrycalloc (count? count : 1, sizeof *fparray);
if (!fparray)
{ {
FD_SET (ctx->log_fd, &fdset); err = gpg_error_from_syserror ();
if (ctx->log_fd > nfds) log_error ("ldap-reaper can't allocate poll array: %s"
nfds = ctx->log_fd; " - waiting 1s\n", gpg_strerror (err));
}
}
nfds++;
/* FIXME: For Windows, we have to use a reader thread on the
pipe that signals an event (and a npth_select_ev variant). */
ret = npth_pselect (nfds + 1, &fdset, NULL, NULL, &timeout, NULL);
if (ret == -1)
{
if (errno != EINTR)
{
log_error (_("npth_select failed: %s - waiting 1s\n"),
strerror (errno));
npth_sleep (1); npth_sleep (1);
}
continue; continue;
} }
fparraysize = count;
}
for (count = 0, ctx = wrapper_list; ctx; ctx = ctx->next)
{
if (ctx->log_fp)
{
if (count > fparraysize)
{
/* Another thread added more items to WRAPPER_LIST.
* Note that the calloc above is a system call and
* thus may caused a context switch. */
goto restart;
}
fparray[count].stream = ctx->log_fp;
fparray[count].want_read = 1;
fparray[count].ignore = 0;
ctx->reaper_idx = count;
count++;
}
else
ctx->reaper_idx = -1;
}
for (; count < fparraysize; count++)
fparray[count].ignore = 1;
if (DBG_EXTPROG)
{
for (count=0; count < fparraysize; count++)
if (!fparray[count].ignore)
log_debug ("ldap-reaper: fp[%d] stream=%p want=%d\n",
count, fparray[count].stream,fparray[count].want_read);
}
ret = es_poll (fparray, fparraysize, millisecs);
if (ret < 0)
{
err = gpg_error_from_syserror ();
log_error ("ldap-reaper failed to poll: %s"
" - waiting 1s\n", gpg_strerror (err));
/* In case the reason for the error is a too large array, we
* release it so that it will be allocated smaller in the
* next round. */
xfree (fparray);
fparray = NULL;
fparraysize = 0;
npth_sleep (1);
continue;
}
if (DBG_EXTPROG)
{
for (count=0; count < fparraysize; count++)
if (!fparray[count].ignore)
log_debug ("ldap-reaper: fp[%d] stream=%p got=%d\n",
count, fparray[count].stream, fparray[count].got_read);
}
/* All timestamps before exptime should be considered expired. */ /* All timestamps before exptime should be considered expired. */
exptime = time (NULL); exptime = time (NULL);
@ -316,30 +380,35 @@ ldap_wrapper_thread (void *dummy)
/* Note that there is no need to lock the list because we always /* Note that there is no need to lock the list because we always
add entries at the head (with a pending event status) and add entries at the head (with a pending event status) and
thus traversing the list will even work if we have a context thus traversing the list will even work if we have a context
switch in waitpid (which should anyway only happen with Pth's switch in waitpid. */
hard system call mapping). */
for (ctx = wrapper_list; ctx; ctx = ctx->next) for (ctx = wrapper_list; ctx; ctx = ctx->next)
{ {
/* Check whether there is any logging to be done. */ /* Check whether there is any logging to be done. We need
if (nfds && ctx->log_fd != -1 && FD_ISSET (ctx->log_fd, &fdset)) * to check FPARRAYSIZE because it can be 0 in case es_poll
* returned a timeout. */
if (fparraysize && ctx->log_fp && ctx->reaper_idx >= 0)
{
log_assert (ctx->reaper_idx < fparraysize);
if (fparray[ctx->reaper_idx].got_read)
{ {
if (read_log_data (ctx)) if (read_log_data (ctx))
{ {
SAFE_CLOSE (ctx->log_fd); SAFE_CLOSE (ctx->log_fp);
any_action = 1; any_action = 1;
} }
} }
}
/* Check whether the process is still running. */ /* Check whether the process is still running. */
if (ctx->pid != (pid_t)(-1)) if (ctx->pid != (pid_t)(-1))
{ {
gpg_error_t err;
int status; int status;
err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0, err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0,
&status); &status);
if (!err) if (!err)
{ {
if (DBG_EXTPROG)
log_info (_("ldap wrapper %d ready"), (int)ctx->pid); log_info (_("ldap wrapper %d ready"), (int)ctx->pid);
ctx->ready = 1; ctx->ready = 1;
gnupg_release_process (ctx->pid); gnupg_release_process (ctx->pid);
@ -375,9 +444,9 @@ ldap_wrapper_thread (void *dummy)
ctx->stamp = (time_t)(-1); ctx->stamp = (time_t)(-1);
log_info (_("ldap wrapper %d stalled - killing\n"), log_info (_("ldap wrapper %d stalled - killing\n"),
(int)ctx->pid); (int)ctx->pid);
/* We need to close the log fd because the cleanup loop /* We need to close the log stream because the cleanup
waits for it. */ * loop waits for it. */
SAFE_CLOSE (ctx->log_fd); SAFE_CLOSE (ctx->log_fp);
any_action = 1; any_action = 1;
} }
} }
@ -385,26 +454,27 @@ ldap_wrapper_thread (void *dummy)
/* If something has been printed to the log file or we got an /* If something has been printed to the log file or we got an
EOF from a wrapper, we now print the list of active EOF from a wrapper, we now print the list of active
wrappers. */ wrappers. */
if (any_action && DBG_LOOKUP) if (any_action && DBG_EXTPROG)
{ {
log_info ("ldap worker stati:\n"); log_debug ("ldap worker stati:\n");
for (ctx = wrapper_list; ctx; ctx = ctx->next) for (ctx = wrapper_list; ctx; ctx = ctx->next)
log_info (" c=%p pid=%d/%d rdr=%p ctrl=%p/%d la=%lu rdy=%d\n", log_debug (" c=%p pid=%d/%d rdr=%p logfp=%p"
" ctrl=%p/%d la=%lu rdy=%d\n",
ctx, ctx,
(int)ctx->pid, (int)ctx->printable_pid, (int)ctx->pid, (int)ctx->printable_pid,
ctx->reader, ctx->reader, ctx->log_fp,
ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0,
(unsigned long)ctx->stamp, ctx->ready); (unsigned long)ctx->stamp, ctx->ready);
} }
/* Use a separate loop to check whether ready marked wrappers /* Use an extra loop to check whether ready marked wrappers may
may be removed. We may only do so if the ksba reader object * be removed. We may only do so if the ksba reader object is
is not anymore in use or we are in shutdown state. */ * not anymore in use or we are in shutdown state. */
again: again:
for (ctx_prev=NULL, ctx=wrapper_list; ctx; ctx_prev=ctx, ctx=ctx->next) for (ctx_prev=NULL, ctx=wrapper_list; ctx; ctx_prev=ctx, ctx=ctx->next)
if (ctx->ready if (ctx->ready
&& ((ctx->log_fd == -1 && !ctx->reader) || shutting_down)) && ((!ctx->log_fp && !ctx->reader) || shutting_down))
{ {
if (ctx_prev) if (ctx_prev)
ctx_prev->next = ctx->next; ctx_prev->next = ctx->next;
@ -412,7 +482,7 @@ ldap_wrapper_thread (void *dummy)
wrapper_list = ctx->next; wrapper_list = ctx->next;
destroy_wrapper (ctx); destroy_wrapper (ctx);
/* We need to restart because destroy_wrapper might have /* We need to restart because destroy_wrapper might have
done a context switch. */ * done a context switch. */
goto again; goto again;
} }
} }
@ -451,8 +521,6 @@ ldap_wrapper_launch_thread (void)
/* Wait until all ldap wrappers have terminated. We assume that the /* Wait until all ldap wrappers have terminated. We assume that the
kill has already been sent to all of them. */ kill has already been sent to all of them. */
void void
@ -478,23 +546,23 @@ ldap_wrapper_release_context (ksba_reader_t reader)
for (ctx=wrapper_list; ctx; ctx=ctx->next) for (ctx=wrapper_list; ctx; ctx=ctx->next)
if (ctx->reader == reader) if (ctx->reader == reader)
{ {
if (DBG_LOOKUP) if (DBG_EXTPROG)
log_info ("releasing ldap worker c=%p pid=%d/%d rdr=%p ctrl=%p/%d\n", log_debug ("releasing ldap worker c=%p pid=%d/%d rdr=%p ctrl=%p/%d\n",
ctx, ctx,
(int)ctx->pid, (int)ctx->printable_pid, (int)ctx->pid, (int)ctx->printable_pid,
ctx->reader, ctx->reader,
ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0); ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0);
ctx->reader = NULL; ctx->reader = NULL;
SAFE_CLOSE (ctx->fd); SAFE_CLOSE (ctx->fp);
if (ctx->ctrl) if (ctx->ctrl)
{ {
ctx->ctrl->refcount--; ctx->ctrl->refcount--;
ctx->ctrl = NULL; ctx->ctrl = NULL;
} }
if (ctx->fd_error) if (ctx->fp_err)
log_info (_("reading from ldap wrapper %d failed: %s\n"), log_info (_("reading from ldap wrapper %d failed: %s\n"),
ctx->printable_pid, gpg_strerror (ctx->fd_error)); ctx->printable_pid, gpg_strerror (ctx->fp_err));
break; break;
} }
} }
@ -513,34 +581,34 @@ ldap_wrapper_connection_cleanup (ctrl_t ctrl)
ctx->ctrl = NULL; ctx->ctrl = NULL;
if (ctx->pid != (pid_t)(-1)) if (ctx->pid != (pid_t)(-1))
gnupg_kill_process (ctx->pid); gnupg_kill_process (ctx->pid);
if (ctx->fd_error) if (ctx->fp_err)
log_info (_("reading from ldap wrapper %d failed: %s\n"), log_info (_("reading from ldap wrapper %d failed: %s\n"),
ctx->printable_pid, gpg_strerror (ctx->fd_error)); ctx->printable_pid, gpg_strerror (ctx->fp_err));
} }
} }
/* This is the callback used by the ldap wrapper to feed the ksba /* This is the callback used by the ldap wrapper to feed the ksba
reader with the wrappers stdout. See the description of * reader with the wrapper's stdout. See the description of
ksba_reader_set_cb for details. */ * ksba_reader_set_cb for details. */
static int static int
reader_callback (void *cb_value, char *buffer, size_t count, size_t *nread) reader_callback (void *cb_value, char *buffer, size_t count, size_t *nread)
{ {
struct wrapper_context_s *ctx = cb_value; struct wrapper_context_s *ctx = cb_value;
size_t nleft = count; size_t nleft = count;
int nfds;
struct timespec abstime; struct timespec abstime;
struct timespec curtime; struct timespec curtime;
struct timespec timeout; struct timespec timeout;
int saved_errno; int millisecs;
fd_set fdset, read_fdset; gpgrt_poll_t fparray[1];
int ret; int ret;
gpg_error_t err;
/* FIXME: We might want to add some internal buffering because the /* FIXME: We might want to add some internal buffering because the
ksba code does not do any buffering for itself (because a ksba ksba code does not do any buffering for itself (because a ksba
reader may be detached from another stream to read other data and reader may be detached from another stream to read other data and
the it would be cumbersome to get back already buffered then it would be cumbersome to get back already buffered stuff). */
stuff). */
if (!buffer && !count && !nread) if (!buffer && !count && !nread)
return -1; /* Rewind is not supported. */ return -1; /* Rewind is not supported. */
@ -548,66 +616,66 @@ reader_callback (void *cb_value, char *buffer, size_t count, size_t *nread)
/* If we ever encountered a read error, don't continue (we don't want to /* If we ever encountered a read error, don't continue (we don't want to
possibly overwrite the last error cause). Bail out also if the possibly overwrite the last error cause). Bail out also if the
file descriptor has been closed. */ file descriptor has been closed. */
if (ctx->fd_error || ctx->fd == -1) if (ctx->fp_err || !ctx->fp)
{ {
*nread = 0; *nread = 0;
return -1; return -1;
} }
FD_ZERO (&fdset); memset (fparray, 0, sizeof fparray);
FD_SET (ctx->fd, &fdset); fparray[0].stream = ctx->fp;
nfds = ctx->fd + 1; fparray[0].want_read = 1;
npth_clock_gettime (&abstime); npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL; abstime.tv_sec += TIMERTICK_INTERVAL;
while (nleft > 0) while (nleft > 0)
{ {
int n;
gpg_error_t err;
npth_clock_gettime (&curtime); npth_clock_gettime (&curtime);
if (!(npth_timercmp (&curtime, &abstime, <))) if (!(npth_timercmp (&curtime, &abstime, <)))
{ {
err = dirmngr_tick (ctx->ctrl); err = dirmngr_tick (ctx->ctrl);
if (err) if (err)
{ {
ctx->fd_error = err; ctx->fp_err = err;
SAFE_CLOSE (ctx->fd); SAFE_CLOSE (ctx->fp);
return -1; return -1;
} }
npth_clock_gettime (&abstime); npth_clock_gettime (&abstime);
abstime.tv_sec += TIMERTICK_INTERVAL; abstime.tv_sec += TIMERTICK_INTERVAL;
} }
npth_timersub (&abstime, &curtime, &timeout); npth_timersub (&abstime, &curtime, &timeout);
millisecs = timeout.tv_sec * 1000;
millisecs += timeout.tv_nsec / 1000000;
if (millisecs < 0)
millisecs = 1;
read_fdset = fdset; ret = es_poll (fparray, DIM (fparray), millisecs);
ret = npth_pselect (nfds, &read_fdset, NULL, NULL, &timeout, NULL); if (ret < 0)
saved_errno = errno;
if (ret == -1 && saved_errno != EINTR)
{ {
ctx->fd_error = gpg_error_from_errno (errno); ctx->fp_err = gpg_error_from_syserror ();
SAFE_CLOSE (ctx->fd); log_error ("error polling stdout of ldap wrapper %d: %s\n",
ctx->printable_pid, gpg_strerror (ctx->fp_err));
SAFE_CLOSE (ctx->fp);
return -1; return -1;
} }
if (ret <= 0) if (!ret)
{
/* Timeout. Will be handled when calculating the next timeout. */ /* Timeout. Will be handled when calculating the next timeout. */
continue; continue;
}
/* This should not block now that select returned with a file if (fparray[0].got_read)
descriptor. So it shouldn't be necessary to use npth_read
(and it is slightly dangerous in the sense that a concurrent
thread might (accidentially?) change the status of ctx->fd
before we read. FIXME: Set ctx->fd to nonblocking? */
n = read (ctx->fd, buffer, nleft);
if (n < 0)
{ {
ctx->fd_error = gpg_error_from_errno (errno); size_t n;
SAFE_CLOSE (ctx->fd);
if (es_read (ctx->fp, buffer, nleft, &n))
{
ctx->fp_err = gpg_error_from_syserror ();
SAFE_CLOSE (ctx->fp);
return -1; return -1;
} }
else if (!n) else if (!n) /* EOF */
{ {
if (nleft == count) if (nleft == count)
return -1; /* EOF. */ return -1; /* EOF. */
@ -618,11 +686,13 @@ reader_callback (void *cb_value, char *buffer, size_t count, size_t *nread)
if (n > 0 && ctx->stamp != (time_t)(-1)) if (n > 0 && ctx->stamp != (time_t)(-1))
ctx->stamp = time (NULL); ctx->stamp = time (NULL);
} }
}
*nread = count - nleft; *nread = count - nleft;
return 0; return 0;
} }
/* Fork and exec the LDAP wrapper and return a new libksba reader /* Fork and exec the LDAP wrapper and return a new libksba reader
object at READER. ARGV is a NULL terminated list of arguments for object at READER. ARGV is a NULL terminated list of arguments for
the wrapper. The function returns 0 on success or an error code. the wrapper. The function returns 0 on success or an error code.
@ -646,7 +716,7 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
int j; int j;
const char **arg_list; const char **arg_list;
const char *pgmname; const char *pgmname;
int outpipe[2], errpipe[2]; estream_t outfp, errfp;
/* It would be too simple to connect stderr just to our logging /* It would be too simple to connect stderr just to our logging
stream. The problem is that if we are running multi-threaded stream. The problem is that if we are running multi-threaded
@ -696,41 +766,21 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
return err; return err;
} }
err = gnupg_create_inbound_pipe (outpipe, NULL, 0); err = gnupg_spawn_process (pgmname, arg_list,
if (!err) NULL, NULL, GNUPG_SPAWN_NONBLOCK,
{ NULL, &outfp, &errfp, &pid);
err = gnupg_create_inbound_pipe (errpipe, NULL, 0);
if (err)
{
close (outpipe[0]);
close (outpipe[1]);
}
}
if (err)
{
log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
xfree (arg_list); xfree (arg_list);
xfree (ctx);
return err;
}
err = gnupg_spawn_process_fd (pgmname, arg_list,
-1, outpipe[1], errpipe[1], &pid);
xfree (arg_list);
close (outpipe[1]);
close (errpipe[1]);
if (err) if (err)
{ {
close (outpipe[0]);
close (errpipe[0]);
xfree (ctx); xfree (ctx);
log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
return err; return err;
} }
ctx->pid = pid; ctx->pid = pid;
ctx->printable_pid = (int) pid; ctx->printable_pid = (int) pid;
ctx->fd = outpipe[0]; ctx->fp = outfp;
ctx->log_fd = errpipe[0]; ctx->log_fp = errfp;
ctx->ctrl = ctrl; ctx->ctrl = ctrl;
ctrl->refcount++; ctrl->refcount++;
ctx->stamp = time (NULL); ctx->stamp = time (NULL);
@ -752,9 +802,9 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[])
ctx->reader = *reader; ctx->reader = *reader;
ctx->next = wrapper_list; ctx->next = wrapper_list;
wrapper_list = ctx; wrapper_list = ctx;
if (opt.verbose) if (DBG_EXTPROG)
log_info ("ldap wrapper %d started (reader %p)\n", log_debug ("ldap wrapper %d started (%p, %s)\n",
(int)ctx->pid, ctx->reader); (int)ctx->pid, ctx->reader, pgmname);
/* Need to wait for the first byte so we are able to detect an empty /* Need to wait for the first byte so we are able to detect an empty
output and not let the consumer see an EOF without further error output and not let the consumer see an EOF without further error