2002-03-17 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / wait.c
1 /* wait.c 
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001, 2002 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27 #include <errno.h>
28 #include <sys/types.h>
29
30 #include "util.h"
31 #include "context.h"
32 #include "ops.h"
33 #include "wait.h"
34 #include "sema.h"
35 #include "io.h"
36 #include "engine.h"
37
38 struct wait_item_s;
39 struct proc_s;
40
41 static struct proc_s *proc_queue;
42 DEFINE_STATIC_LOCK (proc_queue_lock);
43
44 static int fd_table_size;
45 static struct io_select_fd_s *fd_table;
46 DEFINE_STATIC_LOCK (fd_table_lock);
47
48 static GpgmeIdleFunc idle_function;
49
50
51 struct proc_s
52 {
53   struct proc_s *next;
54   int pid;
55   GpgmeCtx ctx;
56   struct wait_item_s *handler_list;
57   /* Non-zero if the process has been completed.  */
58   int done;
59   /* Non-zero if the status for this process has been returned
60      already.  */
61   int reported;
62 };
63
64 struct wait_item_s {
65     struct wait_item_s *next;
66     int (*handler)(void*,int,int);
67     void *handler_value;
68     int inbound;       /* this is an inbound data handler fd */
69     struct proc_s *proc; /* backlink */
70     int done;
71     int frozen; /* copy of the frozen flag from the fd_table */
72 };
73
74
75
76 static int do_select ( void );
77 static void run_idle (void);
78
79
80 /* only to be called with a locked proc_queue */
81 static int
82 count_running_fds (struct proc_s *proc)
83 {
84   struct wait_item_s *q;
85   int count = 0;
86
87   for (q = proc->handler_list; q; q=q->next)
88     {
89       if (!q->frozen && !q->done)
90         count++;
91     }
92   return count;
93 }
94
95 /* only to be called with a locked proc_queue */
96 static void
97 set_process_done (struct proc_s *proc)
98 {
99   struct wait_item_s *q, *q2;
100   int i;
101
102   assert (proc);
103   DEBUG2 ("set_process_done(%p) pid=%d", proc, proc->pid);
104   LOCK (fd_table_lock);
105   for (q = proc->handler_list; q; q=q2)
106     {
107       q2 = q->next;
108       for (i = 0; i < fd_table_size; i++)
109         {
110           if (fd_table[i].fd != -1 && q == fd_table[i].opaque)
111             {
112               fd_table[i].opaque = NULL;
113               fd_table[i].fd = -1;
114             }
115         }
116       xfree (q);
117     }
118   UNLOCK (fd_table_lock);
119   proc->handler_list = NULL;
120   proc->done = 1;
121 }
122
123 void
124 _gpgme_remove_proc_from_wait_queue (int pid)
125 {
126     struct proc_s *proc, *last;
127
128     DEBUG1 ("removing process %d", pid);
129     LOCK (proc_queue_lock);
130     for (last = NULL, proc = proc_queue; proc; last = proc, proc = proc->next)
131       {
132         if (proc->pid == pid)
133           {
134             set_process_done (proc);
135             if (!last) 
136               proc_queue = proc->next;
137             else 
138               last->next = proc->next;
139             xfree (proc);
140             break;
141           }
142       }
143     UNLOCK (proc_queue_lock);
144 }
145
146
147 /**
148  * gpgme_wait:
149  * @c: 
150  * @hang: 
151  * 
152  * Wait for a finished request, if @c is given the function does only
153  * wait on a finished request for that context, otherwise it will return
154  * on any request.  When @hang is true the function will wait, otherwise
155  * it will return immediately when there is no pending finished request.
156  * 
157  * Return value: Context of the finished request or NULL if @hang is false
158  *  and no (or the given) request has finished.
159  **/
160 GpgmeCtx 
161 gpgme_wait (GpgmeCtx ctx, GpgmeError *status, int hang)
162 {
163   GpgmeCtx retctx = _gpgme_wait_on_condition (ctx, hang, NULL);
164   if (status)
165     *status = retctx->error;
166   return retctx;
167 }
168
169 GpgmeCtx 
170 _gpgme_wait_on_condition (GpgmeCtx ctx, int hang, volatile int *cond)
171 {
172   DEBUG3 ("waiting... ctx=%p hang=%d cond=%p", ctx, hang, cond);
173   do
174     {
175       int any = 0;
176       struct proc_s *proc;
177
178       do_select ();
179
180       if (cond && *cond)
181         hang = 0;
182       else
183         {
184           LOCK (proc_queue_lock);
185           for (proc = proc_queue; proc; proc = proc->next)
186             {
187               /* A process is done if it has completed voluntarily, or
188                  if the context it lived in was canceled.  */
189               if (!proc->done && !count_running_fds (proc))
190                 set_process_done (proc);
191               else if (!proc->done && proc->ctx->cancel)
192                 {
193                   set_process_done (proc);
194                   proc->ctx->cancel = 0;
195                   proc->ctx->error = mk_error (Canceled);
196                 }
197               /* A process that is done is eligible for election if it
198                  is in the requested context or if it was not yet
199                  reported.  */
200               if (proc->done && (proc->ctx == ctx || (!ctx && !proc->reported)))
201                 {
202                   if (!ctx)
203                     ctx = proc->ctx;
204                   hang = 0;
205                   ctx->pending = 0;
206                   proc->reported = 1;
207                 }
208               if (!proc->done)
209                 any = 1;
210             }
211           UNLOCK (proc_queue_lock);
212           if (!any)
213             hang = 0;
214         }
215       /* fixme: We should check here for hanging processes.  */
216
217       if (hang)
218         run_idle ();
219     }
220   while (hang && (!ctx || !ctx->cancel));
221   if (ctx && ctx->cancel)
222     {
223       /* FIXME: Paranoia?  */
224       ctx->cancel = 0;
225       ctx->pending = 0;
226       ctx->error = mk_error (Canceled);
227     }
228   return ctx;
229 }
230
231
232 /*
233  * We use this function to do the select stuff for all running
234  * gpgs.  A future version might provide a facility to delegate
235  * those selects to the GDK select stuff.
236  * This function must be called only by one thread!!
237  * Returns: 0 = nothing to run
238  *          1 = did run something 
239  */
240
241 static int
242 do_select (void)
243 {
244   int i, n;
245   int any = 0;
246     
247   n = _gpgme_io_select (fd_table, fd_table_size);
248   if (n <= 0) 
249     return 0; /* error or timeout */
250
251   for (i = 0; i < fd_table_size && n; i++)
252     {
253       if (fd_table[i].fd != -1 && fd_table[i].signaled 
254           && !fd_table[i].frozen)
255         {
256           struct wait_item_s *q;
257
258           assert (n);
259           n--;
260             
261           q = fd_table[i].opaque;
262           assert (q);
263           assert (q->proc);
264           assert (!q->done);
265           any = 1;
266           if (q->handler (q->handler_value,
267                           q->proc->pid, fd_table[i].fd))
268             {
269               DEBUG2 ("setting fd %d (q=%p) done", fd_table[i].fd, q);
270               q->done = 1;
271               /* Free the table entry.  */
272               LOCK (fd_table_lock);
273               fd_table[i].for_read = 0;
274               fd_table[i].for_write = 0;
275               fd_table[i].fd = -1;
276               fd_table[i].opaque = NULL;
277               UNLOCK (fd_table_lock);
278             }
279         }
280     }
281     
282   return any;
283 }
284
285
286 /* 
287  * called by rungpg.c to register something for select()
288  */
289 GpgmeError
290 _gpgme_register_pipe_handler (void *opaque, 
291                               int (*handler)(void*,int,int),
292                               void *handler_value,
293                               int pid, int fd, int inbound)
294 {
295   GpgmeCtx ctx = opaque;
296   struct wait_item_s *q;
297   struct proc_s *proc;
298   int i;
299
300   assert (opaque);
301   assert (handler);
302
303   /* Allocate a structure to hold info about the handler.  */
304   q = xtrycalloc (1, sizeof *q);
305   if (!q)
306     return mk_error (Out_Of_Core);
307   q->inbound = inbound;
308   q->handler = handler;
309   q->handler_value = handler_value;
310
311   /* Put this into the process queue.  */
312   LOCK (proc_queue_lock);
313   for (proc = proc_queue; proc && proc->pid != pid; proc = proc->next)
314     ;
315   if (!proc)
316     {
317       /* A new process.  */
318       proc = xtrycalloc (1, sizeof *proc);
319       if (!proc)
320         {
321           UNLOCK (proc_queue_lock);
322           return mk_error (Out_Of_Core);
323         }
324       proc->pid = pid;
325       proc->ctx = ctx;
326       proc->next = proc_queue;
327       proc_queue = proc;
328     }
329   assert (proc->ctx == ctx);
330   q->proc = proc;
331   q->next = proc->handler_list;
332   proc->handler_list = q;
333   UNLOCK (proc_queue_lock);
334     
335   LOCK (fd_table_lock);
336  again:  
337   for (i=0; i < fd_table_size; i++)
338     {
339       if (fd_table[i].fd == -1)
340         {
341           fd_table[i].fd = fd;
342           fd_table[i].for_read = inbound;    
343           fd_table[i].for_write = !inbound;    
344           fd_table[i].signaled = 0;
345           fd_table[i].frozen = 0;
346           fd_table[i].opaque = q;
347           UNLOCK (fd_table_lock);
348           return 0;
349         }
350     }
351   if ( fd_table_size < 50 ) {
352     /* FIXME: We have to wait until there are no other readers of the 
353      * table, i.e that the io_select is not active in another thread */
354     struct io_select_fd_s *tmp;
355     
356     tmp = xtryrealloc (fd_table, (fd_table_size + 10) * sizeof *tmp);
357     if (tmp)
358       {
359         for (i = 0; i < 10; i++)
360           tmp[fd_table_size+i].fd = -1;
361         fd_table_size += i;
362         fd_table = tmp;
363         goto again;
364       }
365   }
366
367   UNLOCK (fd_table_lock);
368   xfree (q);
369   /* FIXME: Remove the proc table entry.  */
370   return mk_error (Too_Many_Procs);
371 }
372
373
374 void
375 _gpgme_freeze_fd (int fd)
376 {
377   int i;
378
379   LOCK (fd_table_lock);
380   for (i = 0; i < fd_table_size; i++)
381     {
382       if (fd_table[i].fd == fd)
383         {
384           struct wait_item_s *q;
385
386           fd_table[i].frozen = 1;
387           q = fd_table[i].opaque;
388           if (q)
389             q->frozen = 1;
390           DEBUG2 ("fd %d frozen (q=%p)", fd, q);
391           break;
392         }
393     }
394   UNLOCK (fd_table_lock);
395 }
396
397 void
398 _gpgme_thaw_fd (int fd)
399 {
400   int i;
401
402   LOCK (fd_table_lock);
403   for (i = 0; i < fd_table_size; i++)
404     {
405       if (fd_table[i].fd == fd)
406         {
407           struct wait_item_s *q;
408
409           fd_table[i].frozen = 0;
410           q = fd_table[i].opaque;
411           if (q)
412             q->frozen = 0;
413           DEBUG2 ("fd %d thawed (q=%p)", fd, q);
414           break;
415         }
416     }
417   UNLOCK (fd_table_lock);
418 }
419
420
421 /**
422  * gpgme_register_idle:
423  * @fnc: Callers idle function
424  * 
425  * Register a function with GPGME called by GPGME whenever it feels
426  * that is is idle.  NULL may be used to remove this function.
427  *
428  * Return value: The idle function pointer that was passed to the
429  * function at the last time it was invoked, or NULL if the function
430  * is invoked the first time.
431  **/
432 GpgmeIdleFunc
433 gpgme_register_idle (GpgmeIdleFunc idle)
434 {
435   GpgmeIdleFunc old_idle = idle_function;
436
437   idle_function = idle;
438   return old_idle;
439 }
440
441
442 static void
443 run_idle ()
444 {
445   _gpgme_engine_housecleaning ();
446   if (idle_function)
447     idle_function ();
448 }