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