Compare commits

...

10 Commits

Author SHA1 Message Date
Werner Koch
b2749d77da
core: Make the refactored user event loop work.
* src/wait.c (user_io_cb_handler): Set FD signaled.
--

With this patch the regression test suite now passes.

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-14 13:53:43 +02:00
Werner Koch
0378250846
core: Make the refactored global wait work.
* src/fdtable.h (FDTABLE_FLAG_NOT_DONE): New flag.
* src/fdtable.c (_gpgme_fdtable_io_cb_count): Rename to ...
(_gpgme_fdtable_get_count): this and add arg 'flags'.
(_gpgme_fdtable_run_io_cbs): Add return arg 'r_owner'.
* src/wait.c (gpgme_wait_ext): Improve tracing.  Act upon error codes
from running the callbacks.
* tests/gpg/t-wait.c (main): Remove the sleep.
--

Now t-wait works again.  There are still problems with the user
defined events but it is another step in our refactoring work.

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-14 11:10:40 +02:00
Werner Koch
153f1128f8
core: Use a better name for the internal synchronous wait.
* src/wait.c (_gpgme_wait_on_condition): Rename to ...
(_gpgme_sync_wait): this.  Small code refactoring.
(_gpgme_wait_one, _gpgme_wait_one_ext): Remove and change all callers
to use _gpgme_sync_wait.

* src/fdtable.c (_gpgme_fdtable_run_io_cbs): Move TRACE up.
--

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-14 10:16:06 +02:00
Werner Koch
28e620fa16
core: Refactor the wait code utilizing the new fdtable.
* src/fdtable.c, src/fdtable.h: Largely extend.
* src/wait-global.c, src/wait-private.c, src/wait-user.c: Remove and
move code to ...
* src/wait.c: here.
(_gpgme_fd_table_init, fd_table_put): Remove.  Do not call them.
(_gpgme_add_io_cb, _gpgme_add_io_cb_user): Change to use the fdtable.
(_gpgme_remove_io_cb, _gpgme_remove_io_cb_user):  Ditto.
(_gpgme_wait_global_event_cb):  Ditto.
(gpgme_wait_ext, _gpgme_wait_on_condition): Ditto.
* src/wait.h (struct io_cb_tag_s): Add fields 'serial' and 'desc'.
Change 'idx' to 'fd'.
(struct fd_table): Remove.
* src/context.h (struct gpgme_context): Remoce 'fdt'.  Rename io_cbs
to user_io_cbs for clarity.
* src/engine-gpgsm.c: Unify trace output.
(start): Pass a description along with the IO handlers.
* src/priv-io.h (struct io_select_fd_s): Rename to io_select_s.
(io_select_t): New.
* src/gpgme.c (_gpgme_cancel_with_err): Replace arg 'ctx' by 'serial'.
(gpgme_cancel): Adjust.
--

This is the second part of a larger refactoring of the wait/event
code.  Does currently only work on Unix and with the private wait
functions (i.e. the async operations don't yet work).

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-13 08:40:33 +02:00
Werner Koch
ef50bffc71
core: Use fully correct command args for gpg --verify.
* src/engine-gpg.c (gpg_verify): Supply --verify for fixed gpg
versions.
--

With 2.1.something we print a warning if no explict command was
given.  Since 2.1.16 this has been fixed to also allow the combination
of --verify and --output.  Thus we now always use "--verify".

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-12 12:54:11 +02:00
Werner Koch
3b32f7a97f
core: At debug levels up to 9 print only an ascii dump.
* src/debug.c (_gpgme_debug_buffer): Switch between two output
formats.
--

The new format is much more practical than the bunch of hex digits
followed by just 16 ascii chars.  To get the old behaviour use a debug
level of 10.

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-12 12:31:44 +02:00
Werner Koch
2a3cdb3e81
core: Improve code by using strconcat at two places.
* src/engine-gpgsm.c (gpgsm_export): Use _gpgme_strconcat.
(gpgsm_keylist): Ditto.
--

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-12 10:50:35 +02:00
Werner Koch
5cfdf878fb
core: Link all context objects and add _gpgme_get_ctx.
* src/context.h (struct gpgme_context): Add field 'next_ctx'.
* src/gpgme.c (def_lc_lock): Replace by ...
(context_list_lock): new.
(context_list): New variable.
(gpgme_new): Add new context object to the list.
(gpgme_release): Remove context object from the list.
(_gpgme_get_ctx): New function.
--

To allow mapping a context serial number back to a context object and
to check whether a serialno has still a context object, we need to
link them all together.  We already take a lock to setup the locale
and thus the only overhead is in freeing the context.

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-12 10:50:30 +02:00
Werner Koch
92883efe71
core: Introduce a context serial number.
* src/context.h: Include stdint.h.
(struct gpgme_context): Add field 'serial'.
(CTXSERIAL): New.
* src/gpgme.c: (last_ctx_serial): New.
(gpgme_new): Set serial.

* src/gpgme.c: Change trace calls to use the serial.
* src/wait.c (_gpgme_add_io_cb): Ditto
(_gpgme_remove_io_cb): Ditto.

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-07 12:10:40 +02:00
Werner Koch
52d8ed8dfb
core: Replace the posix close notify mechanism by a new generic one.
* src/fdtable.c, src/fdtable.h: New.
* src/posix-io.c (notify_table_item_s): Remove.
(notify_table, notify_table_size, notify_table_lock): Remove.
(_gpgme_io_pipe): Put new fds into the table.
(_gpgme_io_dup): Ditto.
(_gpgme_io_close): Replace notify stuff by a call to the fdtable.
(_gpgme_io_set_close_notify): Remove.  Change all callers to to use
_gpgme_fdtable_add_close_notify.
* src/Makefile.am (main_sources): Add new files.
--

This is the first part or a larger change to unify the tracking of
file descriptors.  Right now this has only been implemented for
Posix and thus the code will not yet build for Windows.

Signed-off-by: Werner Koch <wk@gnupg.org>
2019-06-07 11:17:53 +02:00
45 changed files with 1744 additions and 1283 deletions

View File

@ -74,7 +74,8 @@ main_sources = \
data-estream.c \ data-estream.c \
data-compat.c data-identify.c \ data-compat.c data-identify.c \
signers.c sig-notation.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 \ op-support.c \
encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \ encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \
sign.c passphrase.c progress.c \ sign.c passphrase.c progress.c \

View File

@ -21,6 +21,8 @@
#ifndef CONTEXT_H #ifndef CONTEXT_H
#define CONTEXT_H #define CONTEXT_H
#include <stdint.h>
#include "gpgme.h" #include "gpgme.h"
#include "engine.h" #include "engine.h"
#include "wait.h" #include "wait.h"
@ -71,13 +73,25 @@ struct ctx_op_data
}; };
typedef struct ctx_op_data *ctx_op_data_t; typedef struct ctx_op_data *ctx_op_data_t;
/* The context defines an environment in which crypto operations can /* The context defines an environment in which crypto operations can
be performed (sequentially). */ * be performed (sequentially). */
struct gpgme_context struct gpgme_context
{ {
DECLARE_LOCK (lock); 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. */ /* True if the context was canceled asynchronously. */
int canceled; int canceled;
@ -180,10 +194,21 @@ struct gpgme_context
gpgme_status_cb_t status_cb; gpgme_status_cb_t status_cb;
void *status_cb_value; void *status_cb_value;
/* A list of file descriptors in active use by the current /* User specific I/O callbacks. */
operation. */ struct gpgme_io_cbs user_io_cbs;
struct fd_table fdt;
struct gpgme_io_cbs 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 */ #endif /* CONTEXT_H */

View File

@ -403,6 +403,8 @@ _gpgme_debug_buffer (int lvl, const char *const fmt,
if (!buffer) if (!buffer)
return; return;
if (lvl > 9)
{
while (idx < len) while (idx < len)
{ {
char str[51]; 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); _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);
}
}
}

View File

@ -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); err = decrypt_verify_start (ctx, 1, GPGME_DECRYPT_VERIFY, cipher, plain);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
ctx->ignore_mdc_error = 0; /* Always reset. */ ctx->ignore_mdc_error = 0; /* Always reset. */
return TRACE_ERR (err); return TRACE_ERR (err);
} }
@ -177,7 +177,7 @@ gpgme_op_decrypt_ext (gpgme_ctx_t ctx,
else else
err = _gpgme_decrypt_start (ctx, 1, flags, cipher, plain); err = _gpgme_decrypt_start (ctx, 1, flags, cipher, plain);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
ctx->ignore_mdc_error = 0; /* Always reset. */ ctx->ignore_mdc_error = 0; /* Always reset. */
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -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); err = _gpgme_decrypt_start (ctx, 1, 0, cipher, plain);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
ctx->ignore_mdc_error = 0; /* Always reset. */ ctx->ignore_mdc_error = 0; /* Always reset. */
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -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, err = delete_start (ctx, 1, key,
allow_secret ? GPGME_DELETE_ALLOW_SECRET : 0); allow_secret ? GPGME_DELETE_ALLOW_SECRET : 0);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return err; 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); err = delete_start (ctx, 1, key, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return err; return err;
} }

