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