Merge branch 'master' into javascript-binding
[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 #ifdef HAVE_SYS_TYPES_H
30 # include <sys/types.h>
31 #endif
32
33 #include "util.h"
34 #include "context.h"
35 #include "ops.h"
36 #include "wait.h"
37 #include "sema.h"
38 #include "priv-io.h"
39 #include "engine.h"
40 #include "debug.h"
41
42 \f
43 void
44 _gpgme_fd_table_init (fd_table_t fdt)
45 {
46   fdt->fds = NULL;
47   fdt->size = 0;
48 }
49
50 void
51 _gpgme_fd_table_deinit (fd_table_t fdt)
52 {
53   if (fdt->fds)
54     free (fdt->fds);
55 }
56
57
58 /* XXX We should keep a marker and roll over for speed.  */
59 static gpgme_error_t
60 fd_table_put (fd_table_t fdt, int fd, int dir, void *opaque, int *idx)
61 {
62   unsigned int i, j;
63   struct io_select_fd_s *new_fds;
64
65   for (i = 0; i < fdt->size; i++)
66     {
67       if (fdt->fds[i].fd == -1)
68         break;
69     }
70   if (i == fdt->size)
71     {
72 #define FDT_ALLOCSIZE 10
73       new_fds = realloc (fdt->fds, (fdt->size + FDT_ALLOCSIZE)
74                          * sizeof (*new_fds));
75       if (!new_fds)
76         return gpg_error_from_syserror ();
77
78       fdt->fds = new_fds;
79       fdt->size += FDT_ALLOCSIZE;
80       for (j = 0; j < FDT_ALLOCSIZE; j++)
81         fdt->fds[i + j].fd = -1;
82     }
83
84   fdt->fds[i].fd = fd;
85   fdt->fds[i].for_read = (dir == 1);
86   fdt->fds[i].for_write = (dir == 0);
87   fdt->fds[i].signaled = 0;
88   fdt->fds[i].opaque = opaque;
89   *idx = i;
90   return 0;
91 }
92
93 \f
94 /* Register the file descriptor FD with the handler FNC (which gets
95    FNC_DATA as its first argument) for the direction DIR.  DATA should
96    be the context for which the fd is added.  R_TAG will hold the tag
97    that can be used to remove the fd.  */
98 gpgme_error_t
99 _gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc,
100                   void *fnc_data, void **r_tag)
101 {
102   gpgme_error_t err;
103   gpgme_ctx_t ctx = (gpgme_ctx_t) data;
104   fd_table_t fdt;
105   struct wait_item_s *item;
106   struct tag *tag;
107
108   assert (fnc);
109   assert (ctx);
110
111   fdt = &ctx->fdt;
112   assert (fdt);
113
114   tag = malloc (sizeof *tag);
115   if (!tag)
116     return gpg_error_from_syserror ();
117   tag->ctx = ctx;
118
119   /* Allocate a structure to hold information about the handler.  */
120   item = calloc (1, sizeof *item);
121   if (!item)
122     {
123       free (tag);
124       return gpg_error_from_syserror ();
125     }
126   item->ctx = ctx;
127   item->dir = dir;
128   item->handler = fnc;
129   item->handler_value = fnc_data;
130
131   err = fd_table_put (fdt, fd, dir, item, &tag->idx);
132   if (err)
133     {
134       free (tag);
135       free (item);
136       return err;
137     }
138
139   TRACE3 (DEBUG_CTX, "_gpgme_add_io_cb", ctx,
140           "fd %d, dir=%d -> tag=%p", fd, dir, tag);
141
142   *r_tag = tag;
143   return 0;
144 }
145
146
147 void
148 _gpgme_remove_io_cb (void *data)
149 {
150   struct tag *tag = data;
151   gpgme_ctx_t ctx;
152   fd_table_t fdt;
153   int idx;
154
155   assert (tag);
156   ctx = tag->ctx;
157   assert (ctx);
158   fdt = &ctx->fdt;
159   assert (fdt);
160   idx = tag->idx;
161
162   TRACE2 (DEBUG_CTX, "_gpgme_remove_io_cb", data,
163           "setting fd 0x%x (item=%p) done", fdt->fds[idx].fd,
164           fdt->fds[idx].opaque);
165
166   free (fdt->fds[idx].opaque);
167   free (tag);
168
169   /* Free the table entry.  */
170   fdt->fds[idx].fd = -1;
171   fdt->fds[idx].for_read = 0;
172   fdt->fds[idx].for_write = 0;
173   fdt->fds[idx].opaque = NULL;
174 }
175
176 \f
177 /* This is slightly embarrassing.  The problem is that running an I/O
178    callback _may_ influence the status of other file descriptors.  Our
179    own event loops could compensate for that, but the external event
180    loops cannot.  FIXME: We may still want to optimize this a bit when
181    we are called from our own event loops.  So if CHECKED is 1, the
182    check is skipped.  */
183 gpgme_error_t
184 _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
185                   gpgme_error_t *op_err)
186 {
187   struct wait_item_s *item;
188   struct io_cb_data iocb_data;
189   gpgme_error_t err;
190
191   item = (struct wait_item_s *) an_fds->opaque;
192   assert (item);
193
194   if (!checked)
195     {
196       int nr;
197       struct io_select_fd_s fds;
198
199       TRACE0 (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check");
200       fds = *an_fds;
201       fds.signaled = 0;
202       /* Just give it a quick poll.  */
203       nr = _gpgme_io_select (&fds, 1, 1);
204       assert (nr <= 1);
205       if (nr < 0)
206         return errno;
207       else if (nr == 0)
208         /* The status changed in the meantime, there is nothing left
209            to do.  */
210         return 0;
211     }
212
213   TRACE2 (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)",
214           item->handler_value, an_fds->fd);
215
216   iocb_data.handler_value = item->handler_value;
217   iocb_data.op_err = 0;
218   err = item->handler (&iocb_data, an_fds->fd);
219
220   *op_err = iocb_data.op_err;
221   return err;
222 }