View File

@ -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); err = interact_start (ctx, 1, key, flags, fnc, fnc_value, out);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return err; 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); err = edit_start (ctx, 1, 0, key, fnc, fnc_value, out);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); 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); err = edit_start (ctx, 1, 1, key, fnc, fnc_value, out);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -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); err = encrypt_sign_start (ctx, 1, recp, recpstring, flags, plain, cipher);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -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); err = encrypt_start (ctx, 1, recp, recpstring, flags, plain, cipher);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -145,7 +145,7 @@ llass_get_req_version (void)
} }
static void static gpg_error_t
close_notify_handler (int fd, void *opaque) close_notify_handler (int fd, void *opaque)
{ {
engine_llass_t llass = 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.fd = -1;
llass->status_cb.tag = NULL; 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) if (llass->status_cb.fd < 0)
return gpg_error_from_syserror (); 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)) close_notify_handler, llass))
{ {
_gpgme_io_close (llass->status_cb.fd); _gpgme_io_close (llass->status_cb.fd);

View File

@ -21,6 +21,7 @@
#define ENGINE_BACKEND_H #define ENGINE_BACKEND_H
#include "engine.h" #include "engine.h"
#include "fdtable.h"
struct engine_ops struct engine_ops
{ {

View File

@ -111,7 +111,7 @@ g13_get_req_version (void)
} }
static void static gpg_error_t
close_notify_handler (int fd, void *opaque) close_notify_handler (int fd, void *opaque)
{ {
engine_g13_t g13 = 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.fd = -1;
g13->status_cb.tag = NULL; 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) if (g13->status_cb.fd < 0)
return gpg_error_from_syserror (); 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)) close_notify_handler, g13))
{ {
_gpgme_io_close (g13->status_cb.fd); _gpgme_io_close (g13->status_cb.fd);

View File

@ -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) close_notify_handler (int fd, void *opaque)
{ {
engine_gpg_t gpg = 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 /* 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 (); rc = gpg_error_from_syserror ();
goto leave; 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) 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)) close_notify_handler, gpg))
{ {
rc = gpg_error (GPG_ERR_GENERAL); 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; gpg->colon.buffer = NULL;
return saved_err; return saved_err;
} }
if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg) if (_gpgme_fdtable_add_close_notify (gpg->colon.fd[0],
|| _gpgme_io_set_close_notify (gpg->colon.fd[1], close_notify_handler, gpg)
|| _gpgme_fdtable_add_close_notify (gpg->colon.fd[1],
close_notify_handler, gpg)) close_notify_handler, gpg))
return gpg_error (GPG_ERR_GENERAL); return gpg_error (GPG_ERR_GENERAL);
gpg->colon.eof = 0; gpg->colon.eof = 0;
@ -1112,9 +1114,9 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
free_argv (argv); free_argv (argv);
return saved_err; return saved_err;
} }
if (_gpgme_io_set_close_notify (fds[0], if (_gpgme_fdtable_add_close_notify (fds[0],
close_notify_handler, gpg) close_notify_handler, gpg)
|| _gpgme_io_set_close_notify (fds[1], || _gpgme_fdtable_add_close_notify (fds[1],
close_notify_handler, close_notify_handler,
gpg)) gpg))
{ {
@ -3276,6 +3278,8 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
err = add_arg (gpg, "-"); err = add_arg (gpg, "-");
if (!err) if (!err)
err = add_input_size_hint (gpg, sig); err = add_input_size_hint (gpg, sig);
if (!err && have_gpg_version (gpg, "2.1.16"))
err = add_arg (gpg, "--verify");
if (!err) if (!err)
err = add_arg (gpg, "--"); err = add_arg (gpg, "--");
if (!err) if (!err)

View File

@ -138,11 +138,13 @@ gpgsm_get_req_version (void)
} }
static void static gpg_error_t
close_notify_handler (int fd, void *opaque) close_notify_handler (int fd, void *opaque)
{ {
engine_gpgsm_t gpgsm = opaque; engine_gpgsm_t gpgsm = opaque;
TRACE_BEG (DEBUG_SYSIO, "gpgsm:close_notify_handler", NULL,
"fd=%d", fd);
assert (fd != -1); assert (fd != -1);
if (gpgsm->status_cb.fd == fd) 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 * The status fd however is closed right after it received the
* "OK" from the command. So we use this event to also close * "OK" from the command. So we use this event to also close
* the diag fd. */ * the diag fd. */
TRACE_LOG ("closing diag fd");
_gpgme_io_close (gpgsm->diag_cb.fd); _gpgme_io_close (gpgsm->diag_cb.fd);
} }
else if (gpgsm->input_cb.fd == fd) else if (gpgsm->input_cb.fd == fd)
@ -174,6 +177,7 @@ close_notify_handler (int fd, void *opaque)
free (gpgsm->input_helper_memory); free (gpgsm->input_helper_memory);
gpgsm->input_helper_memory = NULL; gpgsm->input_helper_memory = NULL;
} }
TRACE_LOG ("ready with input_fd");
} }
else if (gpgsm->output_cb.fd == 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->io_cbs.remove) (gpgsm->output_cb.tag);
gpgsm->output_cb.fd = -1; gpgsm->output_cb.fd = -1;
gpgsm->output_cb.tag = NULL; gpgsm->output_cb.tag = NULL;
TRACE_LOG ("ready with output_fd");
} }
else if (gpgsm->message_cb.fd == 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->io_cbs.remove) (gpgsm->message_cb.tag);
gpgsm->message_cb.fd = -1; gpgsm->message_cb.fd = -1;
gpgsm->message_cb.tag = NULL; gpgsm->message_cb.tag = NULL;
TRACE_LOG ("ready with message_fd");
} }
else if (gpgsm->diag_cb.fd == 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->io_cbs.remove) (gpgsm->diag_cb.tag);
gpgsm->diag_cb.fd = -1; gpgsm->diag_cb.fd = -1;
gpgsm->diag_cb.tag = NULL; 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 !USE_DESCRIPTOR_PASSING
if (!err if (!err
&& (_gpgme_io_set_close_notify (gpgsm->input_cb.fd, && (_gpgme_fdtable_add_close_notify (gpgsm->input_cb.fd,
close_notify_handler, gpgsm) 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) 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))) close_notify_handler, gpgsm)))
{ {
err = gpg_error (GPG_ERR_GENERAL); err = gpg_error (GPG_ERR_GENERAL);
goto leave; goto leave;
} }
#endif #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)) close_notify_handler, gpgsm))
{ {
err = gpg_error (GPG_ERR_GENERAL); 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->fd = dir ? fds[0] : fds[1];
iocb_data->server_fd = dir ? fds[1] : fds[0]; 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)) close_notify_handler, gpgsm))
{ {
err = gpg_error (GPG_ERR_GENERAL); err = gpg_error (GPG_ERR_GENERAL);
@ -901,7 +910,7 @@ status_handler (void *opaque, int fd)
/* Try our best to terminate the connection friendly. */ /* Try our best to terminate the connection friendly. */
/* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, 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)); fd, err, gpg_strerror (err));
} }
else if (linelen >= 3 else if (linelen >= 3
@ -913,7 +922,7 @@ status_handler (void *opaque, int fd)
if (! err) if (! err)
err = gpg_error (GPG_ERR_GENERAL); err = gpg_error (GPG_ERR_GENERAL);
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, 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"); fd, err ? gpg_strerror (err) : "ok");
/* Try our best to terminate the connection friendly. */ /* Try our best to terminate the connection friendly. */
/* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ /* 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); err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
} }
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, 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"); fd, err ? gpg_strerror (err) : "ok");
_gpgme_io_close (gpgsm->status_cb.fd); _gpgme_io_close (gpgsm->status_cb.fd);
return err; return err;
@ -1017,7 +1026,7 @@ status_handler (void *opaque, int fd)
} }
} }
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, 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"); fd, err? gpg_strerror (err):"ok");
} }
else if (linelen > 2 else if (linelen > 2
@ -1059,7 +1068,7 @@ status_handler (void *opaque, int fd)
} }
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, 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"); fd, err? gpg_strerror (err):"ok");
} }
else if (linelen > 2 else if (linelen > 2
@ -1097,7 +1106,7 @@ status_handler (void *opaque, int fd)
else else
fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm, 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"); fd, line+2, err? gpg_strerror (err):"ok");
} }
else if (linelen >= 7 else if (linelen >= 7
@ -1122,12 +1131,14 @@ status_handler (void *opaque, int fd)
static gpgme_error_t 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; gpgme_error_t err;
TRACE_BEG (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm, TRACE_BEG (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", NULL,
"fd=%d, dir %d", iocbd->fd, iocbd->dir); "fd=%d, dir %d (%s-handler)",
iocbd->fd, iocbd->dir, handler_desc);
err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv, err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
iocbd->fd, iocbd->dir, iocbd->fd, iocbd->dir,
handler, iocbd->data, &iocbd->tag); handler, iocbd->data, &iocbd->tag);
@ -1188,7 +1199,7 @@ start (engine_gpgsm_t gpgsm, const char *command)
if (gpgsm->status_cb.fd < 0) if (gpgsm->status_cb.fd < 0)
return gpg_error_from_syserror (); 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)) close_notify_handler, gpgsm))
{ {
_gpgme_io_close (gpgsm->status_cb.fd); _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); 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) 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) 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) 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) 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) if (!err)
err = assuan_write_line (gpgsm->assuan_ctx, command); err = assuan_write_line (gpgsm->assuan_ctx, command);
@ -1296,6 +1311,7 @@ gpgsm_delete (void *engine, gpgme_key_t key, unsigned int flags)
} }
length++; length++;
/* Fixme: Ugly quoting code below - use some standard function. */
line = malloc (length); line = malloc (length);
if (!line) if (!line)
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
@ -1351,6 +1367,7 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
int invalid_recipients = 0; int invalid_recipients = 0;
int i; int i;
/* FIXME: Uyse a membuf etc to build up LINE. */
linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */ linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
line = malloc (10 + 40 + 1); line = malloc (10 + 40 + 1);
if (!line) if (!line)
@ -1508,6 +1525,7 @@ gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
engine_gpgsm_t gpgsm = engine; engine_gpgsm_t gpgsm = engine;
gpgme_error_t err = 0; gpgme_error_t err = 0;
char *cmd; char *cmd;
const char *s1, *s2;
if (!gpgsm) if (!gpgsm)
return gpg_error (GPG_ERR_INV_VALUE); 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) if (!pattern)
pattern = ""; pattern = "";
cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1); s1 = s2 = "";
if (!cmd)
return gpg_error_from_syserror ();
strcpy (cmd, "EXPORT ");
if ((mode & GPGME_EXPORT_MODE_SECRET)) if ((mode & GPGME_EXPORT_MODE_SECRET))
{ {
strcat (cmd, "--secret "); s1 = "--secret ";
if ((mode & GPGME_EXPORT_MODE_RAW)) if ((mode & GPGME_EXPORT_MODE_RAW))
strcat (cmd, "--raw "); s2 = "--raw ";
else if ((mode & GPGME_EXPORT_MODE_PKCS12)) 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; gpgsm->output_cb.data = keydata;
err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" 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) if (!gpgsm)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
/*FIXME: Replace by membuf and a quoting function. */
if (pattern && *pattern) if (pattern && *pattern)
{ {
const char **pat = pattern; const char **pat = pattern;
@ -1859,21 +1877,12 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
"OPTION offline=0" , "OPTION offline=0" ,
NULL, NULL); NULL, NULL);
if (secret_only)
/* Length is "LISTSECRETKEYS " + p + '\0'. */ line = _gpgme_strconcat ("LISTSECRETKEYS ", pattern, NULL);
line = malloc (15 + strlen (pattern) + 1); else
line = _gpgme_strconcat ("LISTKEYS ", pattern, NULL);
if (!line) if (!line)
return gpg_error_from_syserror (); 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, INPUT_FD);
gpgsm_clear_fd (gpgsm, OUTPUT_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" , "OPTION offline=0" ,
NULL, NULL); NULL, NULL);
/* FIXME: Repalce by membuf and quoting function. */
if (pattern && *pattern) if (pattern && *pattern)
{ {
const char **pat = pattern; const char **pat = pattern;

View File

@ -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) close_notify_handler (int fd, void *opaque)
{ {
engine_spawn_t esp = 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; esp->fd_data_map = NULL;
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
} }
if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, esp) if (_gpgme_fdtable_add_close_notify (fds[0],
|| _gpgme_io_set_close_notify (fds[1], close_notify_handler, esp)) close_notify_handler, esp)
|| _gpgme_fdtable_add_close_notify (fds[1],
close_notify_handler, esp))
{ {
/* FIXME: Need error cleanup. */ /* FIXME: Need error cleanup. */
return gpg_error (GPG_ERR_GENERAL); return gpg_error (GPG_ERR_GENERAL);

View File

@ -135,7 +135,7 @@ uiserver_get_req_version (void)
} }
static void static gpg_error_t
close_notify_handler (int fd, void *opaque) close_notify_handler (int fd, void *opaque)
{ {
engine_uiserver_t uiserver = 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.fd = -1;
uiserver->message_cb.tag = NULL; 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->fd = dir ? fds[0] : fds[1];
iocb_data->server_fd = dir ? fds[1] : fds[0]; 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)) close_notify_handler, uiserver))
{ {
err = gpg_error (GPG_ERR_GENERAL); err = gpg_error (GPG_ERR_GENERAL);
@ -921,7 +923,7 @@ start (engine_uiserver_t uiserver, const char *command)
if (uiserver->status_cb.fd < 0) if (uiserver->status_cb.fd < 0)
return gpg_error_from_syserror (); 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)) close_notify_handler, uiserver))
{ {
_gpgme_io_close (uiserver->status_cb.fd); _gpgme_io_close (uiserver->status_cb.fd);

View File

@ -202,7 +202,7 @@ gpgme_op_export (gpgme_ctx_t ctx, const char *pattern,
err = export_start (ctx, 1, pattern, mode, keydata); err = export_start (ctx, 1, pattern, mode, keydata);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return err; 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); err = export_ext_start (ctx, 1, pattern, mode, keydata);
if (!err) if (!err)
{ {
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
if (!err) if (!err)
{ {
/* For this synchronous operation we check for operational /* 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); err = export_keys_start (ctx, 1, keys, mode, keydata);
if (!err) if (!err)
{ {
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
if (!err) if (!err)
{ {
/* For this synchronous operation we check for operational /* For this synchronous operation we check for operational
@ -478,4 +478,3 @@ gpgme_op_export_keys (gpgme_ctx_t ctx,
return TRACE_ERR (err); return TRACE_ERR (err);
} }

714
src/fdtable.c Normal file
View 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
View 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*/

View File

@ -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); err = genkey_start (ctx, 1, parms, pubkey, seckey);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); 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, err = createkey_start (ctx, 1,
userid, algo, reserved, expires, anchorkey, flags); userid, algo, reserved, expires, anchorkey, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); 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); err = createsubkey_start (ctx, 1, key, algo, reserved, expires, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }
@ -571,7 +571,7 @@ gpgme_op_adduid (gpgme_ctx_t ctx,
err = addrevuid_start (ctx, 1, 0, key, userid, flags); err = addrevuid_start (ctx, 1, 0, key, userid, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); 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); err = addrevuid_start (ctx, 1, GENKEY_EXTRAFLAG_REVOKE, key, userid, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); 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); return err = gpg_error (GPG_ERR_UNKNOWN_NAME);
if (synchronous && !err) if (synchronous && !err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -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); err = getauditlog_start (ctx, 1, output, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -1,7 +1,7 @@
/* gpgme.c - GnuPG Made Easy. /* gpgme.c - GnuPG Made Easy.
* Copyright (C) 2000 Werner Koch (dd9jn) * Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012, * 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. * This file is part of GPGME.
* *
@ -42,18 +42,32 @@
#include "mbox-util.h" #include "mbox-util.h"
/* The default locale. */ /* The lock used to protect access to the default locale, the global
DEFINE_STATIC_LOCK (def_lc_lock); * 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_ctype;
static char *def_lc_messages; 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; gpgme_error_t _gpgme_selftest = GPG_ERR_NOT_OPERATIONAL;
/* Protects all reference counters in result structures. All other /* 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); DEFINE_STATIC_LOCK (result_ref_lock);
/* Set the global flag NAME to VALUE. Return 0 on success. Note that /* 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 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_error_t err;
gpgme_ctx_t ctx; gpgme_ctx_t ctx;
TRACE_BEG (DEBUG_CTX, "gpgme_new", r_ctx, ""); TRACE_BEG (DEBUG_CTX, "gpgme_new", NULL, "");
if (_gpgme_selftest) if (_gpgme_selftest)
return TRACE_ERR (_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->include_certs = GPGME_INCLUDE_CERTS_DEFAULT;
ctx->protocol = GPGME_PROTOCOL_OpenPGP; ctx->protocol = GPGME_PROTOCOL_OpenPGP;
ctx->sub_protocol = GPGME_PROTOCOL_DEFAULT; ctx->sub_protocol = GPGME_PROTOCOL_DEFAULT;
_gpgme_fd_table_init (&ctx->fdt);
LOCK (def_lc_lock); LOCK (context_list_lock);
if (def_lc_ctype) if (def_lc_ctype)
{ {
ctx->lc_ctype = strdup (def_lc_ctype); ctx->lc_ctype = strdup (def_lc_ctype);
if (!ctx->lc_ctype) if (!ctx->lc_ctype)
{ {
int saved_err = gpg_error_from_syserror (); int saved_err = gpg_error_from_syserror ();
UNLOCK (def_lc_lock); UNLOCK (context_list_lock);
_gpgme_engine_info_release (ctx->engine_info); _gpgme_engine_info_release (ctx->engine_info);
free (ctx); free (ctx);
return TRACE_ERR (saved_err); return TRACE_ERR (saved_err);
@ -145,7 +158,7 @@ gpgme_new (gpgme_ctx_t *r_ctx)
if (!ctx->lc_messages) if (!ctx->lc_messages)
{ {
int saved_err = gpg_error_from_syserror (); int saved_err = gpg_error_from_syserror ();
UNLOCK (def_lc_lock); UNLOCK (context_list_lock);
if (ctx->lc_ctype) if (ctx->lc_ctype)
free (ctx->lc_ctype); free (ctx->lc_ctype);
_gpgme_engine_info_release (ctx->engine_info); _gpgme_engine_info_release (ctx->engine_info);
@ -155,44 +168,92 @@ gpgme_new (gpgme_ctx_t *r_ctx)
} }
else else
def_lc_messages = NULL; 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; *r_ctx = ctx;
TRACE_SUC ("ctx=%p", ctx); TRACE_SUC ("ctx=%lu (%p)", CTXSERIAL(ctx), ctx);
return 0; 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_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) gpg_error_t op_err)
{ {
gpgme_error_t err; gpgme_error_t err;
gpgme_ctx_t ctx;
struct gpgme_io_event_done_data data; struct gpgme_io_event_done_data data;
TRACE_BEG (DEBUG_CTX, "_gpgme_cancel_with_err", ctx, "ctx_err=%i, op_err=%i", TRACE_BEG (DEBUG_CTX, "_gpgme_cancel_with_err", NULL,
ctx_err, op_err); "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); err = _gpgme_engine_cancel (ctx->engine);
if (err)
return TRACE_ERR (err);
}
else else
{
err = _gpgme_engine_cancel_op (ctx->engine); err = _gpgme_engine_cancel_op (ctx->engine);
if (err)
return TRACE_ERR (err);
}
if (!err)
{
data.err = ctx_err; data.err = ctx_err;
data.op_err = op_err; data.op_err = op_err;
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data); _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; gpg_error_t err;
TRACE_BEG (DEBUG_CTX, "gpgme_cancel", ctx, ""); TRACE_BEG (DEBUG_CTX, "gpgme_cancel", NULL, "ctx=%lu", CTXSERIAL (ctx));
if (!ctx) if (!ctx)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); 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); return TRACE_ERR (err);
} }
@ -217,7 +278,8 @@ gpgme_cancel (gpgme_ctx_t ctx)
gpgme_error_t gpgme_error_t
gpgme_cancel_async (gpgme_ctx_t ctx) 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) if (!ctx)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
@ -234,14 +296,34 @@ gpgme_cancel_async (gpgme_ctx_t ctx)
void void
gpgme_release (gpgme_ctx_t ctx) gpgme_release (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_release", ctx, ""); TRACE (DEBUG_CTX, "gpgme_release", NULL, "ctx=%lu", CTXSERIAL (ctx));
if (!ctx) if (!ctx)
return; 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); _gpgme_engine_release (ctx->engine);
ctx->engine = NULL; ctx->engine = NULL;
_gpgme_fd_table_deinit (&ctx->fdt); /* FIXME: Remove stale FDs belonging to us? */
_gpgme_release_result (ctx); _gpgme_release_result (ctx);
_gpgme_signers_clear (ctx); _gpgme_signers_clear (ctx);
_gpgme_sig_notation_clear (ctx); _gpgme_sig_notation_clear (ctx);
@ -325,8 +407,8 @@ _gpgme_release_result (gpgme_ctx_t ctx)
gpgme_error_t gpgme_error_t
gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol) gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
{ {
TRACE_BEG (DEBUG_CTX, "gpgme_set_protocol", ctx, "protocol=%i (%s)", TRACE_BEG (DEBUG_CTX, "gpgme_set_protocol", NULL, "ctx=%lu protocol=%i (%s)",
protocol, gpgme_get_protocol_name (protocol) CTXSERIAL (ctx), protocol, gpgme_get_protocol_name (protocol)
? gpgme_get_protocol_name (protocol) : "invalid"); ? gpgme_get_protocol_name (protocol) : "invalid");
if (protocol != GPGME_PROTOCOL_OpenPGP if (protocol != GPGME_PROTOCOL_OpenPGP
@ -360,8 +442,8 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
gpgme_protocol_t gpgme_protocol_t
gpgme_get_protocol (gpgme_ctx_t ctx) gpgme_get_protocol (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_protocol", ctx, TRACE (DEBUG_CTX, "gpgme_get_protocol", NULL,
"ctx->protocol=%i (%s)", ctx->protocol, "ctx=%lu protocol=%i (%s)", CTXSERIAL (ctx), ctx->protocol,
gpgme_get_protocol_name (ctx->protocol) gpgme_get_protocol_name (ctx->protocol)
? gpgme_get_protocol_name (ctx->protocol) : "invalid"); ? gpgme_get_protocol_name (ctx->protocol) : "invalid");
@ -372,8 +454,8 @@ gpgme_get_protocol (gpgme_ctx_t ctx)
gpgme_error_t gpgme_error_t
gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol) gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
{ {
TRACE (DEBUG_CTX, "gpgme_set_sub_protocol", ctx, "protocol=%i (%s)", TRACE (DEBUG_CTX, "gpgme_set_sub_protocol", NULL, "ctx=%lu protocol=%i (%s)",
protocol, gpgme_get_protocol_name (protocol) CTXSERIAL (ctx), protocol, gpgme_get_protocol_name (protocol)
? gpgme_get_protocol_name (protocol) : "invalid"); ? gpgme_get_protocol_name (protocol) : "invalid");
if (!ctx) if (!ctx)
@ -387,8 +469,9 @@ gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
gpgme_protocol_t gpgme_protocol_t
gpgme_get_sub_protocol (gpgme_ctx_t ctx) gpgme_get_sub_protocol (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_sub_protocol", ctx, TRACE (DEBUG_CTX, "gpgme_get_sub_protocol", NULL,
"ctx->sub_protocol=%i (%s)", ctx->sub_protocol, "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)
? gpgme_get_protocol_name (ctx->sub_protocol) : "invalid"); ? 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; char *p = NULL;
TRACE_BEG (DEBUG_CTX, "gpgme_set_sender", ctx, "sender='%s'", TRACE_BEG (DEBUG_CTX, "gpgme_set_sender", NULL, "ctx=%lu sender='%s'",
address?address:"(null)"); CTXSERIAL (ctx), address?address:"(null)");
if (!ctx || (address && !(p = _gpgme_mailbox_from_userid (address)))) if (!ctx || (address && !(p = _gpgme_mailbox_from_userid (address))))
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); 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 * const char *
gpgme_get_sender (gpgme_ctx_t ctx) gpgme_get_sender (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_sender", ctx, "sender='%s'", TRACE (DEBUG_CTX, "gpgme_get_sender", NULL, "ctx=%lu sender='%s'",
ctx?ctx->sender:""); CTXSERIAL (ctx), ctx?ctx->sender:"");
return ctx->sender; return ctx->sender;
} }
@ -473,8 +556,8 @@ gpgme_get_sender (gpgme_ctx_t ctx)
void void
gpgme_set_armor (gpgme_ctx_t ctx, int use_armor) gpgme_set_armor (gpgme_ctx_t ctx, int use_armor)
{ {
TRACE (DEBUG_CTX, "gpgme_set_armor", ctx, "use_armor=%i (%s)", TRACE (DEBUG_CTX, "gpgme_set_armor", NULL, "ctx=%lu use_armor=%i (%s)",
use_armor, use_armor ? "yes" : "no"); CTXSERIAL (ctx), use_armor, use_armor ? "yes" : "no");
if (!ctx) if (!ctx)
return; return;
@ -487,8 +570,8 @@ gpgme_set_armor (gpgme_ctx_t ctx, int use_armor)
int int
gpgme_get_armor (gpgme_ctx_t ctx) gpgme_get_armor (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_armor", ctx, "ctx->use_armor=%i (%s)", TRACE (DEBUG_CTX, "gpgme_get_armor", NULL, "ctx=%lu use_armor=%i (%s)",
ctx->use_armor, ctx->use_armor ? "yes" : "no"); CTXSERIAL (ctx), ctx->use_armor, ctx->use_armor ? "yes" : "no");
return ctx->use_armor; 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; gpgme_error_t err = 0;
int abool; int abool;
TRACE (DEBUG_CTX, "gpgme_set_ctx_flag", ctx, TRACE (DEBUG_CTX, "gpgme_set_ctx_flag", NULL,
"name='%s' value='%s'", "ctx=%lu name='%s' value='%s'",
name? name:"(null)", value?value:"(null)"); CTXSERIAL (ctx), name? name:"(null)", value?value:"(null)");
abool = (value && *value)? !!atoi (value) : 0; abool = (value && *value)? !!atoi (value) : 0;
@ -635,8 +718,8 @@ gpgme_get_ctx_flag (gpgme_ctx_t ctx, const char *name)
void void
gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode) gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode)
{ {
TRACE (DEBUG_CTX, "gpgme_set_textmode", ctx, "use_textmode=%i (%s)", TRACE (DEBUG_CTX, "gpgme_set_textmode", NULL, "ctx=%lu use_textmode=%i (%s)",
use_textmode, use_textmode ? "yes" : "no"); CTXSERIAL (ctx), use_textmode, use_textmode ? "yes" : "no");
if (!ctx) if (!ctx)
return; return;
@ -648,8 +731,8 @@ gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode)
int int
gpgme_get_textmode (gpgme_ctx_t ctx) gpgme_get_textmode (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_textmode", ctx, "ctx->use_textmode=%i (%s)", TRACE (DEBUG_CTX, "gpgme_get_textmode", NULL, "ctx=%lu use_textmode=%i (%s)",
ctx->use_textmode, ctx->use_textmode ? "yes" : "no"); CTXSERIAL (ctx), ctx->use_textmode, ctx->use_textmode ? "yes" : "no");
return ctx->use_textmode; return ctx->use_textmode;
} }
@ -659,8 +742,8 @@ gpgme_get_textmode (gpgme_ctx_t ctx)
void void
gpgme_set_offline (gpgme_ctx_t ctx, int offline) gpgme_set_offline (gpgme_ctx_t ctx, int offline)
{ {
TRACE (DEBUG_CTX, "gpgme_set_offline", ctx, "offline=%i (%s)", TRACE (DEBUG_CTX, "gpgme_set_offline", NULL, "ctx=%lu offline=%i (%s)",
offline, offline ? "yes" : "no"); CTXSERIAL (ctx), offline, offline ? "yes" : "no");
if (!ctx) if (!ctx)
return; return;
@ -672,8 +755,8 @@ gpgme_set_offline (gpgme_ctx_t ctx, int offline)
int int
gpgme_get_offline (gpgme_ctx_t ctx) gpgme_get_offline (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_offline", ctx, "ctx->offline=%i (%s)", TRACE (DEBUG_CTX, "gpgme_get_offline", NULL, "ctx=%lu offline=%i (%s)",
ctx->offline, ctx->offline ? "yes" : "no"); CTXSERIAL (ctx), ctx->offline, ctx->offline ? "yes" : "no");
return ctx->offline; return ctx->offline;
} }
@ -694,7 +777,8 @@ gpgme_set_include_certs (gpgme_ctx_t ctx, int nr_of_certs)
else else
ctx->include_certs = nr_of_certs; 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)"); 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 int
gpgme_get_include_certs (gpgme_ctx_t ctx) gpgme_get_include_certs (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_include_certs", ctx, "ctx->include_certs=%i", TRACE (DEBUG_CTX, "gpgme_get_include_certs", NULL, "ctx=%lu include_certs=%i",
ctx->include_certs); CTXSERIAL (ctx), ctx->include_certs);
return ctx->include_certs; return ctx->include_certs;
} }
@ -716,8 +800,8 @@ gpgme_get_include_certs (gpgme_ctx_t ctx)
gpgme_error_t gpgme_error_t
gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode) 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", TRACE (DEBUG_CTX, "gpgme_set_keylist_mode", NULL, "ctx=%lu keylist_mode=0x%x",
mode); CTXSERIAL (ctx), mode);
if (!ctx) if (!ctx)
return gpg_error (GPG_ERR_INV_VALUE); 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_keylist_mode_t
gpgme_get_keylist_mode (gpgme_ctx_t ctx) gpgme_get_keylist_mode (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_keylist_mode", ctx, TRACE (DEBUG_CTX, "gpgme_get_keylist_mode", NULL,
"ctx->keylist_mode=0x%x", ctx->keylist_mode); "ctx=%lu keylist_mode=0x%x", CTXSERIAL (ctx), ctx->keylist_mode);
return ctx->keylist_mode; return ctx->keylist_mode;
} }
@ -741,8 +825,8 @@ gpgme_get_keylist_mode (gpgme_ctx_t ctx)
gpgme_error_t gpgme_error_t
gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t mode) gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t mode)
{ {
TRACE (DEBUG_CTX, "gpgme_set_pinentry_mode", ctx, "pinentry_mode=%u", TRACE (DEBUG_CTX, "gpgme_set_pinentry_mode", NULL, "ctx=%lu pinentry_mode=%u",
(unsigned int)mode); CTXSERIAL (ctx), (unsigned int)mode);
if (!ctx) if (!ctx)
return gpg_error (GPG_ERR_INV_VALUE); 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_pinentry_mode_t
gpgme_get_pinentry_mode (gpgme_ctx_t ctx) gpgme_get_pinentry_mode (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_get_pinentry_mode", ctx, TRACE (DEBUG_CTX, "gpgme_get_pinentry_mode", NULL, "ctx=%lu pinentry_mode=%u",
"ctx->pinentry_mode=%u", (unsigned int)ctx->pinentry_mode); CTXSERIAL (ctx), (unsigned int)ctx->pinentry_mode);
return 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, gpgme_set_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb,
void *cb_value) void *cb_value)
{ {
TRACE (DEBUG_CTX, "gpgme_set_passphrase_cb", ctx, TRACE (DEBUG_CTX, "gpgme_set_passphrase_cb", NULL,
"passphrase_cb=%p/%p", cb, cb_value); "ctx=%lu passphrase_cb=%p/%p", CTXSERIAL (ctx), cb, cb_value);
if (!ctx) if (!ctx)
return; return;
@ -797,9 +881,9 @@ void
gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *r_cb, gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *r_cb,
void **r_cb_value) void **r_cb_value)
{ {
TRACE (DEBUG_CTX, "gpgme_get_passphrase_cb", ctx, TRACE (DEBUG_CTX, "gpgme_get_passphrase_cb", NULL,
"ctx->passphrase_cb=%p/%p", "ctx=%lu passphrase_cb=%p/%p",
ctx->passphrase_cb, ctx->passphrase_cb_value); CTXSERIAL (ctx), ctx->passphrase_cb, ctx->passphrase_cb_value);
if (r_cb) if (r_cb)
*r_cb = ctx->passphrase_cb; *r_cb = ctx->passphrase_cb;
if (r_cb_value) if (r_cb_value)
@ -812,8 +896,8 @@ gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *r_cb,
void void
gpgme_set_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t cb, void *cb_value) 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", TRACE (DEBUG_CTX, "gpgme_set_progress_cb", NULL, "ctx=%lu progress_cb=%p/%p",
cb, cb_value); CTXSERIAL (ctx), cb, cb_value);
if (!ctx) if (!ctx)
return; return;
@ -829,8 +913,8 @@ void
gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb, gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb,
void **r_cb_value) void **r_cb_value)
{ {
TRACE (DEBUG_CTX, "gpgme_get_progress_cb", ctx, "ctx->progress_cb=%p/%p", TRACE (DEBUG_CTX, "gpgme_get_progress_cb", NULL, "ctx=%lu progress_cb=%p/%p",
ctx->progress_cb, ctx->progress_cb_value); CTXSERIAL (ctx), ctx->progress_cb, ctx->progress_cb_value);
if (r_cb) if (r_cb)
*r_cb = ctx->progress_cb; *r_cb = ctx->progress_cb;
if (r_cb_value) if (r_cb_value)
@ -843,8 +927,8 @@ gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb,
void void
gpgme_set_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t cb, void *cb_value) 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", TRACE (DEBUG_CTX, "gpgme_set_status_cb", NULL, "ctx=%lu status_cb=%p/%p",
cb, cb_value); CTXSERIAL (ctx), cb, cb_value);
if (!ctx) if (!ctx)
return; return;
@ -860,7 +944,8 @@ void
gpgme_get_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t *r_cb, gpgme_get_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t *r_cb,
void **r_cb_value) 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); ctx ? ctx->status_cb : NULL, ctx ? ctx->status_cb_value : NULL);
if (r_cb) if (r_cb)
@ -888,21 +973,22 @@ gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs)
if (io_cbs) if (io_cbs)
{ {
TRACE (DEBUG_CTX, "gpgme_set_io_cbs", ctx, TRACE (DEBUG_CTX, "gpgme_set_io_cbs", NULL,
"io_cbs=%p (add=%p/%p, remove=%p, event=%p/%p", "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, io_cbs->add, io_cbs->add_priv, io_cbs->remove,
io_cbs->event, io_cbs->event_priv); io_cbs->event, io_cbs->event_priv);
ctx->io_cbs = *io_cbs; ctx->user_io_cbs = *io_cbs;
} }
else else
{ {
TRACE (DEBUG_CTX, "gpgme_set_io_cbs", ctx, TRACE (DEBUG_CTX, "gpgme_set_io_cbs", NULL,
"io_cbs=%p (default)", io_cbs); "ctx=%lu io_cbs=%p (default)", CTXSERIAL (ctx), io_cbs);
ctx->io_cbs.add = NULL; ctx->user_io_cbs.add = NULL;
ctx->io_cbs.add_priv = NULL; ctx->user_io_cbs.add_priv = NULL;
ctx->io_cbs.remove = NULL; ctx->user_io_cbs.remove = NULL;
ctx->io_cbs.event = NULL; ctx->user_io_cbs.event = NULL;
ctx->io_cbs.event_priv = 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) gpgme_io_read (int fd, void *buffer, size_t count)
{ {
int ret; int ret;
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_read", fd, TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_read", NULL,
"buffer=%p, count=%zu", buffer, count); "fd=%d buffer=%p count=%zu", fd, buffer, count);
ret = _gpgme_io_read (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) gpgme_io_write (int fd, const void *buffer, size_t count)
{ {
int ret; int ret;
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_write", fd, TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_write", NULL,
"buffer=%p, count=%zu", buffer, count); "fd=%d buffer=%p count=%zu", fd, buffer, count);
ret = _gpgme_io_write (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; const char *buffer = buffer_arg;
int ret = 0; int ret = 0;
TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_writen", fd, TRACE_BEG (DEBUG_GLOBAL, "gpgme_io_writen", NULL,
"buffer=%p, count=%zu", buffer, count); "fd=%d buffer=%p count=%zu", fd, buffer, count);
while (count) while (count)
{ {
ret = _gpgme_io_write (fd, buffer, 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 void
gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs) gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs)
{ {
TRACE (DEBUG_CTX, "gpgme_get_io_cbs", ctx, TRACE (DEBUG_CTX, "gpgme_get_io_cbs", NULL,
"io_cbs=%p, ctx->io_cbs.add=%p/%p, .remove=%p, .event=%p/%p", "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, io_cbs->add, io_cbs->add_priv, io_cbs->remove,
io_cbs->event, io_cbs->event_priv); 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_ctype = NULL;
char *new_lc_messages = NULL; char *new_lc_messages = NULL;
TRACE_BEG (DEBUG_CTX, "gpgme_set_locale", ctx, TRACE_BEG (DEBUG_CTX, "gpgme_set_locale", NULL,
"category=%i, value=%s", category, value ? value : "(null)"); "ctx=%lu category=%i value=%s",
CTXSERIAL (ctx), category, value ? value : "(null)");
#define PREPARE_ONE_LOCALE(lcat, ucat) \ #define PREPARE_ONE_LOCALE(lcat, ucat) \
if (!failed && value \ if (!failed && value \
@ -1034,7 +1123,7 @@ gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value)
} }
if (!ctx) if (!ctx)
LOCK (def_lc_lock); LOCK (context_list_lock);
#ifdef LC_CTYPE #ifdef LC_CTYPE
SET_ONE_LOCALE (ctype, CTYPE); SET_ONE_LOCALE (ctype, CTYPE);
#endif #endif
@ -1042,7 +1131,7 @@ gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value)
SET_ONE_LOCALE (messages, MESSAGES); SET_ONE_LOCALE (messages, MESSAGES);
#endif #endif
if (!ctx) if (!ctx)
UNLOCK (def_lc_lock); UNLOCK (context_list_lock);
return TRACE_ERR (0); 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_engine_info_t
gpgme_ctx_get_engine_info (gpgme_ctx_t ctx) gpgme_ctx_get_engine_info (gpgme_ctx_t ctx)
{ {
TRACE (DEBUG_CTX, "gpgme_ctx_get_engine_info", ctx, TRACE (DEBUG_CTX, "gpgme_ctx_get_engine_info", NULL,
"ctx->engine_info=%p", ctx->engine_info); "ctx=%lu engine_info=%p", CTXSERIAL (ctx), ctx->engine_info);
return 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) const char *file_name, const char *home_dir)
{ {
gpgme_error_t err; gpgme_error_t err;
TRACE_BEG (DEBUG_CTX, "gpgme_ctx_set_engine_info", ctx, TRACE_BEG (DEBUG_CTX, "gpgme_ctx_set_engine_info", NULL,
"protocol=%i (%s), file_name=%s, home_dir=%s", "ctx=%lu protocol=%i (%s), file_name=%s, home_dir=%s",
proto, gpgme_get_protocol_name (proto) CTXSERIAL (ctx), proto, gpgme_get_protocol_name (proto)
? gpgme_get_protocol_name (proto) : "unknown", ? gpgme_get_protocol_name (proto) : "unknown",
file_name ? file_name : "(default)", file_name ? file_name : "(default)",
home_dir ? home_dir : "(default)"); home_dir ? home_dir : "(default)");
@ -1112,7 +1201,8 @@ _gpgme_sig_notation_clear (gpgme_ctx_t ctx)
void void
gpgme_sig_notation_clear (gpgme_ctx_t ctx) 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) if (!ctx)
return; 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 notation;
gpgme_sig_notation_t *lastp; gpgme_sig_notation_t *lastp;
TRACE_BEG (DEBUG_CTX, "gpgme_sig_notation_add", ctx, TRACE_BEG (DEBUG_CTX, "gpgme_sig_notation_add", NULL,
"name=%s, value=%s, flags=0x%x", "ctx=%lu name=%s value=%s flags=0x%x",
CTXSERIAL (ctx),
name ? name : "(null)", value ? value : "(null)", name ? name : "(null)", value ? value : "(null)",
flags); flags);
@ -1167,11 +1258,12 @@ gpgme_sig_notation_get (gpgme_ctx_t ctx)
{ {
if (!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; return NULL;
} }
TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx, TRACE (DEBUG_CTX, "gpgme_sig_notation_get", NULL,
"ctx->sig_notations=%p", ctx->sig_notations); "ctx=%lu sig_notations=%p", CTXSERIAL (ctx), ctx->sig_notations);
return ctx->sig_notations; return ctx->sig_notations;
} }

View File

@ -316,7 +316,7 @@ gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
err = _gpgme_op_import_start (ctx, 1, keydata); err = _gpgme_op_import_start (ctx, 1, keydata);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); 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); err = _gpgme_op_import_keys_start (ctx, 1, keys);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -1216,7 +1216,7 @@ gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
if (!opd->key_queue) 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) if (err)
return TRACE_ERR (err); return TRACE_ERR (err);

