Compare commits
10 Commits
master
...
wk/new-wai
Author | SHA1 | Date | |
---|---|---|---|
|
b2749d77da | ||
|
0378250846 | ||
|
153f1128f8 | ||
|
28e620fa16 | ||
|
ef50bffc71 | ||
|
3b32f7a97f | ||
|
2a3cdb3e81 | ||
|
5cfdf878fb | ||
|
92883efe71 | ||
|
52d8ed8dfb |
@ -74,7 +74,8 @@ main_sources = \
|
||||
data-estream.c \
|
||||
data-compat.c data-identify.c \
|
||||
signers.c sig-notation.c \
|
||||
wait.c wait-global.c wait-private.c wait-user.c wait.h \
|
||||
fdtable.c fdtable.h \
|
||||
wait.c wait.h \
|
||||
op-support.c \
|
||||
encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \
|
||||
sign.c passphrase.c progress.c \
|
||||
|
@ -21,6 +21,8 @@
|
||||
#ifndef CONTEXT_H
|
||||
#define CONTEXT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gpgme.h"
|
||||
#include "engine.h"
|
||||
#include "wait.h"
|
||||
@ -71,13 +73,25 @@ struct ctx_op_data
|
||||
};
|
||||
typedef struct ctx_op_data *ctx_op_data_t;
|
||||
|
||||
|
||||
|
||||
/* The context defines an environment in which crypto operations can
|
||||
be performed (sequentially). */
|
||||
* be performed (sequentially). */
|
||||
struct gpgme_context
|
||||
{
|
||||
DECLARE_LOCK (lock);
|
||||
|
||||
/* The unique serial number of this context object. This is used
|
||||
* for a weak reference of the context. Using the address of the
|
||||
* context is not always possible becuase it might have already been
|
||||
* freed and reused. */
|
||||
uint64_t serial;
|
||||
|
||||
/* A link to the next context. We keep all contex object in the
|
||||
* linked list to so that we are abale to find a context by its
|
||||
* serial number. */
|
||||
gpgme_ctx_t next_ctx;
|
||||
|
||||
/* True if the context was canceled asynchronously. */
|
||||
int canceled;
|
||||
|
||||
@ -180,10 +194,21 @@ struct gpgme_context
|
||||
gpgme_status_cb_t status_cb;
|
||||
void *status_cb_value;
|
||||
|
||||
/* A list of file descriptors in active use by the current
|
||||
operation. */
|
||||
struct fd_table fdt;
|
||||
struct gpgme_io_cbs io_cbs;
|
||||
/* User specific I/O callbacks. */
|
||||
struct gpgme_io_cbs user_io_cbs;
|
||||
};
|
||||
|
||||
|
||||
/* Macro to retrieve the serial number. Returns 0 if CTX is NULL. */
|
||||
#define CTXSERIAL(ctx) (ctx? (unsigned long)ctx->serial : 0)
|
||||
|
||||
/*-- gpgme.c --*/
|
||||
|
||||
gpg_error_t _gpgme_get_ctx (uint64_t serial, gpgme_ctx_t *r_ctx);
|
||||
|
||||
gpgme_error_t _gpgme_cancel_with_err (uint64_t serial, gpg_error_t ctx_err,
|
||||
gpg_error_t op_err);
|
||||
|
||||
|
||||
|
||||
#endif /* CONTEXT_H */
|
||||
|
32
src/debug.c
32
src/debug.c
@ -403,6 +403,8 @@ _gpgme_debug_buffer (int lvl, const char *const fmt,
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
if (lvl > 9)
|
||||
{
|
||||
while (idx < len)
|
||||
{
|
||||
char str[51];
|
||||
@ -433,3 +435,33 @@ _gpgme_debug_buffer (int lvl, const char *const fmt,
|
||||
_gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL, fmt, func, str);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (idx < len)
|
||||
{
|
||||
char str[48+4+1];
|
||||
char *strp = str;
|
||||
|
||||
for (j = 0; j < 48; j++)
|
||||
{
|
||||
unsigned char val;
|
||||
if (idx < len)
|
||||
{
|
||||
val = buffer[idx++];
|
||||
if (val == '\n')
|
||||
{
|
||||
*strp++ = '<';
|
||||
*strp++ = 'L';
|
||||
*strp++ = 'F';
|
||||
*strp++ = '>';
|
||||
break;
|
||||
}
|
||||
*strp++ = (val > 31 && val < 127)? val : '.';
|
||||
}
|
||||
}
|
||||
*strp = 0;
|
||||
|
||||
_gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL, fmt, func, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ gpgme_op_decrypt_verify (gpgme_ctx_t ctx, gpgme_data_t cipher,
|
||||
|
||||
err = decrypt_verify_start (ctx, 1, GPGME_DECRYPT_VERIFY, cipher, plain);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
ctx->ignore_mdc_error = 0; /* Always reset. */
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
@ -177,7 +177,7 @@ gpgme_op_decrypt_ext (gpgme_ctx_t ctx,
|
||||
else
|
||||
err = _gpgme_decrypt_start (ctx, 1, flags, cipher, plain);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
ctx->ignore_mdc_error = 0; /* Always reset. */
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
@ -613,7 +613,7 @@ gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
|
||||
|
||||
err = _gpgme_decrypt_start (ctx, 1, 0, cipher, plain);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
ctx->ignore_mdc_error = 0; /* Always reset. */
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ gpgme_op_delete (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret)
|
||||
err = delete_start (ctx, 1, key,
|
||||
allow_secret ? GPGME_DELETE_ALLOW_SECRET : 0);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -204,6 +204,6 @@ gpgme_op_delete_ext (gpgme_ctx_t ctx, const gpgme_key_t key,
|
||||
|
||||
err = delete_start (ctx, 1, key, flags);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return err;
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ gpgme_op_interact (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags,
|
||||
|
||||
err = interact_start (ctx, 1, key, flags, fnc, fnc_value, out);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -266,7 +266,7 @@ gpgme_op_edit (gpgme_ctx_t ctx, gpgme_key_t key,
|
||||
err = edit_start (ctx, 1, 0, key, fnc, fnc_value, out);
|
||||
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
@ -309,6 +309,6 @@ gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key,
|
||||
|
||||
err = edit_start (ctx, 1, 1, key, fnc, fnc_value, out);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ gpgme_op_encrypt_sign_ext (gpgme_ctx_t ctx, gpgme_key_t recp[],
|
||||
|
||||
err = encrypt_sign_start (ctx, 1, recp, recpstring, flags, plain, cipher);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
@ -319,7 +319,7 @@ gpgme_op_encrypt_ext (gpgme_ctx_t ctx, gpgme_key_t recp[],
|
||||
|
||||
err = encrypt_start (ctx, 1, recp, recpstring, flags, plain, cipher);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ llass_get_req_version (void)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
static gpg_error_t
|
||||
close_notify_handler (int fd, void *opaque)
|
||||
{
|
||||
engine_llass_t llass = opaque;
|
||||
@ -158,6 +158,7 @@ close_notify_handler (int fd, void *opaque)
|
||||
llass->status_cb.fd = -1;
|
||||
llass->status_cb.tag = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -720,7 +721,7 @@ start (engine_llass_t llass, const char *command)
|
||||
if (llass->status_cb.fd < 0)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
if (_gpgme_io_set_close_notify (llass->status_cb.fd,
|
||||
if (_gpgme_fdtable_add_close_notify (llass->status_cb.fd,
|
||||
close_notify_handler, llass))
|
||||
{
|
||||
_gpgme_io_close (llass->status_cb.fd);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define ENGINE_BACKEND_H
|
||||
|
||||
#include "engine.h"
|
||||
#include "fdtable.h"
|
||||
|
||||
struct engine_ops
|
||||
{
|
||||
|
@ -111,7 +111,7 @@ g13_get_req_version (void)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
static gpg_error_t
|
||||
close_notify_handler (int fd, void *opaque)
|
||||
{
|
||||
engine_g13_t g13 = opaque;
|
||||
@ -124,6 +124,7 @@ close_notify_handler (int fd, void *opaque)
|
||||
g13->status_cb.fd = -1;
|
||||
g13->status_cb.tag = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -686,7 +687,7 @@ start (engine_g13_t g13, const char *command)
|
||||
if (g13->status_cb.fd < 0)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
if (_gpgme_io_set_close_notify (g13->status_cb.fd,
|
||||
if (_gpgme_fdtable_add_close_notify (g13->status_cb.fd,
|
||||
close_notify_handler, g13))
|
||||
{
|
||||
_gpgme_io_close (g13->status_cb.fd);
|
||||
|
@ -173,7 +173,7 @@ gpg_io_event (void *engine, gpgme_event_io_t type, void *type_data)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
static gpg_error_t
|
||||
close_notify_handler (int fd, void *opaque)
|
||||
{
|
||||
engine_gpg_t gpg = opaque;
|
||||
@ -217,6 +217,7 @@ close_notify_handler (int fd, void *opaque)
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If FRONT is true, push at the front of the list. Use this for
|
||||
@ -525,9 +526,9 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,
|
||||
rc = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
if (_gpgme_io_set_close_notify (gpg->status.fd[0],
|
||||
if (_gpgme_fdtable_add_close_notify (gpg->status.fd[0],
|
||||
close_notify_handler, gpg)
|
||||
|| _gpgme_io_set_close_notify (gpg->status.fd[1],
|
||||
|| _gpgme_fdtable_add_close_notify (gpg->status.fd[1],
|
||||
close_notify_handler, gpg))
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_GENERAL);
|
||||
@ -778,8 +779,9 @@ gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
|
||||
gpg->colon.buffer = NULL;
|
||||
return saved_err;
|
||||
}
|
||||
if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg)
|
||||
|| _gpgme_io_set_close_notify (gpg->colon.fd[1],
|
||||
if (_gpgme_fdtable_add_close_notify (gpg->colon.fd[0],
|
||||
close_notify_handler, gpg)
|
||||
|| _gpgme_fdtable_add_close_notify (gpg->colon.fd[1],
|
||||
close_notify_handler, gpg))
|
||||
return gpg_error (GPG_ERR_GENERAL);
|
||||
gpg->colon.eof = 0;
|
||||
@ -1112,9 +1114,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
|
||||
free_argv (argv);
|
||||
return saved_err;
|
||||
}
|
||||
if (_gpgme_io_set_close_notify (fds[0],
|
||||
if (_gpgme_fdtable_add_close_notify (fds[0],
|
||||
close_notify_handler, gpg)
|
||||
|| _gpgme_io_set_close_notify (fds[1],
|
||||
|| _gpgme_fdtable_add_close_notify (fds[1],
|
||||
close_notify_handler,
|
||||
gpg))
|
||||
{
|
||||
@ -3276,6 +3278,8 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
|
||||
err = add_arg (gpg, "-");
|
||||
if (!err)
|
||||
err = add_input_size_hint (gpg, sig);
|
||||
if (!err && have_gpg_version (gpg, "2.1.16"))
|
||||
err = add_arg (gpg, "--verify");
|
||||
if (!err)
|
||||
err = add_arg (gpg, "--");
|
||||
if (!err)
|
||||
|
@ -138,11 +138,13 @@ gpgsm_get_req_version (void)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
static gpg_error_t
|
||||
close_notify_handler (int fd, void *opaque)
|
||||
{
|
||||
engine_gpgsm_t gpgsm = opaque;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, "gpgsm:close_notify_handler", NULL,
|
||||
"fd=%d", fd);
|
||||
assert (fd != -1);
|
||||
if (gpgsm->status_cb.fd == fd)
|
||||
{
|
||||
@ -156,6 +158,7 @@ close_notify_handler (int fd, void *opaque)
|
||||
* The status fd however is closed right after it received the
|
||||
* "OK" from the command. So we use this event to also close
|
||||
* the diag fd. */
|
||||
TRACE_LOG ("closing diag fd");
|
||||
_gpgme_io_close (gpgsm->diag_cb.fd);
|
||||
}
|
||||
else if (gpgsm->input_cb.fd == fd)
|
||||
@ -174,6 +177,7 @@ close_notify_handler (int fd, void *opaque)
|
||||
free (gpgsm->input_helper_memory);
|
||||
gpgsm->input_helper_memory = NULL;
|
||||
}
|
||||
TRACE_LOG ("ready with input_fd");
|
||||
}
|
||||
else if (gpgsm->output_cb.fd == fd)
|
||||
{
|
||||
@ -181,6 +185,7 @@ close_notify_handler (int fd, void *opaque)
|
||||
(*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
|
||||
gpgsm->output_cb.fd = -1;
|
||||
gpgsm->output_cb.tag = NULL;
|
||||
TRACE_LOG ("ready with output_fd");
|
||||
}
|
||||
else if (gpgsm->message_cb.fd == fd)
|
||||
{
|
||||
@ -188,6 +193,7 @@ close_notify_handler (int fd, void *opaque)
|
||||
(*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
|
||||
gpgsm->message_cb.fd = -1;
|
||||
gpgsm->message_cb.tag = NULL;
|
||||
TRACE_LOG ("ready with message_fd");
|
||||
}
|
||||
else if (gpgsm->diag_cb.fd == fd)
|
||||
{
|
||||
@ -195,7 +201,10 @@ close_notify_handler (int fd, void *opaque)
|
||||
(*gpgsm->io_cbs.remove) (gpgsm->diag_cb.tag);
|
||||
gpgsm->diag_cb.fd = -1;
|
||||
gpgsm->diag_cb.tag = NULL;
|
||||
TRACE_LOG ("ready with diag_fd");
|
||||
}
|
||||
TRACE_SUC ("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -544,18 +553,18 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir,
|
||||
|
||||
#if !USE_DESCRIPTOR_PASSING
|
||||
if (!err
|
||||
&& (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
|
||||
&& (_gpgme_fdtable_add_close_notify (gpgsm->input_cb.fd,
|
||||
close_notify_handler, gpgsm)
|
||||
|| _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
|
||||
|| _gpgme_fdtable_add_close_notify (gpgsm->output_cb.fd,
|
||||
close_notify_handler, gpgsm)
|
||||
|| _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
|
||||
|| _gpgme_fdtable_add_close_notify (gpgsm->message_cb.fd,
|
||||
close_notify_handler, gpgsm)))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_GENERAL);
|
||||
goto leave;
|
||||
}
|
||||
#endif
|
||||
if (!err && _gpgme_io_set_close_notify (gpgsm->diag_cb.fd,
|
||||
if (!err && _gpgme_fdtable_add_close_notify (gpgsm->diag_cb.fd,
|
||||
close_notify_handler, gpgsm))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_GENERAL);
|
||||
@ -816,7 +825,7 @@ gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
|
||||
iocb_data->fd = dir ? fds[0] : fds[1];
|
||||
iocb_data->server_fd = dir ? fds[1] : fds[0];
|
||||
|
||||
if (_gpgme_io_set_close_notify (iocb_data->fd,
|
||||
if (_gpgme_fdtable_add_close_notify (iocb_data->fd,
|
||||
close_notify_handler, gpgsm))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_GENERAL);
|
||||
@ -901,7 +910,7 @@ status_handler (void *opaque, int fd)
|
||||
/* Try our best to terminate the connection friendly. */
|
||||
/* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
|
||||
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
|
||||
"fd 0x%x: error from assuan (%d) getting status line : %s",
|
||||
"fd=%d: error from assuan (%d) getting status line : %s",
|
||||
fd, err, gpg_strerror (err));
|
||||
}
|
||||
else if (linelen >= 3
|
||||
@ -913,7 +922,7 @@ status_handler (void *opaque, int fd)
|
||||
if (! err)
|
||||
err = gpg_error (GPG_ERR_GENERAL);
|
||||
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
|
||||
"fd 0x%x: ERR line - mapped to: %s",
|
||||
"fd=%d: ERR line - mapped to: %s",
|
||||
fd, err ? gpg_strerror (err) : "ok");
|
||||
/* Try our best to terminate the connection friendly. */
|
||||
/* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
|
||||
@ -942,7 +951,7 @@ status_handler (void *opaque, int fd)
|
||||
err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
|
||||
}
|
||||
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
|
||||
"fd 0x%x: OK line - final status: %s",
|
||||
"fd=%d: OK line - final status: %s",
|
||||
fd, err ? gpg_strerror (err) : "ok");
|
||||
_gpgme_io_close (gpgsm->status_cb.fd);
|
||||
return err;
|
||||
@ -1017,7 +1026,7 @@ status_handler (void *opaque, int fd)
|
||||
}
|
||||
}
|
||||
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
|
||||
"fd 0x%x: D line; final status: %s",
|
||||
"fd=%d: D line; final status: %s",
|
||||
fd, err? gpg_strerror (err):"ok");
|
||||
}
|
||||
else if (linelen > 2
|
||||
@ -1059,7 +1068,7 @@ status_handler (void *opaque, int fd)
|
||||
}
|
||||
|
||||
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
|
||||
"fd 0x%x: D inlinedata; final status: %s",
|
||||
"fd=%d: D inlinedata; final status: %s",
|
||||
fd, err? gpg_strerror (err):"ok");
|
||||
}
|
||||
else if (linelen > 2
|
||||
@ -1097,7 +1106,7 @@ status_handler (void *opaque, int fd)
|
||||
else
|
||||
fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
|
||||
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
|
||||
"fd 0x%x: S line (%s) - final status: %s",
|
||||
"fd=%d: S line (%s) - final status: %s",
|
||||
fd, line+2, err? gpg_strerror (err):"ok");
|
||||
}
|
||||
else if (linelen >= 7
|
||||
@ -1122,12 +1131,14 @@ status_handler (void *opaque, int fd)
|
||||
|
||||
|
||||
static gpgme_error_t
|
||||
add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
|
||||
add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler,
|
||||
const char *handler_desc)
|
||||
{
|
||||
gpgme_error_t err;
|
||||
|
||||
TRACE_BEG (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
|
||||
"fd=%d, dir %d", iocbd->fd, iocbd->dir);
|
||||
TRACE_BEG (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", NULL,
|
||||
"fd=%d, dir %d (%s-handler)",
|
||||
iocbd->fd, iocbd->dir, handler_desc);
|
||||
err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
|
||||
iocbd->fd, iocbd->dir,
|
||||
handler, iocbd->data, &iocbd->tag);
|
||||
@ -1188,7 +1199,7 @@ start (engine_gpgsm_t gpgsm, const char *command)
|
||||
if (gpgsm->status_cb.fd < 0)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
|
||||
if (_gpgme_fdtable_add_close_notify (gpgsm->status_cb.fd,
|
||||
close_notify_handler, gpgsm))
|
||||
{
|
||||
_gpgme_io_close (gpgsm->status_cb.fd);
|
||||
@ -1196,15 +1207,19 @@ start (engine_gpgsm_t gpgsm, const char *command)
|
||||
return gpg_error (GPG_ERR_GENERAL);
|
||||
}
|
||||
|
||||
err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
|
||||
err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler, "status");
|
||||
if (!err && gpgsm->input_cb.fd != -1)
|
||||
err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
|
||||
err = add_io_cb (gpgsm, &gpgsm->input_cb,
|
||||
_gpgme_data_outbound_handler, "outbound");
|
||||
if (!err && gpgsm->output_cb.fd != -1)
|
||||
err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
|
||||
err = add_io_cb (gpgsm, &gpgsm->output_cb,
|
||||
_gpgme_data_inbound_handler, "inbound");
|
||||
if (!err && gpgsm->message_cb.fd != -1)
|
||||
err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
|
||||
err = add_io_cb (gpgsm, &gpgsm->message_cb,
|
||||
_gpgme_data_outbound_handler, "outbound");
|
||||
if (!err && gpgsm->diag_cb.fd != -1)
|
||||
err = add_io_cb (gpgsm, &gpgsm->diag_cb, _gpgme_data_inbound_handler);
|
||||
err = add_io_cb (gpgsm, &gpgsm->diag_cb,
|
||||
_gpgme_data_inbound_handler, "inbound");
|
||||
|
||||
if (!err)
|
||||
err = assuan_write_line (gpgsm->assuan_ctx, command);
|
||||
@ -1296,6 +1311,7 @@ gpgsm_delete (void *engine, gpgme_key_t key, unsigned int flags)
|
||||
}
|
||||
length++;
|
||||
|
||||
/* Fixme: Ugly quoting code below - use some standard function. */
|
||||
line = malloc (length);
|
||||
if (!line)
|
||||
return gpg_error_from_syserror ();
|
||||
@ -1351,6 +1367,7 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
|
||||
int invalid_recipients = 0;
|
||||
int i;
|
||||
|
||||
/* FIXME: Uyse a membuf etc to build up LINE. */
|
||||
linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
|
||||
line = malloc (10 + 40 + 1);
|
||||
if (!line)
|
||||
@ -1508,6 +1525,7 @@ gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
|
||||
engine_gpgsm_t gpgsm = engine;
|
||||
gpgme_error_t err = 0;
|
||||
char *cmd;
|
||||
const char *s1, *s2;
|
||||
|
||||
if (!gpgsm)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
@ -1515,20 +1533,19 @@ gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
|
||||
if (!pattern)
|
||||
pattern = "";
|
||||
|
||||
cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
|
||||
if (!cmd)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
strcpy (cmd, "EXPORT ");
|
||||
s1 = s2 = "";
|
||||
if ((mode & GPGME_EXPORT_MODE_SECRET))
|
||||
{
|
||||
strcat (cmd, "--secret ");
|
||||
s1 = "--secret ";
|
||||
if ((mode & GPGME_EXPORT_MODE_RAW))
|
||||
strcat (cmd, "--raw ");
|
||||
s2 = "--raw ";
|
||||
else if ((mode & GPGME_EXPORT_MODE_PKCS12))
|
||||
strcat (cmd, "--pkcs12 ");
|
||||
s2 = "--pkcs12 ";
|
||||
}
|
||||
strcat (cmd, pattern);
|
||||
|
||||
cmd = _gpgme_strconcat ("EXPORT ", s1, s2, pattern, NULL);
|
||||
if (!cmd)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
gpgsm->output_cb.data = keydata;
|
||||
err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
|
||||
@ -1559,6 +1576,7 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
|
||||
if (!gpgsm)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
/*FIXME: Replace by membuf and a quoting function. */
|
||||
if (pattern && *pattern)
|
||||
{
|
||||
const char **pat = pattern;
|
||||
@ -1859,21 +1877,12 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
|
||||
"OPTION offline=0" ,
|
||||
NULL, NULL);
|
||||
|
||||
|
||||
/* Length is "LISTSECRETKEYS " + p + '\0'. */
|
||||
line = malloc (15 + strlen (pattern) + 1);
|
||||
if (secret_only)
|
||||
line = _gpgme_strconcat ("LISTSECRETKEYS ", pattern, NULL);
|
||||
else
|
||||
line = _gpgme_strconcat ("LISTKEYS ", pattern, NULL);
|
||||
if (!line)
|
||||
return gpg_error_from_syserror ();
|
||||
if (secret_only)
|
||||
{
|
||||
strcpy (line, "LISTSECRETKEYS ");
|
||||
strcpy (&line[15], pattern);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy (line, "LISTKEYS ");
|
||||
strcpy (&line[9], pattern);
|
||||
}
|
||||
|
||||
gpgsm_clear_fd (gpgsm, INPUT_FD);
|
||||
gpgsm_clear_fd (gpgsm, OUTPUT_FD);
|
||||
@ -1934,6 +1943,7 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
|
||||
"OPTION offline=0" ,
|
||||
NULL, NULL);
|
||||
|
||||
/* FIXME: Repalce by membuf and quoting function. */
|
||||
if (pattern && *pattern)
|
||||
{
|
||||
const char **pat = pattern;
|
||||
|
@ -84,7 +84,7 @@ static gpgme_error_t engspawn_cancel (void *engine);
|
||||
|
||||
|
||||
|
||||
static void
|
||||
static gpg_error_t
|
||||
close_notify_handler (int fd, void *opaque)
|
||||
{
|
||||
engine_spawn_t esp = opaque;
|
||||
@ -110,6 +110,7 @@ close_notify_handler (int fd, void *opaque)
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -180,8 +181,10 @@ build_fd_data_map (engine_spawn_t esp)
|
||||
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))
|
||||
if (_gpgme_fdtable_add_close_notify (fds[0],
|
||||
close_notify_handler, esp)
|
||||
|| _gpgme_fdtable_add_close_notify (fds[1],
|
||||
close_notify_handler, esp))
|
||||
{
|
||||
/* FIXME: Need error cleanup. */
|
||||
return gpg_error (GPG_ERR_GENERAL);
|
||||
|
@ -135,7 +135,7 @@ uiserver_get_req_version (void)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
static gpg_error_t
|
||||
close_notify_handler (int fd, void *opaque)
|
||||
{
|
||||
engine_uiserver_t uiserver = opaque;
|
||||
@ -179,6 +179,8 @@ close_notify_handler (int fd, void *opaque)
|
||||
uiserver->message_cb.fd = -1;
|
||||
uiserver->message_cb.tag = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -585,7 +587,7 @@ uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
|
||||
iocb_data->fd = dir ? fds[0] : fds[1];
|
||||
iocb_data->server_fd = dir ? fds[1] : fds[0];
|
||||
|
||||
if (_gpgme_io_set_close_notify (iocb_data->fd,
|
||||
if (_gpgme_fdtable_add_close_notify (iocb_data->fd,
|
||||
close_notify_handler, uiserver))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_GENERAL);
|
||||
@ -921,7 +923,7 @@ start (engine_uiserver_t uiserver, const char *command)
|
||||
if (uiserver->status_cb.fd < 0)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
|
||||
if (_gpgme_fdtable_add_close_notify (uiserver->status_cb.fd,
|
||||
close_notify_handler, uiserver))
|
||||
{
|
||||
_gpgme_io_close (uiserver->status_cb.fd);
|
||||
|
@ -202,7 +202,7 @@ gpgme_op_export (gpgme_ctx_t ctx, const char *pattern,
|
||||
|
||||
err = export_start (ctx, 1, pattern, mode, keydata);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -319,7 +319,7 @@ gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[],
|
||||
err = export_ext_start (ctx, 1, pattern, mode, keydata);
|
||||
if (!err)
|
||||
{
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
if (!err)
|
||||
{
|
||||
/* For this synchronous operation we check for operational
|
||||
@ -459,7 +459,7 @@ gpgme_op_export_keys (gpgme_ctx_t ctx,
|
||||
err = export_keys_start (ctx, 1, keys, mode, keydata);
|
||||
if (!err)
|
||||
{
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
if (!err)
|
||||
{
|
||||
/* For this synchronous operation we check for operational
|
||||
@ -478,4 +478,3 @@ gpgme_op_export_keys (gpgme_ctx_t ctx,
|
||||
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
714
src/fdtable.c
Normal file
714
src/fdtable.c
Normal file
@ -0,0 +1,714 @@
|
||||
/* fdtable.c - Keep track of file descriptors.
|
||||
* Copyright (C) 2019 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 <https://gnu.org/licenses/>.
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "gpgme.h"
|
||||
#include "debug.h"
|
||||
#include "context.h"
|
||||
#include "fdtable.h"
|
||||
|
||||
|
||||
/* The table to hold information about file descriptors. Currently we
|
||||
* use a linear search and extend the table as needed. Eventually we
|
||||
* may swicth to a hash table and allocate items on the fly. */
|
||||
struct fdtable_item_s
|
||||
{
|
||||
int fd; /* -1 indicates an unused entry. */
|
||||
|
||||
uint64_t owner; /* The S/N of the context owning this FD. */
|
||||
|
||||
/* ACTIVE is set if this fd is in the global event loop, has an
|
||||
* active callback (.io_cb), and has seen the start event. */
|
||||
unsigned int active:1;
|
||||
/* DONE is set if this fd was previously active but is not active
|
||||
* any longer, either because is finished successfully or its I/O
|
||||
* callback returned an error. Note that ACTIVE and DONE should
|
||||
* never both be set. */
|
||||
unsigned int done:1;
|
||||
|
||||
/* Infos for io_select. */
|
||||
unsigned int for_read:1;
|
||||
unsigned int for_write:1;
|
||||
unsigned int signaled:1;
|
||||
|
||||
/* We are in a closing handler. Note that while this flag is active
|
||||
* the remove code holds an index into the table. Thus we better
|
||||
* make sure that the index won't change. Or change the removal
|
||||
* code to re-find the fd. */
|
||||
unsigned int closing:1;
|
||||
|
||||
/* We are currently running the IO callback. */
|
||||
unsigned int io_cb_running:1;
|
||||
|
||||
/* The I/O callback handler with its value context. */
|
||||
struct {
|
||||
gpgme_io_cb_t cb;
|
||||
void *value;
|
||||
} io_cb;
|
||||
|
||||
/* The error code and the operational error for the done status. */
|
||||
gpg_error_t done_status;
|
||||
gpg_error_t done_op_err;
|
||||
|
||||
/* The callback to be called before the descriptor is actually closed. */
|
||||
struct {
|
||||
fdtable_handler_t handler;
|
||||
void *value;
|
||||
} close_notify;
|
||||
};
|
||||
typedef struct fdtable_item_s *fdtable_item_t;
|
||||
|
||||
/* The actual table, its size and the lock to guard access. */
|
||||
static fdtable_item_t fdtable;
|
||||
static unsigned int fdtablesize;
|
||||
DEFINE_STATIC_LOCK (fdtable_lock);
|
||||
|
||||
|
||||
|
||||
/* Insert FD into our file descriptor table. This function checks
|
||||
* that FD is not yet in the table. On success 0 is returned; if FD
|
||||
* is already in the table GPG_ERR_DUP_KEY is returned. Other error
|
||||
* codes may also be returned. */
|
||||
gpg_error_t
|
||||
_gpgme_fdtable_insert (int fd)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int firstunused, idx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "fd=%d", fd);
|
||||
|
||||
if (fd < 0 )
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
|
||||
firstunused = -1;
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd == -1)
|
||||
{
|
||||
if (firstunused == -1)
|
||||
firstunused = idx;
|
||||
}
|
||||
else if (fdtable[idx].fd == fd)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_DUP_KEY);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (firstunused == -1)
|
||||
{
|
||||
/* We need to increase the size of the table. The approach we
|
||||
* take is straightforward to minimize the risk of bugs. */
|
||||
fdtable_item_t newtbl;
|
||||
size_t newsize = fdtablesize + 64;
|
||||
|
||||
newtbl = calloc (newsize, sizeof *newtbl);
|
||||
if (!newtbl)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
newtbl[idx] = fdtable[idx];
|
||||
for (; idx < newsize; idx++)
|
||||
newtbl[idx].fd = -1;
|
||||
|
||||
free (fdtable);
|
||||
fdtable = newtbl;
|
||||
idx = fdtablesize;
|
||||
fdtablesize = newsize;
|
||||
}
|
||||
else
|
||||
idx = firstunused;
|
||||
|
||||
fdtable[idx].fd = fd;
|
||||
fdtable[idx].owner = 0;
|
||||
fdtable[idx].active = 0;
|
||||
fdtable[idx].done = 0;
|
||||
fdtable[idx].for_read = 0;
|
||||
fdtable[idx].for_write = 0;
|
||||
fdtable[idx].signaled = 0;
|
||||
fdtable[idx].closing = 0;
|
||||
fdtable[idx].io_cb_running = 0;
|
||||
fdtable[idx].io_cb.cb = NULL;
|
||||
fdtable[idx].io_cb.value = NULL;
|
||||
fdtable[idx].close_notify.handler = NULL;
|
||||
fdtable[idx].close_notify.value = NULL;
|
||||
err = 0;
|
||||
|
||||
leave:
|
||||
UNLOCK (fdtable_lock);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
||||
/* Add the close notification HANDLER to the table under the key FD.
|
||||
* FD must exist. VALUE is a pointer passed to the handler along with
|
||||
* the FD. */
|
||||
gpg_error_t
|
||||
_gpgme_fdtable_add_close_notify (int fd,
|
||||
fdtable_handler_t handler, void *value)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int idx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "fd=%d", fd);
|
||||
|
||||
if (fd < 0 )
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd == fd)
|
||||
break;
|
||||
if (idx == fdtablesize)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_NO_KEY);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (fdtable[idx].close_notify.handler)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_DUP_VALUE);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
fdtable[idx].close_notify.handler = handler;
|
||||
fdtable[idx].close_notify.value = value;
|
||||
err = 0;
|
||||
|
||||
leave:
|
||||
UNLOCK (fdtable_lock);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
||||
/* Set the I/O callback for the FD. FD must already exist otherwise
|
||||
* GPG_ERR_NO_KEY is returned. OWNER is the serial of the owning
|
||||
* context. If DIRECTION is 1 the callback wants to read from it; if
|
||||
* it is 0 the callback want to write to it. CB is the actual
|
||||
* callback and CB_VALUE the values passed to that callback. If a
|
||||
* callback as already been set GPG_ERR_DUP_VALUE is returned. To
|
||||
* remove the handler, FD and OWNER must be passed as usual but CB be
|
||||
* passed as NULL.
|
||||
*/
|
||||
gpg_error_t
|
||||
_gpgme_fdtable_set_io_cb (int fd, uint64_t owner, int direction,
|
||||
gpgme_io_cb_t cb, void *cb_value)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int idx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "fd=%d ctx=%lu dir=%d",
|
||||
fd, (unsigned long)owner, direction);
|
||||
|
||||
if (fd < 0 || !owner)
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
|
||||
if (cb)
|
||||
{
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd == fd)
|
||||
break;
|
||||
if (idx == fdtablesize)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_NO_KEY);
|
||||
TRACE_LOG ("with_cb: fd=%d owner=%lu", fd, (unsigned long)owner);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (fdtable[idx].io_cb.cb)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_DUP_VALUE);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
fdtable[idx].owner = owner;
|
||||
|
||||
fdtable[idx].for_read = (direction == 1);
|
||||
fdtable[idx].for_write = (direction == 0);
|
||||
fdtable[idx].signaled = 0;
|
||||
|
||||
fdtable[idx].io_cb.cb = cb;
|
||||
fdtable[idx].io_cb.value = cb_value;
|
||||
}
|
||||
else /* Remove. */
|
||||
{
|
||||
/* We compare also the owner as a cross-check. */
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd == fd && fdtable[idx].owner == owner)
|
||||
break;
|
||||
if (idx == fdtablesize)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_NO_KEY);
|
||||
TRACE_LOG ("remove: fd=%d owner=%lu", fd, (unsigned long)owner);
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
TRACE_LOG (" TBL: fd=%d owner=%lu", fdtable[idx].fd, (unsigned long)fdtable[idx].owner);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
fdtable[idx].for_read = 0;
|
||||
fdtable[idx].for_write = 0;
|
||||
fdtable[idx].signaled = 0;
|
||||
|
||||
fdtable[idx].io_cb.cb = NULL;
|
||||
fdtable[idx].io_cb.value = NULL;
|
||||
fdtable[idx].owner = 0;
|
||||
}
|
||||
err = 0;
|
||||
|
||||
leave:
|
||||
UNLOCK (fdtable_lock);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
||||
/* Set all FDs of OWNER into the active state. */
|
||||
gpg_error_t
|
||||
_gpgme_fdtable_set_active (uint64_t owner)
|
||||
{
|
||||
int idx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", (unsigned long)owner);
|
||||
|
||||
if (!owner )
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd != -1 && fdtable[idx].owner == owner
|
||||
&& fdtable[idx].io_cb.cb)
|
||||
{
|
||||
fdtable[idx].active = 1;
|
||||
fdtable[idx].done = 0;
|
||||
}
|
||||
|
||||
UNLOCK (fdtable_lock);
|
||||
return TRACE_ERR (0);
|
||||
}
|
||||
|
||||
|
||||
/* Set all FDs of OWNER into the done state. STATUS and OP_ERR are
|
||||
* recorded. */
|
||||
gpg_error_t
|
||||
_gpgme_fdtable_set_done (uint64_t owner, gpg_error_t status, gpg_error_t op_err)
|
||||
{
|
||||
int idx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", (unsigned long)owner);
|
||||
|
||||
if (!owner )
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd != -1 && fdtable[idx].owner == owner
|
||||
&& fdtable[idx].active)
|
||||
{
|
||||
fdtable[idx].active = 0;
|
||||
fdtable[idx].done = 1;
|
||||
fdtable[idx].done_status = status;
|
||||
fdtable[idx].done_op_err = op_err;
|
||||
}
|
||||
|
||||
UNLOCK (fdtable_lock);
|
||||
return TRACE_ERR (0);
|
||||
}
|
||||
|
||||
|
||||
/* Walk over all fds in FDS and copy the signaled flag if set. It
|
||||
* does not clear any signal flag in the global table. */
|
||||
void
|
||||
_gpgme_fdtable_set_signaled (io_select_t fds, unsigned int nfds)
|
||||
{
|
||||
int idx;
|
||||
unsigned int n, count;
|
||||
|
||||
if (!nfds)
|
||||
return;
|
||||
|
||||
/* FIXME: Highly inefficient code in case of large select lists. */
|
||||
count = 0;
|
||||
LOCK (fdtable_lock);
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
{
|
||||
if (fdtable[idx].fd == -1)
|
||||
continue;
|
||||
for (n = 0; n < nfds; n++)
|
||||
if (fdtable[idx].fd == fds[n].fd)
|
||||
{
|
||||
if (fds[n].signaled && !fdtable[idx].signaled)
|
||||
{
|
||||
fdtable[idx].signaled = 1;
|
||||
count++; /* Only for tracing. */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
UNLOCK (fdtable_lock);
|
||||
|
||||
TRACE (DEBUG_SYSIO, __func__, NULL, "fds newly signaled=%u", count);
|
||||
}
|
||||
|
||||
|
||||
/* Remove FD from the table after calling the close handler. Note
|
||||
* that at the time the close handler is called the FD has been
|
||||
* removed form the table. Thus the close handler may not access the
|
||||
* fdtable anymore and assume that FD is still there. Callers may
|
||||
* want to handle the error code GPG_ERR_NO_KEY which indicates that
|
||||
* FD is not anymore or not yet in the table. */
|
||||
gpg_error_t
|
||||
_gpgme_fdtable_remove (int fd)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int idx;
|
||||
fdtable_handler_t handler;
|
||||
void *handlervalue;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "fd=%d", fd);
|
||||
|
||||
if (fd < 0 )
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd == fd)
|
||||
break;
|
||||
if (idx == fdtablesize)
|
||||
{
|
||||
UNLOCK (fdtable_lock);
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_NO_KEY));
|
||||
}
|
||||
|
||||
TRACE_LOG ("removal of fd=%d owner=%lu (closing=%d)",
|
||||
fdtable[idx].fd, (unsigned long)fdtable[idx].owner,
|
||||
fdtable[idx].closing);
|
||||
|
||||
handler = fdtable[idx].close_notify.handler;
|
||||
fdtable[idx].close_notify.handler = NULL;
|
||||
handlervalue = fdtable[idx].close_notify.value;
|
||||
fdtable[idx].close_notify.value = NULL;
|
||||
|
||||
/* The handler might call into the fdtable again, so if we have a
|
||||
* handler we can't immediately close it but instead record the fact
|
||||
* and remove the entry from the table only after the handler has
|
||||
* been run. */
|
||||
if (handler)
|
||||
fdtable[idx].closing = 1;
|
||||
else if (!fdtable[idx].closing)
|
||||
fdtable[idx].fd = -1;
|
||||
|
||||
UNLOCK (fdtable_lock);
|
||||
|
||||
if (handler)
|
||||
{
|
||||
err = handler (fd, handlervalue);
|
||||
LOCK (fdtable_lock);
|
||||
TRACE_LOG ("final removal of fd=%d owner=%lu (closing=%d)",
|
||||
fdtable[idx].fd, (unsigned long)fdtable[idx].owner,
|
||||
fdtable[idx].closing);
|
||||
fdtable[idx].fd = -1;
|
||||
UNLOCK (fdtable_lock);
|
||||
}
|
||||
else
|
||||
err = 0;
|
||||
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
||||
/* Return the number of FDs for OWNER (or for all if OWNER is 0)
|
||||
* which match FLAGS. Recognized flag values are:
|
||||
* 0 - Number FDs with IO callbacks
|
||||
* FDTABLE_FLAG_ACTIVE - Number of FDs in the active state.
|
||||
* FDTABLE_FLAG_DONE - Number of FDs in the done state.
|
||||
* FDTABLE_FLAG_NOT_DONE - Number of FDs not in the done state.
|
||||
*/
|
||||
unsigned int
|
||||
_gpgme_fdtable_get_count (uint64_t owner, unsigned int flags)
|
||||
{
|
||||
int idx;
|
||||
unsigned int count = 0;
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd != -1 && (!owner || fdtable[idx].owner == owner))
|
||||
{
|
||||
if (fdtable[idx].closing)
|
||||
continue;
|
||||
|
||||
if ((flags & FDTABLE_FLAG_DONE) && fdtable[idx].done)
|
||||
count++;
|
||||
else if ((flags & FDTABLE_FLAG_NOT_DONE) && !fdtable[idx].done)
|
||||
count++;
|
||||
else if ((flags & FDTABLE_FLAG_ACTIVE) && fdtable[idx].active)
|
||||
count++;
|
||||
else if (!flags && fdtable[idx].io_cb.cb)
|
||||
count++;
|
||||
}
|
||||
UNLOCK (fdtable_lock);
|
||||
|
||||
TRACE (DEBUG_SYSIO, __func__, NULL, "ctx=%lu flags=0x%u -> count=%u",
|
||||
(unsigned long)owner, flags, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/* Run all signaled IO callbacks of OWNER or all signaled callbacks if
|
||||
* OWNER is 0. Returns an error code on the first real error
|
||||
* encountered. If R_OP_ERR is not NULL an optional operational error
|
||||
* can be stored there. For EOF the respective flags are set. */
|
||||
gpg_error_t
|
||||
_gpgme_fdtable_run_io_cbs (uint64_t owner, gpg_error_t *r_op_err,
|
||||
uint64_t *r_owner)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int idx;
|
||||
int fd;
|
||||
gpgme_io_cb_t iocb;
|
||||
struct io_cb_data iocb_data;
|
||||
uint64_t serial;
|
||||
unsigned int cb_count;
|
||||
gpgme_ctx_t actx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", owner);
|
||||
|
||||
if (r_op_err)
|
||||
*r_op_err = 0;
|
||||
if (r_owner)
|
||||
*r_owner = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
fd = -1;
|
||||
LOCK (fdtable_lock);
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd != -1 && (!owner || fdtable[idx].owner == owner)
|
||||
&& fdtable[idx].signaled)
|
||||
{
|
||||
fd = fdtable[idx].fd;
|
||||
serial = fdtable[idx].owner;
|
||||
iocb = fdtable[idx].io_cb.cb;
|
||||
iocb_data.handler_value = fdtable[idx].io_cb.value;
|
||||
iocb_data.op_err = 0;
|
||||
fdtable[idx].signaled = 0;
|
||||
if (iocb)
|
||||
{
|
||||
fdtable[idx].io_cb_running = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
UNLOCK (fdtable_lock);
|
||||
if (fd == -1)
|
||||
break; /* No more callbacks found. */
|
||||
|
||||
/* If the context object is still valid and has not been
|
||||
* canceled, we run the I/O callback. */
|
||||
err = _gpgme_get_ctx (serial, &actx);
|
||||
if (!err)
|
||||
{
|
||||
err = iocb (&iocb_data, fd);
|
||||
if (err)
|
||||
TRACE_LOG ("iocb(fd=%d) err=%s", fd, gpg_strerror (err));
|
||||
}
|
||||
|
||||
/* Clear the running flag and while we are at it also count the
|
||||
* remaining callbacks. */
|
||||
cb_count = 0;
|
||||
LOCK (fdtable_lock);
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
{
|
||||
if (fdtable[idx].fd == -1)
|
||||
continue;
|
||||
if (fdtable[idx].fd == fd)
|
||||
fdtable[idx].io_cb_running = 0;
|
||||
if (fdtable[idx].owner == serial)
|
||||
cb_count++;
|
||||
}
|
||||
UNLOCK (fdtable_lock);
|
||||
|
||||
/* Handle errors or success from the IO callback. In the error
|
||||
* case we close all fds belonging to the same context. In the
|
||||
* success case we check whether any callback is left and only
|
||||
* if that is not the case, tell the engine that we are done.
|
||||
* The latter indirectly sets the fd into the done state. */
|
||||
if (err)
|
||||
{
|
||||
_gpgme_cancel_with_err (serial, err, 0);
|
||||
if (r_owner)
|
||||
*r_owner = serial;
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
else if (iocb_data.op_err)
|
||||
{
|
||||
/* An operational error occurred. Cancel the current
|
||||
* operation but not the session, and signal it. */
|
||||
_gpgme_cancel_with_err (serial, 0, iocb_data.op_err);
|
||||
|
||||
/* NOTE: This relies on the operational error being
|
||||
* generated after the operation really has completed, for
|
||||
* example after no further status line output is generated.
|
||||
* Otherwise the following I/O will spill over into the next
|
||||
* operation. */
|
||||
if (r_op_err)
|
||||
*r_op_err = iocb_data.op_err;
|
||||
if (r_owner)
|
||||
*r_owner = serial;
|
||||
return TRACE_ERR (0);
|
||||
}
|
||||
else if (!cb_count && actx)
|
||||
{
|
||||
struct gpgme_io_event_done_data data = { 0, 0 };
|
||||
_gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &data);
|
||||
}
|
||||
}
|
||||
|
||||
return TRACE_ERR (0);
|
||||
}
|
||||
|
||||
|
||||
/* Retrieve a list of file descriptors owned by OWNER, or with OWNER
|
||||
* being 0 of all fds, and store that list as a new array at R_FDS.
|
||||
* Return the number of FDS in that list or 0 if none were selected.
|
||||
* FLAGS give further selection flags:
|
||||
* FDTABLE_FLAG_ACTIVE - Only those with the active flag set.
|
||||
* FDTABLE_FLAG_DONE - Only those with the done flag set.
|
||||
* FDTABLE_FLAG_FOR_READ - Only those with the readable FDs.
|
||||
* FDTABLE_FLAG_FOR_WRITE - Only those with the writable FDs.
|
||||
* FDTABLE_FLAG_SIGNALED - Only those with the signaled flag set.
|
||||
* FDTABLE_FLAG_NOT_SIGNALED - Only those with the signaled flag cleared.
|
||||
* FDTABLE_FLAG_CLEAR - Clear the signaled flag..
|
||||
*/
|
||||
unsigned int
|
||||
_gpgme_fdtable_get_fds (io_select_t *r_fds, uint64_t owner, unsigned int flags)
|
||||
{
|
||||
int idx;
|
||||
unsigned int count = 0;
|
||||
io_select_t fds;
|
||||
|
||||
*r_fds = NULL;
|
||||
gpg_err_set_errno (0);
|
||||
/* We take an easy approach and allocate the array at the size of
|
||||
* the entire fdtable. */
|
||||
fds = calloc (fdtablesize, sizeof *fds);
|
||||
if (!fds)
|
||||
return 0;
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd != -1 && (!owner || fdtable[idx].owner == owner))
|
||||
{
|
||||
if ((flags & FDTABLE_FLAG_ACTIVE) && !fdtable[idx].active)
|
||||
continue;
|
||||
if ((flags & FDTABLE_FLAG_DONE) && !fdtable[idx].done)
|
||||
continue;
|
||||
if ((flags & FDTABLE_FLAG_NOT_DONE) && fdtable[idx].done)
|
||||
continue;
|
||||
if ((flags & FDTABLE_FLAG_FOR_READ) && !fdtable[idx].for_read)
|
||||
continue;
|
||||
if ((flags & FDTABLE_FLAG_FOR_WRITE) && !fdtable[idx].for_write)
|
||||
continue;
|
||||
if ((flags & FDTABLE_FLAG_SIGNALED) && !fdtable[idx].signaled)
|
||||
continue;
|
||||
if ((flags & FDTABLE_FLAG_NOT_SIGNALED) && fdtable[idx].signaled)
|
||||
continue;
|
||||
|
||||
if (fdtable[idx].io_cb_running || fdtable[idx].closing)
|
||||
continue; /* The callback has not yet finished or we are
|
||||
* already closing. Does not make sense to allow
|
||||
* selecting on it. */
|
||||
|
||||
fds[count].fd = fdtable[idx].fd;
|
||||
fds[count].for_read = fdtable[idx].for_read;
|
||||
fds[count].for_write = fdtable[idx].for_write;
|
||||
fds[count].signaled =
|
||||
(flags & FDTABLE_FLAG_SIGNALED)? 0 : fdtable[idx].signaled;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
UNLOCK (fdtable_lock);
|
||||
*r_fds = fds;
|
||||
|
||||
TRACE (DEBUG_SYSIO, __func__, NULL, "ctx=%lu count=%u",
|
||||
(unsigned long)owner, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/* If OWNER is 0 return the status info of the first fd with the done
|
||||
* flag set. If OWNER is not 0 search for a matching owner with the
|
||||
* done flag set and return its status info. Returns the serial
|
||||
* number of the context found. */
|
||||
uint64_t
|
||||
_gpgme_fdtable_get_done (uint64_t owner,
|
||||
gpg_error_t *r_status, gpg_error_t *r_op_err)
|
||||
{
|
||||
uint64_t serial = 0;
|
||||
int idx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", (unsigned long)owner);
|
||||
|
||||
if (r_status)
|
||||
*r_status = 0;
|
||||
if (r_op_err)
|
||||
*r_op_err = 0;
|
||||
|
||||
LOCK (fdtable_lock);
|
||||
|
||||
for (idx=0; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd != -1 && (!owner || fdtable[idx].owner == owner)
|
||||
&& fdtable[idx].done)
|
||||
{
|
||||
/* Found. If an owner has been given also clear the done
|
||||
* flags from all other fds of this owner. Note that they
|
||||
* have the same status info anyway. */
|
||||
TRACE_LOG ("found fd=%d", fdtable[idx].fd);
|
||||
if (r_status)
|
||||
*r_status = fdtable[idx].done_status;
|
||||
if (r_op_err)
|
||||
*r_op_err = fdtable[idx].done_op_err;
|
||||
fdtable[idx].done = 0;
|
||||
serial = fdtable[idx].owner;
|
||||
if (owner)
|
||||
{
|
||||
for (; idx < fdtablesize; idx++)
|
||||
if (fdtable[idx].fd != -1 && fdtable[idx].owner == owner)
|
||||
fdtable[idx].done = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
UNLOCK (fdtable_lock);
|
||||
|
||||
TRACE_SUC ("ctx=%lu", (unsigned long)serial);
|
||||
return serial;
|
||||
}
|
83
src/fdtable.h
Normal file
83
src/fdtable.h
Normal file
@ -0,0 +1,83 @@
|
||||
/* fdtable.h - Keep track of file descriptors.
|
||||
* Copyright (C) 2019 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 <https://gnu.org/licenses/>.
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef GPGME_FDTABLE_H
|
||||
#define GPGME_FDTABLE_H
|
||||
|
||||
#include "priv-io.h"
|
||||
|
||||
/* Flags used by _gpgme_fdtable_get_fds. */
|
||||
#define FDTABLE_FLAG_ACTIVE 1 /* Only those with the active flag set. */
|
||||
#define FDTABLE_FLAG_DONE 2 /* Only those with the done flag set */
|
||||
#define FDTABLE_FLAG_NOT_DONE 4 /* Only those with the done flag cleared. */
|
||||
#define FDTABLE_FLAG_FOR_READ 16 /* Only those with the signaled flag set. */
|
||||
#define FDTABLE_FLAG_FOR_WRITE 32 /* Only those with the for_read flag set. */
|
||||
#define FDTABLE_FLAG_SIGNALED 64 /* Only those with the signaled flag set. */
|
||||
#define FDTABLE_FLAG_NOT_SIGNALED 128 /* Ditto reversed. */
|
||||
#define FDTABLE_FLAG_CLEAR 256 /* Clear the signaled flag. */
|
||||
|
||||
|
||||
/* The handler type associated with an FD. It is called with the FD
|
||||
* and the registered pointer. The handler may return an error code
|
||||
* but there is no guarantee that this code is used; in particular
|
||||
* errors from close notifications can't inhibit the the closing. */
|
||||
typedef gpg_error_t (*fdtable_handler_t) (int, void*);
|
||||
|
||||
|
||||
/* Insert a new FD into the table. */
|
||||
gpg_error_t _gpgme_fdtable_insert (int fd);
|
||||
|
||||
/* Add a close notification handler to the FD item. */
|
||||
gpg_error_t _gpgme_fdtable_add_close_notify (int fd,
|
||||
fdtable_handler_t handler,
|
||||
void *value);
|
||||
/* Set or remove the I/O callback. */
|
||||
gpg_error_t _gpgme_fdtable_set_io_cb (int fd, uint64_t owner, int direction,
|
||||
gpgme_io_cb_t cb, void *cb_value);
|
||||
|
||||
/* Set all FDs of OWNER into the active state. */
|
||||
gpg_error_t _gpgme_fdtable_set_active (uint64_t owner);
|
||||
|
||||
/* Set all FDs of OWNER into the done state. */
|
||||
gpg_error_t _gpgme_fdtable_set_done (uint64_t owner,
|
||||
gpg_error_t status, gpg_error_t op_err);
|
||||
|
||||
/* Walk over all FDS and copy the signaled flag if set. */
|
||||
void _gpgme_fdtable_set_signaled (io_select_t fds, unsigned int nfds);
|
||||
|
||||
/* Remove FD from the table. This also runs the close handlers. */
|
||||
gpg_error_t _gpgme_fdtable_remove (int fd);
|
||||
|
||||
/* Return the number of active I/O callbacks for OWNER. */
|
||||
unsigned int _gpgme_fdtable_get_count (uint64_t owner, unsigned int flags);
|
||||
|
||||
/* Run all the signaled IO callbacks of OWNER. */
|
||||
gpg_error_t _gpgme_fdtable_run_io_cbs (uint64_t owner, gpg_error_t *r_op_err,
|
||||
uint64_t *r_owner);
|
||||
|
||||
/* Return a list of FDs matching the OWNER and FLAGS. */
|
||||
unsigned int _gpgme_fdtable_get_fds (io_select_t *r_fds,
|
||||
uint64_t owner, unsigned int flags);
|
||||
|
||||
/* Return the status info for the entry of OWNER. */
|
||||
uint64_t _gpgme_fdtable_get_done (uint64_t owner, gpg_error_t *r_status,
|
||||
gpg_error_t *r_op_err);
|
||||
|
||||
#endif /*GPGME_FDTABLE_H*/
|
12
src/genkey.c
12
src/genkey.c
@ -316,7 +316,7 @@ gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
|
||||
|
||||
err = genkey_start (ctx, 1, parms, pubkey, seckey);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo,
|
||||
err = createkey_start (ctx, 1,
|
||||
userid, algo, reserved, expires, anchorkey, flags);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
@ -487,7 +487,7 @@ gpgme_op_createsubkey (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo,
|
||||
|
||||
err = createsubkey_start (ctx, 1, key, algo, reserved, expires, flags);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
@ -571,7 +571,7 @@ gpgme_op_adduid (gpgme_ctx_t ctx,
|
||||
|
||||
err = addrevuid_start (ctx, 1, 0, key, userid, flags);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
@ -608,7 +608,7 @@ gpgme_op_revuid (gpgme_ctx_t ctx,
|
||||
|
||||
err = addrevuid_start (ctx, 1, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
@ -640,7 +640,7 @@ set_uid_flag (gpgme_ctx_t ctx, int synchronous,
|
||||
return err = gpg_error (GPG_ERR_UNKNOWN_NAME);
|
||||
|
||||
if (synchronous && !err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,6 @@ gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output, unsigned int flags)
|
||||
|
||||
err = getauditlog_start (ctx, 1, output, flags);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
324
src/gpgme.c
324
src/gpgme.c
@ -1,7 +1,7 @@
|
||||
/* gpgme.c - GnuPG Made Easy.
|
||||
* Copyright (C) 2000 Werner Koch (dd9jn)
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012,
|
||||
* 2014, 2015 g10 Code GmbH
|
||||
* 2014, 2015, 2019 g10 Code GmbH
|
||||
*
|
||||
* This file is part of GPGME.
|
||||
*
|
||||
@ -42,18 +42,32 @@
|
||||
#include "mbox-util.h"
|
||||
|
||||
|
||||
/* The default locale. */
|
||||
DEFINE_STATIC_LOCK (def_lc_lock);
|
||||
/* The lock used to protect access to the default locale, the global
|
||||
* serial counter, and the list of context objects. */
|
||||
DEFINE_STATIC_LOCK (context_list_lock);
|
||||
|
||||
/* The default locale. Access is protected by CONTEXT_LIST_LOCK. */
|
||||
static char *def_lc_ctype;
|
||||
static char *def_lc_messages;
|
||||
|
||||
|
||||
/* A serial number to identify a context. To make debugging easier by
|
||||
* distinguishing this from the data object s/n we initialize it with
|
||||
* an arbitrary offset. Debug output of this should be done using
|
||||
* decimal notation. Updates are protected by CONTEXT_LIST_LOCK. */
|
||||
static uint64_t last_ctx_serial = 200000;
|
||||
|
||||
/* A linked list of all context objects. Protected by
|
||||
* CONTEXT_LIST_LOCK. */
|
||||
static gpgme_ctx_t context_list;
|
||||
|
||||
gpgme_error_t _gpgme_selftest = GPG_ERR_NOT_OPERATIONAL;
|
||||
|
||||
/* Protects all reference counters in result structures. All other
|
||||
accesses to a result structure are read only. */
|
||||
* accesses to a result structure are read only. */
|
||||
DEFINE_STATIC_LOCK (result_ref_lock);
|
||||
|
||||
|
||||
|
||||
|
||||
/* Set the global flag NAME to VALUE. Return 0 on success. Note that
|
||||
this function does not use gpgme_error and thus a non-zero return
|
||||
@ -94,7 +108,7 @@ gpgme_new (gpgme_ctx_t *r_ctx)
|
||||
{
|
||||
gpgme_error_t err;
|
||||
gpgme_ctx_t ctx;
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_new", r_ctx, "");
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_new", NULL, "");
|
||||
|
||||
if (_gpgme_selftest)
|
||||
return TRACE_ERR (_gpgme_selftest);
|
||||
@ -121,16 +135,15 @@ gpgme_new (gpgme_ctx_t *r_ctx)
|
||||
ctx->include_certs = GPGME_INCLUDE_CERTS_DEFAULT;
|
||||
ctx->protocol = GPGME_PROTOCOL_OpenPGP;
|
||||
ctx->sub_protocol = GPGME_PROTOCOL_DEFAULT;
|
||||
_gpgme_fd_table_init (&ctx->fdt);
|
||||
|
||||
LOCK (def_lc_lock);
|
||||
LOCK (context_list_lock);
|
||||
if (def_lc_ctype)
|
||||
{
|
||||
ctx->lc_ctype = strdup (def_lc_ctype);
|
||||
if (!ctx->lc_ctype)
|
||||
{
|
||||
int saved_err = gpg_error_from_syserror ();
|
||||
UNLOCK (def_lc_lock);
|
||||
UNLOCK (context_list_lock);
|
||||
_gpgme_engine_info_release (ctx->engine_info);
|
||||
free (ctx);
|
||||
return TRACE_ERR (saved_err);
|
||||
@ -145,7 +158,7 @@ gpgme_new (gpgme_ctx_t *r_ctx)
|
||||
if (!ctx->lc_messages)
|
||||
{
|
||||
int saved_err = gpg_error_from_syserror ();
|
||||
UNLOCK (def_lc_lock);
|
||||
UNLOCK (context_list_lock);
|
||||
if (ctx->lc_ctype)
|
||||
free (ctx->lc_ctype);
|
||||
_gpgme_engine_info_release (ctx->engine_info);
|
||||
@ -155,44 +168,92 @@ gpgme_new (gpgme_ctx_t *r_ctx)
|
||||
}
|
||||
else
|
||||
def_lc_messages = NULL;
|
||||
UNLOCK (def_lc_lock);
|
||||
|
||||
ctx->serial = ++last_ctx_serial;
|
||||
|
||||
/* Put the object itno a list. */
|
||||
ctx->next_ctx = context_list;
|
||||
context_list = ctx;
|
||||
|
||||
UNLOCK (context_list_lock);
|
||||
|
||||
*r_ctx = ctx;
|
||||
|
||||
TRACE_SUC ("ctx=%p", ctx);
|
||||
TRACE_SUC ("ctx=%lu (%p)", CTXSERIAL(ctx), ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Check the status of the context described by SERIAL.
|
||||
* Returns:
|
||||
* 0 - All fine
|
||||
* GPG_ERR_CANCELED - Context operation has been canceled.
|
||||
* GPG_ERR_NO_OBJ - Context ist not anymore known.
|
||||
* If R_CTX is not NULl and the context exists, it is stored there.
|
||||
*/
|
||||
gpg_error_t
|
||||
_gpgme_get_ctx (uint64_t serial, gpgme_ctx_t *r_ctx)
|
||||
{
|
||||
gpg_error_t err;
|
||||
gpgme_ctx_t ctx = NULL;
|
||||
|
||||
LOCK (context_list_lock);
|
||||
for (ctx = context_list; ctx; ctx = ctx->next_ctx)
|
||||
if (ctx->serial == serial)
|
||||
break;
|
||||
UNLOCK (context_list_lock);
|
||||
if (ctx)
|
||||
{
|
||||
/* FIXME: The lock is only used for the canceled flag. We
|
||||
* should remove it and rely on the global
|
||||
* context_list_lock. */
|
||||
LOCK (ctx->lock);
|
||||
err = ctx->canceled? gpg_error (GPG_ERR_CANCELED) : 0;
|
||||
UNLOCK (ctx->lock);
|
||||
}
|
||||
else
|
||||
err = gpg_error (GPG_ERR_NO_OBJ);
|
||||
if (r_ctx)
|
||||
*r_ctx = ctx;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Cancel the context indetified with SERIAL. Pass CTX_ERR or OP_ERR
|
||||
* down to the engine. */
|
||||
gpgme_error_t
|
||||
_gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err,
|
||||
_gpgme_cancel_with_err (uint64_t serial, gpg_error_t ctx_err,
|
||||
gpg_error_t op_err)
|
||||
{
|
||||
gpgme_error_t err;
|
||||
gpgme_ctx_t ctx;
|
||||
struct gpgme_io_event_done_data data;
|
||||
|
||||
TRACE_BEG (DEBUG_CTX, "_gpgme_cancel_with_err", ctx, "ctx_err=%i, op_err=%i",
|
||||
ctx_err, op_err);
|
||||
TRACE_BEG (DEBUG_CTX, "_gpgme_cancel_with_err", NULL,
|
||||
"ctx=%lu ctx_err=%i op_err=%i",
|
||||
(unsigned long)serial, ctx_err, op_err);
|
||||
|
||||
if (ctx_err)
|
||||
{
|
||||
LOCK (context_list_lock);
|
||||
for (ctx = context_list; ctx; ctx = ctx->next_ctx)
|
||||
if (ctx->serial == serial)
|
||||
break;
|
||||
UNLOCK (context_list_lock);
|
||||
|
||||
if (!ctx)
|
||||
err = gpg_error (GPG_ERR_NO_OBJ);
|
||||
else if (ctx_err)
|
||||
err = _gpgme_engine_cancel (ctx->engine);
|
||||
if (err)
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = _gpgme_engine_cancel_op (ctx->engine);
|
||||
if (err)
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
data.err = ctx_err;
|
||||
data.op_err = op_err;
|
||||
|
||||
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data);
|
||||
|
||||
return TRACE_ERR (0);
|
||||
}
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
||||
@ -202,12 +263,12 @@ gpgme_cancel (gpgme_ctx_t ctx)
|
||||
{
|
||||
gpg_error_t err;
|
||||
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_cancel", ctx, "");
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_cancel", NULL, "ctx=%lu", CTXSERIAL (ctx));
|
||||
|
||||
if (!ctx)
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
|
||||
|
||||
err = _gpgme_cancel_with_err (ctx, gpg_error (GPG_ERR_CANCELED), 0);
|
||||
err = _gpgme_cancel_with_err (ctx->serial, gpg_error (GPG_ERR_CANCELED), 0);
|
||||
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
@ -217,7 +278,8 @@ gpgme_cancel (gpgme_ctx_t ctx)
|
||||
gpgme_error_t
|
||||
gpgme_cancel_async (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_cancel_async", ctx, "");
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_cancel_async", NULL,
|
||||
"ctx=%lu", CTXSERIAL (ctx));
|
||||
|
||||
if (!ctx)
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
|
||||
@ -234,14 +296,34 @@ gpgme_cancel_async (gpgme_ctx_t ctx)
|
||||
void
|
||||
gpgme_release (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_release", ctx, "");
|
||||
TRACE (DEBUG_CTX, "gpgme_release", NULL, "ctx=%lu", CTXSERIAL (ctx));
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
LOCK (context_list_lock);
|
||||
if (context_list == ctx)
|
||||
{
|
||||
context_list = ctx->next_ctx;
|
||||
ctx->next_ctx = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
gpgme_ctx_t tmpctx;
|
||||
|
||||
for (tmpctx = context_list; tmpctx; tmpctx = tmpctx->next_ctx)
|
||||
if (tmpctx->next_ctx == ctx)
|
||||
{
|
||||
tmpctx->next_ctx = ctx->next_ctx;
|
||||
ctx->next_ctx = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
UNLOCK (context_list_lock);
|
||||
|
||||
_gpgme_engine_release (ctx->engine);
|
||||
ctx->engine = NULL;
|
||||
_gpgme_fd_table_deinit (&ctx->fdt);
|
||||
/* FIXME: Remove stale FDs belonging to us? */
|
||||
_gpgme_release_result (ctx);
|
||||
_gpgme_signers_clear (ctx);
|
||||
_gpgme_sig_notation_clear (ctx);
|
||||
@ -325,8 +407,8 @@ _gpgme_release_result (gpgme_ctx_t ctx)
|
||||
gpgme_error_t
|
||||
gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
|
||||
{
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_set_protocol", ctx, "protocol=%i (%s)",
|
||||
protocol, gpgme_get_protocol_name (protocol)
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_set_protocol", NULL, "ctx=%lu protocol=%i (%s)",
|
||||
CTXSERIAL (ctx), protocol, gpgme_get_protocol_name (protocol)
|
||||
? gpgme_get_protocol_name (protocol) : "invalid");
|
||||
|
||||
if (protocol != GPGME_PROTOCOL_OpenPGP
|
||||
@ -360,8 +442,8 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
|
||||
gpgme_protocol_t
|
||||
gpgme_get_protocol (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_protocol", ctx,
|
||||
"ctx->protocol=%i (%s)", ctx->protocol,
|
||||
TRACE (DEBUG_CTX, "gpgme_get_protocol", NULL,
|
||||
"ctx=%lu protocol=%i (%s)", CTXSERIAL (ctx), ctx->protocol,
|
||||
gpgme_get_protocol_name (ctx->protocol)
|
||||
? gpgme_get_protocol_name (ctx->protocol) : "invalid");
|
||||
|
||||
@ -372,8 +454,8 @@ gpgme_get_protocol (gpgme_ctx_t ctx)
|
||||
gpgme_error_t
|
||||
gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_sub_protocol", ctx, "protocol=%i (%s)",
|
||||
protocol, gpgme_get_protocol_name (protocol)
|
||||
TRACE (DEBUG_CTX, "gpgme_set_sub_protocol", NULL, "ctx=%lu protocol=%i (%s)",
|
||||
CTXSERIAL (ctx), protocol, gpgme_get_protocol_name (protocol)
|
||||
? gpgme_get_protocol_name (protocol) : "invalid");
|
||||
|
||||
if (!ctx)
|
||||
@ -387,8 +469,9 @@ gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
|
||||
gpgme_protocol_t
|
||||
gpgme_get_sub_protocol (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_sub_protocol", ctx,
|
||||
"ctx->sub_protocol=%i (%s)", ctx->sub_protocol,
|
||||
TRACE (DEBUG_CTX, "gpgme_get_sub_protocol", NULL,
|
||||
"ctx=%lu sub_protocol=%i (%s)",
|
||||
CTXSERIAL (ctx), ctx->sub_protocol,
|
||||
gpgme_get_protocol_name (ctx->sub_protocol)
|
||||
? gpgme_get_protocol_name (ctx->sub_protocol) : "invalid");
|
||||
|
||||
@ -444,8 +527,8 @@ gpgme_set_sender (gpgme_ctx_t ctx, const char *address)
|
||||
{
|
||||
char *p = NULL;
|
||||
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_set_sender", ctx, "sender='%s'",
|
||||
address?address:"(null)");
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_set_sender", NULL, "ctx=%lu sender='%s'",
|
||||
CTXSERIAL (ctx), address?address:"(null)");
|
||||
|
||||
if (!ctx || (address && !(p = _gpgme_mailbox_from_userid (address))))
|
||||
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
|
||||
@ -462,8 +545,8 @@ gpgme_set_sender (gpgme_ctx_t ctx, const char *address)
|
||||
const char *
|
||||
gpgme_get_sender (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_sender", ctx, "sender='%s'",
|
||||
ctx?ctx->sender:"");
|
||||
TRACE (DEBUG_CTX, "gpgme_get_sender", NULL, "ctx=%lu sender='%s'",
|
||||
CTXSERIAL (ctx), ctx?ctx->sender:"");
|
||||
|
||||
return ctx->sender;
|
||||
}
|
||||
@ -473,8 +556,8 @@ gpgme_get_sender (gpgme_ctx_t ctx)
|
||||
void
|
||||
gpgme_set_armor (gpgme_ctx_t ctx, int use_armor)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_armor", ctx, "use_armor=%i (%s)",
|
||||
use_armor, use_armor ? "yes" : "no");
|
||||
TRACE (DEBUG_CTX, "gpgme_set_armor", NULL, "ctx=%lu use_armor=%i (%s)",
|
||||
CTXSERIAL (ctx), use_armor, use_armor ? "yes" : "no");
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
@ -487,8 +570,8 @@ gpgme_set_armor (gpgme_ctx_t ctx, int use_armor)
|
||||
int
|
||||
gpgme_get_armor (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_armor", ctx, "ctx->use_armor=%i (%s)",
|
||||
ctx->use_armor, ctx->use_armor ? "yes" : "no");
|
||||
TRACE (DEBUG_CTX, "gpgme_get_armor", NULL, "ctx=%lu use_armor=%i (%s)",
|
||||
CTXSERIAL (ctx), ctx->use_armor, ctx->use_armor ? "yes" : "no");
|
||||
return ctx->use_armor;
|
||||
}
|
||||
|
||||
@ -502,9 +585,9 @@ gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value)
|
||||
gpgme_error_t err = 0;
|
||||
int abool;
|
||||
|
||||
TRACE (DEBUG_CTX, "gpgme_set_ctx_flag", ctx,
|
||||
"name='%s' value='%s'",
|
||||
name? name:"(null)", value?value:"(null)");
|
||||
TRACE (DEBUG_CTX, "gpgme_set_ctx_flag", NULL,
|
||||
"ctx=%lu name='%s' value='%s'",
|
||||
CTXSERIAL (ctx), name? name:"(null)", value?value:"(null)");
|
||||
|
||||
abool = (value && *value)? !!atoi (value) : 0;
|
||||
|
||||
@ -635,8 +718,8 @@ gpgme_get_ctx_flag (gpgme_ctx_t ctx, const char *name)
|
||||
void
|
||||
gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_textmode", ctx, "use_textmode=%i (%s)",
|
||||
use_textmode, use_textmode ? "yes" : "no");
|
||||
TRACE (DEBUG_CTX, "gpgme_set_textmode", NULL, "ctx=%lu use_textmode=%i (%s)",
|
||||
CTXSERIAL (ctx), use_textmode, use_textmode ? "yes" : "no");
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
@ -648,8 +731,8 @@ gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode)
|
||||
int
|
||||
gpgme_get_textmode (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_textmode", ctx, "ctx->use_textmode=%i (%s)",
|
||||
ctx->use_textmode, ctx->use_textmode ? "yes" : "no");
|
||||
TRACE (DEBUG_CTX, "gpgme_get_textmode", NULL, "ctx=%lu use_textmode=%i (%s)",
|
||||
CTXSERIAL (ctx), ctx->use_textmode, ctx->use_textmode ? "yes" : "no");
|
||||
return ctx->use_textmode;
|
||||
}
|
||||
|
||||
@ -659,8 +742,8 @@ gpgme_get_textmode (gpgme_ctx_t ctx)
|
||||
void
|
||||
gpgme_set_offline (gpgme_ctx_t ctx, int offline)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_offline", ctx, "offline=%i (%s)",
|
||||
offline, offline ? "yes" : "no");
|
||||
TRACE (DEBUG_CTX, "gpgme_set_offline", NULL, "ctx=%lu offline=%i (%s)",
|
||||
CTXSERIAL (ctx), offline, offline ? "yes" : "no");
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
@ -672,8 +755,8 @@ gpgme_set_offline (gpgme_ctx_t ctx, int offline)
|
||||
int
|
||||
gpgme_get_offline (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_offline", ctx, "ctx->offline=%i (%s)",
|
||||
ctx->offline, ctx->offline ? "yes" : "no");
|
||||
TRACE (DEBUG_CTX, "gpgme_get_offline", NULL, "ctx=%lu offline=%i (%s)",
|
||||
CTXSERIAL (ctx), ctx->offline, ctx->offline ? "yes" : "no");
|
||||
return ctx->offline;
|
||||
}
|
||||
|
||||
@ -694,7 +777,8 @@ gpgme_set_include_certs (gpgme_ctx_t ctx, int nr_of_certs)
|
||||
else
|
||||
ctx->include_certs = nr_of_certs;
|
||||
|
||||
TRACE (DEBUG_CTX, "gpgme_set_include_certs", ctx, "nr_of_certs=%i%s",
|
||||
TRACE (DEBUG_CTX, "gpgme_set_include_certs", NULL, "ctx=%lu nr_of_certs=%i%s",
|
||||
CTXSERIAL (ctx),
|
||||
nr_of_certs, nr_of_certs == ctx->include_certs ? "" : " (-2)");
|
||||
}
|
||||
|
||||
@ -704,8 +788,8 @@ gpgme_set_include_certs (gpgme_ctx_t ctx, int nr_of_certs)
|
||||
int
|
||||
gpgme_get_include_certs (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_include_certs", ctx, "ctx->include_certs=%i",
|
||||
ctx->include_certs);
|
||||
TRACE (DEBUG_CTX, "gpgme_get_include_certs", NULL, "ctx=%lu include_certs=%i",
|
||||
CTXSERIAL (ctx), ctx->include_certs);
|
||||
return ctx->include_certs;
|
||||
}
|
||||
|
||||
@ -716,8 +800,8 @@ gpgme_get_include_certs (gpgme_ctx_t ctx)
|
||||
gpgme_error_t
|
||||
gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_keylist_mode", ctx, "keylist_mode=0x%x",
|
||||
mode);
|
||||
TRACE (DEBUG_CTX, "gpgme_set_keylist_mode", NULL, "ctx=%lu keylist_mode=0x%x",
|
||||
CTXSERIAL (ctx), mode);
|
||||
|
||||
if (!ctx)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
@ -731,8 +815,8 @@ gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode)
|
||||
gpgme_keylist_mode_t
|
||||
gpgme_get_keylist_mode (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_keylist_mode", ctx,
|
||||
"ctx->keylist_mode=0x%x", ctx->keylist_mode);
|
||||
TRACE (DEBUG_CTX, "gpgme_get_keylist_mode", NULL,
|
||||
"ctx=%lu keylist_mode=0x%x", CTXSERIAL (ctx), ctx->keylist_mode);
|
||||
return ctx->keylist_mode;
|
||||
}
|
||||
|
||||
@ -741,8 +825,8 @@ gpgme_get_keylist_mode (gpgme_ctx_t ctx)
|
||||
gpgme_error_t
|
||||
gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t mode)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_pinentry_mode", ctx, "pinentry_mode=%u",
|
||||
(unsigned int)mode);
|
||||
TRACE (DEBUG_CTX, "gpgme_set_pinentry_mode", NULL, "ctx=%lu pinentry_mode=%u",
|
||||
CTXSERIAL (ctx), (unsigned int)mode);
|
||||
|
||||
if (!ctx)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
@ -768,8 +852,8 @@ gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t mode)
|
||||
gpgme_pinentry_mode_t
|
||||
gpgme_get_pinentry_mode (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_pinentry_mode", ctx,
|
||||
"ctx->pinentry_mode=%u", (unsigned int)ctx->pinentry_mode);
|
||||
TRACE (DEBUG_CTX, "gpgme_get_pinentry_mode", NULL, "ctx=%lu pinentry_mode=%u",
|
||||
CTXSERIAL (ctx), (unsigned int)ctx->pinentry_mode);
|
||||
return ctx->pinentry_mode;
|
||||
}
|
||||
|
||||
@ -780,8 +864,8 @@ void
|
||||
gpgme_set_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb,
|
||||
void *cb_value)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_passphrase_cb", ctx,
|
||||
"passphrase_cb=%p/%p", cb, cb_value);
|
||||
TRACE (DEBUG_CTX, "gpgme_set_passphrase_cb", NULL,
|
||||
"ctx=%lu passphrase_cb=%p/%p", CTXSERIAL (ctx), cb, cb_value);
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
@ -797,9 +881,9 @@ void
|
||||
gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *r_cb,
|
||||
void **r_cb_value)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_passphrase_cb", ctx,
|
||||
"ctx->passphrase_cb=%p/%p",
|
||||
ctx->passphrase_cb, ctx->passphrase_cb_value);
|
||||
TRACE (DEBUG_CTX, "gpgme_get_passphrase_cb", NULL,
|
||||
"ctx=%lu passphrase_cb=%p/%p",
|
||||
CTXSERIAL (ctx), ctx->passphrase_cb, ctx->passphrase_cb_value);
|
||||
if (r_cb)
|
||||
*r_cb = ctx->passphrase_cb;
|
||||
if (r_cb_value)
|
||||
@ -812,8 +896,8 @@ gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *r_cb,
|
||||
void
|
||||
gpgme_set_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t cb, void *cb_value)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_progress_cb", ctx, "progress_cb=%p/%p",
|
||||
cb, cb_value);
|
||||
TRACE (DEBUG_CTX, "gpgme_set_progress_cb", NULL, "ctx=%lu progress_cb=%p/%p",
|
||||
CTXSERIAL (ctx), cb, cb_value);
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
@ -829,8 +913,8 @@ void
|
||||
gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb,
|
||||
void **r_cb_value)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_progress_cb", ctx, "ctx->progress_cb=%p/%p",
|
||||
ctx->progress_cb, ctx->progress_cb_value);
|
||||
TRACE (DEBUG_CTX, "gpgme_get_progress_cb", NULL, "ctx=%lu progress_cb=%p/%p",
|
||||
CTXSERIAL (ctx), ctx->progress_cb, ctx->progress_cb_value);
|
||||
if (r_cb)
|
||||
*r_cb = ctx->progress_cb;
|
||||
if (r_cb_value)
|
||||
@ -843,8 +927,8 @@ gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb,
|
||||
void
|
||||
gpgme_set_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t cb, void *cb_value)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_status_cb", ctx, "status_cb=%p/%p",
|
||||
cb, cb_value);
|
||||
TRACE (DEBUG_CTX, "gpgme_set_status_cb", NULL, "ctx=%lu status_cb=%p/%p",
|
||||
CTXSERIAL (ctx), cb, cb_value);
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
@ -860,7 +944,8 @@ void
|
||||
gpgme_get_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t *r_cb,
|
||||
void **r_cb_value)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_status_cb", ctx, "ctx->status_cb=%p/%p",
|
||||
TRACE (DEBUG_CTX, "gpgme_get_status_cb", NULL, "ctx=%lu status_cb=%p/%p",
|
||||
CTXSERIAL (ctx),
|
||||
ctx ? ctx->status_cb : NULL, ctx ? ctx->status_cb_value : NULL);
|
||||
|
||||
if (r_cb)
|
||||
@ -888,21 +973,22 @@ gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs)
|
||||
|
||||
if (io_cbs)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_io_cbs", ctx,
|
||||
"io_cbs=%p (add=%p/%p, remove=%p, event=%p/%p",
|
||||
TRACE (DEBUG_CTX, "gpgme_set_io_cbs", NULL,
|
||||
"ctx=%lu io_cbs=%p (add=%p/%p, remove=%p, event=%p/%p",
|
||||
CTXSERIAL (ctx),
|
||||
io_cbs, io_cbs->add, io_cbs->add_priv, io_cbs->remove,
|
||||
io_cbs->event, io_cbs->event_priv);
|
||||
ctx->io_cbs = *io_cbs;
|
||||
ctx->user_io_cbs = *io_cbs;
|
||||
}
|
||||
else
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_set_io_cbs", ctx,
|
||||
"io_cbs=%p (default)", io_cbs);
|
||||
ctx->io_cbs.add = NULL;
|
||||
ctx->io_cbs.add_priv = NULL;
|
||||
ctx->io_cbs.remove = NULL;
|
||||
ctx->io_cbs.event = NULL;
|
||||
ctx->io_cbs.event_priv = NULL;
|
||||
TRACE (DEBUG_CTX, "gpgme_set_io_cbs", NULL,
|
||||
"ctx=%lu io_cbs=%p (default)", CTXSERIAL (ctx), io_cbs);
|
||||
ctx->user_io_cbs.add = NULL;
|
||||
ctx->user_io_cbs.add_priv = NULL;
|
||||
ctx->user_io_cbs.remove = NULL;
|
||||
ctx->user_io_cbs.event = NULL;
|
||||
ctx->user_io_cbs.event_priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -913,8 +999,8 @@ gpgme_ssize_t
|
||||
gpgme_io_read (int fd, void *buffer, size_t count)
|
||||
{
|
||||
int ret;
|
||||
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_read", fd,
|
||||
"buffer=%p, count=%zu", buffer, count);
|
||||
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_read", NULL,
|
||||
"fd=%d buffer=%p count=%zu", fd, buffer, count);
|
||||
|
||||
ret = _gpgme_io_read (fd, buffer, count);
|
||||
|
||||
@ -929,8 +1015,8 @@ gpgme_ssize_t
|
||||
gpgme_io_write (int fd, const void *buffer, size_t count)
|
||||
{
|
||||
int ret;
|
||||
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_write", fd,
|
||||
"buffer=%p, count=%zu", buffer, count);
|
||||
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_write", NULL,
|
||||
"fd=%d buffer=%p count=%zu", fd, buffer, count);
|
||||
|
||||
ret = _gpgme_io_write (fd, buffer, count);
|
||||
|
||||
@ -948,8 +1034,9 @@ gpgme_io_writen (int fd, const void *buffer_arg, size_t count)
|
||||
{
|
||||
const char *buffer = buffer_arg;
|
||||
int ret = 0;
|
||||
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_writen", fd,
|
||||
"buffer=%p, count=%zu", buffer, count);
|
||||
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_writen", NULL,
|
||||
"fd=%d buffer=%p count=%zu", fd, buffer, count);
|
||||
|
||||
while (count)
|
||||
{
|
||||
ret = _gpgme_io_write (fd, buffer, count);
|
||||
@ -967,12 +1054,13 @@ gpgme_io_writen (int fd, const void *buffer_arg, size_t count)
|
||||
void
|
||||
gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_get_io_cbs", ctx,
|
||||
"io_cbs=%p, ctx->io_cbs.add=%p/%p, .remove=%p, .event=%p/%p",
|
||||
TRACE (DEBUG_CTX, "gpgme_get_io_cbs", NULL,
|
||||
"ctx=%lu io_cbs=%p ctx->io_cbs.add=%p/%p .remove=%p, .event=%p/%p",
|
||||
CTXSERIAL (ctx),
|
||||
io_cbs, io_cbs->add, io_cbs->add_priv, io_cbs->remove,
|
||||
io_cbs->event, io_cbs->event_priv);
|
||||
|
||||
*io_cbs = ctx->io_cbs;
|
||||
*io_cbs = ctx->user_io_cbs;
|
||||
}
|
||||
|
||||
|
||||
@ -985,8 +1073,9 @@ gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value)
|
||||
char *new_lc_ctype = NULL;
|
||||
char *new_lc_messages = NULL;
|
||||
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_set_locale", ctx,
|
||||
"category=%i, value=%s", category, value ? value : "(null)");
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_set_locale", NULL,
|
||||
"ctx=%lu category=%i value=%s",
|
||||
CTXSERIAL (ctx), category, value ? value : "(null)");
|
||||
|
||||
#define PREPARE_ONE_LOCALE(lcat, ucat) \
|
||||
if (!failed && value \
|
||||
@ -1034,7 +1123,7 @@ gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value)
|
||||
}
|
||||
|
||||
if (!ctx)
|
||||
LOCK (def_lc_lock);
|
||||
LOCK (context_list_lock);
|
||||
#ifdef LC_CTYPE
|
||||
SET_ONE_LOCALE (ctype, CTYPE);
|
||||
#endif
|
||||
@ -1042,7 +1131,7 @@ gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value)
|
||||
SET_ONE_LOCALE (messages, MESSAGES);
|
||||
#endif
|
||||
if (!ctx)
|
||||
UNLOCK (def_lc_lock);
|
||||
UNLOCK (context_list_lock);
|
||||
|
||||
return TRACE_ERR (0);
|
||||
}
|
||||
@ -1054,8 +1143,8 @@ gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value)
|
||||
gpgme_engine_info_t
|
||||
gpgme_ctx_get_engine_info (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_ctx_get_engine_info", ctx,
|
||||
"ctx->engine_info=%p", ctx->engine_info);
|
||||
TRACE (DEBUG_CTX, "gpgme_ctx_get_engine_info", NULL,
|
||||
"ctx=%lu engine_info=%p", CTXSERIAL (ctx), ctx->engine_info);
|
||||
return ctx->engine_info;
|
||||
}
|
||||
|
||||
@ -1067,9 +1156,9 @@ gpgme_ctx_set_engine_info (gpgme_ctx_t ctx, gpgme_protocol_t proto,
|
||||
const char *file_name, const char *home_dir)
|
||||
{
|
||||
gpgme_error_t err;
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_ctx_set_engine_info", ctx,
|
||||
"protocol=%i (%s), file_name=%s, home_dir=%s",
|
||||
proto, gpgme_get_protocol_name (proto)
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_ctx_set_engine_info", NULL,
|
||||
"ctx=%lu protocol=%i (%s), file_name=%s, home_dir=%s",
|
||||
CTXSERIAL (ctx), proto, gpgme_get_protocol_name (proto)
|
||||
? gpgme_get_protocol_name (proto) : "unknown",
|
||||
file_name ? file_name : "(default)",
|
||||
home_dir ? home_dir : "(default)");
|
||||
@ -1112,7 +1201,8 @@ _gpgme_sig_notation_clear (gpgme_ctx_t ctx)
|
||||
void
|
||||
gpgme_sig_notation_clear (gpgme_ctx_t ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_sig_notation_clear", ctx, "");
|
||||
TRACE (DEBUG_CTX, "gpgme_sig_notation_clear", NULL, "ctx=%lu",
|
||||
CTXSERIAL (ctx));
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
@ -1134,8 +1224,9 @@ gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name,
|
||||
gpgme_sig_notation_t notation;
|
||||
gpgme_sig_notation_t *lastp;
|
||||
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_sig_notation_add", ctx,
|
||||
"name=%s, value=%s, flags=0x%x",
|
||||
TRACE_BEG (DEBUG_CTX, "gpgme_sig_notation_add", NULL,
|
||||
"ctx=%lu name=%s value=%s flags=0x%x",
|
||||
CTXSERIAL (ctx),
|
||||
name ? name : "(null)", value ? value : "(null)",
|
||||
flags);
|
||||
|
||||
@ -1167,11 +1258,12 @@ gpgme_sig_notation_get (gpgme_ctx_t ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
{
|
||||
TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx, "");
|
||||
TRACE (DEBUG_CTX, "gpgme_sig_notation_get", NULL, "ctx=%lu",
|
||||
CTXSERIAL (ctx));
|
||||
return NULL;
|
||||
}
|
||||
TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx,
|
||||
"ctx->sig_notations=%p", ctx->sig_notations);
|
||||
TRACE (DEBUG_CTX, "gpgme_sig_notation_get", NULL,
|
||||
"ctx=%lu sig_notations=%p", CTXSERIAL (ctx), ctx->sig_notations);
|
||||
|
||||
return ctx->sig_notations;
|
||||
}
|
||||
|
@ -316,7 +316,7 @@ gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
|
||||
|
||||
err = _gpgme_op_import_start (ctx, 1, keydata);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
@ -433,7 +433,7 @@ gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys)
|
||||
|
||||
err = _gpgme_op_import_keys_start (ctx, 1, keys);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
@ -1216,7 +1216,7 @@ gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
|
||||
|
||||
if (!opd->key_queue)
|
||||
{
|
||||
err = _gpgme_wait_on_condition (ctx, &opd->key_cond, NULL);
|
||||
err = _gpgme_sync_wait (ctx, &opd->key_cond, NULL);
|
||||
if (err)
|
||||
return TRACE_ERR (err);
|
||||
|
||||
|
@ -214,6 +214,6 @@ gpgme_op_keysign (gpgme_ctx_t ctx, gpgme_key_t key, const char *userid,
|
||||
|
||||
err = keysign_start (ctx, 1, key, userid, expires, flags);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (type == 1 || (type == 2 && !ctx->io_cbs.add))
|
||||
if (type == 1 || (type == 2 && !ctx->user_io_cbs.add))
|
||||
{
|
||||
/* Use private event loop. */
|
||||
io_cbs.add = _gpgme_add_io_cb;
|
||||
@ -182,7 +182,7 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type)
|
||||
io_cbs.event = _gpgme_wait_private_event_cb;
|
||||
io_cbs.event_priv = ctx;
|
||||
}
|
||||
else if (! ctx->io_cbs.add)
|
||||
else if (!ctx->user_io_cbs.add)
|
||||
{
|
||||
/* Use global event loop. */
|
||||
io_cbs.add = _gpgme_add_io_cb;
|
||||
@ -194,9 +194,9 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type)
|
||||
else
|
||||
{
|
||||
/* Use user event loop. */
|
||||
io_cbs.add = _gpgme_wait_user_add_io_cb;
|
||||
io_cbs.add = _gpgme_add_io_cb_user;
|
||||
io_cbs.add_priv = ctx;
|
||||
io_cbs.remove = _gpgme_wait_user_remove_io_cb;
|
||||
io_cbs.remove = _gpgme_remove_io_cb_user;
|
||||
io_cbs.event = _gpgme_wait_user_event_cb;
|
||||
io_cbs.event_priv = ctx;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = _gpgme_wait_one_ext (ctx, &op_err);
|
||||
err = _gpgme_sync_wait (ctx, NULL, &op_err);
|
||||
if (op_err)
|
||||
{
|
||||
TRACE_LOG ("op_err = %s <%s>", gpgme_strerror (op_err),
|
||||
|
10
src/ops.h
10
src/ops.h
@ -26,9 +26,6 @@
|
||||
#include "context.h"
|
||||
|
||||
|
||||
/* From gpgme.c. */
|
||||
gpgme_error_t _gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err,
|
||||
gpg_error_t op_err);
|
||||
/* Clear all notation data from the context. */
|
||||
void _gpgme_sig_notation_clear (gpgme_ctx_t ctx);
|
||||
|
||||
@ -36,10 +33,9 @@ void _gpgme_release_result (gpgme_ctx_t ctx);
|
||||
|
||||
|
||||
/* From wait.c. */
|
||||
gpgme_error_t _gpgme_wait_one (gpgme_ctx_t ctx);
|
||||
gpgme_error_t _gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err);
|
||||
gpgme_error_t _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond,
|
||||
gpgme_error_t *op_err);
|
||||
gpgme_error_t _gpgme_sync_wait (gpgme_ctx_t ctx,
|
||||
volatile int *cond,
|
||||
gpg_error_t *op_err);
|
||||
|
||||
|
||||
/* From data.c. */
|
||||
|
@ -199,7 +199,6 @@ gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags)
|
||||
|
||||
err = passwd_start (ctx, 1, key, flags);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
164
src/posix-io.c
164
src/posix-io.c
@ -59,6 +59,7 @@
|
||||
#include "priv-io.h"
|
||||
#include "sema.h"
|
||||
#include "ath.h"
|
||||
#include "fdtable.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
@ -152,22 +153,6 @@ _gpgme_io_fd2str (char *buf, int buflen, int fd)
|
||||
return snprintf (buf, buflen, "%d", fd);
|
||||
}
|
||||
|
||||
|
||||
/* The table to hold notification handlers. We use a linear search
|
||||
and extend the table as needed. */
|
||||
struct notify_table_item_s
|
||||
{
|
||||
int fd; /* -1 indicates an unused entry. */
|
||||
_gpgme_close_notify_handler_t handler;
|
||||
void *value;
|
||||
};
|
||||
typedef struct notify_table_item_s *notify_table_item_t;
|
||||
|
||||
static notify_table_item_t notify_table;
|
||||
static size_t notify_table_size;
|
||||
DEFINE_STATIC_LOCK (notify_table_lock);
|
||||
|
||||
|
||||
|
||||
int
|
||||
_gpgme_io_read (int fd, void *buffer, size_t count)
|
||||
@ -208,27 +193,43 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
|
||||
int
|
||||
_gpgme_io_pipe (int filedes[2], int inherit_idx)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int res;
|
||||
int saved_errno;
|
||||
int err;
|
||||
int i;
|
||||
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", NULL,
|
||||
"inherit_idx=%i (GPGME uses it for %s)",
|
||||
inherit_idx, inherit_idx ? "reading" : "writing");
|
||||
|
||||
err = pipe (filedes);
|
||||
if (err < 0)
|
||||
return TRACE_SYSRES (err);
|
||||
res = pipe (filedes);
|
||||
if (res < 0)
|
||||
return TRACE_SYSRES (res);
|
||||
|
||||
/* FIXME: Should get the old flags first. */
|
||||
err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
|
||||
res = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
|
||||
saved_errno = errno;
|
||||
if (err < 0)
|
||||
if (res < 0)
|
||||
{
|
||||
close (filedes[0]);
|
||||
close (filedes[1]);
|
||||
}
|
||||
errno = saved_errno;
|
||||
if (res)
|
||||
return TRACE_SYSRES (res);
|
||||
|
||||
for (i=0; i < 2; i++)
|
||||
{
|
||||
err = _gpgme_fdtable_insert (filedes[i]);
|
||||
if (err)
|
||||
return TRACE_SYSRES (err);
|
||||
{
|
||||
TRACE_LOG ("fdtable_insert failed for fd=%d: %s\n",
|
||||
filedes[i], gpg_strerror (err));
|
||||
close (filedes[0]);
|
||||
close (filedes[1]);
|
||||
gpg_err_set_errno (EIO);
|
||||
return TRACE_SYSRES (-1);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_SUC ("read fd=%d write fd=%d", filedes[0], filedes[1]);
|
||||
return 0;
|
||||
@ -238,38 +239,23 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx)
|
||||
int
|
||||
_gpgme_io_close (int fd)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int res;
|
||||
_gpgme_close_notify_handler_t handler = NULL;
|
||||
void *handler_value;
|
||||
int idx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", NULL, "fd=%d", fd);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return TRACE_SYSRES (-1);
|
||||
}
|
||||
return TRACE_SYSRES (0); /* Igore invalid FDs. */
|
||||
|
||||
/* First call the notify handler. */
|
||||
LOCK (notify_table_lock);
|
||||
for (idx=0; idx < notify_table_size; idx++)
|
||||
/* First remove from the table which also runs the close handlers.
|
||||
* Having the FD not (yet) in the table is possible and thus we
|
||||
* ignore that error code. */
|
||||
err = _gpgme_fdtable_remove (fd);
|
||||
if (err && gpg_err_code (err) != GPG_ERR_NO_KEY)
|
||||
{
|
||||
if (notify_table[idx].fd == fd)
|
||||
{
|
||||
handler = notify_table[idx].handler;
|
||||
handler_value = notify_table[idx].value;
|
||||
notify_table[idx].handler = NULL;
|
||||
notify_table[idx].value = NULL;
|
||||
notify_table[idx].fd = -1; /* Mark slot as free. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
UNLOCK (notify_table_lock);
|
||||
if (handler)
|
||||
{
|
||||
TRACE_LOG ("invoking close handler %p/%p", handler, handler_value);
|
||||
handler (fd, handler_value);
|
||||
TRACE_LOG ("fdtable_remove failed for fd=%d: %s\n",
|
||||
fd, gpg_strerror (err));
|
||||
gpg_err_set_errno (EINVAL);
|
||||
return TRACE_SYSRES (-1);
|
||||
}
|
||||
|
||||
/* Then do the close. */
|
||||
@ -278,59 +264,6 @@ _gpgme_io_close (int fd)
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
|
||||
void *value)
|
||||
{
|
||||
int res = 0;
|
||||
int idx;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_close_notify", NULL,
|
||||
"fd=%d close_handler=%p/%p", fd, handler, value);
|
||||
|
||||
assert (fd != -1);
|
||||
|
||||
LOCK (notify_table_lock);
|
||||
for (idx=0; idx < notify_table_size; idx++)
|
||||
if (notify_table[idx].fd == -1)
|
||||
break;
|
||||
if (idx == notify_table_size)
|
||||
{
|
||||
/* We need to increase the size of the table. The approach we
|
||||
take is straightforward to minimize the risk of bugs. */
|
||||
notify_table_item_t newtbl;
|
||||
size_t newsize = notify_table_size + 64;
|
||||
|
||||
newtbl = calloc (newsize, sizeof *newtbl);
|
||||
if (!newtbl)
|
||||
{
|
||||
res = -1;
|
||||
goto leave;
|
||||
}
|
||||
for (idx=0; idx < notify_table_size; idx++)
|
||||
newtbl[idx] = notify_table[idx];
|
||||
for (; idx < newsize; idx++)
|
||||
{
|
||||
newtbl[idx].fd = -1;
|
||||
newtbl[idx].handler = NULL;
|
||||
newtbl[idx].value = NULL;
|
||||
}
|
||||
free (notify_table);
|
||||
notify_table = newtbl;
|
||||
idx = notify_table_size;
|
||||
notify_table_size = newsize;
|
||||
}
|
||||
notify_table[idx].fd = fd;
|
||||
notify_table[idx].handler = handler;
|
||||
notify_table[idx].value = value;
|
||||
|
||||
leave:
|
||||
UNLOCK (notify_table_lock);
|
||||
|
||||
return TRACE_SYSRES (res);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_gpgme_io_set_nonblocking (int fd)
|
||||
{
|
||||
@ -689,10 +622,14 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
|
||||
}
|
||||
|
||||
|
||||
/* Select on the list of fds. Returns: -1 = error, 0 = timeout or
|
||||
nothing to select, > 0 = number of signaled fds. */
|
||||
/* Select on the list of fds.
|
||||
*
|
||||
* Returns: -1 = error,
|
||||
* 0 = timeout or nothing to select,
|
||||
* > 0 = number of signaled fds.
|
||||
*/
|
||||
int
|
||||
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
|
||||
_gpgme_io_select (io_select_t fds, unsigned int nfds, int nonblock)
|
||||
{
|
||||
fd_set readfds;
|
||||
fd_set writefds;
|
||||
@ -705,7 +642,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
|
||||
struct timeval timeout = { 1, 0 };
|
||||
void *dbg_help = NULL;
|
||||
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", NULL,
|
||||
"nfds=%zu, nonblock=%u", nfds, nonblock);
|
||||
"nfds=%u, nonblock=%u", nfds, nonblock);
|
||||
|
||||
FD_ZERO (&readfds);
|
||||
FD_ZERO (&writefds);
|
||||
@ -803,6 +740,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
|
||||
return TRACE_SYSRES (count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
_gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
|
||||
@ -885,17 +823,29 @@ _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
|
||||
int
|
||||
_gpgme_io_dup (int fd)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int new_fd;
|
||||
|
||||
do
|
||||
new_fd = dup (fd);
|
||||
while (new_fd == -1 && errno == EINTR);
|
||||
|
||||
TRACE (DEBUG_SYSIO, "_gpgme_io_dup", NULL, "fd=%d -> fd=%d", fd, new_fd);
|
||||
TRACE (DEBUG_SYSIO, __func__, NULL, "fd=%d -> fd=%d", fd, new_fd);
|
||||
err = _gpgme_fdtable_insert (new_fd);
|
||||
if (err)
|
||||
{
|
||||
TRACE (DEBUG_SYSIO, __func__, NULL,
|
||||
"fdtable_insert failed for fd=%d: %s\n",
|
||||
new_fd, gpg_strerror (err));
|
||||
close (new_fd);
|
||||
new_fd = -1;
|
||||
gpg_err_set_errno (EIO);
|
||||
}
|
||||
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
_gpgme_io_socket (int domain, int type, int proto)
|
||||
|
@ -51,14 +51,15 @@ struct spawn_fd_item_s
|
||||
int arg_loc;
|
||||
};
|
||||
|
||||
struct io_select_fd_s
|
||||
struct io_select_s
|
||||
{
|
||||
int fd;
|
||||
int for_read;
|
||||
int for_write;
|
||||
int signaled;
|
||||
void *opaque;
|
||||
unsigned int for_read:1;
|
||||
unsigned int for_write:1;
|
||||
unsigned int signaled:1;
|
||||
};
|
||||
typedef struct io_select_s *io_select_t;
|
||||
|
||||
|
||||
/* These function are either defined in posix-io.c or w32-io.c. */
|
||||
void _gpgme_io_subsystem_init (void);
|
||||
@ -68,9 +69,6 @@ int _gpgme_io_read (int fd, void *buffer, size_t count);
|
||||
int _gpgme_io_write (int fd, const void *buffer, size_t count);
|
||||
int _gpgme_io_pipe (int filedes[2], int inherit_idx);
|
||||
int _gpgme_io_close (int fd);
|
||||
typedef void (*_gpgme_close_notify_handler_t) (int,void*);
|
||||
int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
|
||||
void *value);
|
||||
int _gpgme_io_set_nonblocking (int fd);
|
||||
|
||||
/* Under Windows do not allocate a console. */
|
||||
@ -92,7 +90,7 @@ int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
|
||||
void (*atfork) (void *opaque, int reserved),
|
||||
void *atforkvalue, pid_t *r_pid);
|
||||
|
||||
int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock);
|
||||
int _gpgme_io_select (io_select_t fds, unsigned int nfds, int nonblock);
|
||||
|
||||
/* Write the printable version of FD to the buffer BUF of length
|
||||
BUFLEN. The printable version is the representation on the command
|
||||
|
@ -495,6 +495,6 @@ gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig,
|
||||
|
||||
err = sign_start (ctx, 1, plain, sig, mode);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
@ -101,6 +101,6 @@ gpgme_op_spawn (gpgme_ctx_t ctx, const char *file, const char *argv[],
|
||||
err = spawn_start (ctx, 1, file, argv, datain, dataout, dataerr, flags);
|
||||
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
@ -180,6 +180,6 @@ gpgme_op_tofu_policy (gpgme_ctx_t ctx,
|
||||
|
||||
err = tofu_policy_start (ctx, 1, key, policy);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ gpgme_op_trustlist_next (gpgme_ctx_t ctx, gpgme_trust_item_t *r_item)
|
||||
|
||||
if (!opd->trust_queue)
|
||||
{
|
||||
err = _gpgme_wait_on_condition (ctx, &opd->trust_cond, NULL);
|
||||
err = _gpgme_sync_wait (ctx, &opd->trust_cond, NULL);
|
||||
if (err)
|
||||
return TRACE_ERR (err);
|
||||
if (!opd->trust_cond)
|
||||
|
@ -911,7 +911,7 @@ parse_error (gpgme_signature_t sig, char *args, int set_status)
|
||||
&& gpg_err_code (err) == GPG_ERR_BAD_DATA)
|
||||
{
|
||||
/* This indicates a double plaintext. The only solid way to
|
||||
handle this is by failing the oepration. */
|
||||
handle this is by failing the operation. */
|
||||
return gpg_error (GPG_ERR_BAD_DATA);
|
||||
}
|
||||
else if (!set_status)
|
||||
@ -1194,7 +1194,7 @@ gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
|
||||
|
||||
err = verify_start (ctx, 1, sig, signed_text, plaintext);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one (ctx);
|
||||
err = _gpgme_sync_wait (ctx, NULL, NULL);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ gpgme_op_vfs_transact (gpgme_ctx_t ctx,
|
||||
err = vfs_start (ctx, 1, command, data_cb, data_cb_value,
|
||||
inq_cb, inq_cb_value, status_cb, status_cb_value);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one_ext (ctx, op_err);
|
||||
err = _gpgme_sync_wait (ctx, NULL, op_err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -202,4 +202,3 @@ gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[],
|
||||
err = _gpgme_op_vfs_create (ctx, recp, container_file, flags, op_err);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ gpgme_op_vfs_transact (gpgme_ctx_t ctx,
|
||||
err = vfs_start (ctx, 1, command, data_cb, data_cb_value,
|
||||
inq_cb, inq_cb_value, status_cb, status_cb_value);
|
||||
if (!err)
|
||||
err = _gpgme_wait_one_ext (ctx, op_err);
|
||||
err = _gpgme_sync_wait (ctx, NULL, op_err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -244,4 +244,3 @@ gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file,
|
||||
err = _gpgme_op_vfs_mount (ctx, container_file, mount_dir, flags, op_err);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ static struct
|
||||
/* The context of an associated writer object or NULL. */
|
||||
struct writer_context_s *writer;
|
||||
|
||||
/* A notification handler. Noet that we current support only one
|
||||
/* A notification handler. Note that we current support only one
|
||||
* callback per fd. */
|
||||
struct {
|
||||
_gpgme_close_notify_handler_t handler;
|
||||
|
@ -1,401 +0,0 @@
|
||||
/* wait-global.c
|
||||
* Copyright (C) 2000 Werner Koch (dd9jn)
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005 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 <https://gnu.org/licenses/>.
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "gpgme.h"
|
||||
#include "sema.h"
|
||||
#include "util.h"
|
||||
#include "context.h"
|
||||
#include "wait.h"
|
||||
#include "priv-io.h"
|
||||
#include "ops.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* The global event loop is used for all asynchronous operations
|
||||
(except key listing) for which no user I/O callbacks are specified.
|
||||
|
||||
A context sets up its initial I/O callbacks and then sends the
|
||||
GPGME_EVENT_START event. After that, it is added to the global
|
||||
list of active contexts.
|
||||
|
||||
The gpgme_wait function contains a select() loop over all file
|
||||
descriptors in all active contexts. If an error occurs, it closes
|
||||
all fds in that context and moves the context to the global done
|
||||
list. Likewise, if a context has removed all I/O callbacks, it is
|
||||
moved to the global done list.
|
||||
|
||||
All contexts in the global done list are eligible for being
|
||||
returned by gpgme_wait if requested by the caller. */
|
||||
|
||||
/* The ctx_list_lock protects the list of active and done contexts.
|
||||
Insertion into any of these lists is only allowed when the lock is
|
||||
held. This allows a muli-threaded program to loop over gpgme_wait
|
||||
and in parallel start asynchronous gpgme operations.
|
||||
|
||||
However, the fd tables in the contexts are not protected by this
|
||||
lock. They are only allowed to change either before the context is
|
||||
added to the active list (ie, before the start event is signalled)
|
||||
or in a callback handler. */
|
||||
DEFINE_STATIC_LOCK (ctx_list_lock);
|
||||
|
||||
/* A ctx_list_item is an item in the global list of active or done
|
||||
contexts. */
|
||||
struct ctx_list_item
|
||||
{
|
||||
/* Every ctx_list_item is an element in a doubly linked list. The
|
||||
list pointers are protected by the ctx_list_lock. */
|
||||
struct ctx_list_item *next;
|
||||
struct ctx_list_item *prev;
|
||||
|
||||
gpgme_ctx_t ctx;
|
||||
/* The status is set when the ctx is moved to the done list. */
|
||||
gpgme_error_t status;
|
||||
gpgme_error_t op_err;
|
||||
};
|
||||
|
||||
/* The active list contains all contexts that are in the global event
|
||||
loop, have active I/O callbacks, and have already seen the start
|
||||
event. */
|
||||
static struct ctx_list_item *ctx_active_list;
|
||||
|
||||
/* The done list contains all contexts that have previously been
|
||||
active but now are not active any longer, either because they
|
||||
finished successfully or an I/O callback returned an error. The
|
||||
status field in the list item contains the error value (or 0 if
|
||||
successful). */
|
||||
static struct ctx_list_item *ctx_done_list;
|
||||
|
||||
|
||||
/* Enter the context CTX into the active list. */
|
||||
static gpgme_error_t
|
||||
ctx_active (gpgme_ctx_t ctx)
|
||||
{
|
||||
struct ctx_list_item *li = malloc (sizeof (struct ctx_list_item));
|
||||
if (!li)
|
||||
return gpg_error_from_syserror ();
|
||||
li->ctx = ctx;
|
||||
|
||||
LOCK (ctx_list_lock);
|
||||
/* Add LI to active list. */
|
||||
li->next = ctx_active_list;
|
||||
li->prev = NULL;
|
||||
if (ctx_active_list)
|
||||
ctx_active_list->prev = li;
|
||||
ctx_active_list = li;
|
||||
UNLOCK (ctx_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Enter the context CTX into the done list with status STATUS. */
|
||||
static void
|
||||
ctx_done (gpgme_ctx_t ctx, gpgme_error_t status, gpgme_error_t op_err)
|
||||
{
|
||||
struct ctx_list_item *li;
|
||||
|
||||
LOCK (ctx_list_lock);
|
||||
li = ctx_active_list;
|
||||
while (li && li->ctx != ctx)
|
||||
li = li->next;
|
||||
assert (li);
|
||||
|
||||
/* Remove LI from active list. */
|
||||
if (li->next)
|
||||
li->next->prev = li->prev;
|
||||
if (li->prev)
|
||||
li->prev->next = li->next;
|
||||
else
|
||||
ctx_active_list = li->next;
|
||||
|
||||
li->status = status;
|
||||
li->op_err = op_err;
|
||||
|
||||
/* Add LI to done list. */
|
||||
li->next = ctx_done_list;
|
||||
li->prev = NULL;
|
||||
if (ctx_done_list)
|
||||
ctx_done_list->prev = li;
|
||||
ctx_done_list = li;
|
||||
UNLOCK (ctx_list_lock);
|
||||
}
|
||||
|
||||
|
||||
/* Find finished context CTX (or any context if CTX is NULL) and
|
||||
return its status in STATUS after removing it from the done list.
|
||||
If a matching context could be found, return it. Return NULL if no
|
||||
context could be found. */
|
||||
static gpgme_ctx_t
|
||||
ctx_wait (gpgme_ctx_t ctx, gpgme_error_t *status, gpgme_error_t *op_err)
|
||||
{
|
||||
struct ctx_list_item *li;
|
||||
|
||||
LOCK (ctx_list_lock);
|
||||
li = ctx_done_list;
|
||||
if (ctx)
|
||||
{
|
||||
/* A specific context is requested. */
|
||||
while (li && li->ctx != ctx)
|
||||
li = li->next;
|
||||
}
|
||||
if (li)
|
||||
{
|
||||
ctx = li->ctx;
|
||||
if (status)
|
||||
*status = li->status;
|
||||
if (op_err)
|
||||
*op_err = li->op_err;
|
||||
|
||||
/* Remove LI from done list. */
|
||||
if (li->next)
|
||||
li->next->prev = li->prev;
|
||||
if (li->prev)
|
||||
li->prev->next = li->next;
|
||||
else
|
||||
ctx_done_list = li->next;
|
||||
free (li);
|
||||
}
|
||||
else
|
||||
ctx = NULL;
|
||||
UNLOCK (ctx_list_lock);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
/* Internal I/O callback functions. */
|
||||
|
||||
/* The add_io_cb and remove_io_cb handlers are shared with the private
|
||||
event loops. */
|
||||
|
||||
void
|
||||
_gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
|
||||
void *type_data)
|
||||
{
|
||||
gpgme_ctx_t ctx = (gpgme_ctx_t) data;
|
||||
|
||||
assert (ctx);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GPGME_EVENT_START:
|
||||
{
|
||||
gpgme_error_t err = ctx_active (ctx);
|
||||
|
||||
if (err)
|
||||
/* An error occurred. Close all fds in this context, and
|
||||
send the error in a done event. */
|
||||
_gpgme_cancel_with_err (ctx, err, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_DONE:
|
||||
{
|
||||
gpgme_io_event_done_data_t done_data =
|
||||
(gpgme_io_event_done_data_t) type_data;
|
||||
|
||||
ctx_done (ctx, done_data->err, done_data->op_err);
|
||||
}
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_NEXT_KEY:
|
||||
assert (!"Unexpected event GPGME_EVENT_NEXT_KEY");
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_NEXT_TRUSTITEM:
|
||||
assert (!"Unexpected event GPGME_EVENT_NEXT_TRUSTITEM");
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (!"Unexpected event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Perform asynchronous operations in the global event loop (ie, any
|
||||
asynchronous operation except key listing and trustitem listing
|
||||
operations). If CTX is not a null pointer, the function will
|
||||
return if the asynchronous operation in the context CTX finished.
|
||||
Otherwise the function will return if any asynchronous operation
|
||||
finished. If HANG is zero, the function will not block for a long
|
||||
time. Otherwise the function does not return until an operation
|
||||
matching CTX finished.
|
||||
|
||||
If a matching context finished, it is returned, and *STATUS is set
|
||||
to the error value of the operation in that context. Otherwise, if
|
||||
the timeout expires, NULL is returned and *STATUS is 0. If an
|
||||
error occurs, NULL is returned and *STATUS is set to the error
|
||||
value. */
|
||||
gpgme_ctx_t
|
||||
gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status,
|
||||
gpgme_error_t *op_err, int hang)
|
||||
{
|
||||
do
|
||||
{
|
||||
unsigned int i = 0;
|
||||
struct ctx_list_item *li;
|
||||
struct fd_table fdt;
|
||||
int nr;
|
||||
|
||||
/* Collect the active file descriptors. */
|
||||
LOCK (ctx_list_lock);
|
||||
for (li = ctx_active_list; li; li = li->next)
|
||||
i += li->ctx->fdt.size;
|
||||
fdt.fds = malloc (i * sizeof (struct io_select_fd_s));
|
||||
if (!fdt.fds)
|
||||
{
|
||||
int saved_err = gpg_error_from_syserror ();
|
||||
UNLOCK (ctx_list_lock);
|
||||
if (status)
|
||||
*status = saved_err;
|
||||
if (op_err)
|
||||
*op_err = 0;
|
||||
return NULL;
|
||||
}
|
||||
fdt.size = i;
|
||||
i = 0;
|
||||
for (li = ctx_active_list; li; li = li->next)
|
||||
{
|
||||
memcpy (&fdt.fds[i], li->ctx->fdt.fds,
|
||||
li->ctx->fdt.size * sizeof (struct io_select_fd_s));
|
||||
i += li->ctx->fdt.size;
|
||||
}
|
||||
UNLOCK (ctx_list_lock);
|
||||
|
||||
nr = _gpgme_io_select (fdt.fds, fdt.size, 0);
|
||||
if (nr < 0)
|
||||
{
|
||||
int saved_err = gpg_error_from_syserror ();
|
||||
free (fdt.fds);
|
||||
if (status)
|
||||
*status = saved_err;
|
||||
if (op_err)
|
||||
*op_err = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < fdt.size && nr; i++)
|
||||
{
|
||||
if (fdt.fds[i].fd != -1 && fdt.fds[i].signaled)
|
||||
{
|
||||
gpgme_ctx_t ictx;
|
||||
gpgme_error_t err = 0;
|
||||
gpgme_error_t local_op_err = 0;
|
||||
struct wait_item_s *item;
|
||||
|
||||
assert (nr);
|
||||
nr--;
|
||||
|
||||
item = (struct wait_item_s *) fdt.fds[i].opaque;
|
||||
assert (item);
|
||||
ictx = item->ctx;
|
||||
assert (ictx);
|
||||
|
||||
LOCK (ctx->lock);
|
||||
if (ctx->canceled)
|
||||
err = gpg_error (GPG_ERR_CANCELED);
|
||||
UNLOCK (ctx->lock);
|
||||
|
||||
if (!err)
|
||||
err = _gpgme_run_io_cb (&fdt.fds[i], 0, &local_op_err);
|
||||
if (err || local_op_err)
|
||||
{
|
||||
/* An error occurred. Close all fds in this context,
|
||||
and signal it. */
|
||||
_gpgme_cancel_with_err (ictx, err, local_op_err);
|
||||
|
||||
/* Break out of the loop, and retry the select()
|
||||
from scratch, because now all fds should be
|
||||
gone. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free (fdt.fds);
|
||||
|
||||
/* Now some contexts might have finished successfully. */
|
||||
LOCK (ctx_list_lock);
|
||||
retry:
|
||||
for (li = ctx_active_list; li; li = li->next)
|
||||
{
|
||||
gpgme_ctx_t actx = li->ctx;
|
||||
|
||||
for (i = 0; i < actx->fdt.size; i++)
|
||||
if (actx->fdt.fds[i].fd != -1)
|
||||
break;
|
||||
if (i == actx->fdt.size)
|
||||
{
|
||||
struct gpgme_io_event_done_data data;
|
||||
data.err = 0;
|
||||
data.op_err = 0;
|
||||
|
||||
/* FIXME: This does not perform too well. We have to
|
||||
release the lock because the I/O event handler
|
||||
acquires it to remove the context from the active
|
||||
list. Two alternative strategies are worth
|
||||
considering: Either implement the DONE event handler
|
||||
here in a lock-free manner, or save a list of all
|
||||
contexts to be released and call the DONE events
|
||||
afterwards. */
|
||||
UNLOCK (ctx_list_lock);
|
||||
_gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &data);
|
||||
LOCK (ctx_list_lock);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
UNLOCK (ctx_list_lock);
|
||||
|
||||
{
|
||||
gpgme_ctx_t dctx = ctx_wait (ctx, status, op_err);
|
||||
|
||||
if (dctx)
|
||||
{
|
||||
ctx = dctx;
|
||||
hang = 0;
|
||||
}
|
||||
else if (!hang)
|
||||
{
|
||||
ctx = NULL;
|
||||
if (status)
|
||||
*status = 0;
|
||||
if (op_err)
|
||||
*op_err = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (hang);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
gpgme_ctx_t
|
||||
gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
|
||||
{
|
||||
return gpgme_wait_ext (ctx, status, NULL, hang);
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
/* wait-private.c
|
||||
* Copyright (C) 2000 Werner Koch (dd9jn)
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005 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 <https://gnu.org/licenses/>.
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "gpgme.h"
|
||||
#include "context.h"
|
||||
#include "wait.h"
|
||||
#include "ops.h"
|
||||
#include "priv-io.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/* The private event loops are used for all blocking operations, and
|
||||
for the key and trust item listing operations. They are completely
|
||||
separated from each other. */
|
||||
|
||||
|
||||
/* Internal I/O callback functions. */
|
||||
|
||||
/* The add_io_cb and remove_io_cb handlers are shared with the global
|
||||
event loops. */
|
||||
|
||||
void
|
||||
_gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type,
|
||||
void *type_data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GPGME_EVENT_START:
|
||||
/* Nothing to do here, as the wait routine is called after the
|
||||
initialization is finished. */
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_DONE:
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_NEXT_KEY:
|
||||
_gpgme_op_keylist_event_cb (data, type, type_data);
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_NEXT_TRUSTITEM:
|
||||
_gpgme_op_trustlist_event_cb (data, type, type_data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If COND is a null pointer, wait until the blocking operation in CTX
|
||||
finished and return its error value. Otherwise, wait until COND is
|
||||
satisfied or the operation finished. */
|
||||
gpgme_error_t
|
||||
_gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond,
|
||||
gpgme_error_t *op_err_p)
|
||||
{
|
||||
gpgme_error_t err = 0;
|
||||
int hang = 1;
|
||||
|
||||
if (op_err_p)
|
||||
*op_err_p = 0;
|
||||
|
||||
do
|
||||
{
|
||||
int nr = _gpgme_io_select (ctx->fdt.fds, ctx->fdt.size, 0);
|
||||
unsigned int i;
|
||||
|
||||
if (nr < 0)
|
||||
{
|
||||
/* An error occurred. Close all fds in this context, and
|
||||
signal it. */
|
||||
err = gpg_error_from_syserror ();
|
||||
_gpgme_cancel_with_err (ctx, err, 0);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ctx->fdt.size && nr; i++)
|
||||
{
|
||||
if (ctx->fdt.fds[i].fd != -1 && ctx->fdt.fds[i].signaled)
|
||||
{
|
||||
gpgme_error_t op_err = 0;
|
||||
|
||||
ctx->fdt.fds[i].signaled = 0;
|
||||
assert (nr);
|
||||
nr--;
|
||||
|
||||
LOCK (ctx->lock);
|
||||
if (ctx->canceled)
|
||||
err = gpg_error (GPG_ERR_CANCELED);
|
||||
UNLOCK (ctx->lock);
|
||||
|
||||
if (!err)
|
||||
err = _gpgme_run_io_cb (&ctx->fdt.fds[i], 0, &op_err);
|
||||
if (err)
|
||||
{
|
||||
/* An error occurred. Close all fds in this context,
|
||||
and signal it. */
|
||||
_gpgme_cancel_with_err (ctx, err, 0);
|
||||
|
||||
return err;
|
||||
}
|
||||
else if (op_err)
|
||||
{
|
||||
/* An operational error occurred. Cancel the current
|
||||
operation but not the session, and signal it. */
|
||||
_gpgme_cancel_with_err (ctx, 0, op_err);
|
||||
|
||||
/* NOTE: This relies on the operational error being
|
||||
generated after the operation really has
|
||||
completed, for example after no further status
|
||||
line output is generated. Otherwise the
|
||||
following I/O will spill over into the next
|
||||
operation. */
|
||||
if (op_err_p)
|
||||
*op_err_p = op_err;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ctx->fdt.size; i++)
|
||||
if (ctx->fdt.fds[i].fd != -1)
|
||||
break;
|
||||
if (i == ctx->fdt.size)
|
||||
{
|
||||
struct gpgme_io_event_done_data data;
|
||||
data.err = 0;
|
||||
data.op_err = 0;
|
||||
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data);
|
||||
hang = 0;
|
||||
}
|
||||
if (cond && *cond)
|
||||
hang = 0;
|
||||
}
|
||||
while (hang);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Wait until the blocking operation in context CTX has finished and
|
||||
return the error value. This variant can not be used for
|
||||
session-based protocols. */
|
||||
gpgme_error_t
|
||||
_gpgme_wait_one (gpgme_ctx_t ctx)
|
||||
{
|
||||
return _gpgme_wait_on_condition (ctx, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Wait until the blocking operation in context CTX has finished and
|
||||
return the error value. This is the right variant to use for
|
||||
sesion-based protocols. */
|
||||
gpgme_error_t
|
||||
_gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err)
|
||||
{
|
||||
return _gpgme_wait_on_condition (ctx, NULL, op_err);
|
||||
}
|
133
src/wait-user.c
133
src/wait-user.c
@ -1,133 +0,0 @@
|
||||
/* wait-user.c
|
||||
* Copyright (C) 2000 Werner Koch (dd9jn)
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005 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 <https://gnu.org/licenses/>.
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#include "gpgme.h"
|
||||
#include "context.h"
|
||||
#include "priv-io.h"
|
||||
#include "wait.h"
|
||||
#include "ops.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/* The user event loops are used for all asynchronous operations for
|
||||
which a user callback is defined. */
|
||||
|
||||
|
||||
/* Internal I/O Callbacks. */
|
||||
|
||||
gpgme_error_t
|
||||
_gpgme_user_io_cb_handler (void *data, int fd)
|
||||
{
|
||||
gpgme_error_t err = 0;
|
||||
gpgme_error_t op_err = 0;
|
||||
struct tag *tag = (struct tag *) data;
|
||||
gpgme_ctx_t ctx;
|
||||
|
||||
(void)fd;
|
||||
|
||||
assert (data);
|
||||
ctx = tag->ctx;
|
||||
assert (ctx);
|
||||
|
||||
LOCK (ctx->lock);
|
||||
if (ctx->canceled)
|
||||
err = gpg_error (GPG_ERR_CANCELED);
|
||||
UNLOCK (ctx->lock);
|
||||
|
||||
if (! err)
|
||||
err = _gpgme_run_io_cb (&ctx->fdt.fds[tag->idx], 0, &op_err);
|
||||
if (err || op_err)
|
||||
_gpgme_cancel_with_err (ctx, err, op_err);
|
||||
else
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ctx->fdt.size; i++)
|
||||
if (ctx->fdt.fds[i].fd != -1)
|
||||
break;
|
||||
|
||||
if (i == ctx->fdt.size)
|
||||
{
|
||||
struct gpgme_io_event_done_data done_data;
|
||||
|
||||
done_data.err = 0;
|
||||
done_data.op_err = 0;
|
||||
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &done_data);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Register the file descriptor FD with the handler FNC (which gets
|
||||
FNC_DATA as its first argument) for the direction DIR. DATA should
|
||||
be the context for which the fd is added. R_TAG will hold the tag
|
||||
that can be used to remove the fd. */
|
||||
gpgme_error_t
|
||||
_gpgme_wait_user_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc,
|
||||
void *fnc_data, void **r_tag)
|
||||
{
|
||||
gpgme_ctx_t ctx = (gpgme_ctx_t) data;
|
||||
struct tag *tag;
|
||||
gpgme_error_t err;
|
||||
|
||||
assert (ctx);
|
||||
err = _gpgme_add_io_cb (data, fd, dir, fnc, fnc_data, r_tag);
|
||||
if (err)
|
||||
return err;
|
||||
tag = *r_tag;
|
||||
assert (tag);
|
||||
err = (*ctx->io_cbs.add) (ctx->io_cbs.add_priv, fd, dir,
|
||||
_gpgme_user_io_cb_handler, *r_tag,
|
||||
&tag->user_tag);
|
||||
if (err)
|
||||
_gpgme_remove_io_cb (*r_tag);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_gpgme_wait_user_remove_io_cb (void *data)
|
||||
{
|
||||
struct tag *tag = (struct tag *) data;
|
||||
gpgme_ctx_t ctx;
|
||||
|
||||
assert (tag);
|
||||
ctx = tag->ctx;
|
||||
|
||||
(*ctx->io_cbs.remove) (tag->user_tag);
|
||||
_gpgme_remove_io_cb (data);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type, void *type_data)
|
||||
{
|
||||
gpgme_ctx_t ctx = data;
|
||||
|
||||
if (ctx->io_cbs.event)
|
||||
(*ctx->io_cbs.event) (ctx->io_cbs.event_priv, type, type_data);
|
||||
}
|
545
src/wait.c
545
src/wait.c
@ -38,188 +38,467 @@
|
||||
#include "priv-io.h"
|
||||
#include "engine.h"
|
||||
#include "debug.h"
|
||||
#include "fdtable.h"
|
||||
|
||||
|
||||
void
|
||||
_gpgme_fd_table_init (fd_table_t fdt)
|
||||
|
||||
/* Wrapper for the user wait handler to match the exported prototype.
|
||||
* This is used by _gpgme_add_io_cb_user. */
|
||||
static gpg_error_t
|
||||
user_io_cb_handler (void *data, int fd)
|
||||
{
|
||||
fdt->fds = NULL;
|
||||
fdt->size = 0;
|
||||
struct io_cb_tag_s *tag = data;
|
||||
gpg_error_t err;
|
||||
uint64_t serial;
|
||||
gpgme_ctx_t ctx;
|
||||
gpg_error_t op_err;
|
||||
struct io_select_s iosel;
|
||||
|
||||
(void)fd;
|
||||
|
||||
assert (data);
|
||||
serial = tag->serial;
|
||||
assert (serial);
|
||||
|
||||
iosel.fd = fd;
|
||||
iosel.for_read = 0; /* we don't care. */
|
||||
iosel.for_write = 0; /* we don't care. */
|
||||
iosel.signaled = 1; /* we are only called when I/O is pending. */
|
||||
_gpgme_fdtable_set_signaled (&iosel, 1);
|
||||
|
||||
err = _gpgme_fdtable_run_io_cbs (serial, &op_err, NULL);
|
||||
if (err || op_err)
|
||||
;
|
||||
else if (!_gpgme_fdtable_get_count (serial, 0))
|
||||
{
|
||||
/* No more active callbacks - emit a DONE. */
|
||||
struct gpgme_io_event_done_data done_data = { 0, 0 };
|
||||
_gpgme_get_ctx (serial, &ctx);
|
||||
if (ctx)
|
||||
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &done_data);
|
||||
}
|
||||
|
||||
void
|
||||
_gpgme_fd_table_deinit (fd_table_t fdt)
|
||||
{
|
||||
if (fdt->fds)
|
||||
free (fdt->fds);
|
||||
}
|
||||
|
||||
|
||||
/* XXX We should keep a marker and roll over for speed. */
|
||||
static gpgme_error_t
|
||||
fd_table_put (fd_table_t fdt, int fd, int dir, void *opaque, int *idx)
|
||||
{
|
||||
unsigned int i, j;
|
||||
struct io_select_fd_s *new_fds;
|
||||
|
||||
for (i = 0; i < fdt->size; i++)
|
||||
{
|
||||
if (fdt->fds[i].fd == -1)
|
||||
break;
|
||||
}
|
||||
if (i == fdt->size)
|
||||
{
|
||||
#define FDT_ALLOCSIZE 10
|
||||
new_fds = realloc (fdt->fds, (fdt->size + FDT_ALLOCSIZE)
|
||||
* sizeof (*new_fds));
|
||||
if (!new_fds)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
fdt->fds = new_fds;
|
||||
fdt->size += FDT_ALLOCSIZE;
|
||||
for (j = 0; j < FDT_ALLOCSIZE; j++)
|
||||
fdt->fds[i + j].fd = -1;
|
||||
}
|
||||
|
||||
fdt->fds[i].fd = fd;
|
||||
fdt->fds[i].for_read = (dir == 1);
|
||||
fdt->fds[i].for_write = (dir == 0);
|
||||
fdt->fds[i].signaled = 0;
|
||||
fdt->fds[i].opaque = opaque;
|
||||
*idx = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Register the file descriptor FD with the handler FNC (which gets
|
||||
FNC_DATA as its first argument) for the direction DIR. DATA should
|
||||
be the context for which the fd is added. R_TAG will hold the tag
|
||||
that can be used to remove the fd. */
|
||||
that can be used to remove the fd. This function is used for the
|
||||
global and the private wait loops. */
|
||||
gpgme_error_t
|
||||
_gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc,
|
||||
void *fnc_data, void **r_tag)
|
||||
{
|
||||
gpgme_error_t err;
|
||||
gpgme_ctx_t ctx = (gpgme_ctx_t) data;
|
||||
fd_table_t fdt;
|
||||
struct wait_item_s *item;
|
||||
struct tag *tag;
|
||||
struct io_cb_tag_s *tag;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu fd=%d, dir %d",
|
||||
CTXSERIAL (ctx), fd, dir);
|
||||
|
||||
if (!fnc)
|
||||
return gpg_error (GPG_ERR_INV_ARG);
|
||||
|
||||
assert (fnc);
|
||||
assert (ctx);
|
||||
|
||||
fdt = &ctx->fdt;
|
||||
assert (fdt);
|
||||
|
||||
tag = malloc (sizeof *tag);
|
||||
tag = calloc (1, sizeof *tag);
|
||||
if (!tag)
|
||||
return gpg_error_from_syserror ();
|
||||
tag->ctx = ctx;
|
||||
tag->serial = ctx->serial;
|
||||
tag->fd = fd;
|
||||
|
||||
/* Allocate a structure to hold information about the handler. */
|
||||
item = calloc (1, sizeof *item);
|
||||
if (!item)
|
||||
{
|
||||
free (tag);
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
item->ctx = ctx;
|
||||
item->dir = dir;
|
||||
item->handler = fnc;
|
||||
item->handler_value = fnc_data;
|
||||
|
||||
err = fd_table_put (fdt, fd, dir, item, &tag->idx);
|
||||
err = _gpgme_fdtable_set_io_cb (fd, ctx->serial, dir, fnc, fnc_data);
|
||||
if (err)
|
||||
{
|
||||
free (tag);
|
||||
free (item);
|
||||
return err;
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
TRACE (DEBUG_CTX, "_gpgme_add_io_cb", ctx,
|
||||
"fd=%d, dir=%d -> tag=%p", fd, dir, tag);
|
||||
|
||||
*r_tag = tag;
|
||||
|
||||
TRACE_SUC ("tag=%p", tag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Register the file descriptor FD with the handler FNC (which gets
|
||||
FNC_DATA as its first argument) for the direction DIR. DATA should
|
||||
be the context for which the fd is added. R_TAG will hold the tag
|
||||
that can be used to remove the fd. This function is used for the
|
||||
user wait loops. */
|
||||
gpg_error_t
|
||||
_gpgme_add_io_cb_user (void *data, int fd, int dir, gpgme_io_cb_t fnc,
|
||||
void *fnc_data, void **r_tag)
|
||||
{
|
||||
gpgme_ctx_t ctx = (gpgme_ctx_t) data;
|
||||
struct io_cb_tag_s *tag;
|
||||
gpgme_error_t err;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu fd=%d, dir %d",
|
||||
CTXSERIAL (ctx), fd, dir);
|
||||
|
||||
assert (ctx);
|
||||
err = _gpgme_add_io_cb (data, fd, dir, fnc, fnc_data, r_tag);
|
||||
if (err)
|
||||
return TRACE_ERR (err);
|
||||
tag = *r_tag;
|
||||
assert (tag);
|
||||
|
||||
err = ctx->user_io_cbs.add (ctx->user_io_cbs.add_priv, fd, dir,
|
||||
user_io_cb_handler, *r_tag,
|
||||
&tag->user_tag);
|
||||
if (err)
|
||||
_gpgme_remove_io_cb (*r_tag);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
||||
|
||||
/* This function is used for the global and the private wait loops. */
|
||||
void
|
||||
_gpgme_remove_io_cb (void *data)
|
||||
{
|
||||
struct tag *tag = data;
|
||||
gpgme_ctx_t ctx;
|
||||
fd_table_t fdt;
|
||||
int idx;
|
||||
struct io_cb_tag_s *tag = data;
|
||||
gpg_error_t err;
|
||||
|
||||
assert (tag);
|
||||
ctx = tag->ctx;
|
||||
assert (ctx);
|
||||
fdt = &ctx->fdt;
|
||||
assert (fdt);
|
||||
idx = tag->idx;
|
||||
|
||||
TRACE (DEBUG_CTX, "_gpgme_remove_io_cb", data,
|
||||
"setting fd 0x%x (item=%p) done", fdt->fds[idx].fd,
|
||||
fdt->fds[idx].opaque);
|
||||
|
||||
free (fdt->fds[idx].opaque);
|
||||
free (tag);
|
||||
|
||||
/* Free the table entry. */
|
||||
fdt->fds[idx].fd = -1;
|
||||
fdt->fds[idx].for_read = 0;
|
||||
fdt->fds[idx].for_write = 0;
|
||||
fdt->fds[idx].opaque = NULL;
|
||||
err = _gpgme_fdtable_set_io_cb (tag->fd, tag->serial, 0, NULL, NULL);
|
||||
if (err)
|
||||
{
|
||||
TRACE (DEBUG_CTX, __func__, NULL, "tag=%p (ctx=%lu fd=%d) failed: %s",
|
||||
tag, tag->serial, tag->fd, gpg_strerror (err));
|
||||
}
|
||||
else
|
||||
{
|
||||
TRACE (DEBUG_CTX, __func__, NULL, "tag=%p (ctx=%lu fd=%d) done",
|
||||
tag, tag->serial, tag->fd);
|
||||
}
|
||||
free (tag);
|
||||
}
|
||||
|
||||
|
||||
/* This function is used for the user wait loops. */
|
||||
void
|
||||
_gpgme_remove_io_cb_user (void *data)
|
||||
{
|
||||
struct io_cb_tag_s *tag = data;
|
||||
gpgme_ctx_t ctx;
|
||||
|
||||
assert (tag);
|
||||
_gpgme_get_ctx (tag->serial, &ctx);
|
||||
|
||||
if (ctx)
|
||||
ctx->user_io_cbs.remove (tag->user_tag);
|
||||
_gpgme_remove_io_cb (data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This is slightly embarrassing. The problem is that running an I/O
|
||||
callback _may_ influence the status of other file descriptors. Our
|
||||
own event loops could compensate for that, but the external event
|
||||
loops cannot. FIXME: We may still want to optimize this a bit when
|
||||
we are called from our own event loops. So if CHECKED is 1, the
|
||||
check is skipped. FIXME: Give an example on how the status of other
|
||||
fds can be influenced. */
|
||||
gpgme_error_t
|
||||
_gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
|
||||
gpgme_error_t *op_err)
|
||||
{
|
||||
struct wait_item_s *item;
|
||||
struct io_cb_data iocb_data;
|
||||
gpgme_error_t err;
|
||||
/* The internal I/O callback function used for the global event loop.
|
||||
That loop is used for all asynchronous operations (except key
|
||||
listing) for which no user I/O callbacks are specified.
|
||||
|
||||
item = (struct wait_item_s *) an_fds->opaque;
|
||||
assert (item);
|
||||
A context sets up its initial I/O callbacks and then sends the
|
||||
GPGME_EVENT_START event. After that, it is added to the global
|
||||
list of active contexts.
|
||||
|
||||
if (!checked)
|
||||
The gpgme_wait function contains a select() loop over all file
|
||||
descriptors in all active contexts. If an error occurs, it closes
|
||||
all fds in that context and moves the context to the global done
|
||||
list. Likewise, if a context has removed all I/O callbacks, it is
|
||||
moved to the global done list.
|
||||
|
||||
All contexts in the global done list are eligible for being
|
||||
returned by gpgme_wait if requested by the caller. */
|
||||
void
|
||||
_gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
|
||||
void *type_data)
|
||||
{
|
||||
gpgme_ctx_t ctx = (gpgme_ctx_t) data;
|
||||
gpg_error_t err;
|
||||
|
||||
assert (ctx);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GPGME_EVENT_START:
|
||||
{
|
||||
err = _gpgme_fdtable_set_active (ctx->serial);
|
||||
if (err)
|
||||
/* An error occurred. Close all fds in this context, and
|
||||
send the error in a done event. */
|
||||
_gpgme_cancel_with_err (ctx->serial, err, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_DONE:
|
||||
{
|
||||
gpgme_io_event_done_data_t done_data =
|
||||
(gpgme_io_event_done_data_t) type_data;
|
||||
|
||||
_gpgme_fdtable_set_done (ctx->serial,
|
||||
done_data->err, done_data->op_err);
|
||||
}
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_NEXT_KEY:
|
||||
assert (!"Unexpected event GPGME_EVENT_NEXT_KEY");
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_NEXT_TRUSTITEM:
|
||||
assert (!"Unexpected event GPGME_EVENT_NEXT_TRUSTITEM");
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (!"Unexpected event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* The internal I/O callback function used for private event loops.
|
||||
* The private event loops are used for all blocking operations, and
|
||||
* for the key and trust item listing operations. They are completely
|
||||
* separated from each other. */
|
||||
void
|
||||
_gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type,
|
||||
void *type_data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GPGME_EVENT_START:
|
||||
/* Nothing to do here, as the wait routine is called after the
|
||||
initialization is finished. */
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_DONE:
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_NEXT_KEY:
|
||||
_gpgme_op_keylist_event_cb (data, type, type_data);
|
||||
break;
|
||||
|
||||
case GPGME_EVENT_NEXT_TRUSTITEM:
|
||||
_gpgme_op_trustlist_event_cb (data, type, type_data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* The internal I/O callback function used for user event loops. User
|
||||
* event loops are used for all asynchronous operations for which a
|
||||
* user callback is defined. */
|
||||
void
|
||||
_gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type, void *type_data)
|
||||
{
|
||||
gpgme_ctx_t ctx = data;
|
||||
|
||||
if (ctx->user_io_cbs.event)
|
||||
ctx->user_io_cbs.event (ctx->user_io_cbs.event_priv, type, type_data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Perform asynchronous operations in the global event loop (ie, any
|
||||
asynchronous operation except key listing and trustitem listing
|
||||
operations). If CTX is not a null pointer, the function will
|
||||
return if the asynchronous operation in the context CTX finished.
|
||||
Otherwise the function will return if any asynchronous operation
|
||||
finished. If HANG is zero, the function will not block for a long
|
||||
time. Otherwise the function does not return until an operation
|
||||
matching CTX finished.
|
||||
|
||||
If a matching context finished, it is returned, and *STATUS is set
|
||||
to the error value of the operation in that context. Otherwise, if
|
||||
the timeout expires, NULL is returned and *STATUS is 0. If an
|
||||
error occurs, NULL is returned and *STATUS is set to the error
|
||||
value. */
|
||||
gpgme_ctx_t
|
||||
gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status,
|
||||
gpgme_error_t *op_err, int hang)
|
||||
{
|
||||
gpg_error_t err;
|
||||
io_select_t fds = NULL;
|
||||
unsigned int nfds;
|
||||
int nr;
|
||||
struct io_select_fd_s fds;
|
||||
uint64_t serial;
|
||||
|
||||
TRACE (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check");
|
||||
fds = *an_fds;
|
||||
fds.signaled = 0;
|
||||
/* Just give it a quick poll. */
|
||||
nr = _gpgme_io_select (&fds, 1, 1);
|
||||
assert (nr <= 1);
|
||||
if (nr < 0)
|
||||
return gpg_error_from_syserror ();
|
||||
else if (nr == 0)
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu hang=%d",
|
||||
CTXSERIAL (ctx), hang);
|
||||
|
||||
do
|
||||
{
|
||||
/* The status changed in the meantime, there is nothing left
|
||||
* to do. */
|
||||
return 0;
|
||||
/* Get all fds of CTX (or all if CTX is NULL) we want to wait
|
||||
* for and which are in the active state. */
|
||||
TRACE_LOG
|
||||
("active=%u done=%u !done=%d cbs=%u",
|
||||
_gpgme_fdtable_get_count (ctx?ctx->serial:0,FDTABLE_FLAG_ACTIVE),
|
||||
_gpgme_fdtable_get_count (ctx?ctx->serial:0,FDTABLE_FLAG_DONE),
|
||||
_gpgme_fdtable_get_count (ctx?ctx->serial:0,FDTABLE_FLAG_NOT_DONE),
|
||||
_gpgme_fdtable_get_count (ctx?ctx->serial:0,0));
|
||||
|
||||
free (fds);
|
||||
nfds = _gpgme_fdtable_get_fds (&fds, ctx? ctx->serial : 0,
|
||||
( FDTABLE_FLAG_ACTIVE
|
||||
| FDTABLE_FLAG_CLEAR));
|
||||
if (!nfds)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
if (gpg_err_code (err) != GPG_ERR_MISSING_ERRNO)
|
||||
{
|
||||
if (status)
|
||||
*status = err;
|
||||
if (op_err)
|
||||
*op_err = 0;
|
||||
ctx = NULL;
|
||||
goto leave;
|
||||
}
|
||||
/* Nothing to select. */
|
||||
|
||||
if (!_gpgme_fdtable_get_count (ctx? ctx->serial : 0,
|
||||
FDTABLE_FLAG_NOT_DONE))
|
||||
{
|
||||
if (status)
|
||||
*status = 0;
|
||||
if (op_err)
|
||||
*op_err = 0;
|
||||
goto leave;
|
||||
}
|
||||
/* There are not yet active FDS. Use the select's standard
|
||||
* timeout and then try again. */
|
||||
}
|
||||
|
||||
TRACE (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)",
|
||||
item->handler_value, an_fds->fd);
|
||||
|
||||
iocb_data.handler_value = item->handler_value;
|
||||
iocb_data.op_err = 0;
|
||||
err = item->handler (&iocb_data, an_fds->fd);
|
||||
|
||||
*op_err = iocb_data.op_err;
|
||||
return err;
|
||||
nr = _gpgme_io_select (fds, nfds, 0);
|
||||
if (nr < 0)
|
||||
{
|
||||
if (status)
|
||||
*status = gpg_error_from_syserror ();
|
||||
if (op_err)
|
||||
*op_err = 0;
|
||||
ctx = NULL;
|
||||
goto leave;
|
||||
}
|
||||
_gpgme_fdtable_set_signaled (fds, nfds);
|
||||
|
||||
err = _gpgme_fdtable_run_io_cbs (ctx? ctx->serial : 0, op_err, &serial);
|
||||
if (err || (op_err && *op_err))
|
||||
{
|
||||
if (status)
|
||||
*status = err;
|
||||
if (serial)
|
||||
_gpgme_get_ctx (serial, &ctx);
|
||||
hang = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
serial = _gpgme_fdtable_get_done (ctx? ctx->serial : 0, status,
|
||||
op_err);
|
||||
if (serial)
|
||||
{
|
||||
_gpgme_get_ctx (serial, &ctx);
|
||||
hang = 0;
|
||||
}
|
||||
else if (!hang)
|
||||
{
|
||||
ctx = NULL;
|
||||
if (status)
|
||||
*status = 0;
|
||||
if (op_err)
|
||||
*op_err = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (hang);
|
||||
|
||||
leave:
|
||||
free (fds);
|
||||
if (status)
|
||||
TRACE_LOG ("status=%d", *status);
|
||||
if (op_err)
|
||||
TRACE_LOG ("op_err=%d", *op_err);
|
||||
TRACE_SUC ("result=%lu", ctx? ctx->serial : 0);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
gpgme_ctx_t
|
||||
gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
|
||||
{
|
||||
return gpgme_wait_ext (ctx, status, NULL, hang);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Wait until the blocking operation in context CTX has finished and
|
||||
* return the error value. If COND is not NULL return early if COND
|
||||
* is satisfied. A session based error will be returned at R_OP_ERR
|
||||
* if it is not NULL. */
|
||||
gpgme_error_t
|
||||
_gpgme_sync_wait (gpgme_ctx_t ctx, volatile int *cond, gpg_error_t *r_op_err)
|
||||
{
|
||||
gpgme_error_t err = 0;
|
||||
int hang = 1;
|
||||
io_select_t fds = NULL;
|
||||
unsigned int nfds;
|
||||
int nr;
|
||||
|
||||
TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu", CTXSERIAL (ctx));
|
||||
|
||||
if (r_op_err)
|
||||
*r_op_err = 0;
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_VALUE);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
/* Get all fds of CTX we want to wait for. */
|
||||
free (fds);
|
||||
nfds = _gpgme_fdtable_get_fds (&fds, ctx->serial,
|
||||
FDTABLE_FLAG_CLEAR);
|
||||
if (!nfds)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
if (gpg_err_code (err) != GPG_ERR_MISSING_ERRNO)
|
||||
goto leave;
|
||||
}
|
||||
else
|
||||
{
|
||||
nr = _gpgme_io_select (fds, nfds, 0);
|
||||
if (nr < 0)
|
||||
{
|
||||
/* An error occurred. Close all fds in this context, and
|
||||
signal it. */
|
||||
err = gpg_error_from_syserror ();
|
||||
_gpgme_cancel_with_err (ctx->serial, err, 0);
|
||||
goto leave;
|
||||
}
|
||||
_gpgme_fdtable_set_signaled (fds, nfds);
|
||||
|
||||
err = _gpgme_fdtable_run_io_cbs (ctx->serial, r_op_err, NULL);
|
||||
if (err || (r_op_err && *r_op_err))
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!_gpgme_fdtable_get_count (ctx->serial, 0))
|
||||
{
|
||||
/* No more matching fds with IO callbacks. */
|
||||
struct gpgme_io_event_done_data data = {0, 0};
|
||||
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data);
|
||||
hang = 0;
|
||||
}
|
||||
if (cond && *cond)
|
||||
hang = 0;
|
||||
}
|
||||
while (hang);
|
||||
err = 0;
|
||||
|
||||
leave:
|
||||
free (fds);
|
||||
return TRACE_ERR (err);
|
||||
}
|
||||
|
62
src/wait.h
62
src/wait.h
@ -1,6 +1,6 @@
|
||||
/* wait.h - Definitions for the wait queue interface.
|
||||
Copyright (C) 2000 Werner Koch (dd9jn)
|
||||
Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
|
||||
Copyright (C) 2001, 2002, 2003, 2004. 2019 g10 Code GmbH
|
||||
|
||||
This file is part of GPGME.
|
||||
|
||||
@ -25,58 +25,46 @@
|
||||
#include "gpgme.h"
|
||||
#include "sema.h"
|
||||
|
||||
struct fd_table
|
||||
/* A registered fd handler can be removed using the tag that
|
||||
* identifies it. In the public API that tag is an an opaque
|
||||
* pointer. */
|
||||
struct io_cb_tag_s
|
||||
{
|
||||
struct io_select_fd_s *fds;
|
||||
size_t size;
|
||||
};
|
||||
typedef struct fd_table *fd_table_t;
|
||||
/* The s/n of the context for which the fd was registered. */
|
||||
uint64_t serial;
|
||||
|
||||
/* Wait items are hooked into the io_select_fd_s to connect an fd with
|
||||
a callback handler. */
|
||||
struct wait_item_s
|
||||
{
|
||||
gpgme_ctx_t ctx;
|
||||
gpgme_io_cb_t handler;
|
||||
void *handler_value;
|
||||
int dir;
|
||||
};
|
||||
|
||||
/* A registered fd handler is removed later using the tag that
|
||||
identifies it. */
|
||||
struct tag
|
||||
{
|
||||
/* The context for which the fd was registered. */
|
||||
gpgme_ctx_t ctx;
|
||||
|
||||
/* The index into the fd table for this context. */
|
||||
int idx;
|
||||
/* The actual fd. */
|
||||
int fd;
|
||||
|
||||
/* This is used by the wrappers for the user event loop. */
|
||||
void *user_tag;
|
||||
|
||||
/* A string used describing the data. This is used for tracing. */
|
||||
const char *desc;
|
||||
};
|
||||
|
||||
|
||||
void _gpgme_fd_table_init (fd_table_t fdt);
|
||||
void _gpgme_fd_table_deinit (fd_table_t fdt);
|
||||
|
||||
gpgme_error_t _gpgme_add_io_cb (void *data, int fd, int dir,
|
||||
gpgme_io_cb_t fnc, void *fnc_data, void **r_tag);
|
||||
gpgme_io_cb_t fnc, void *fnc_data,
|
||||
void **r_tag);
|
||||
gpgme_error_t _gpgme_add_io_cb_user (void *data, int fd, int dir,
|
||||
gpgme_io_cb_t fnc, void *fnc_data,
|
||||
void **r_tag);
|
||||
|
||||
void _gpgme_remove_io_cb (void *tag);
|
||||
void _gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type,
|
||||
void *type_data);
|
||||
void _gpgme_remove_io_cb_user (void *tag);
|
||||
|
||||
|
||||
void _gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
|
||||
void *type_data);
|
||||
|
||||
gpgme_error_t _gpgme_wait_user_add_io_cb (void *data, int fd, int dir,
|
||||
gpgme_io_cb_t fnc, void *fnc_data,
|
||||
void **r_tag);
|
||||
void _gpgme_wait_user_remove_io_cb (void *tag);
|
||||
|
||||
void _gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type,
|
||||
void *type_data);
|
||||
void _gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type,
|
||||
void *type_data);
|
||||
|
||||
gpgme_error_t _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
|
||||
gpgme_error_t *err);
|
||||
|
||||
|
||||
|
||||
/* Session based interfaces require to make a distinction between IPC
|
||||
|
@ -59,7 +59,7 @@ main (void)
|
||||
fail_if_err (err);
|
||||
|
||||
while (gpgme_wait (ctx, &err, 0) == NULL && err == 0)
|
||||
sleep(1);
|
||||
;
|
||||
|
||||
if (gpgme_err_code (err) != GPG_ERR_NO_DATA)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user