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