View File

@ -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); err = keysign_start (ctx, 1, key, userid, expires, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -173,7 +173,7 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type)
return err; 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. */ /* Use private event loop. */
io_cbs.add = _gpgme_add_io_cb; 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 = _gpgme_wait_private_event_cb;
io_cbs.event_priv = ctx; io_cbs.event_priv = ctx;
} }
else if (! ctx->io_cbs.add) else if (!ctx->user_io_cbs.add)
{ {
/* Use global event loop. */ /* Use global event loop. */
io_cbs.add = _gpgme_add_io_cb; io_cbs.add = _gpgme_add_io_cb;
@ -194,9 +194,9 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type)
else else
{ {
/* Use user event loop. */ /* 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.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 = _gpgme_wait_user_event_cb;
io_cbs.event_priv = ctx; io_cbs.event_priv = ctx;
} }

View File

@ -136,7 +136,7 @@ gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx,
if (err) if (err)
goto out; goto out;
err = _gpgme_wait_one_ext (ctx, &op_err); err = _gpgme_sync_wait (ctx, NULL, &op_err);
if (op_err) if (op_err)
{ {
TRACE_LOG ("op_err = %s <%s>", gpgme_strerror (op_err), TRACE_LOG ("op_err = %s <%s>", gpgme_strerror (op_err),

View File

@ -26,9 +26,6 @@
#include "context.h" #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. */ /* Clear all notation data from the context. */
void _gpgme_sig_notation_clear (gpgme_ctx_t ctx); void _gpgme_sig_notation_clear (gpgme_ctx_t ctx);
@ -36,10 +33,9 @@ void _gpgme_release_result (gpgme_ctx_t ctx);
/* From wait.c. */ /* From wait.c. */
gpgme_error_t _gpgme_wait_one (gpgme_ctx_t ctx); gpgme_error_t _gpgme_sync_wait (gpgme_ctx_t ctx,
gpgme_error_t _gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err); volatile int *cond,
gpgme_error_t _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond, gpg_error_t *op_err);
gpgme_error_t *op_err);
/* From data.c. */ /* From data.c. */

View File

@ -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); err = passwd_start (ctx, 1, key, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -59,6 +59,7 @@
#include "priv-io.h" #include "priv-io.h"
#include "sema.h" #include "sema.h"
#include "ath.h" #include "ath.h"
#include "fdtable.h"
#include "debug.h" #include "debug.h"
@ -152,22 +153,6 @@ _gpgme_io_fd2str (char *buf, int buflen, int fd)
return snprintf (buf, buflen, "%d", 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 int
_gpgme_io_read (int fd, void *buffer, size_t count) _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 int
_gpgme_io_pipe (int filedes[2], int inherit_idx) _gpgme_io_pipe (int filedes[2], int inherit_idx)
{ {
gpg_error_t err;
int res;
int saved_errno; int saved_errno;
int err; int i;
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", NULL, TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", NULL,
"inherit_idx=%i (GPGME uses it for %s)", "inherit_idx=%i (GPGME uses it for %s)",
inherit_idx, inherit_idx ? "reading" : "writing"); inherit_idx, inherit_idx ? "reading" : "writing");
err = pipe (filedes); res = pipe (filedes);
if (err < 0) if (res < 0)
return TRACE_SYSRES (err); return TRACE_SYSRES (res);
/* FIXME: Should get the old flags first. */ /* 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; saved_errno = errno;
if (err < 0) if (res < 0)
{ {
close (filedes[0]); close (filedes[0]);
close (filedes[1]); close (filedes[1]);
} }
errno = saved_errno; errno = saved_errno;
if (res)
return TRACE_SYSRES (res);
for (i=0; i < 2; i++)
{
err = _gpgme_fdtable_insert (filedes[i]);
if (err) 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]); TRACE_SUC ("read fd=%d write fd=%d", filedes[0], filedes[1]);
return 0; return 0;
@ -238,38 +239,23 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx)
int int
_gpgme_io_close (int fd) _gpgme_io_close (int fd)
{ {
gpg_error_t err;
int res; 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); TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", NULL, "fd=%d", fd);
if (fd == -1) if (fd == -1)
{ return TRACE_SYSRES (0); /* Igore invalid FDs. */
errno = EINVAL;
return TRACE_SYSRES (-1);
}
/* First call the notify handler. */ /* First remove from the table which also runs the close handlers.
LOCK (notify_table_lock); * Having the FD not (yet) in the table is possible and thus we
for (idx=0; idx < notify_table_size; idx++) * 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) TRACE_LOG ("fdtable_remove failed for fd=%d: %s\n",
{ fd, gpg_strerror (err));
handler = notify_table[idx].handler; gpg_err_set_errno (EINVAL);
handler_value = notify_table[idx].value; return TRACE_SYSRES (-1);
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);
} }
/* Then do the close. */ /* 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 int
_gpgme_io_set_nonblocking (int fd) _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 /* Select on the list of fds.
nothing to select, > 0 = number of signaled fds. */ *
* Returns: -1 = error,
* 0 = timeout or nothing to select,
* > 0 = number of signaled fds.
*/
int 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 readfds;
fd_set writefds; 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 }; struct timeval timeout = { 1, 0 };
void *dbg_help = NULL; void *dbg_help = NULL;
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", 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 (&readfds);
FD_ZERO (&writefds); 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); return TRACE_SYSRES (count);
} }
int int
_gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags) _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 int
_gpgme_io_dup (int fd) _gpgme_io_dup (int fd)
{ {
gpg_error_t err;
int new_fd; int new_fd;
do do
new_fd = dup (fd); new_fd = dup (fd);
while (new_fd == -1 && errno == EINTR); 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; return new_fd;
} }
int int
_gpgme_io_socket (int domain, int type, int proto) _gpgme_io_socket (int domain, int type, int proto)

