2009-10-26 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / src / wait.c
1 /* wait.c 
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH
4  
5    This file is part of GPGME.
6  
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11    
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <sys/types.h>
30
31 #include "util.h"
32 #include "context.h"
33 #include "ops.h"
34 #include "wait.h"
35 #include "sema.h"
36 #include "priv-io.h"
37 #include "engine.h"
38 #include "debug.h"
39
40 \f
41 void
42 _gpgme_fd_table_init (fd_table_t fdt)
43 {
44   fdt->fds = NULL;
45   fdt->size = 0;
46 }
47
48 void
49 _gpgme_fd_table_deinit (fd_table_t fdt)
50 {
51   if (fdt->fds)
52     free (fdt->fds);
53 }
54
55
56 /* XXX We should keep a marker and roll over for speed.  */
57 static gpgme_error_t
58 fd_table_put (fd_table_t fdt, int fd, int dir, void *opaque, int *idx)
59 {
60   unsigned int i, j;
61   struct io_select_fd_s *new_fds;
62
63   for (i = 0; i < fdt->size; i++)
64     {
65       if (fdt->fds[i].fd == -1)
66         break;
67     }
68   if (i == fdt->size)
69     {
70 #define FDT_ALLOCSIZE 10
71       new_fds = realloc (fdt->fds, (fdt->size + FDT_ALLOCSIZE)
72                          * sizeof (*new_fds));
73       if (!new_fds)
74         return gpg_error_from_errno (errno);
75       
76       fdt->fds = new_fds;
77       fdt->size += FDT_ALLOCSIZE;
78       for (j = 0; j < FDT_ALLOCSIZE; j++)
79         fdt->fds[i + j].fd = -1;
80     }
81
82   fdt->fds[i].fd = fd;
83   fdt->fds[i].for_read = (dir == 1);
84   fdt->fds[i].for_write = (dir == 0);
85   fdt->fds[i].signaled = 0;
86   fdt->fds[i].opaque = opaque;
87   *idx = i;
88   return 0;
89 }
90
91 \f
92 /* Register the file descriptor FD with the handler FNC (which gets
93    FNC_DATA as its first argument) for the direction DIR.  DATA should
94    be the context for which the fd is added.  R_TAG will hold the tag
95    that can be used to remove the fd.  */
96 gpgme_error_t
97 _gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc,
98                   void *fnc_data, void **r_tag)
99 {
100   gpgme_error_t err;
101   gpgme_ctx_t ctx = (gpgme_ctx_t) data;
102   fd_table_t fdt;
103   struct wait_item_s *item;
104   struct tag *tag;
105
106   assert (fnc);
107   assert (ctx);
108
109   fdt = &ctx->fdt;
110   assert (fdt);
111
112   tag = malloc (sizeof *tag);
113   if (!tag)
114     return gpg_error_from_errno (errno);
115   tag->ctx = ctx;
116
117   /* Allocate a structure to hold information about the handler.  */
118   item = calloc (1, sizeof *item);
119   if (!item)
120     {
121       int saved_errno = errno;
122       free (tag);
123       return gpg_error_from_errno (saved_errno);
124     }
125   item->ctx = ctx;
126   item->dir = dir;
127   item->handler = fnc;
128   item->handler_value = fnc_data;
129
130   err = fd_table_put (fdt, fd, dir, item, &tag->idx);
131   if (err)
132     {
133       free (tag);
134       free (item);
135       return err;
136     }
137
138   TRACE3 (DEBUG_CTX, "_gpgme_add_io_cb", ctx,
139           "fd %d, dir=%d -> tag=%p", fd, dir, tag);
140
141   *r_tag = tag;
142   return 0;
143 }
144
145
146 void
147 _gpgme_remove_io_cb (void *data)
148 {
149   struct tag *tag = data;
150   gpgme_ctx_t ctx;
151   fd_table_t fdt;
152   int idx;
153
154   assert (tag);
155   ctx = tag->ctx;
156   assert (ctx);
157   fdt = &ctx->fdt;
158   assert (fdt);
159   idx = tag->idx;
160
161   TRACE2 (DEBUG_CTX, "_gpgme_remove_io_cb", data,
162           "setting fd 0x%x (item=%p) done", fdt->fds[idx].fd,
163           fdt->fds[idx].opaque);
164
165   free (fdt->fds[idx].opaque);
166   free (tag);
167
168   /* Free the table entry.  */
169   fdt->fds[idx].fd = -1;
170   fdt->fds[idx].for_read = 0;
171   fdt->fds[idx].for_write = 0;
172   fdt->fds[idx].opaque = NULL;
173 }
174
175 \f
176 /* This is slightly embarrassing.  The problem is that running an I/O
177    callback _may_ influence the status of other file descriptors.  Our
178    own event loops could compensate for that, but the external event
179    loops cannot.  FIXME: We may still want to optimize this a bit when
180    we are called from our own event loops.  So if CHECKED is 1, the
181    check is skipped.  */
182 gpgme_error_t
183 _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
184                   gpgme_error_t *op_err)
185 {
186   struct wait_item_s *item;
187   struct io_cb_data iocb_data;
188   gpgme_error_t err;
189
190   item = (struct wait_item_s *) an_fds->opaque;
191   assert (item);
192
193   if (!checked)
194     {
195       int nr;
196       struct io_select_fd_s fds;
197
198       TRACE0 (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check");
199       fds = *an_fds;
200       fds.signaled = 0;
201       /* Just give it a quick poll.  */
202       nr = _gpgme_io_select (&fds, 1, 1);
203       assert (nr <= 1);
204       if (nr < 0)
205         return errno;
206       else if (nr == 0)
207         /* The status changed in the meantime, there is nothing left
208            to do.  */
209         return 0;
210     }
211
212   TRACE2 (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)",
213           item->handler_value, an_fds->fd);
214
215   iocb_data.handler_value = item->handler_value;
216   iocb_data.op_err = 0;
217   err = item->handler (&iocb_data, an_fds->fd);
218
219   *op_err = iocb_data.op_err;
220   return err;
221 }