Line data Source code
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 :
43 : void
44 582 : _gpgme_fd_table_init (fd_table_t fdt)
45 : {
46 582 : fdt->fds = NULL;
47 582 : fdt->size = 0;
48 582 : }
49 :
50 : void
51 574 : _gpgme_fd_table_deinit (fd_table_t fdt)
52 : {
53 574 : if (fdt->fds)
54 561 : free (fdt->fds);
55 574 : }
56 :
57 :
58 : /* XXX We should keep a marker and roll over for speed. */
59 : static gpgme_error_t
60 1737 : 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 3164 : for (i = 0; i < fdt->size; i++)
66 : {
67 2595 : if (fdt->fds[i].fd == -1)
68 1168 : break;
69 : }
70 1737 : if (i == fdt->size)
71 : {
72 : #define FDT_ALLOCSIZE 10
73 569 : new_fds = realloc (fdt->fds, (fdt->size + FDT_ALLOCSIZE)
74 : * sizeof (*new_fds));
75 569 : if (!new_fds)
76 0 : return gpg_error_from_syserror ();
77 :
78 569 : fdt->fds = new_fds;
79 569 : fdt->size += FDT_ALLOCSIZE;
80 6259 : for (j = 0; j < FDT_ALLOCSIZE; j++)
81 5690 : fdt->fds[i + j].fd = -1;
82 : }
83 :
84 1737 : fdt->fds[i].fd = fd;
85 1737 : fdt->fds[i].for_read = (dir == 1);
86 1737 : fdt->fds[i].for_write = (dir == 0);
87 1737 : fdt->fds[i].signaled = 0;
88 1737 : fdt->fds[i].opaque = opaque;
89 1737 : *idx = i;
90 1737 : return 0;
91 : }
92 :
93 :
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 1737 : _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 1737 : 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 1737 : assert (fnc);
109 1737 : assert (ctx);
110 :
111 1737 : fdt = &ctx->fdt;
112 1737 : assert (fdt);
113 :
114 1737 : tag = malloc (sizeof *tag);
115 1737 : if (!tag)
116 0 : return gpg_error_from_syserror ();
117 1737 : tag->ctx = ctx;
118 :
119 : /* Allocate a structure to hold information about the handler. */
120 1737 : item = calloc (1, sizeof *item);
121 1737 : if (!item)
122 : {
123 0 : free (tag);
124 0 : return gpg_error_from_syserror ();
125 : }
126 1737 : item->ctx = ctx;
127 1737 : item->dir = dir;
128 1737 : item->handler = fnc;
129 1737 : item->handler_value = fnc_data;
130 :
131 1737 : err = fd_table_put (fdt, fd, dir, item, &tag->idx);
132 1737 : if (err)
133 : {
134 0 : free (tag);
135 0 : free (item);
136 0 : return err;
137 : }
138 :
139 1737 : TRACE3 (DEBUG_CTX, "_gpgme_add_io_cb", ctx,
140 : "fd %d, dir=%d -> tag=%p", fd, dir, tag);
141 :
142 1737 : *r_tag = tag;
143 1737 : return 0;
144 : }
145 :
146 :
147 : void
148 1749 : _gpgme_remove_io_cb (void *data)
149 : {
150 1749 : struct tag *tag = data;
151 : gpgme_ctx_t ctx;
152 : fd_table_t fdt;
153 : int idx;
154 :
155 1749 : assert (tag);
156 1749 : ctx = tag->ctx;
157 1749 : assert (ctx);
158 1749 : fdt = &ctx->fdt;
159 1749 : assert (fdt);
160 1749 : idx = tag->idx;
161 :
162 1749 : 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 1749 : free (fdt->fds[idx].opaque);
167 1749 : free (tag);
168 :
169 : /* Free the table entry. */
170 1749 : fdt->fds[idx].fd = -1;
171 1749 : fdt->fds[idx].for_read = 0;
172 1749 : fdt->fds[idx].for_write = 0;
173 1749 : fdt->fds[idx].opaque = NULL;
174 1749 : }
175 :
176 :
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 8022 : _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 8022 : item = (struct wait_item_s *) an_fds->opaque;
192 8022 : assert (item);
193 :
194 8022 : if (!checked)
195 : {
196 : int nr;
197 : struct io_select_fd_s fds;
198 :
199 8022 : TRACE0 (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check");
200 8020 : fds = *an_fds;
201 8020 : fds.signaled = 0;
202 : /* Just give it a quick poll. */
203 8020 : nr = _gpgme_io_select (&fds, 1, 1);
204 8025 : assert (nr <= 1);
205 8025 : if (nr < 0)
206 1 : return errno;
207 8025 : else if (nr == 0)
208 : /* The status changed in the meantime, there is nothing left
209 : to do. */
210 0 : return 0;
211 : }
212 :
213 8025 : TRACE2 (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)",
214 : item->handler_value, an_fds->fd);
215 :
216 8027 : iocb_data.handler_value = item->handler_value;
217 8027 : iocb_data.op_err = 0;
218 8027 : err = item->handler (&iocb_data, an_fds->fd);
219 :
220 7942 : *op_err = iocb_data.op_err;
221 7942 : return err;
222 : }
|