keylist does now return objects.
[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/time.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <sys/wait.h>
31
32 #include "util.h"
33 #include "context.h"
34 #include "ops.h"
35 #include "wait.h"
36
37 /* Fixme: implement the following stuff to make the code MT safe.
38  * To avoid the need to link against a specific threads lib, such
39  * an implementation should require the caller to register a function
40  * which does this task.
41  * enter_crit() and leave_crit() are used to embrace an area of code
42  * which should be executed only by one thread at a time.
43  * lock_xxxx() and unlock_xxxx()  protect access to an data object.
44  *  */
45 #define enter_crit()    do { } while (0)
46 #define leave_crit()    do { } while (0)
47 #define lock_queue()    do { } while (0)
48 #define unlock_queue()  do { } while (0)
49
50 struct wait_queue_item_s {
51     struct wait_queue_item_s *next;
52     volatile int used; 
53     volatile int active;
54     int (*handler)(void*,pid_t,int);
55     void *handler_value;
56     pid_t pid;
57     int   fd;  
58     int   inbound;       /* this is an inbound data handler fd */
59
60     int exited;
61     int exit_status;  
62     int exit_signal;
63
64     GpgmeCtx ctx;
65 };
66
67
68 static struct wait_queue_item_s wait_queue[SIZEOF_WAIT_QUEUE];
69
70 static int the_big_select ( void );
71
72
73 static void
74 init_wait_queue (void)
75 {
76     int i;
77     static int initialized = 0;
78
79     if ( initialized )  /* FIXME: This leads to a race */
80         return;
81
82     lock_queue ();
83     for (i=1; i < SIZEOF_WAIT_QUEUE; i++ )
84         wait_queue[i-1].next = &wait_queue[i];
85     initialized = 1;
86     unlock_queue();
87 }
88
89 static struct wait_queue_item_s *
90 queue_item_from_context ( GpgmeCtx ctx )
91 {
92     struct wait_queue_item_s *q;
93
94     for (q=wait_queue; q; q = q->next) {
95         if ( q->used && q->ctx == ctx )
96             return q;
97     }
98     return NULL;
99 }
100
101
102 static void
103 propagate_term_results ( const struct wait_queue_item_s *first_q )
104 {
105     struct wait_queue_item_s *q;
106     
107     for (q=wait_queue; q; q = q->next) {
108         if ( q->used && q != first_q && !q->exited
109              && q->pid == first_q->pid  ) {
110             q->exited = first_q->exited;
111             q->exit_status = first_q->exit_status;
112             q->exit_signal = first_q->exit_signal;
113         }
114     }
115 }
116
117 static int
118 count_active_fds ( pid_t pid )
119 {
120     struct wait_queue_item_s *q;
121     int count = 0;
122     
123     for (q=wait_queue; q; q = q->next) {
124         if ( q->used && q->active && q->pid == pid  ) 
125             count++;
126     }
127     return count;
128 }
129
130
131 /* remove the given process from the queue */
132 static void
133 remove_process ( pid_t pid )
134 {
135     struct wait_queue_item_s *q;
136     
137     for (q=wait_queue; q; q = q->next) {
138         if ( q->used ) {
139             close (q->fd);
140             q->handler = NULL;
141             q->ctx = NULL;
142             q->used = 0;
143         }
144     }
145 }
146
147
148
149 /**
150  * gpgme_wait:
151  * @c: 
152  * @hang: 
153  * 
154  * Wait for a finished request, if @c is given the function does only
155  * wait on a finsihed request for that context, otherwise it will return
156  * on any request.  When @hang is true the function will wait, otherwise
157  * it will return immediately when there is no pending finished request.
158  * 
159  * Return value: Context of the finished request or NULL if @hang is false
160  *  and no (or the given) request has finished.
161  **/
162 GpgmeCtx 
163 gpgme_wait ( GpgmeCtx c, int hang ) 
164 {
165     return _gpgme_wait_on_condition ( c, hang, NULL );
166 }
167
168 GpgmeCtx 
169 _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )
170 {
171     struct wait_queue_item_s *q;
172
173     init_wait_queue ();
174     do {
175         int did_work = the_big_select();
176
177         if ( cond && *cond )
178             hang = 0;
179
180         if ( !did_work ) {
181             int status;
182
183             /* We did no read/write - see whether this process is still
184              * alive */
185             assert (c); /* !c is not yet implemented */
186             q = queue_item_from_context ( c );
187             assert (q);
188             
189             if (q->exited)
190                 ;
191             else if ( waitpid ( q->pid, &status, WNOHANG ) == q->pid ) {
192                 q->exited = 1;     
193                 if ( WIFSIGNALED (status) ) {
194                     q->exit_status = 4; /* Need some value here */
195                     q->exit_signal = WTERMSIG (status);
196                 }
197                 else if ( WIFEXITED (status) ) {
198                     q->exit_status = WEXITSTATUS (status);
199                 }
200                 else {
201                     q->exited++;
202                     q->exit_status = 4;
203                 }
204                 propagate_term_results (q);
205             }
206
207             if ( q->exited ) {
208                 if ( !count_active_fds (q->pid) ) {
209                     /* Hmmm, as long as we don't have a callback for
210                      * the exit status, we have no use for these
211                      * values and therefore we can remove this from
212                      * the queue */
213                     remove_process (q->pid);
214                     hang = 0;
215                 }
216             }
217         }
218     } while (hang);
219     return c;
220 }
221
222
223
224 /*
225  * We use this function to do the select stuff for all running
226  * gpgs.  A future version might provide a facility to delegate
227  * those selects to the GDK select stuff.
228  * This function must be called only by one thread!!
229  * FIXME: The data structures and  algorithms are stupid.
230  * Returns: 0 = nothing to run
231  *          1 = did run something 
232  */
233
234 static int
235 the_big_select ( void )
236 {
237     static fd_set readfds;
238     static fd_set writefds;
239     struct wait_queue_item_s *q;
240     int max_fd, n;
241     struct timeval timeout = { 1, 0 }; /* Use a one second timeout */
242     
243     FD_ZERO ( &readfds );
244     FD_ZERO ( &writefds );
245     max_fd = 0;
246     lock_queue ();
247     for ( q = wait_queue; q; q = q->next ) {
248         if ( q->used && q->active ) {
249             if (q->inbound) {
250                 assert ( !FD_ISSET ( q->fd, &readfds ) );
251                 FD_SET ( q->fd, &readfds );
252             }
253             else {
254                 assert ( !FD_ISSET ( q->fd, &writefds ) );
255                 FD_SET ( q->fd, &writefds );
256             }
257             if ( q->fd > max_fd )
258                 max_fd = q->fd;
259           }
260     }
261     unlock_queue ();
262
263
264     n = select ( max_fd+1, &readfds, &writefds, NULL, &timeout );
265     if ( n <= 0 ) {
266         if ( n && errno != EINTR ) {
267             fprintf (stderr, "the_big_select: select failed: %s\n",
268                      strerror (errno) );
269         }
270         return 0;
271     }
272
273     /* something has to be done.  Go over the queue and call
274      * the handlers */
275  restart:
276     while ( n ) {
277         lock_queue ();
278         for ( q = wait_queue; q; q = q->next ) {
279             if ( q->used && q->active 
280                  && FD_ISSET (q->fd, q->inbound? &readfds : &writefds ) ) {
281                 FD_CLR (q->fd, q->inbound? &readfds : &writefds );
282                 assert (n);
283                 n--;
284                 unlock_queue ();
285                 if ( q->handler (q->handler_value, q->pid, q->fd ) )
286                     q->active = 0;
287                 goto restart;
288             }
289         }
290         unlock_queue ();
291     }
292     return 1;
293 }
294
295
296
297 /* 
298  * called by rungpg.c to register something for select()
299  */
300 GpgmeError
301 _gpgme_register_pipe_handler( void *opaque, 
302                               int (*handler)(void*,pid_t,int),
303                               void *handler_value,
304                               pid_t pid, int fd, int inbound )
305 {
306     GpgmeCtx ctx = opaque;
307     struct wait_queue_item_s *q;
308
309     init_wait_queue();
310     assert (opaque);
311     assert (handler);
312     
313     lock_queue ();
314     for ( q = wait_queue; q; q = q->next ) {
315         if ( !q->used ) {
316             q->used = 1;
317             q->active = 0;
318             break;
319         }
320     }
321     unlock_queue ();
322     if ( !q ) 
323         return mk_error (Too_Many_Procs);
324
325     q->fd = fd;
326     q->inbound = inbound;
327     q->handler = handler;
328     q->handler_value = handler_value;
329     q->pid = pid;
330     q->ctx = ctx;
331     
332     /* and enable this entry for the next select */
333     q->exited = 0;
334     q->active = 1;
335     return 0;
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353