Just a backup for now
[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 "wait.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_queue()    do { } while (0)
47 #define unlock_queue()  do { } while (0)
48
49 struct wait_queue_item_s {
50     struct wait_queue_item_s *next;
51     volatile int used; 
52     volatile int active;
53     int (*handler)(void*,pid_t,int);
54     void *handler_value;
55     pid_t pid;
56     int   fd;  
57     int   inbound;       /* this is an inbound data handler fd */
58
59     int exited;
60     int exit_status;  
61     int exit_signal;
62
63     GpgmeCtx ctx;
64 };
65
66
67 static struct wait_queue_item_s wait_queue[SIZEOF_WAIT_QUEUE];
68
69 static int the_big_select ( void );
70
71
72 static void
73 init_wait_queue (void)
74 {
75     int i;
76     static int initialized = 0;
77
78     if ( initialized )  /* FIXME: This leads to a race */
79         return;
80
81     lock_queue ();
82     for (i=1; i < SIZEOF_WAIT_QUEUE; i++ )
83         wait_queue[i-1].next = &wait_queue[i];
84     initialized = 1;
85     unlock_queue();
86 }
87
88 static struct wait_queue_item_s *
89 queue_item_from_context ( GpgmeCtx ctx )
90 {
91     struct wait_queue_item_s *q;
92
93     for (q=wait_queue; q; q = q->next) {
94         if ( q->used && q->ctx == ctx )
95             return q;
96     }
97     return NULL;
98 }
99
100
101 /**
102  * gpgme_wait:
103  * @c: 
104  * @hang: 
105  * 
106  * Wait for a finished request, if @c is given the function does only
107  * wait on a finsihed request for that context, otherwise it will return
108  * on any request.  When @hang is true the function will wait, otherwise
109  * it will return immediately when there is no pending finished request.
110  * 
111  * Return value: Context of the finished request or NULL if @hang is false
112  *  and no (or the given) request has finished.
113  **/
114 GpgmeCtx 
115 gpgme_wait ( GpgmeCtx c, int hang )
116 {
117     struct wait_queue_item_s *q;
118
119     init_wait_queue ();
120     do {
121         if ( !the_big_select() ) {
122             int status;
123
124             /* We did no read/write - see whether this process is still
125              * alive */
126             assert (c); /* !c is not yet implemented */
127             q = queue_item_from_context ( c );
128             assert (q);
129             
130             if ( waitpid ( q->pid, &status, WNOHANG ) == q->pid ) {
131                 q->exited = 1;     
132                 if ( WIFSIGNALED (status) ) {
133                     q->exit_status = 4; /* Need some value here */
134                     q->exit_signal = WTERMSIG (status);
135                 }
136                 else if ( WIFEXITED (status) ) {
137                     q->exit_status = WEXITSTATUS (status);
138                 }
139                 else {
140                     q->exited++;
141                     q->exit_status = 4;
142                 }
143                 /* okay, the process has terminated - we are ready */
144                 hang = 0;
145             }
146         }
147     } while (hang);
148     return c;
149 }
150
151
152
153 /*
154  * We use this function to do the select stuff for all running
155  * gpgs.  A future version might provide a facility to delegate
156  * those selects to the GDK select stuff.
157  * This function must be called only by one thread!!
158  * FIXME: The data structures and  algorithms are stupid.
159  * Returns: 0 = nothing to run
160  *          1 = did run something 
161  */
162
163 static int
164 the_big_select ( void )
165 {
166     static fd_set readfds;
167     static fd_set writefds;
168     struct wait_queue_item_s *q;
169     int max_fd, n;
170     struct timeval timeout = { 1, 0 }; /* Use a one second timeout */
171     
172     FD_ZERO ( &readfds );
173     FD_ZERO ( &writefds );
174     max_fd = 0;
175     lock_queue ();
176     for ( q = wait_queue; q; q = q->next ) {
177         if ( q->used && q->active ) {
178             if (q->inbound) {
179                 assert ( !FD_ISSET ( q->fd, &readfds ) );
180                 FD_SET ( q->fd, &readfds );
181             }
182             else {
183                 assert ( !FD_ISSET ( q->fd, &writefds ) );
184                 FD_SET ( q->fd, &writefds );
185             }
186             if ( q->fd > max_fd )
187                 max_fd = q->fd;
188           }
189     }
190     unlock_queue ();
191
192
193     n = select ( max_fd+1, &readfds, &writefds, NULL, &timeout );
194     if ( n <= 0 ) {
195         if ( n && errno != EINTR ) {
196             fprintf (stderr, "the_big_select: select failed: %s\n",
197                      strerror (errno) );
198         }
199         return 0;
200     }
201
202     /* something has to be done.  Go over the queue and call
203      * the handlers */
204  restart:
205     while ( n ) {
206         lock_queue ();
207         for ( q = wait_queue; q; q = q->next ) {
208             if ( q->used && q->active 
209                  && FD_ISSET (q->fd, q->inbound? &readfds : &writefds ) ) {
210                 FD_CLR (q->fd, q->inbound? &readfds : &writefds );
211                 assert (n);
212                 n--;
213                 unlock_queue ();
214                 if ( q->handler (q->handler_value, q->pid, q->fd ) )
215                     q->active = 0;
216                 goto restart;
217             }
218         }
219         unlock_queue ();
220     }
221     return 1;
222 }
223
224
225
226 /* 
227  * called by rungpg.c to register something for select()
228  */
229 GpgmeError
230 _gpgme_register_pipe_handler( void *opaque, 
231                               int (*handler)(void*,pid_t,int),
232                               void *handler_value,
233                               pid_t pid, int fd, int inbound )
234 {
235     GpgmeCtx ctx = opaque;
236     struct wait_queue_item_s *q;
237
238     init_wait_queue();
239     assert (opaque);
240     assert (handler);
241     
242     lock_queue ();
243     for ( q = wait_queue; q; q = q->next ) {
244         if ( !q->used ) {
245             q->used = 1;
246             q->active = 0;
247             break;
248         }
249     }
250     unlock_queue ();
251     if ( !q ) 
252         return mk_error (Too_Many_Procs);
253
254     q->fd = fd;
255     q->inbound = inbound;
256     q->handler = handler;
257     q->handler_value = handler_value;
258     q->pid = pid;
259     q->ctx = ctx;
260     
261     /* and enable this entry for the next select */
262     q->exited = 0;
263     q->active = 1;
264     return 0;
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282