Fix gpg_addkey() to work with GPGME_CREATE_NOPASSWD as well.
[gpgme.git] / src / wait-global.c
1 /* wait-global.c
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
4
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdlib.h>
26 #include <assert.h>
27 #include <string.h>
28 #include <errno.h>
29
30 #include "gpgme.h"
31 #include "sema.h"
32 #include "util.h"
33 #include "context.h"
34 #include "wait.h"
35 #include "priv-io.h"
36 #include "ops.h"
37 #include "debug.h"
38
39 /* The global event loop is used for all asynchronous operations
40    (except key listing) for which no user I/O callbacks are specified.
41
42    A context sets up its initial I/O callbacks and then sends the
43    GPGME_EVENT_START event.  After that, it is added to the global
44    list of active contexts.
45
46    The gpgme_wait function contains a select() loop over all file
47    descriptors in all active contexts.  If an error occurs, it closes
48    all fds in that context and moves the context to the global done
49    list.  Likewise, if a context has removed all I/O callbacks, it is
50    moved to the global done list.
51
52    All contexts in the global done list are eligible for being
53    returned by gpgme_wait if requested by the caller.  */
54
55 /* The ctx_list_lock protects the list of active and done contexts.
56    Insertion into any of these lists is only allowed when the lock is
57    held.  This allows a muli-threaded program to loop over gpgme_wait
58    and in parallel start asynchronous gpgme operations.
59
60    However, the fd tables in the contexts are not protected by this
61    lock.  They are only allowed to change either before the context is
62    added to the active list (ie, before the start event is signalled)
63    or in a callback handler.  */
64 DEFINE_STATIC_LOCK (ctx_list_lock);
65
66 /* A ctx_list_item is an item in the global list of active or done
67    contexts.  */
68 struct ctx_list_item
69 {
70   /* Every ctx_list_item is an element in a doubly linked list.  The
71      list pointers are protected by the ctx_list_lock.  */
72   struct ctx_list_item *next;
73   struct ctx_list_item *prev;
74
75   gpgme_ctx_t ctx;
76   /* The status is set when the ctx is moved to the done list.  */
77   gpgme_error_t status;
78   gpgme_error_t op_err;
79 };
80
81 /* The active list contains all contexts that are in the global event
82    loop, have active I/O callbacks, and have already seen the start
83    event.  */
84 static struct ctx_list_item *ctx_active_list;
85
86 /* The done list contains all contexts that have previously been
87    active but now are not active any longer, either because they
88    finished successfully or an I/O callback returned an error.  The
89    status field in the list item contains the error value (or 0 if
90    successful).  */
91 static struct ctx_list_item *ctx_done_list;
92
93 \f
94 /* Enter the context CTX into the active list.  */
95 static gpgme_error_t
96 ctx_active (gpgme_ctx_t ctx)
97 {
98   struct ctx_list_item *li = malloc (sizeof (struct ctx_list_item));
99   if (!li)
100     return gpg_error_from_syserror ();
101   li->ctx = ctx;
102
103   LOCK (ctx_list_lock);
104   /* Add LI to active list.  */
105   li->next = ctx_active_list;
106   li->prev = NULL;
107   if (ctx_active_list)
108     ctx_active_list->prev = li;
109   ctx_active_list = li;
110   UNLOCK (ctx_list_lock);
111   return 0;
112 }
113
114
115 /* Enter the context CTX into the done list with status STATUS.  */
116 static void
117 ctx_done (gpgme_ctx_t ctx, gpgme_error_t status, gpgme_error_t op_err)
118 {
119   struct ctx_list_item *li;
120
121   LOCK (ctx_list_lock);
122   li = ctx_active_list;
123   while (li && li->ctx != ctx)
124     li = li->next;
125   assert (li);
126
127   /* Remove LI from active list.  */
128   if (li->next)
129     li->next->prev = li->prev;
130   if (li->prev)
131     li->prev->next = li->next;
132   else
133     ctx_active_list = li->next;
134
135   li->status = status;
136   li->op_err = op_err;
137
138   /* Add LI to done list.  */
139   li->next = ctx_done_list;
140   li->prev = NULL;
141   if (ctx_done_list)
142     ctx_done_list->prev = li;
143   ctx_done_list = li;
144   UNLOCK (ctx_list_lock);
145 }
146
147
148 /* Find finished context CTX (or any context if CTX is NULL) and
149    return its status in STATUS after removing it from the done list.
150    If a matching context could be found, return it.  Return NULL if no
151    context could be found.  */
152 static gpgme_ctx_t
153 ctx_wait (gpgme_ctx_t ctx, gpgme_error_t *status, gpgme_error_t *op_err)
154 {
155   struct ctx_list_item *li;
156
157   LOCK (ctx_list_lock);
158   li = ctx_done_list;
159   if (ctx)
160     {
161       /* A specific context is requested.  */
162       while (li && li->ctx != ctx)
163         li = li->next;
164     }
165   if (li)
166     {
167       ctx = li->ctx;
168       if (status)
169         *status = li->status;
170       if (op_err)
171         *op_err = li->op_err;
172
173       /* Remove LI from done list.  */
174       if (li->next)
175         li->next->prev = li->prev;
176       if (li->prev)
177         li->prev->next = li->next;
178       else
179         ctx_done_list = li->next;
180       free (li);
181     }
182   else
183     ctx = NULL;
184   UNLOCK (ctx_list_lock);
185   return ctx;
186 }
187
188 \f
189 /* Internal I/O callback functions.  */
190
191 /* The add_io_cb and remove_io_cb handlers are shared with the private
192    event loops.  */
193
194 void
195 _gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
196                              void *type_data)
197 {
198   gpgme_ctx_t ctx = (gpgme_ctx_t) data;
199
200   assert (ctx);
201
202   switch (type)
203     {
204     case GPGME_EVENT_START:
205       {
206         gpgme_error_t err = ctx_active (ctx);
207
208         if (err)
209           /* An error occurred.  Close all fds in this context, and
210              send the error in a done event.  */
211           _gpgme_cancel_with_err (ctx, err, 0);
212       }
213       break;
214
215     case GPGME_EVENT_DONE:
216       {
217         gpgme_io_event_done_data_t done_data =
218           (gpgme_io_event_done_data_t) type_data;
219
220         ctx_done (ctx, done_data->err, done_data->op_err);
221       }
222       break;
223
224     case GPGME_EVENT_NEXT_KEY:
225       assert (!"Unexpected event GPGME_EVENT_NEXT_KEY");
226       break;
227
228     case GPGME_EVENT_NEXT_TRUSTITEM:
229       assert (!"Unexpected event GPGME_EVENT_NEXT_TRUSTITEM");
230       break;
231
232     default:
233       assert (!"Unexpected event");
234       break;
235     }
236 }
237
238
239 \f
240 /* Perform asynchronous operations in the global event loop (ie, any
241    asynchronous operation except key listing and trustitem listing
242    operations).  If CTX is not a null pointer, the function will
243    return if the asynchronous operation in the context CTX finished.
244    Otherwise the function will return if any asynchronous operation
245    finished.  If HANG is zero, the function will not block for a long
246    time.  Otherwise the function does not return until an operation
247    matching CTX finished.
248
249    If a matching context finished, it is returned, and *STATUS is set
250    to the error value of the operation in that context.  Otherwise, if
251    the timeout expires, NULL is returned and *STATUS is 0.  If an
252    error occurs, NULL is returned and *STATUS is set to the error
253    value.  */
254 gpgme_ctx_t
255 gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status,
256                 gpgme_error_t *op_err, int hang)
257 {
258   do
259     {
260       unsigned int i = 0;
261       struct ctx_list_item *li;
262       struct fd_table fdt;
263       int nr;
264
265       /* Collect the active file descriptors.  */
266       LOCK (ctx_list_lock);
267       for (li = ctx_active_list; li; li = li->next)
268         i += li->ctx->fdt.size;
269       fdt.fds = malloc (i * sizeof (struct io_select_fd_s));
270       if (!fdt.fds)
271         {
272           int saved_err = gpg_error_from_syserror ();
273           UNLOCK (ctx_list_lock);
274           if (status)
275             *status = saved_err;
276           if (op_err)
277             *op_err = 0;
278           return NULL;
279         }
280       fdt.size = i;
281       i = 0;
282       for (li = ctx_active_list; li; li = li->next)
283         {
284           memcpy (&fdt.fds[i], li->ctx->fdt.fds,
285                   li->ctx->fdt.size * sizeof (struct io_select_fd_s));
286           i += li->ctx->fdt.size;
287         }
288       UNLOCK (ctx_list_lock);
289
290       nr = _gpgme_io_select (fdt.fds, fdt.size, 0);
291       if (nr < 0)
292         {
293           int saved_err = gpg_error_from_syserror ();
294           free (fdt.fds);
295           if (status)
296             *status = saved_err;
297           if (op_err)
298             *op_err = 0;
299           return NULL;
300         }
301
302       for (i = 0; i < fdt.size && nr; i++)
303         {
304           if (fdt.fds[i].fd != -1 && fdt.fds[i].signaled)
305             {
306               gpgme_ctx_t ictx;
307               gpgme_error_t err = 0;
308               gpgme_error_t local_op_err = 0;
309               struct wait_item_s *item;
310
311               assert (nr);
312               nr--;
313
314               item = (struct wait_item_s *) fdt.fds[i].opaque;
315               assert (item);
316               ictx = item->ctx;
317               assert (ictx);
318
319               LOCK (ctx->lock);
320               if (ctx->canceled)
321                 err = gpg_error (GPG_ERR_CANCELED);
322               UNLOCK (ctx->lock);
323
324               if (!err)
325                 err = _gpgme_run_io_cb (&fdt.fds[i], 0, &local_op_err);
326               if (err || local_op_err)
327                 {
328                   /* An error occurred.  Close all fds in this context,
329                      and signal it.  */
330                   _gpgme_cancel_with_err (ictx, err, local_op_err);
331
332                   /* Break out of the loop, and retry the select()
333                      from scratch, because now all fds should be
334                      gone.  */
335                   break;
336                 }
337             }
338         }
339       free (fdt.fds);
340
341       /* Now some contexts might have finished successfully.  */
342       LOCK (ctx_list_lock);
343     retry:
344       for (li = ctx_active_list; li; li = li->next)
345         {
346           gpgme_ctx_t actx = li->ctx;
347
348           for (i = 0; i < actx->fdt.size; i++)
349             if (actx->fdt.fds[i].fd != -1)
350               break;
351           if (i == actx->fdt.size)
352             {
353               struct gpgme_io_event_done_data data;
354               data.err = 0;
355               data.op_err = 0;
356
357               /* FIXME: This does not perform too well.  We have to
358                  release the lock because the I/O event handler
359                  acquires it to remove the context from the active
360                  list.  Two alternative strategies are worth
361                  considering: Either implement the DONE event handler
362                  here in a lock-free manner, or save a list of all
363                  contexts to be released and call the DONE events
364                  afterwards.  */
365               UNLOCK (ctx_list_lock);
366               _gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &data);
367               LOCK (ctx_list_lock);
368               goto retry;
369             }
370         }
371       UNLOCK (ctx_list_lock);
372
373       {
374         gpgme_ctx_t dctx = ctx_wait (ctx, status, op_err);
375
376         if (dctx)
377           {
378             ctx = dctx;
379             hang = 0;
380           }
381         else if (!hang)
382           {
383             ctx = NULL;
384             if (status)
385               *status = 0;
386             if (op_err)
387               *op_err = 0;
388           }
389       }
390     }
391   while (hang);
392
393   return ctx;
394 }
395
396
397 gpgme_ctx_t
398 gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
399 {
400   return gpgme_wait_ext (ctx, status, NULL, hang);
401 }