2002-05-09  Marcus Brinkmann  <marcus@g10code.de>

	* gpgme.texi (Overview): Replace note about thread-safeness.
	(Multi Threading): New section.

gpgme/
2002-05-08  Marcus Brinkmann  <marcus@g10code.de>

	* w32-util.c: New static variable GET_PATH_LOCK.
	(_gpgme_get_gpg_path): Remove superfluous NULL initializer.
	Take lock while determining path.
	(_gpgme_get_gpgsm_path): Likewise.
	* version.c (do_subsystem_inits): Set DONE to 1 after
	initialization.
	(gpgme_get_engine_info): New variable ENGINE_INFO_LOCK.  Take lock
	while determining engine info.
	* rungpg.c (_gpgme_gpg_get_version): New variable
	GPG_VERSION_LOCK.  Take the lock while determining the program
	version.
	* posix-io.c: Include "sema.h".
	(_gpgme_io_spawn): New variable FIXED_SIGNALS_LOCK.  Take the lock
	while fixing the signals.
	(_gpgme_io_select): Make READFDS and WRITEFDS non-static.
	* key.c: Include "sema.h".  New globals KEY_CACHE_LOCK and
	KEY_REF_LOCK.
	(capabilities_to_string): Make STRINGS very const.
	(_gpgme_key_cache_add): Lock the key cache.
	(_gpgme_key_cache_get): Likewise.
	(gpgme_key_ref, gpgme_key_release): Lock the key_ref_lock.
	* import.c (append_xml_impinfo): Make IMPORTED_FIELDS and
	IMPORT_RES_FIELDS very const.  Make FIELD and FIELD_NAME a litle
	const.
	* engine.c (_gpgme_engine_get_info): New variable
	ENGINE_INFO_LOCK.  Take lock while determining engine info.
	* engine-gpgsm.c: Include "sema.h".
	(_gpgme_gpgsm_get_version): New variable GPGSM_VERSION_LOCK.  Take
	lock while getting program version.
This commit is contained in:
Marcus Brinkmann 2002-05-09 03:38:12 +00:00
parent d27e6506b1
commit 4e0d4d7cf3
13 changed files with 877 additions and 638 deletions

3
TODO
View File