View File

@ -51,14 +51,15 @@ struct spawn_fd_item_s
int arg_loc; int arg_loc;
}; };
struct io_select_fd_s struct io_select_s
{ {
int fd; int fd;
int for_read; unsigned int for_read:1;
int for_write; unsigned int for_write:1;
int signaled; unsigned int signaled:1;
void *opaque;
}; };
typedef struct io_select_s *io_select_t;
/* These function are either defined in posix-io.c or w32-io.c. */ /* These function are either defined in posix-io.c or w32-io.c. */
void _gpgme_io_subsystem_init (void); 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_write (int fd, const void *buffer, size_t count);
int _gpgme_io_pipe (int filedes[2], int inherit_idx); int _gpgme_io_pipe (int filedes[2], int inherit_idx);
int _gpgme_io_close (int fd); 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); int _gpgme_io_set_nonblocking (int fd);
/* Under Windows do not allocate a console. */ /* 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 (*atfork) (void *opaque, int reserved),
void *atforkvalue, pid_t *r_pid); 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 /* Write the printable version of FD to the buffer BUF of length
BUFLEN. The printable version is the representation on the command BUFLEN. The printable version is the representation on the command

View File

@ -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); err = sign_start (ctx, 1, plain, sig, mode);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -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); err = spawn_start (ctx, 1, file, argv, datain, dataout, dataerr, flags);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -180,6 +180,6 @@ gpgme_op_tofu_policy (gpgme_ctx_t ctx,
err = tofu_policy_start (ctx, 1, key, policy); err = tofu_policy_start (ctx, 1, key, policy);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -230,7 +230,7 @@ gpgme_op_trustlist_next (gpgme_ctx_t ctx, gpgme_trust_item_t *r_item)
if (!opd->trust_queue) 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) if (err)
return TRACE_ERR (err); return TRACE_ERR (err);
if (!opd->trust_cond) if (!opd->trust_cond)

View File

@ -911,7 +911,7 @@ parse_error (gpgme_signature_t sig, char *args, int set_status)
&& gpg_err_code (err) == GPG_ERR_BAD_DATA) && gpg_err_code (err) == GPG_ERR_BAD_DATA)
{ {
/* This indicates a double plaintext. The only solid way to /* 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); return gpg_error (GPG_ERR_BAD_DATA);
} }
else if (!set_status) 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); err = verify_start (ctx, 1, sig, signed_text, plaintext);
if (!err) if (!err)
err = _gpgme_wait_one (ctx); err = _gpgme_sync_wait (ctx, NULL, NULL);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -97,7 +97,7 @@ gpgme_op_vfs_transact (gpgme_ctx_t ctx,
err = vfs_start (ctx, 1, command, data_cb, data_cb_value, err = vfs_start (ctx, 1, command, data_cb, data_cb_value,
inq_cb, inq_cb_value, status_cb, status_cb_value); inq_cb, inq_cb_value, status_cb, status_cb_value);
if (!err) if (!err)
err = _gpgme_wait_one_ext (ctx, op_err); err = _gpgme_sync_wait (ctx, NULL, op_err);
return 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); err = _gpgme_op_vfs_create (ctx, recp, container_file, flags, op_err);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -154,7 +154,7 @@ gpgme_op_vfs_transact (gpgme_ctx_t ctx,
err = vfs_start (ctx, 1, command, data_cb, data_cb_value, err = vfs_start (ctx, 1, command, data_cb, data_cb_value,
inq_cb, inq_cb_value, status_cb, status_cb_value); inq_cb, inq_cb_value, status_cb, status_cb_value);
if (!err) if (!err)
err = _gpgme_wait_one_ext (ctx, op_err); err = _gpgme_sync_wait (ctx, NULL, op_err);
return 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); err = _gpgme_op_vfs_mount (ctx, container_file, mount_dir, flags, op_err);
return TRACE_ERR (err); return TRACE_ERR (err);
} }

View File

@ -143,7 +143,7 @@ static struct
/* The context of an associated writer object or NULL. */ /* The context of an associated writer object or NULL. */
struct writer_context_s *writer; 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. */ * callback per fd. */
struct { struct {
_gpgme_close_notify_handler_t handler; _gpgme_close_notify_handler_t handler;

View File

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

View File

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

View File

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

View File

@ -38,188 +38,467 @@
#include "priv-io.h" #include "priv-io.h"
#include "engine.h" #include "engine.h"
#include "debug.h" #include "debug.h"
#include "fdtable.h"
void /* Wrapper for the user wait handler to match the exported prototype.
_gpgme_fd_table_init (fd_table_t fdt) * This is used by _gpgme_add_io_cb_user. */
static gpg_error_t
user_io_cb_handler (void *data, int fd)
{ {
fdt->fds = NULL; struct io_cb_tag_s *tag = data;
fdt->size = 0; 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; return 0;
} }
/* Register the file descriptor FD with the handler FNC (which gets /* Register the file descriptor FD with the handler FNC (which gets
FNC_DATA as its first argument) for the direction DIR. DATA should 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 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_error_t
_gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc, _gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc,
void *fnc_data, void **r_tag) void *fnc_data, void **r_tag)
{ {
gpgme_error_t err; gpgme_error_t err;
gpgme_ctx_t ctx = (gpgme_ctx_t) data; gpgme_ctx_t ctx = (gpgme_ctx_t) data;
fd_table_t fdt; struct io_cb_tag_s *tag;
struct wait_item_s *item;
struct tag *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 (fnc);
assert (ctx); assert (ctx);
fdt = &ctx->fdt; tag = calloc (1, sizeof *tag);
assert (fdt);
tag = malloc (sizeof *tag);
if (!tag) if (!tag)
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
tag->ctx = ctx; tag->serial = ctx->serial;
tag->fd = fd;
/* Allocate a structure to hold information about the handler. */ err = _gpgme_fdtable_set_io_cb (fd, ctx->serial, dir, fnc, fnc_data);
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);
if (err) if (err)
{ {
free (tag); free (tag);
free (item); return TRACE_ERR (err);
return err;
} }
TRACE (DEBUG_CTX, "_gpgme_add_io_cb", ctx,
"fd=%d, dir=%d -> tag=%p", fd, dir, tag);
*r_tag = tag; *r_tag = tag;
TRACE_SUC ("tag=%p", tag);
return 0; 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 void
_gpgme_remove_io_cb (void *data) _gpgme_remove_io_cb (void *data)
{ {
struct tag *tag = data; struct io_cb_tag_s *tag = data;
gpgme_ctx_t ctx; gpg_error_t err;
fd_table_t fdt;
int idx;
assert (tag); assert (tag);
ctx = tag->ctx;
assert (ctx);
fdt = &ctx->fdt;
assert (fdt);
idx = tag->idx;
TRACE (DEBUG_CTX, "_gpgme_remove_io_cb", data, err = _gpgme_fdtable_set_io_cb (tag->fd, tag->serial, 0, NULL, NULL);
"setting fd 0x%x (item=%p) done", fdt->fds[idx].fd, if (err)
fdt->fds[idx].opaque); {
TRACE (DEBUG_CTX, __func__, NULL, "tag=%p (ctx=%lu fd=%d) failed: %s",
free (fdt->fds[idx].opaque); tag, tag->serial, tag->fd, gpg_strerror (err));
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;
} }
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 /* The internal I/O callback function used for the global event loop.
callback _may_ influence the status of other file descriptors. Our That loop is used for all asynchronous operations (except key
own event loops could compensate for that, but the external event listing) for which no user I/O callbacks are specified.
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;
item = (struct wait_item_s *) an_fds->opaque; A context sets up its initial I/O callbacks and then sends the
assert (item); 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; int nr;
struct io_select_fd_s fds; uint64_t serial;
TRACE (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check"); TRACE_BEG (DEBUG_SYSIO, __func__, NULL, "ctx=%lu hang=%d",
fds = *an_fds; CTXSERIAL (ctx), hang);
fds.signaled = 0;
/* Just give it a quick poll. */ do
nr = _gpgme_io_select (&fds, 1, 1);
assert (nr <= 1);
if (nr < 0)
return gpg_error_from_syserror ();
else if (nr == 0)
{ {
/* The status changed in the meantime, there is nothing left /* Get all fds of CTX (or all if CTX is NULL) we want to wait
* to do. */ * for and which are in the active state. */
return 0; 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)", nr = _gpgme_io_select (fds, nfds, 0);
item->handler_value, an_fds->fd); if (nr < 0)
{
iocb_data.handler_value = item->handler_value; if (status)
iocb_data.op_err = 0; *status = gpg_error_from_syserror ();
err = item->handler (&iocb_data, an_fds->fd); if (op_err)
*op_err = 0;
*op_err = iocb_data.op_err; ctx = NULL;
return err; 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);
} }

