Add a passphrase callback and minor changes to the interface
[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     int exited;
57     int exit_status;  
58     int exit_signal;
59     GpgmeCtx ctx;
60 };
61
62 static int fd_table_size;
63 static struct io_select_fd_s *fd_table;
64
65 static int do_select ( 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 void
83 propagate_term_results ( const struct wait_item_s *first_q )
84 {
85     struct wait_item_s *q;
86     int i;
87     
88     for (i=0; i < fd_table_size; i++ ) {
89         if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque)
90              && q != first_q && !q->exited
91              && q->pid == first_q->pid  ) {
92             q->exited = first_q->exited;
93             q->exit_status = first_q->exit_status;
94             q->exit_signal = first_q->exit_signal;
95         }
96     }
97 }
98
99 static int
100 count_active_fds ( int pid )
101 {
102     struct wait_item_s *q;
103     int i, count = 0;
104     
105     for (i=0; i < fd_table_size; i++ ) {
106         if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque)
107              && q->active && q->pid == pid  ) 
108             count++;
109     }
110     return count;
111 }
112
113 static void
114 clear_active_fds ( int pid )
115 {
116     struct wait_item_s *q;
117     int i;
118     
119     for (i=0; i < fd_table_size; i++ ) {
120         if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque)
121              && q->active && q->pid == pid  ) 
122             q->active = 0;
123     }
124 }
125
126
127 /* remove the given process from the queue */
128 static void
129 remove_process ( int pid )
130 {
131     struct wait_item_s *q;
132     int i;
133
134     for (i=0; i < fd_table_size; i++ ) {
135         if (fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->pid == pid ) {
136             xfree (q);
137             fd_table[i].opaque = NULL;
138             
139             if ( !fd_table[i].is_closed ) {
140                 _gpgme_io_close (fd_table[i].fd);
141                 fd_table[i].is_closed = 1;
142             }
143             fd_table[i].fd = -1;
144         }
145     }
146 }
147
148
149
150 /**
151  * gpgme_wait:
152  * @c: 
153  * @hang: 
154  * 
155  * Wait for a finished request, if @c is given the function does only
156  * wait on a finsihed request for that context, otherwise it will return
157  * on any request.  When @hang is true the function will wait, otherwise
158  * it will return immediately when there is no pending finished request.
159  * 
160  * Return value: Context of the finished request or NULL if @hang is false
161  *  and no (or the given) request has finished.
162  **/
163 GpgmeCtx 
164 gpgme_wait ( GpgmeCtx c, int hang ) 
165 {
166     return _gpgme_wait_on_condition ( c, hang, NULL );
167 }
168
169 GpgmeCtx 
170 _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )
171 {
172     struct wait_item_s *q;
173
174     do {
175         int did_work = do_select();
176
177         if ( cond && *cond )
178             hang = 0;
179
180         if ( !did_work ) {
181             /* We did no read/write - see whether the process is still
182              * alive */
183             assert (c); /* !c is not yet implemented */
184             q = queue_item_from_context ( c );
185             assert (q);
186             
187             if (q->exited) {
188                 /* this is the second time we reached this and we got no
189                  * more data from the pipe (which may happen to to buffering).
190                  * Set all FDs inactive.
191                  */
192                 clear_active_fds (q->pid);
193             }
194             else if ( _gpgme_io_waitpid (q->pid, 0,
195                                           &q->exit_status, &q->exit_signal)){
196                 q->exited = 1;     
197                 propagate_term_results (q);
198             }
199
200             if ( q->exited ) {
201                 if ( !count_active_fds (q->pid) ) {
202                     /* Hmmm, as long as we don't have a callback for
203                      * the exit status, we have no use for these
204                      * values and therefore we can remove this from
205                      * the queue */
206                     remove_process (q->pid);
207                     hang = 0;
208                 }
209             }
210         }
211     } while (hang);
212     return c;
213 }
214
215
216
217 /*
218  * We use this function to do the select stuff for all running
219  * gpgs.  A future version might provide a facility to delegate
220  * those selects to the GDK select stuff.
221  * This function must be called only by one thread!!
222  * Returns: 0 = nothing to run
223  *          1 = did run something 
224  */
225
226 static int
227 do_select ( void )
228 {
229     struct wait_item_s *q;
230     int i, n;
231     int any=0;
232     
233     n = _gpgme_io_select ( fd_table, fd_table_size );
234     if ( n <= 0 ) 
235         return 0; /* error or timeout */
236
237     for (i=0; i < fd_table_size /*&& n*/; i++ ) {
238         if ( fd_table[i].fd != -1 && fd_table[i].signaled 
239              && !fd_table[i].frozen ) {
240             q = fd_table[i].opaque;
241             assert (n);
242             n--;
243             if ( q->active )
244                 any = 1;
245             if ( q->active && q->handler (q->handler_value,
246                                           q->pid, fd_table[i].fd ) ) {
247                 q->active = 0;
248                 fd_table[i].for_read = 0;
249                 fd_table[i].for_write = 0;
250                 fd_table[i].is_closed = 1;
251             }
252         }
253     }
254     
255     return any;
256 }
257
258
259
260 /* 
261  * called by rungpg.c to register something for select()
262  */
263 GpgmeError
264 _gpgme_register_pipe_handler ( void *opaque, 
265                               int (*handler)(void*,int,int),
266                               void *handler_value,
267                               int pid, int fd, int inbound )
268 {
269     GpgmeCtx ctx = opaque;
270     struct wait_item_s *q;
271     int i;
272
273     assert (opaque);
274     assert (handler);
275     
276     q = xtrycalloc ( 1, sizeof *q );
277     if ( !q )
278         return mk_error (Out_Of_Core);
279     q->inbound = inbound;
280     q->handler = handler;
281     q->handler_value = handler_value;
282     q->pid = pid;
283     q->ctx = ctx;
284     q->active = 1;
285
286     lock_table ();
287  again:  
288     for (i=0; i < fd_table_size; i++ ) {
289         if ( fd_table[i].fd == -1 ) {
290             fd_table[i].fd = fd;
291             fd_table[i].is_closed = 0;
292             fd_table[i].for_read = inbound;    
293             fd_table[i].for_write = !inbound;    
294             fd_table[i].signaled = 0;
295             fd_table[i].frozen = 0;
296             fd_table[i].opaque = q;
297             unlock_table ();
298             return 0;
299         }
300     }
301     if ( fd_table_size < 50 ) {
302         /* FIXME: We have to wait until there are no other readers of the 
303          * table, i.e that the io_select is not active in another thread */
304         struct io_select_fd_s *tmp;
305
306         tmp = xtryrealloc ( fd_table, (fd_table_size + 10) * sizeof *tmp );
307         if ( tmp ) {
308             for (i=0; i < 10; i++ )
309                 tmp[fd_table_size+i].fd = -1;
310             fd_table_size += i;
311             fd_table = tmp;
312             goto again;
313         }
314     }
315
316     unlock_table ();
317     xfree (q);
318     return mk_error (Too_Many_Procs);
319 }
320
321
322 void
323 _gpgme_freeze_fd ( int fd )
324 {
325     int i;
326
327     lock_table ();
328     for (i=0; i < fd_table_size; i++ ) {
329         if ( fd_table[i].fd == fd ) {
330             fd_table[i].frozen = 1;
331             fprintf (stderr, "** FD %d frozen\n", fd );
332             break;
333         }
334     }
335     unlock_table ();
336 }
337
338 void
339 _gpgme_thaw_fd ( int fd )
340 {
341     int i;
342
343     lock_table ();
344     for (i=0; i < fd_table_size; i++ ) {
345         if ( fd_table[i].fd == fd ) {
346             fd_table[i].frozen = 0;
347             fprintf (stderr, "** FD %d thawed\n", fd );
348             break;
349         }
350     }
351     unlock_table ();
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368