@ -8,8 +8,6 @@ Hey Emacs, this is -*- outline -*- mode!
* Allow to use GTK's main loop instead of the select stuff in * Allow to use GTK's main loop instead of the select stuff in
wait.c wait.c
* add locking to the key cache?
* cleanup the namespace - we use log_* assuan_* ascii_* mutex_* * cleanup the namespace - we use log_* assuan_* ascii_* mutex_*
But those are only used internally. Some linker tricks should make But those are only used internally. Some linker tricks should make
it possible to hide them from the user (didn't work last time, try it possible to hide them from the user (didn't work last time, try
@ -30,6 +28,7 @@ Hey Emacs, this is -*- outline -*- mode!
*** For pipemode, make sure to release the pipemode callback data object. *** For pipemode, make sure to release the pipemode callback data object.
* Operations * Operations
** gpgme_wait needs to be made thread safe!!!
** Export status handler need much more work. ** Export status handler need much more work.
** Import should return a useful error when one happened. ** Import should return a useful error when one happened.
** Genkey should return something more useful than General_Error. ** Genkey should return something more useful than General_Error.

View File

@ -1,3 +1,8 @@
2002-05-09 Marcus Brinkmann <marcus@g10code.de>
* gpgme.texi (Overview): Replace note about thread-safeness.
(Multi Threading): New section.
2002-05-03 Werner Koch <wk@gnupg.org> 2002-05-03 Werner Koch <wk@gnupg.org>
* gpgme.texi (Manipulating Data Buffers): Changed some data types * gpgme.texi (Manipulating Data Buffers): Changed some data types

View File

@ -104,6 +104,7 @@ Preparation
* Header:: What header file you need to include. * Header:: What header file you need to include.
* Building the Source:: Compiler options to be used. * Building the Source:: Compiler options to be used.
* Library Version Check:: Getting and verifying the library version. * Library Version Check:: Getting and verifying the library version.
* Multi Threading:: How GPGME can be used in an MT environment.
Protocols and Engines Protocols and Engines
@ -278,11 +279,9 @@ including listing keys, querying their attributes, generating,
importing, exporting and deleting keys, and acquiring information importing, exporting and deleting keys, and acquiring information
about the trust path. about the trust path.
@cindex thread-safeness With some precautions, @acronym{GPGME} can be used in a multi-threaded
@cindex multi-threading environment, although it is not completely thread safe and thus needs
@strong{Caution:} The @acronym{GPGME} library is not thread-safe. It the support of the application.
will be to some extent in the future, but currently great care has to
be taken if @acronym{GPGME} is used in a multi-threaded environment.
@node Preparation @node Preparation
@ -298,6 +297,7 @@ of the library are verified.
* Header:: What header file you need to include. * Header:: What header file you need to include.
* Building the Source:: Compiler options to be used. * Building the Source:: Compiler options to be used.
* Library Version Check:: Getting and verifying the library version. * Library Version Check:: Getting and verifying the library version.
* Multi Threading:: How GPGME can be used in an MT environment.
@end menu @end menu
@ -402,6 +402,81 @@ features are provided by the installed version of the library.
@end deftypefun @end deftypefun
@node Multi Threading
@section Multi Threading
@cindex thread-safeness
@cindex multi-threading
The @acronym{GPGME} library is not entirely thread-safe, but it can
still be used in a multi-threaded environment if some care is taken.
If the following requirements are met, there should be no race
conditions to worry about:
@itemize @bullet
@item
The function @code{gpgme_check_version} must be called before any
other function in the library, because it initializes the locking
subsystem in @acronym{GPGME}. To achieve this in all generality, it
is necessary to synchronize the call to this function with all other
calls to functions in the library, using the synchronization
mechanisms available in your thread library. Otherwise, specific
compiler or CPU memory cache optimizations could lead to the situation
where a thread is started and uses @acronym{GPGME} before the effects
of the initialization are visible for this thread. It doesn't even
suffice to call @code{gpgme_check_version} before creating this other
thread@footnote{In SMP systems the new thread could be started on
another CPU before the effects of the initialization are seen by that
CPU's memory cache. Not doing proper synchronization here leads to
the same problems the double-checked locking idiom has. You might
find that if you don't do proper synchronization, it still works in
most configurations. Don't let this fool you. Someday it might lead
to subtle bugs when someone tries it on a DEC Alpha or an SMP
machine.}.
For example, if you are using POSIX threads, each thread that wants to
call functions in @acronym{GPGME} could call the following function
before any function in the library:
@example
#include <pthread.h>
void
initialize_gpgme (void)
{
static int gpgme_init;
static pthread_mutext_t gpgme_init_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock (&gpgme_init_lock);
if (!gpgme_init)
{
gpgme_check_version ();
gpgme_init = 1;
}
pthread_mutex_unlock (&gpgme_init_lock);
}
@end example
@item
Any @code{GpgmeData}, @code{GpgmeCtx} and @code{GpgmeRecipients}
object must only be accessed by one thread at a time. If multiple
threads want to deal with the same object, the caller has to make sure
that operations on this object are fully synchronized.
@item
Only one thread is allowed to call @code{gpgme_wait} at a time. If
multiple threads call this function, the caller must make sure that
all invocations are fully synchronized.
@item
Unfortunately, the last rule implies that all calls to one of the
following functions have to be fully synchronized together with
@code{gpgme_wait}, as they call @code{gpgme_wait} internally: All
functions @code{gpgme_op_FOO} that have a corresponding
@code{gpgme_op_FOO_start} function, @code{gpgme_op_keylist_next},
@code{gpgme_op_trustlist_next}.
@end itemize
@node Protocols and Engines @node Protocols and Engines
@chapter Protocols and Engines @chapter Protocols and Engines
@cindex protocol @cindex protocol

View File

@ -1,3 +1,35 @@
2002-05-08 Marcus Brinkmann <marcus@g10code.de>
* w32-util.c: New static variable GET_PATH_LOCK.
(_gpgme_get_gpg_path): Remove superfluous NULL initializer.
Take lock while determining path.
(_gpgme_get_gpgsm_path): Likewise.
* version.c (do_subsystem_inits): Set DONE to 1 after
initialization.
(gpgme_get_engine_info): New variable ENGINE_INFO_LOCK. Take lock
while determining engine info.
* rungpg.c (_gpgme_gpg_get_version): New variable
GPG_VERSION_LOCK. Take the lock while determining the program
version.
* posix-io.c: Include "sema.h".
(_gpgme_io_spawn): New variable FIXED_SIGNALS_LOCK. Take the lock
while fixing the signals.
(_gpgme_io_select): Make READFDS and WRITEFDS non-static.
* key.c: Include "sema.h". New globals KEY_CACHE_LOCK and
KEY_REF_LOCK.
(capabilities_to_string): Make STRINGS very const.
(_gpgme_key_cache_add): Lock the key cache.
(_gpgme_key_cache_get): Likewise.
(gpgme_key_ref, gpgme_key_release): Lock the key_ref_lock.
* import.c (append_xml_impinfo): Make IMPORTED_FIELDS and
IMPORT_RES_FIELDS very const. Make FIELD and FIELD_NAME a litle
const.
* engine.c (_gpgme_engine_get_info): New variable
ENGINE_INFO_LOCK. Take lock while determining engine info.
* engine-gpgsm.c: Include "sema.h".
(_gpgme_gpgsm_get_version): New variable GPGSM_VERSION_LOCK. Take
lock while getting program version.
2002-05-08 Marcus Brinkmann <marcus@g10code.de> 2002-05-08 Marcus Brinkmann <marcus@g10code.de>
* debug.h: New file. * debug.h: New file.

View File

@ -48,6 +48,7 @@
#include "wait.h" #include "wait.h"
#include "io.h" #include "io.h"
#include "key.h" #include "key.h"
#include "sema.h"
#include "engine-gpgsm.h" #include "engine-gpgsm.h"
@ -99,10 +100,12 @@ const char *
_gpgme_gpgsm_get_version (void) _gpgme_gpgsm_get_version (void)
{ {
static const char *gpgsm_version; static const char *gpgsm_version;
DEFINE_STATIC_LOCK (gpgsm_version_lock);
/* FIXME: Locking. */ LOCK (gpgsm_version_lock);
if (!gpgsm_version) if (!gpgsm_version)
gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ()); gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
UNLOCK (gpgsm_version_lock);
return gpgsm_version; return gpgsm_version;
} }

View File

