reap off gpg processes
[gpgme.git] / gpgme / wait.c
1 /* wait.c 
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include "syshdr.h"
29
30 #include "util.h"
31 #include "context.h"
32 #include "ops.h"
33 #include "wait.h"
34 #include "io.h"
35
36 /* Fixme: implement the following stuff to make the code MT safe.
37  * To avoid the need to link against a specific threads lib, such
38  * an implementation should require the caller to register a function
39  * which does this task.
40  * enter_crit() and leave_crit() are used to embrace an area of code
41  * which should be executed only by one thread at a time.
42  * lock_xxxx() and unlock_xxxx()  protect access to an data object.
43  *  */
44 #define enter_crit()    do { } while (0)
45 #define leave_crit()    do { } while (0)
46 #define lock_table()    do { } while (0)
47 #define unlock_table()  do { } while (0)
48
49
50 struct wait_item_s {
51     volatile int active;
52     int (*handler)(void*,int,int);
53     void *handler_value;
54     int pid;
55     int inbound;       /* this is an inbound data handler fd */
56     GpgmeCtx ctx;
57 };
58
59 static int fd_table_size;
60 static struct io_select_fd_s *fd_table;
61
62 static void (*idle_function) (void);
63
64 static int do_select ( void );
65 static void run_idle (void);
66
67
68 static struct wait_item_s *
69 queue_item_from_context ( GpgmeCtx ctx )
70 {
71     struct wait_item_s *q;
72     int i;
73
74     for (i=0; i < fd_table_size; i++ ) {
75         if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->ctx == ctx )
76             return q;
77     }
78     return NULL;
79 }
80
81
82 static int
83 count_active_and_thawed_fds ( int pid )
84 {
85     struct wait_item_s *q;
86     int i, count = 0;
87     
88     for (i=0; i < fd_table_size; i++ ) {
89         if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque)
90              && q->active && !fd_table[i].frozen && q->pid == pid  ) 
91             count++;
92     }
93     return count;
94 }
95
96 /* remove the given process from the queue */
97 static void
98 remove_process ( int pid )
99 {
100     struct wait_item_s *q;
101     int i;
102
103     for (i=0; i < fd_table_size; i++ ) {
104         if (fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->pid == pid ) {
105             xfree (q);
106             fd_table[i].opaque = NULL;
107             
108             if ( !fd_table[i].is_closed ) {
109                 _gpgme_io_close (fd_table[i].fd);
110                 fd_table[i].is_closed = 1;
111             }
112             fd_table[i].fd = -1;
113         }
114     }
115 }
116
117
118
119 /**
120  * gpgme_wait:
121  * @c: 
122  * @hang: 
123  * 
124  * Wait for a finished request, if @c is given the function does only
125  * wait on a finsihed request for that context, otherwise it will return
126  * on any request.  When @hang is true the function will wait, otherwise
127  * it will return immediately when there is no pending finished request.
128  * 
129  * Return value: Context of the finished request or NULL if @hang is false
130  *  and no (or the given) request has finished.
131  **/
132 GpgmeCtx 
133 gpgme_wait ( GpgmeCtx c, int hang ) 
134 {
135     return _gpgme_wait_on_condition ( c, hang, NULL );
136 }
137
138 GpgmeCtx 
139 _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )
140 {
141     struct wait_item_s *q;
142
143     do {
144         int did_work = do_select();
145
146         if ( cond && *cond )
147             hang = 0;
148
149         if ( !did_work ) {
150             /* We did no read/write - see whether the process is still
151              * alive */
152             assert (c); /* !c is not yet implemented */
153             q = queue_item_from_context ( c );
154             assert (q);
155             
156             if ( !count_active_and_thawed_fds (q->pid) ) {
157                 remove_process (q->pid);
158                 hang = 0;
159             }
160         }
161         if (hang)
162             run_idle ();
163     } while (hang && !c->cancel );
164     c->cancel = 0; /* fixme: fix all functions, to return a cancel error */
165     return c;
166 }
167
168
169
170 /*
171  * We use this function to do the select stuff for all running
172  * gpgs.  A future version might provide a facility to delegate
173  * those selects to the GDK select stuff.
174  * This function must be called only by one thread!!
175  * Returns: 0 = nothing to run
176  *          1 = did run something 
177  */
178
179 static int
180 do_select ( void )
181 {
182     struct wait_item_s *q;
183     int i, n;
184     int any=0;
185     
186     n = _gpgme_io_select ( fd_table, fd_table_size );
187     if ( n <= 0 ) 
188         return 0; /* error or timeout */
189
190     for (i=0; i < fd_table_size && n; i++ ) {
191         if ( fd_table[i].fd != -1 && fd_table[i].signaled 
192              && !fd_table[i].frozen ) {
193             q = fd_table[i].opaque;
194             assert (n);
195             n--;
196             if ( q->active )
197                 any = 1;
198             if ( q->active && q->handler (q->handler_value,
199                                           q->pid, fd_table[i].fd ) ) {
200                 DEBUG1 ("setting fd %d inactive", fd_table[i].fd );
201                 q->active = 0;
202                 fd_table[i].for_read = 0;
203                 fd_table[i].for_write = 0;
204                 fd_table[i].is_closed = 1;
205             }
206         }
207     }
208     
209     return any;
210 }
211
212
213
214 /* 
215  * called by rungpg.c to register something for select()
216  */
217 GpgmeError
218 _gpgme_register_pipe_handler ( void *opaque, 
219                               int (*handler)(void*,int,int),
220                               void *handler_value,
221                               int pid, int fd, int inbound )
222 {
223     GpgmeCtx ctx = opaque;
224     struct wait_item_s *q;
225     int i;
226
227     assert (opaque);
228     assert (handler);
229     
230     q = xtrycalloc ( 1, sizeof *q );
231     if ( !q )
232         return mk_error (Out_Of_Core);
233     q->inbound = inbound;
234     q->handler = handler;
235     q->handler_value = handler_value;
236     q->pid = pid;
237     q->ctx = ctx;
238     q->active = 1;
239
240     lock_table ();
241  again:  
242     for (i=0; i < fd_table_size; i++ ) {
243         if ( fd_table[i].fd == -1 ) {
244             fd_table[i].fd = fd;
245             fd_table[i].is_closed = 0;
246             fd_table[i].for_read = inbound;    
247             fd_table[i].for_write = !inbound;    
248             fd_table[i].signaled = 0;
249             fd_table[i].frozen = 0;
250             fd_table[i].opaque = q;
251             unlock_table ();
252             return 0;
253         }
254     }
255     if ( fd_table_size < 50 ) {
256         /* FIXME: We have to wait until there are no other readers of the 
257          * table, i.e that the io_select is not active in another thread */
258         struct io_select_fd_s *tmp;
259
260         tmp = xtryrealloc ( fd_table, (fd_table_size + 10) * sizeof *tmp );
261         if ( tmp ) {
262             for (i=0; i < 10; i++ )
263                 tmp[fd_table_size+i].fd = -1;
264             fd_table_size += i;
265             fd_table = tmp;
266             goto again;
267         }
268     }
269
270     unlock_table ();
271     xfree (q);
272     return mk_error (Too_Many_Procs);
273 }
274
275
276 void
277 _gpgme_freeze_fd ( int fd )
278 {
279     int i;
280
281     lock_table ();
282     for (i=0; i < fd_table_size; i++ ) {
283         if ( fd_table[i].fd == fd ) {
284             fd_table[i].frozen = 1;
285             DEBUG1 ("fd %d frozen", fd );
286             break;
287         }
288     }
289     unlock_table ();
290 }
291
292 void
293 _gpgme_thaw_fd ( int fd )
294 {
295     int i;
296
297     lock_table ();
298     for (i=0; i < fd_table_size; i++ ) {
299         if ( fd_table[i].fd == fd ) {
300             fd_table[i].frozen = 0;
301             DEBUG1 ("fd %d thawed", fd );
302             break;
303         }
304     }
305     unlock_table ();
306 }
307
308
309 /**
310  * gpgme_register_idle:
311  * @fnc: Callers idle function
312  * 
313  * Register a function with GPGME called by GPGME whenever it feels
314  * that is is idle.  NULL may be used to remove this function.
315  **/
316 void
317 gpgme_register_idle ( void (*fnc)(void) )
318 {
319     idle_function = fnc;
320 }
321
322
323 static void
324 run_idle ()
325 {
326     _gpgme_gpg_housecleaning ();
327     if (idle_function)
328         idle_function ();
329 }
330