9e6f785de32b22190b7bb0e415eaf70e515b8222
[gnupg.git] / dirmngr / ldap-wrapper-ce.c
1 /* ldap-wrapper-ce.c - LDAP access via W32 threads
2  * Copyright (C) 2010 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* 
21    Alternative wrapper for use with WindowsCE.  Under WindowsCE the
22    number of processes is strongly limited (32 processes including the
23    kernel processes) and thus we don't use the process approach but
24    implement a wrapper based on native threads.
25
26    See ldap-wrapper.c for  the standard wrapper interface.
27  */
28
29 #include <config.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <time.h>
38 #include <pth.h>
39 #include <assert.h>
40
41 #include "dirmngr.h"
42 #include "misc.h"
43 #include "ldap-wrapper.h"
44
45 #ifdef USE_LDAPWRAPPER
46 # error This module is not expected to be build.
47 #endif
48
49
50
51 /* Read a fixed amount of data from READER into BUFFER.  */
52 static gpg_error_t
53 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
54 {
55   gpg_error_t err;
56   size_t nread;
57   
58   while (count)
59     {
60       err = ksba_reader_read (reader, buffer, count, &nread);
61       if (err)
62         return err;
63       buffer += nread;
64       count -= nread;
65     }
66   return 0;
67 }
68
69
70
71
72 /* Start the reaper thread for this wrapper.  */
73 void
74 ldap_wrapper_launch_thread (void)
75 {
76   /* Not required.  */
77 }
78
79
80
81
82
83 /* Wait until all ldap wrappers have terminated.  We assume that the
84    kill has already been sent to all of them.  */
85 void
86 ldap_wrapper_wait_connections ()
87 {
88   /* Not required.  */
89 }
90
91
92 /* Cleanup all resources held by the connection associated with
93    CTRL.  This is used after a cancel to kill running wrappers.  */
94 void
95 ldap_wrapper_connection_cleanup (ctrl_t ctrl)
96 {
97   (void)ctrl;
98
99   /* Not required.  */
100 }
101
102
103 \f
104 /* The cookie we use to implement the outstream of the wrapper thread.  */
105 struct outstream_cookie_s
106 {
107   int refcount; /* Reference counter - possible values are 1 and 2.  */
108
109   int eof_seen;       /* EOF indicator.  */
110   size_t buffer_len;  /* The valid length of the BUFFER.  */
111   size_t buffer_pos;  /* The next read position of the BUFFER.  */
112   char buffer[4000];  /* Data buffer.  */
113 };
114
115
116 /* The writer function for the outstream.  This is used to transfer
117    the output of the ldap wrapper thread to the ksba reader object.  */
118 static ssize_t
119 outstream_cookie_writer (void *cookie_arg, const void *buffer, size_t size)
120 {
121   struct outstream_cookie_s *cookie = cookie_arg;
122   const char *src;
123   char *dst;
124   ssize_t nwritten = 0;
125
126   src = buffer;
127   do
128     {
129       /* Wait for free space.  */
130       while (cookie->buffer_len == DIM (cookie->buffer))
131         {
132           /* Buffer is full:  Wait for space.  */
133           pth_yield (NULL);
134         }
135       
136       /* Copy data.  */
137       dst = cookie->buffer + cookie->buffer_len;
138       while (size && cookie->buffer_len < DIM (cookie->buffer))
139         {
140           *dst++ = *src++;
141           size--;
142           cookie->buffer_len++;
143           nwritten++;
144         }
145     }
146   while (size);  /* Until done.  */
147
148   if (nwritten)
149     {
150       /* Signal data is available - a pth_yield is sufficient because
151          the test is explicit.  To increase performance we could do a
152          pth_yield to the other thread and only fall back to yielding
153          to any thread if that returns an error (i.e. the other thread
154          is not runnable).  However our w32pth does not yet support
155          yielding to a specific thread, thus this won't help. */
156       pth_yield (NULL);
157     }
158
159   return nwritten;
160 }
161
162
163 static void
164 outstream_release_cookie (struct outstream_cookie_s *cookie)
165 {
166   cookie->refcount--;
167   if (!cookie->refcount)
168     xfree (cookie);
169 }
170
171
172 /* Closer function for the outstream.  This deallocates the cookie if
173    it won't be used anymore.  */
174 static int
175 outstream_cookie_closer (void *cookie_arg)
176 {
177   struct outstream_cookie_s *cookie = cookie_arg;
178
179   if (!cookie)
180     return 0;  /* Nothing to do.  */
181
182   cookie->eof_seen = 1; /* (only useful if refcount > 1)  */
183
184   assert (cookie->refcount > 0);
185   outstream_release_cookie (cookie);
186   return 0;
187 }
188
189
190 /* The KSBA reader callback which takes the output of the ldap thread
191    form the outstream_cookie_writer and make it available to the ksba
192    reader.  */
193 static int
194 outstream_reader_cb (void *cb_value, char *buffer, size_t count,
195                      size_t *r_nread)
196 {
197   struct outstream_cookie_s *cookie = cb_value;
198   char *dst;
199   const char *src;
200   size_t nread = 0;
201
202   if (!buffer && !count && !r_nread)
203     return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Rewind is not supported.  */
204
205   *r_nread = 0;
206   dst = buffer;
207
208   while (cookie->buffer_pos == cookie->buffer_len)
209     {
210       if (cookie->eof_seen)
211         return gpg_error (GPG_ERR_EOF);
212
213       /* Wait for data to become available.  */
214       pth_yield (NULL);
215     }
216   
217   src = cookie->buffer + cookie->buffer_pos;
218   while (count && cookie->buffer_pos < cookie->buffer_len)
219     {
220       *dst++ = *src++;
221       count--;
222       cookie->buffer_pos++;
223       nread++;
224     }
225
226   if (cookie->buffer_pos == cookie->buffer_len)
227     cookie->buffer_pos = cookie->buffer_len = 0;
228   
229   /* Now there should be some space available.  We do this even if
230      COUNT was zero so to give the writer end a chance to continue.  */
231   pth_yield (NULL);
232
233   *r_nread = nread;
234   return 0; /* Success.  */
235 }
236
237
238 /* This function is called by ksba_reader_release.  */
239 static void
240 outstream_reader_released (void *cb_value, ksba_reader_t r)
241 {
242   struct outstream_cookie_s *cookie = cb_value;
243
244   (void)r;
245
246   assert (cookie->refcount > 0);
247   outstream_release_cookie (cookie);
248 }
249
250
251
252 /* This function is to be used to release a context associated with the
253    given reader object.  This does not release the reader object, though. */
254 void
255 ldap_wrapper_release_context (ksba_reader_t reader)
256 {
257   (void)reader;
258   /* Nothing to do.  */
259 }
260
261
262 \f
263 /* Free a NULL terminated array of malloced strings and the array
264    itself.  */
265 static void
266 free_arg_list (char **arg_list)
267 {
268   int i;
269
270   if (arg_list)
271     {
272       for (i=0; arg_list[i]; i++)
273         xfree (arg_list[i]);
274       xfree (arg_list);
275     }
276 }
277
278
279 /* Copy ARGV into a new array and prepend one element as name of the
280    program (which is more or less a stub).  We need to allocate all
281    the strings to get ownership of them.  */
282 static gpg_error_t
283 create_arg_list (const char *argv[], char ***r_arg_list)
284 {
285   gpg_error_t err;
286   char **arg_list;
287   int i, j;
288
289   for (i = 0; argv[i]; i++)
290     ;
291   arg_list = xtrycalloc (i + 2, sizeof *arg_list);
292   if (!arg_list)
293     goto outofcore;
294
295   i = 0;
296   arg_list[i] = xtrystrdup ("<ldap-wrapper-thread>");
297   if (!arg_list[i])
298     goto outofcore;
299   i++;
300   for (j=0; argv[j]; j++)
301     {
302       arg_list[i] = xtrystrdup (argv[j]);
303       if (!arg_list[i])
304         goto outofcore;
305       i++;
306     }
307   arg_list[i] = NULL;
308   *r_arg_list = arg_list;
309   return 0;
310
311  outofcore:
312   err = gpg_error_from_syserror ();
313   log_error (_("error allocating memory: %s\n"), strerror (errno));
314   free_arg_list (arg_list);
315   *r_arg_list = NULL;
316   return err;
317
318 }
319
320
321 /* Parameters passed to the wrapper thread. */
322 struct ldap_wrapper_thread_parms
323 {
324   char **arg_list;
325   estream_t outstream;
326 };
327
328 /* The thread which runs the LDAP wrapper.  */
329 static void *
330 ldap_wrapper_thread (void *opaque)
331 {
332   struct ldap_wrapper_thread_parms *parms = opaque;
333   
334   /*err =*/ ldap_wrapper_main (parms->arg_list, parms->outstream);
335
336   /* FIXME: Do we need to return ERR?  */
337
338   free_arg_list (parms->arg_list);
339   es_fclose (parms->outstream);
340   xfree (parms);
341   return NULL;
342 }
343
344
345
346 /* Start a new LDAP thread and returns a new libksba reader
347    object at READER.  ARGV is a NULL terminated list of arguments for
348    the wrapper.  The function returns 0 on success or an error code.  */
349 gpg_error_t
350 ldap_wrapper (ctrl_t ctrl, ksba_reader_t *r_reader, const char *argv[])
351 {
352   gpg_error_t err;
353   struct ldap_wrapper_thread_parms *parms;
354   pth_attr_t tattr;
355   es_cookie_io_functions_t outstream_func = { NULL };
356   struct outstream_cookie_s *outstream_cookie;
357   ksba_reader_t reader;
358
359   (void)ctrl;
360
361   *r_reader = NULL;
362
363   parms = xtrycalloc (1, sizeof *parms);
364   if (!parms)
365     return gpg_error_from_syserror ();
366
367   err = create_arg_list (argv, &parms->arg_list);
368   if (err)
369     {
370       xfree (parms);
371       return err;
372     }
373
374   outstream_cookie = xtrycalloc (1, sizeof *outstream_cookie);
375   if (!outstream_cookie)
376     {
377       err = gpg_error_from_syserror ();
378       free_arg_list (parms->arg_list);
379       xfree (parms);
380       return err;
381     }
382   outstream_cookie->refcount++;
383
384   err = ksba_reader_new (&reader);
385   if (!err)
386     err = ksba_reader_set_release_notify (reader,
387                                           outstream_reader_released,
388                                           outstream_cookie);
389   if (!err)
390     err = ksba_reader_set_cb (reader,
391                               outstream_reader_cb, outstream_cookie);
392   if (err)
393     {
394       log_error (_("error initializing reader object: %s\n"),
395                  gpg_strerror (err));
396       ksba_reader_release (reader);
397       outstream_release_cookie (outstream_cookie);
398       free_arg_list (parms->arg_list);
399       xfree (parms);
400       return err;
401     }
402
403
404   outstream_func.func_write = outstream_cookie_writer;
405   outstream_func.func_close = outstream_cookie_closer;
406   parms->outstream = es_fopencookie (outstream_cookie, "wb", outstream_func);
407   if (!parms->outstream)
408     {
409       err = gpg_error_from_syserror ();
410       free_arg_list (parms->arg_list);
411       outstream_release_cookie (outstream_cookie);
412       xfree (parms);
413       return err;
414     }
415   outstream_cookie->refcount++;
416
417   tattr = pth_attr_new();
418   pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0);
419   pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 128*1024);
420   pth_attr_set (tattr, PTH_ATTR_NAME, "ldap-wrapper");
421   
422   if (pth_spawn (tattr, ldap_wrapper_thread, parms))
423     parms = NULL; /* Now owned by the thread.  */
424   else
425     {
426       err = gpg_error_from_syserror ();
427       log_error ("error spawning ldap wrapper thread: %s\n",
428                  strerror (errno) );
429     }
430   pth_attr_destroy (tattr);
431   if (parms)
432     {
433       free_arg_list (parms->arg_list);
434       es_fclose (parms->outstream);
435       xfree (parms);
436     }
437   if (err)
438     {
439       ksba_reader_release (reader);
440       return err;
441     }
442
443   /* Need to wait for the first byte so we are able to detect an empty
444      output and not let the consumer see an EOF without further error
445      indications.  The CRL loading logic assumes that after return
446      from this function, a failed search (e.g. host not found ) is
447      indicated right away. */
448   {
449     unsigned char c;
450
451     err = read_buffer (reader, &c, 1);
452     if (err)
453       {
454         ksba_reader_release (reader);
455         reader = NULL;
456         if (gpg_err_code (err) == GPG_ERR_EOF)
457           return gpg_error (GPG_ERR_NO_DATA);
458         else
459           return err;
460       }
461     ksba_reader_unread (reader, &c, 1);
462   }
463
464   *r_reader = reader;
465
466   return 0;
467 }