@ -36,6 +36,7 @@
#include "rungpg.h" #include "rungpg.h"
#include "engine-gpgsm.h" #include "engine-gpgsm.h"
struct engine_object_s struct engine_object_s
{ {
GpgmeProtocol protocol; GpgmeProtocol protocol;
@ -50,6 +51,7 @@ struct engine_object_s
} engine; } engine;
}; };
struct reap_s struct reap_s
{ {
struct reap_s *next; struct reap_s *next;
@ -61,6 +63,7 @@ struct reap_s
static struct reap_s *reap_list; static struct reap_s *reap_list;
DEFINE_STATIC_LOCK (reap_list_lock); DEFINE_STATIC_LOCK (reap_list_lock);
/* Get the path of the engine for PROTOCOL. */ /* Get the path of the engine for PROTOCOL. */
const char * const char *
_gpgme_engine_get_path (GpgmeProtocol proto) _gpgme_engine_get_path (GpgmeProtocol proto)
@ -76,6 +79,7 @@ _gpgme_engine_get_path (GpgmeProtocol proto)
} }
} }
/* Get the version number of the engine for PROTOCOL. */ /* Get the version number of the engine for PROTOCOL. */
const char * const char *
_gpgme_engine_get_version (GpgmeProtocol proto) _gpgme_engine_get_version (GpgmeProtocol proto)
@ -91,6 +95,7 @@ _gpgme_engine_get_version (GpgmeProtocol proto)
} }
} }
GpgmeError GpgmeError
gpgme_engine_check_version (GpgmeProtocol proto) gpgme_engine_check_version (GpgmeProtocol proto)
{ {
@ -105,6 +110,7 @@ gpgme_engine_check_version (GpgmeProtocol proto)
} }
} }
const char * const char *
_gpgme_engine_get_info (GpgmeProtocol proto) _gpgme_engine_get_info (GpgmeProtocol proto)
{ {
@ -115,25 +121,21 @@ _gpgme_engine_get_info (GpgmeProtocol proto)
" </engine>\n"; " </engine>\n";
static const char *const strproto[3] = { "OpenPGP", "CMS", NULL }; static const char *const strproto[3] = { "OpenPGP", "CMS", NULL };
static const char *engine_info[3]; /* FIXME: MAX_PROTO + 1*/ static const char *engine_info[3]; /* FIXME: MAX_PROTO + 1*/
const char *path; DEFINE_STATIC_LOCK (engine_info_lock);
const char *version;
char *info;
if (proto > 2 /* FIXME MAX_PROTO */ || !strproto[proto]) if (proto > 2 /* FIXME MAX_PROTO */ || !strproto[proto])
return NULL; return NULL;
/* FIXME: Make sure that only one instance does run. */ LOCK (engine_info_lock);
if (engine_info[proto]) if (!engine_info[proto])
return engine_info[proto]; {
const char *path = _gpgme_engine_get_path (proto);
const char *version = _gpgme_engine_get_version (proto);
path = _gpgme_engine_get_path (proto); if (path && version)
version = _gpgme_engine_get_version (proto); {
char *info = xtrymalloc (strlen (fmt) + strlen (strproto[proto])
if (!path || !version) + strlen (path) + strlen (version) + 1);
return NULL;
info = xtrymalloc (strlen(fmt) + strlen(strproto[proto]) + strlen(path)
+ strlen (version) + 1);
if (!info) if (!info)
info = " <engine>\n" info = " <engine>\n"
" <error>Out of core</error>\n" " <error>Out of core</error>\n"
@ -141,10 +143,13 @@ _gpgme_engine_get_info (GpgmeProtocol proto)
else else
sprintf (info, fmt, strproto[proto], version, path); sprintf (info, fmt, strproto[proto], version, path);
engine_info[proto] = info; engine_info[proto] = info;
}
}
UNLOCK (engine_info_lock);
return engine_info[proto]; return engine_info[proto];
} }
GpgmeError GpgmeError
_gpgme_engine_new (GpgmeProtocol proto, EngineObject *r_engine) _gpgme_engine_new (GpgmeProtocol proto, EngineObject *r_engine)
{ {
@ -193,6 +198,7 @@ _gpgme_engine_new (GpgmeProtocol proto, EngineObject *r_engine)
return err; return err;
} }
void void
_gpgme_engine_release (EngineObject engine) _gpgme_engine_release (EngineObject engine)
{ {
@ -561,6 +567,7 @@ _gpgme_engine_start (EngineObject engine, void *opaque)
return 0; return 0;
} }
void void
_gpgme_engine_add_child_to_reap_list (void *buf, int buflen, pid_t pid) _gpgme_engine_add_child_to_reap_list (void *buf, int buflen, pid_t pid)
{ {

View File

@ -43,7 +43,7 @@ extern "C" {
AM_PATH_GPGME macro) check that this header matches the installed AM_PATH_GPGME macro) check that this header matches the installed
library. Warning: Do not edit the next line. configure will do library. Warning: Do not edit the next line. configure will do
that for you! */ that for you! */
#define GPGME_VERSION "0.3.6" #define GPGME_VERSION "0.3.7-cvs"
/* The opaque data types used by GPGME. */ /* The opaque data types used by GPGME. */

View File

@ -53,14 +53,14 @@ static void
append_xml_impinfo (GpgmeData *rdh, GpgStatusCode code, char *args) append_xml_impinfo (GpgmeData *rdh, GpgStatusCode code, char *args)
{ {
#define MAX_IMPORTED_FIELDS 14 #define MAX_IMPORTED_FIELDS 14
static char *imported_fields[MAX_IMPORTED_FIELDS] static const char *const imported_fields[MAX_IMPORTED_FIELDS]
= { "keyid", "username", 0 }; = { "keyid", "username", 0 };
static char *import_res_fields[MAX_IMPORTED_FIELDS] static const char *const import_res_fields[MAX_IMPORTED_FIELDS]
= { "count", "no_user_id", "imported", "imported_rsa", = { "count", "no_user_id", "imported", "imported_rsa",
"unchanged", "n_uids", "n_subk", "n_sigs", "s_sigsn_revoc", "unchanged", "n_uids", "n_subk", "n_sigs", "s_sigsn_revoc",
"sec_read", "sec_imported", "sec_dups", "skipped_new", 0 }; "sec_read", "sec_imported", "sec_dups", "skipped_new", 0 };
char *field[MAX_IMPORTED_FIELDS]; const char *field[MAX_IMPORTED_FIELDS];
char **field_name = 0; const char *const *field_name = 0;
GpgmeData dh; GpgmeData dh;
int i; int i;

View File

@ -28,6 +28,7 @@
#include "util.h" #include "util.h"
#include "ops.h" #include "ops.h"
#include "key.h" #include "key.h"
#include "sema.h"
#define ALLOC_CHUNK 1024 #define ALLOC_CHUNK 1024
#define my_isdigit(a) ((a) >='0' && (a) <= '9') #define my_isdigit(a) ((a) >='0' && (a) <= '9')
@ -36,17 +37,25 @@
#error unsigned int too short to be used as a hash value #error unsigned int too short to be used as a hash value
#endif #endif
struct key_cache_item_s { struct key_cache_item_s
{
struct key_cache_item_s *next; struct key_cache_item_s *next;
GpgmeKey key; GpgmeKey key;
}; };
/* Protects all key_cache_* variables. */
DEFINE_STATIC_LOCK (key_cache_lock);
static int key_cache_initialized; static int key_cache_initialized;
static struct key_cache_item_s **key_cache; static struct key_cache_item_s **key_cache;
static size_t key_cache_size; static size_t key_cache_size;
static size_t key_cache_max_chain_length; static size_t key_cache_max_chain_length;
static struct key_cache_item_s *key_cache_unused_items; static struct key_cache_item_s *key_cache_unused_items;
/* Protects all reference counters in keys. All other accesses to a
key are either read only or happen before the key is entered into
the cache. */
DEFINE_STATIC_LOCK (key_ref_lock);
static int static int
hextobyte (const byte *s) hextobyte (const byte *s)
{ {
@ -78,14 +87,19 @@ hash_key (const char *fpr, unsigned int *rhash)
unsigned int hash; unsigned int hash;
int c; int c;
if ( !fpr ) return -1; if (!fpr)
if ( (c = hextobyte(fpr)) == -1 ) return -1; return -1;
if ((c = hextobyte (fpr)) == -1)
return -1;
hash = c; hash = c;
if ( (c = hextobyte(fpr+2)) == -1 ) return -1; if ((c = hextobyte (fpr+2)) == -1)
return -1;
hash |= c << 8; hash |= c << 8;
if ( (c = hextobyte(fpr+4)) == -1 ) return -1; if ((c = hextobyte (fpr+4)) == -1)
return -1;
hash |= c << 16; hash |= c << 16;
if ( (c = hextobyte(fpr+6)) == -1 ) return -1; if ((c = hextobyte (fpr+6)) == -1)
return -1;
hash |= c << 24; hash |= c << 24;
*rhash = hash; *rhash = hash;
@ -95,22 +109,26 @@ hash_key (const char *fpr, unsigned int *rhash)
void void
_gpgme_key_cache_init (void) _gpgme_key_cache_init (void)
{ {
if (key_cache_initialized) LOCK (key_cache_lock);
return; if (!key_cache_initialized)
{
key_cache_size = 503; key_cache_size = 503;
key_cache = xtrycalloc (key_cache_size, sizeof *key_cache); key_cache = xtrycalloc (key_cache_size, sizeof *key_cache);
if (!key_cache) { if (!key_cache)
{
key_cache_size = 0; key_cache_size = 0;
key_cache_initialized = 1; key_cache_initialized = 1;
return;
} }
/* else
* The upper bound for our cache size is {
* key_cache_max_chain_length * key_cache_size /* The upper bound for our cache size is
*/ key_cache_max_chain_length * key_cache_size. */
key_cache_max_chain_length = 10; key_cache_max_chain_length = 10;
key_cache_initialized = 1; key_cache_initialized = 1;
} }
}
UNLOCK (key_cache_lock);
}
void void
@ -121,15 +139,20 @@ _gpgme_key_cache_add (GpgmeKey key)
if (!key) if (!key)
return; return;
/* FIXME: add locking */
if (!key_cache_initialized)
_gpgme_key_cache_init (); _gpgme_key_cache_init ();
if (!key_cache_size)
return; /* cache was not enabled */
/* put the key under each fingerprint into the cache. We use the LOCK (key_cache_lock);
* first 4 digits to calculate the hash */ /* Check if cache was enabled. */
for (k=&key->keys; k; k = k->next ) { if (!key_cache_size)
{
UNLOCK (key_cache_lock);
return;
}
/* Put the key under each fingerprint into the cache. We use the
first 4 digits to calculate the hash. */
for (k = &key->keys; k; k = k->next)
{
size_t n; size_t n;
unsigned int hash; unsigned int hash;
struct key_cache_item_s *item; struct key_cache_item_s *item;
@ -138,18 +161,22 @@ _gpgme_key_cache_add (GpgmeKey key)
continue; continue;
hash %= key_cache_size; hash %= key_cache_size;
for (item=key_cache[hash],n=0; item; item = item->next, n++) { for (item = key_cache[hash], n=0; item; item = item->next, n++)
{
struct subkey_s *k2; struct subkey_s *k2;
if (item->key == key) if (item->key == key)
break; /* already in cache */ /* Already in cache. */
/* now do a deeper check */ break;
for (k2=&item->key->keys; k2; k2 = k2->next ) { /* Now do a deeper check. */
if( k2->fingerprint for (k2 = &item->key->keys; k2; k2 = k2->next)
&& !strcmp (k->fingerprint, k2->fingerprint) ) { {
/* okay, replace it with the new copy */ if (k2->fingerprint && !strcmp (k->fingerprint, k2->fingerprint))
{
/* Okay, replace it with the new copy. */
gpgme_key_unref (item->key); gpgme_key_unref (item->key);
item->key = key; item->key = key;
gpgme_key_ref (item->key); gpgme_key_ref (item->key);
UNLOCK (key_cache_lock);
return; return;
} }
} }
@ -157,20 +184,24 @@ _gpgme_key_cache_add (GpgmeKey key)
if (item) if (item)
continue; continue;
if (n > key_cache_max_chain_length ) { /* remove the last entries */ if (n > key_cache_max_chain_length)
{
/* Remove the last entries. */
struct key_cache_item_s *last = NULL; struct key_cache_item_s *last = NULL;
for (item = key_cache[hash]; for (item = key_cache[hash];
item && n < key_cache_max_chain_length; item && n < key_cache_max_chain_length;
last = item, item = item->next, n++ ) { last = item, item = item->next, n++)
; ;
}
if (last) { if (last)
{
struct key_cache_item_s *next; struct key_cache_item_s *next;
assert (last->next == item); assert (last->next == item);
last->next = NULL; last->next = NULL;
for ( ;item; item=next) { for (; item; item = next)
{
next = item->next; next = item->next;
gpgme_key_unref (item->key); gpgme_key_unref (item->key);
item->key = NULL; item->key = NULL;
@ -181,14 +212,19 @@ _gpgme_key_cache_add (GpgmeKey key)
} }
item = key_cache_unused_items; item = key_cache_unused_items;
if (item) { if (item)
{
key_cache_unused_items = item->next; key_cache_unused_items = item->next;
item->next = NULL; item->next = NULL;
} }
else { else
{
item = xtrymalloc (sizeof *item); item = xtrymalloc (sizeof *item);
if (!item) if (!item)
return; /* out of core */ {
UNLOCK (key_cache_lock);
return;
}
} }
item->key = key; item->key = key;
@ -196,6 +232,7 @@ _gpgme_key_cache_add (GpgmeKey key)
item->next = key_cache[hash]; item->next = key_cache[hash];
key_cache[hash] = item; key_cache[hash] = item;
} }
UNLOCK (key_cache_lock);
} }
@ -205,23 +242,36 @@ _gpgme_key_cache_get (const char *fpr)
struct key_cache_item_s *item; struct key_cache_item_s *item;
unsigned int hash; unsigned int hash;
LOCK (key_cache_lock);
/* Check if cache is enabled already. */
if (!key_cache_size) if (!key_cache_size)
return NULL; /* cache not (yet) enabled */ {
UNLOCK (key_cache_lock);
return NULL;
}
if (hash_key (fpr, &hash)) if (hash_key (fpr, &hash))
{
UNLOCK (key_cache_lock);
return NULL; return NULL;
}
hash %= key_cache_size; hash %= key_cache_size;
for (item=key_cache[hash]; item; item = item->next) { for (item = key_cache[hash]; item; item = item->next)
{
struct subkey_s *k; struct subkey_s *k;
for (k=&item->key->keys; k; k = k->next ) { for (k = &item->key->keys; k; k = k->next)
if( k->fingerprint && !strcmp (k->fingerprint, fpr) ) { {
if (k->fingerprint && !strcmp (k->fingerprint, fpr))
{
gpgme_key_ref (item->key); gpgme_key_ref (item->key);
UNLOCK (key_cache_lock);
return item->key; return item->key;
} }
} }
} }
UNLOCK (key_cache_lock);
return NULL; return NULL;
} }
@ -229,14 +279,19 @@ _gpgme_key_cache_get (const char *fpr)
static const char * static const char *
pkalgo_to_string (int algo) pkalgo_to_string (int algo)
{ {
switch (algo) { switch (algo)
{
case 1: case 1:
case 2: case 2:
case 3: return "RSA"; case 3:
return "RSA";
case 16: case 16:
case 20: return "ElG"; case 20:
case 17: return "DSA"; return "ElG";
default: return "Unknown"; case 17:
return "DSA";
default:
return "Unknown";
} }
} }
@ -281,7 +336,9 @@ void
gpgme_key_ref (GpgmeKey key) gpgme_key_ref (GpgmeKey key)
{ {
return_if_fail (key); return_if_fail (key);
LOCK (key_ref_lock);
key->ref_count++; key->ref_count++;
UNLOCK (key_ref_lock);
} }
@ -296,7 +353,8 @@ add_subkey (GpgmeKey key, int secret)
if(!(kk = key->keys.next)) if(!(kk = key->keys.next))
key->keys.next = k; key->keys.next = k;
else { else
{
while (kk->next) while (kk->next)
kk = kk->next; kk = kk->next;
kk->next = k; kk->next = k;
@ -306,12 +364,14 @@ add_subkey (GpgmeKey key, int secret)
return k; return k;
} }
struct subkey_s * struct subkey_s *
_gpgme_key_add_subkey (GpgmeKey key) _gpgme_key_add_subkey (GpgmeKey key)
{ {
return add_subkey (key, 0); return add_subkey (key, 0);
} }
struct subkey_s * struct subkey_s *
_gpgme_key_add_secret_subkey (GpgmeKey key) _gpgme_key_add_secret_subkey (GpgmeKey key)
{ {
@ -319,7 +379,6 @@ _gpgme_key_add_secret_subkey (GpgmeKey key)
} }
/** /**
* gpgme_key_release: * gpgme_key_release:
* @key: Key Object or NULL * @key: Key Object or NULL
@ -338,17 +397,24 @@ gpgme_key_release ( GpgmeKey key )
if (!key) if (!key)
return; return;
LOCK (key_ref_lock);
assert (key->ref_count); assert (key->ref_count);
if (--key->ref_count) if (--key->ref_count)
{
UNLOCK (key_ref_lock);
return; return;
}
UNLOCK (key_ref_lock);
xfree (key->keys.fingerprint); xfree (key->keys.fingerprint);
for (k = key->keys.next; k; k = k2 ) { for (k = key->keys.next; k; k = k2)
{
k2 = k->next; k2 = k->next;
xfree (k->fingerprint); xfree (k->fingerprint);
xfree (k); xfree (k);
} }
for (u = key->uids; u; u = u2 ) { for (u = key->uids; u; u = u2)
{
u2 = u->next; u2 = u->next;
xfree (u); xfree (u);
} }
@ -391,34 +457,47 @@ parse_user_id ( struct user_id_s *uid, char *tail )
int in_email = 0; int in_email = 0;
int in_comment = 0; int in_comment = 0;
for (s=uid->name; *s; s++ ) { for (s = uid->name; *s; s++)
if ( in_email ) { {
if (in_email)
{
if (*s == '<') if (*s == '<')
in_email++; /* not legal but anyway */ /* Not legal but anyway. */
else if (*s== '>') { in_email++;
if ( !--in_email ) { else if (*s == '>')
if (!uid->email_part) { {
if (!--in_email)
{
if (!uid->email_part)
{
uid->email_part = tail; uid->email_part = tail;
tail = set_user_id_part ( tail, start, s-start ); tail = set_user_id_part ( tail, start, s-start );
} }
} }
} }
} }
else if ( in_comment ) { else if (in_comment)
{
if (*s == '(') if (*s == '(')
in_comment++; in_comment++;
else if (*s== ')') { else if (*s== ')')
if ( !--in_comment ) { {
if (!uid->comment_part) { if (!--in_comment)
{
if (!uid->comment_part)
{
uid->comment_part = tail; uid->comment_part = tail;
tail = set_user_id_part ( tail, start, s-start ); tail = set_user_id_part ( tail, start, s-start );
} }
} }
} }
} }
else if ( *s == '<' ) { else if (*s == '<')
if ( in_name ) { {
if ( !uid->name_part ) { if (in_name)
{
if (!uid->name_part)
{
uid->name_part = tail; uid->name_part = tail;
tail = set_user_id_part (tail, start, s-start); tail = set_user_id_part (tail, start, s-start);
} }
@ -427,9 +506,12 @@ parse_user_id ( struct user_id_s *uid, char *tail )
in_email = 1; in_email = 1;
start = s+1; start = s+1;
} }
else if ( *s == '(' ) { else if (*s == '(')
if ( in_name ) { {
if ( !uid->name_part ) { if (in_name)
{
if (!uid->name_part)
{
uid->name_part = tail; uid->name_part = tail;
tail = set_user_id_part (tail, start, s-start ); tail = set_user_id_part (tail, start, s-start );
} }
@ -438,20 +520,23 @@ parse_user_id ( struct user_id_s *uid, char *tail )
in_comment = 1; in_comment = 1;
start = s+1; start = s+1;
} }
else if ( !in_name && *s != ' ' && *s != '\t' ) { else if (!in_name && *s != ' ' && *s != '\t')
{
in_name = 1; in_name = 1;
start = s; start = s;
} }
} }
if ( in_name ) { if (in_name)
if ( !uid->name_part ) { {
if (!uid->name_part)
{
uid->name_part = tail; uid->name_part = tail;
tail = set_user_id_part (tail, start, s-start); tail = set_user_id_part (tail, start, s-start);
} }
} }
/* let unused parts point to an EOS */ /* Let unused parts point to an EOS. */
tail--; tail--;
if (!uid->name_part) if (!uid->name_part)
uid->name_part = tail; uid->name_part = tail;
@ -459,7 +544,6 @@ parse_user_id ( struct user_id_s *uid, char *tail )
uid->email_part = tail; uid->email_part = tail;
if (!uid->comment_part) if (!uid->comment_part)
uid->comment_part = tail; uid->comment_part = tail;
} }
static void static void
@ -471,7 +555,7 @@ parse_x509_user_id ( struct user_id_s *uid, char *tail )
if (*s == '<' && s[strlen (s) - 1] == '>') if (*s == '<' && s[strlen (s) - 1] == '>')
uid->email_part = s; uid->email_part = s;
/* let unused parts point to an EOS */ /* Let unused parts point to an EOS. */
tail--; tail--;
if (!uid->name_part) if (!uid->name_part)
uid->name_part = tail; uid->name_part = tail;
@ -699,7 +783,7 @@ gpgme_key_get_as_xml ( GpgmeKey key )
for (u = key->uids; u; u = u->next) for (u = key->uids; u; u = u->next)
one_uid_as_xml (d,u); one_uid_as_xml (d,u);
/* and now the subkeys */ /* And now the subkeys. */
for (k = key->keys.next; k; k = k->next) for (k = key->keys.next; k; k = k->next)
{ {
_gpgme_data_append_string (d, " <subkey>\n"); _gpgme_data_append_string (d, " <subkey>\n");
@ -731,7 +815,8 @@ gpgme_key_get_as_xml ( GpgmeKey key )
static const char * static const char *
capabilities_to_string (struct subkey_s *k) capabilities_to_string (struct subkey_s *k)
{ {
static char *strings[8] = { static const char *const strings[8] =
{
"", "",
"c", "c",
"s", "s",
@ -747,7 +832,6 @@ capabilities_to_string (struct subkey_s *k)
} }
/** /**
* gpgme_key_get_string_attr: * gpgme_key_get_string_attr:
* @key: Key Object * @key: Key Object
@ -778,7 +862,8 @@ gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
if (idx < 0) if (idx < 0)
return NULL; return NULL;
switch (what) { switch (what)
{
case GPGME_ATTR_KEYID: case GPGME_ATTR_KEYID:
for (k = &key->keys; k && idx; k = k->next, idx--) for (k = &key->keys; k && idx; k = k->next, idx--)
; ;
@ -800,7 +885,8 @@ gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
case GPGME_ATTR_LEN: case GPGME_ATTR_LEN:
case GPGME_ATTR_CREATED: case GPGME_ATTR_CREATED:
case GPGME_ATTR_EXPIRE: case GPGME_ATTR_EXPIRE:
break; /* use another get function */ /* Use another get function. */
break;
case GPGME_ATTR_OTRUST: case GPGME_ATTR_OTRUST:
val = "[fixme]"; val = "[fixme]";
break; break;
@ -827,18 +913,32 @@ gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
case GPGME_ATTR_VALIDITY: case GPGME_ATTR_VALIDITY:
for (u = key->uids; u && idx; u = u->next, idx--) for (u = key->uids; u && idx; u = u->next, idx--)
; ;
if (u) { if (u)
switch (u->validity) { {
case GPGME_VALIDITY_UNKNOWN: val = "?"; break; switch (u->validity)
case GPGME_VALIDITY_UNDEFINED: val = "q"; break; {
case GPGME_VALIDITY_NEVER: val = "n"; break; case GPGME_VALIDITY_UNKNOWN:
case GPGME_VALIDITY_MARGINAL: val = "m"; break; val = "?";
case GPGME_VALIDITY_FULL: val = "f"; break; break;
case GPGME_VALIDITY_ULTIMATE: val = "u"; break; case GPGME_VALIDITY_UNDEFINED:
val = "q";
break;
case GPGME_VALIDITY_NEVER:
val = "n";
break;
case GPGME_VALIDITY_MARGINAL:
val = "m";
break;
case GPGME_VALIDITY_FULL:
val = "f";
break;
case GPGME_VALIDITY_ULTIMATE:
val = "u";
break;
} }
} }
break; break;
case GPGME_ATTR_LEVEL: /* not used here */ case GPGME_ATTR_LEVEL:
case GPGME_ATTR_TYPE: case GPGME_ATTR_TYPE:
case GPGME_ATTR_KEY_REVOKED: case GPGME_ATTR_KEY_REVOKED:
case GPGME_ATTR_KEY_INVALID: case GPGME_ATTR_KEY_INVALID:
@ -849,6 +949,7 @@ gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
case GPGME_ATTR_CAN_ENCRYPT: case GPGME_ATTR_CAN_ENCRYPT:
case GPGME_ATTR_CAN_SIGN: case GPGME_ATTR_CAN_SIGN:
case GPGME_ATTR_CAN_CERTIFY: case GPGME_ATTR_CAN_CERTIFY:
/* Not used here. */
break; break;
case GPGME_ATTR_IS_SECRET: case GPGME_ATTR_IS_SECRET:
if (key->secret) if (key->secret)
@ -870,7 +971,7 @@ gpgme_key_get_string_attr ( GpgmeKey key, GpgmeAttr what,
val = key->chain_id; val = key->chain_id;
break; break;
case GPGME_ATTR_SIG_STATUS: case GPGME_ATTR_SIG_STATUS:
/* not of any use here */ /* Not of any use here. */
break; break;
} }
return val; return val;
@ -908,7 +1009,8 @@ gpgme_key_get_ulong_attr ( GpgmeKey key, GpgmeAttr what,
if (idx < 0) if (idx < 0)
return 0; return 0;
switch (what) { switch (what)
{
case GPGME_ATTR_ALGO: case GPGME_ATTR_ALGO:
for (k = &key->keys; k && idx; k=k->next, idx--) for (k = &key->keys; k && idx; k=k->next, idx--)
; ;

View File

@ -36,6 +36,7 @@
#include "util.h" #include "util.h"
#include "io.h" #include "io.h"
#include "sema.h"
static struct static struct
{ {
@ -149,10 +150,12 @@ _gpgme_io_spawn (const char *path, char **argv,
struct spawn_fd_item_s *fd_child_list, struct spawn_fd_item_s *fd_child_list,
struct spawn_fd_item_s *fd_parent_list) struct spawn_fd_item_s *fd_parent_list)
{ {
static volatile int fixed_signals; static int fixed_signals;
DEFINE_STATIC_LOCK (fixed_signals_lock);
pid_t pid; pid_t pid;
int i; int i;
LOCK (fixed_signals_lock);
if (!fixed_signals) if (!fixed_signals)
{ {
struct sigaction act; struct sigaction act;
@ -166,8 +169,8 @@ _gpgme_io_spawn (const char *path, char **argv,
sigaction (SIGPIPE, &act, NULL); sigaction (SIGPIPE, &act, NULL);
} }
fixed_signals = 1; fixed_signals = 1;
/* XXX: This is not really MT safe. */
} }
UNLOCK (fixed_signals_lock);
pid = fork (); pid = fork ();
if (pid == -1) if (pid == -1)
@ -285,8 +288,8 @@ _gpgme_io_kill (int pid, int hard)
int int
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds) _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds)
{ {
static fd_set readfds; fd_set readfds;
static fd_set writefds; fd_set writefds;
int any, i, max_fd, n, count; int any, i, max_fd, n, count;
struct timeval timeout = { 1, 0 }; /* Use a 1s timeout. */ struct timeval timeout = { 1, 0 }; /* Use a 1s timeout. */
void *dbg_help = NULL; void *dbg_help = NULL;

View File

@ -167,11 +167,12 @@ const char *
_gpgme_gpg_get_version (void) _gpgme_gpg_get_version (void)
{ {
static const char *gpg_version; static const char *gpg_version;
DEFINE_STATIC_LOCK (gpg_version_lock);
/* FIXME: Locking. */ LOCK (gpg_version_lock);
if (!gpg_version) if (!gpg_version)
gpg_version = _gpgme_get_program_version (_gpgme_get_gpg_path ()); gpg_version = _gpgme_get_program_version (_gpgme_get_gpg_path ());
UNLOCK (gpg_version_lock);
return gpg_version; return gpg_version;
} }

View File

@ -43,6 +43,7 @@ do_subsystem_inits (void)
return; return;
_gpgme_sema_subsystem_init (); _gpgme_sema_subsystem_init ();
_gpgme_key_cache_init (); _gpgme_key_cache_init ();
done = 1;
} }
static const char* static const char*
@ -148,14 +149,15 @@ const char *
gpgme_get_engine_info () gpgme_get_engine_info ()
{ {
static const char *engine_info; static const char *engine_info;
DEFINE_STATIC_LOCK (engine_info_lock);
LOCK (engine_info_lock);
if (!engine_info)
{
const char *openpgp_info = _gpgme_engine_get_info (GPGME_PROTOCOL_OpenPGP); const char *openpgp_info = _gpgme_engine_get_info (GPGME_PROTOCOL_OpenPGP);
const char *cms_info = _gpgme_engine_get_info (GPGME_PROTOCOL_CMS); const char *cms_info = _gpgme_engine_get_info (GPGME_PROTOCOL_CMS);
char *info; char *info;
/* FIXME: Make sure that only one instance does run. */
if (engine_info)
return engine_info;
if (!openpgp_info && !cms_info) if (!openpgp_info && !cms_info)
info = "<EngineInfo>\n</EngineInfo>\n"; info = "<EngineInfo>\n</EngineInfo>\n";
else if (!openpgp_info || !cms_info) else if (!openpgp_info || !cms_info)
@ -164,7 +166,8 @@ gpgme_get_engine_info ()
"%s" "%s"
"</EngineInfo>\n"; "</EngineInfo>\n";
info = xtrymalloc (strlen(fmt) + strlen(openpgp_info info = xtrymalloc (strlen (fmt)
+ strlen (openpgp_info
? openpgp_info : cms_info) + 1); ? openpgp_info : cms_info) + 1);
if (info) if (info)
sprintf (info, fmt, openpgp_info ? openpgp_info : cms_info); sprintf (info, fmt, openpgp_info ? openpgp_info : cms_info);
@ -184,9 +187,12 @@ gpgme_get_engine_info ()
" <error>Out of core</error>\n" " <error>Out of core</error>\n"
"</EngineInfo>\n"; "</EngineInfo>\n";
engine_info = info; engine_info = info;
}
UNLOCK (engine_info_lock);
return engine_info; return engine_info;
} }
/** /**
* gpgme_check_engine: * gpgme_check_engine:
* *

View File

@ -37,6 +37,8 @@
#include "util.h" #include "util.h"
DEFINE_STATIC_LOCK (get_path_lock);
/* Return a string from the Win32 Registry or NULL in case of error. /* Return a string from the Win32 Registry or NULL in case of error.
Caller must release the return value. A NULL for root is an alias Caller must release the return value. A NULL for root is an alias
for HKEY_CURRENT_USER. */ for HKEY_CURRENT_USER. */
@ -112,27 +114,31 @@ find_program_in_registry (const char *name)
const char * const char *
_gpgme_get_gpg_path (void) _gpgme_get_gpg_path (void)
{ {
static char *gpg_program = NULL; static char *gpg_program;
LOCK (get_path_lock);
if (!gpg_program) if (!gpg_program)
gpg_program = find_program_in_registry ("gpgProgram"); gpg_program = find_program_in_registry ("gpgProgram");
#ifdef GPG_PATH #ifdef GPG_PATH
if (!gpg_program) if (!gpg_program)
gpg_program = GPG_PATH; gpg_program = GPG_PATH;
#endif #endif
UNLOCK (get_path_lock);
return gpg_program; return gpg_program;
} }
const char * const char *
_gpgme_get_gpgsm_path (void) _gpgme_get_gpgsm_path (void)
{ {
static char *gpgsm_program = NULL; static char *gpgsm_program;
LOCK (get_path_lock);
if (!gpgsm_program) if (!gpgsm_program)
gpgsm_program = find_program_in_registry ("gpgsmProgram"); gpgsm_program = find_program_in_registry ("gpgsmProgram");
#ifdef GPGSM_PATH #ifdef GPGSM_PATH
if (!gpgsm_program) if (!gpgsm_program)
gpgsm_program = GPGSM_PATH; gpgsm_program = GPGSM_PATH;
#endif #endif
UNLOCK (get_path_lock);
return gpgsm_program; return gpgsm_program;
} }