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