gpgme-tool: Use membuf functions to build up strings.
[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_errno (errno);
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_errno (errno);
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       int saved_errno = errno;
124       free (tag);
125       return gpg_error_from_errno (saved_errno);
126     }
127   item->ctx = ctx;
128   item->dir = dir;
129   item->handler = fnc;
130   item->handler_value = fnc_data;
131
132   err = fd_table_put (fdt, fd, dir, item, &tag->idx);
133   if (err)
134     {
135       free (tag);
136       free (item);
137       return err;
138     }
139
140   TRACE3 (DEBUG_CTX, "_gpgme_add_io_cb", ctx,
141           "fd %d, dir=%d -> tag=%p", fd, dir, tag);
142
143   *r_tag = tag;
144   return 0;
145 }
146
147
148 void
149 _gpgme_remove_io_cb (void *data)
150 {
151   struct tag *tag = data;
152   gpgme_ctx_t ctx;
153   fd_table_t fdt;
154   int idx;
155
156   assert (tag);
157   ctx = tag->ctx;
158   assert (ctx);
159   fdt = &ctx->fdt;
160   assert (fdt);
161   idx = tag->idx;
162
163   TRACE2 (DEBUG_CTX, "_gpgme_remove_io_cb", data,
164           "setting fd 0x%x (item=%p) done", fdt->fds[idx].fd,
165           fdt->fds[idx].opaque);
166
167   free (fdt->fds[idx].opaque);
168   free (tag);
169
170   /* Free the table entry.  */
171   fdt->fds[idx].fd = -1;
172   fdt->fds[idx].for_read = 0;
173   fdt->fds[idx].for_write = 0;
174   fdt->fds[idx].opaque = NULL;
175 }
176
177 \f
178 /* This is slightly embarrassing.  The problem is that running an I/O
179    callback _may_ influence the status of other file descriptors.  Our
180    own event loops could compensate for that, but the external event
181    loops cannot.  FIXME: We may still want to optimize this a bit when
182    we are called from our own event loops.  So if CHECKED is 1, the
183    check is skipped.  */
184 gpgme_error_t
185 _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
186                   gpgme_error_t *op_err)
187 {
188   struct wait_item_s *item;
189   struct io_cb_data iocb_data;
190   gpgme_error_t err;
191
192   item = (struct wait_item_s *) an_fds->opaque;
193   assert (item);
194
195   if (!checked)
196     {
197       int nr;
198       struct io_select_fd_s fds;
199
200       TRACE0 (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check");
201       fds = *an_fds;
202       fds.signaled = 0;
203       /* Just give it a quick poll.  */
204       nr = _gpgme_io_select (&fds, 1, 1);
205       assert (nr <= 1);
206       if (nr < 0)
207         return errno;
208       else if (nr == 0)
209         /* The status changed in the meantime, there is nothing left
210            to do.  */
211         return 0;
212     }
213
214   TRACE2 (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)",
215           item->handler_value, an_fds->fd);
216
217   iocb_data.handler_value = item->handler_value;
218   iocb_data.op_err = 0;
219   err = item->handler (&iocb_data, an_fds->fd);
220
221   *op_err = iocb_data.op_err;
222   return err;
223 }