080858e08e049f5a444f9662d9d9220a869f5340
[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
31 #include "util.h"
32 #include "context.h"
33 #include "ops.h"
34 #include "wait.h"
35 #include "io.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_table()    do { } while (0)
48 #define unlock_table()  do { } while (0)
49
50
51 struct wait_item_s {
52     volatile int active;
53     int (*handler)(void*,int,int);
54     void *handler_value;
55     int pid;
56     int inbound;       /* this is an inbound data handler fd */
57     int exited;
58     int exit_status;  
59     int exit_signal;
60     GpgmeCtx ctx;
61 };
62
63 static int fd_table_size;
64 static struct io_select_fd_s *fd_table;
65
66 static int do_select ( void );
67
68
69 static struct wait_item_s *
70 queue_item_from_context ( GpgmeCtx ctx )
71 {
72     struct wait_item_s *q;
73     int i;
74
75     for (i=0; i < fd_table_size; i++ ) {
76         if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->ctx == ctx )
77             return q;
78     }
79     return NULL;
80 }
81
82
83 static void
84 propagate_term_results ( const struct wait_item_s *first_q )
85 {
86     struct wait_item_s *q;
87     int i;
88     
89     for (i=0; i < fd_table_size; i++ ) {
90         if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque)
91              && q != first_q && !q->exited
92              && q->pid == first_q->pid  ) {
93             q->exited = first_q->exited;
94             q->exit_status = first_q->exit_status;
95             q->exit_signal = first_q->exit_signal;
96         }
97     }
98 }
99
100 static int
101 count_active_fds ( int pid )
102 {
103     struct wait_item_s *q;
104     int i, count = 0;
105     
106     for (i=0; i < fd_table_size; i++ ) {
107         if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque)
108              && q->active && q->pid == pid  ) 
109             count++;
110     }
111     return count;
112 }
113
114
115 /* remove the given process from the queue */
116 static void
117 remove_process ( int pid )
118 {
119     struct wait_item_s *q;
120     int i;
121
122     for (i=0; i < fd_table_size; i++ ) {
123         if (fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->pid == pid ) {
124             xfree (q);
125             fd_table[i].opaque = NULL;
126             _gpgme_io_close (fd_table[i].fd);
127             fd_table[i].fd = -1;
128         }
129     }
130 }
131
132
133
134 /**
135  * gpgme_wait:
136  * @c: 
137  * @hang: 
138  * 
139  * Wait for a finished request, if @c is given the function does only
140  * wait on a finsihed request for that context, otherwise it will return
141  * on any request.  When @hang is true the function will wait, otherwise
142  * it will return immediately when there is no pending finished request.
143  * 
144  * Return value: Context of the finished request or NULL if @hang is false
145  *  and no (or the given) request has finished.
146  **/
147 GpgmeCtx 
148 gpgme_wait ( GpgmeCtx c, int hang ) 
149 {
150     return _gpgme_wait_on_condition ( c, hang, NULL );
151 }
152
153 GpgmeCtx 
154 _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond )
155 {
156     struct wait_item_s *q;
157
158     do {
159         int did_work = do_select();
160
161         if ( cond && *cond )
162             hang = 0;
163
164         if ( !did_work ) {
165             /* We did no read/write - see whether the process is still
166              * alive */
167             assert (c); /* !c is not yet implemented */
168             q = queue_item_from_context ( c );
169             assert (q);
170             
171             if (q->exited)
172                 ;
173             else if ( _gpgme_io_waitpid (q->pid, 0,
174                                           &q->exit_status, &q->exit_signal)){
175                 q->exited = 1;     
176                 propagate_term_results (q);
177             }
178
179             if ( q->exited ) {
180                 if ( !count_active_fds (q->pid) ) {
181                     /* Hmmm, as long as we don't have a callback for
182                      * the exit status, we have no use for these
183                      * values and therefore we can remove this from
184                      * the queue */
185                     remove_process (q->pid);
186                     hang = 0;
187                 }
188             }
189         }
190     } while (hang);
191     return c;
192 }
193
194
195
196 /*
197  * We use this function to do the select stuff for all running
198  * gpgs.  A future version might provide a facility to delegate
199  * those selects to the GDK select stuff.
200  * This function must be called only by one thread!!
201  * Returns: 0 = nothing to run
202  *          1 = did run something 
203  */
204
205 static int
206 do_select ( void )
207 {
208     struct wait_item_s *q;
209     int i, n;
210     
211     n = _gpgme_io_select ( fd_table, fd_table_size );
212     if ( n <= 0 ) 
213         return 0; /* error or timeout */
214
215     for (i=0; i < fd_table_size && n; i++ ) {
216         if ( fd_table[i].fd != -1 && fd_table[i].signaled ) {
217             q = fd_table[i].opaque;
218             assert (n);
219             n--;
220             if ( q->active && q->handler (q->handler_value,
221                                           q->pid, fd_table[i].fd ) ) {
222                 q->active = 0;
223                 fd_table[i].for_read = 0;
224                 fd_table[i].for_write = 0;
225             }
226         }
227     }
228     
229     return 1;
230 }
231
232
233
234 /* 
235  * called by rungpg.c to register something for select()
236  */
237 GpgmeError
238 _gpgme_register_pipe_handler( void *opaque, 
239                               int (*handler)(void*,int,int),
240                               void *handler_value,
241                               int pid, int fd, int inbound )
242 {
243     GpgmeCtx ctx = opaque;
244     struct wait_item_s *q;
245     int i;
246
247     assert (opaque);
248     assert (handler);
249     
250     q = xtrycalloc ( 1, sizeof *q );
251     if ( !q )
252         return mk_error (Out_Of_Core);
253     q->inbound = inbound;
254     q->handler = handler;
255     q->handler_value = handler_value;
256     q->pid = pid;
257     q->ctx = ctx;
258     q->active = 1;
259
260     lock_table ();
261  again:  
262     for (i=0; i < fd_table_size; i++ ) {
263         if ( fd_table[i].fd == -1 ) {
264             fd_table[i].fd = fd;
265             fd_table[i].for_read = inbound;    
266             fd_table[i].for_write = !inbound;    
267             fd_table[i].signaled = 0;
268             fd_table[i].opaque = q;
269             unlock_table ();
270             return 0;
271         }
272     }
273     if ( fd_table_size < 50 ) {
274         /* FIXME: We have to wait until there are no other readers of the 
275          * table, i.e that the io_select is not active in another thread */
276         struct io_select_fd_s *tmp;
277
278         tmp = xtryrealloc ( fd_table, (fd_table_size + 10) * sizeof *tmp );
279         if ( tmp ) {
280             for (i=0; i < 10; i++ )
281                 tmp[fd_table_size+i].fd = -1;
282             fd_table_size += i;
283             fd_table = tmp;
284             goto again;
285         }
286     }
287
288     unlock_table ();
289     xfree (q);
290     return mk_error (Too_Many_Procs);
291 }
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308