View File

@ -1,6 +1,6 @@
/* wait.h - Definitions for the wait queue interface. /* wait.h - Definitions for the wait queue interface.
Copyright (C) 2000 Werner Koch (dd9jn) 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. This file is part of GPGME.
@ -25,58 +25,46 @@
#include "gpgme.h" #include "gpgme.h"
#include "sema.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; /* The s/n of the context for which the fd was registered. */
size_t size; uint64_t serial;
};
typedef struct fd_table *fd_table_t;
/* Wait items are hooked into the io_select_fd_s to connect an fd with /* The actual fd. */
a callback handler. */ int fd;
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;
/* This is used by the wrappers for the user event loop. */ /* This is used by the wrappers for the user event loop. */
void *user_tag; 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_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_remove_io_cb (void *tag);
void _gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type, void _gpgme_remove_io_cb_user (void *tag);
void *type_data);
void _gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type, void _gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
void *type_data); 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 _gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type,
void **r_tag); void *type_data);
void _gpgme_wait_user_remove_io_cb (void *tag);
void _gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type, void _gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type,
void *type_data); 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 /* Session based interfaces require to make a distinction between IPC

View File

@ -59,7 +59,7 @@ main (void)
fail_if_err (err); fail_if_err (err);
while (gpgme_wait (ctx, &err, 0) == NULL && err == 0) while (gpgme_wait (ctx, &err, 0) == NULL && err == 0)
sleep(1); ;
if (gpgme_err_code (err) != GPG_ERR_NO_DATA) if (gpgme_err_code (err) != GPG_ERR_NO_DATA)
{ {