1 /* ldap-wrapper-ce.c - LDAP access via W32 threads
2 * Copyright (C) 2010 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
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.
26 See ldap-wrapper.c for the standard wrapper interface.
43 #include "ldap-wrapper.h"
45 #ifdef USE_LDAPWRAPPER
46 # error This module is not expected to be build.
51 /* Read a fixed amount of data from READER into BUFFER. */
53 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
60 err = ksba_reader_read (reader, buffer, count, &nread);
72 /* Start the reaper thread for this wrapper. */
74 ldap_wrapper_launch_thread (void)
83 /* Wait until all ldap wrappers have terminated. We assume that the
84 kill has already been sent to all of them. */
86 ldap_wrapper_wait_connections ()
92 /* Cleanup all resources held by the connection associated with
93 CTRL. This is used after a cancel to kill running wrappers. */
95 ldap_wrapper_connection_cleanup (ctrl_t ctrl)
104 /* The cookie we use to implement the outstream of the wrapper thread. */
105 struct outstream_cookie_s
107 int refcount; /* Reference counter - possible values are 1 and 2. */
109 /* We don't need a mutex for the conditions, as npth provides a
110 simpler condition interface that relies on the global lock. This
111 can be used if we never yield between testing the condition and
113 npth_cond_t wait_data; /* Condition that data is available. */
114 npth_cond_t wait_space; /* Condition that space is available. */
116 int eof_seen; /* EOF indicator. */
117 char buffer[4000]; /* Data ring buffer. */
118 size_t buffer_len; /* The amount of data in the BUFFER. */
119 size_t buffer_pos; /* The next read position of the BUFFER. */
122 #define BUFFER_EMPTY(c) ((c)->buffer_len == 0)
123 #define BUFFER_FULL(c) ((c)->buffer_len == DIM((c)->buffer))
124 #define BUFFER_DATA_AVAILABLE(c) ((c)->buffer_len)
125 #define BUFFER_SPACE_AVAILABLE(c) (DIM((c)->buffer) - (c)->buffer_len)
126 #define BUFFER_INC_POS(c,n) (c)->buffer_pos = ((c)->buffer_pos + (n)) % DIM((c)->buffer)
127 #define BUFFER_CUR_POS(c) (&(c)->buffer[(c)->buffer_pos])
130 buffer_get_data (struct outstream_cookie_s *cookie, char *dst, int cnt)
137 if (BUFFER_DATA_AVAILABLE (cookie) < amount)
138 amount = BUFFER_DATA_AVAILABLE (cookie);
141 /* How large is the part up to the end of the buffer array? */
142 chunk = DIM(cookie->buffer) - cookie->buffer_pos;
146 memcpy (dst, BUFFER_CUR_POS (cookie), chunk);
147 BUFFER_INC_POS (cookie, chunk);
153 memcpy (dst, BUFFER_CUR_POS (cookie), left);
154 BUFFER_INC_POS (cookie, left);
162 buffer_put_data (struct outstream_cookie_s *cookie, const char *src, int cnt)
169 remain = DIM(cookie->buffer) - cookie->buffer_len;
176 /* How large is the part up to the end of the buffer array? */
177 chunk = DIM(cookie->buffer) - cookie->buffer_pos;
181 memcpy (BUFFER_CUR_POS (cookie), src, chunk);
182 BUFFER_INC_POS (cookie, chunk);
188 memcpy (BUFFER_CUR_POS (cookie), src, left);
189 BUFFER_INC_POS (cookie, left);
192 cookie->buffer_len -= amount;
197 /* The writer function for the outstream. This is used to transfer
198 the output of the ldap wrapper thread to the ksba reader object. */
200 outstream_cookie_writer (void *cookie_arg, const void *buffer, size_t size)
202 struct outstream_cookie_s *cookie = cookie_arg;
204 ssize_t nwritten = 0;
213 /* Wait for free space. */
214 while (BUFFER_FULL(cookie))
216 /* Buffer is full: Wait for space. */
217 res = npth_cond_wait (&cookie->wait_space, NULL);
220 gpg_err_set_errno (res);
225 if (BUFFER_EMPTY(cookie))
229 nwritten = buffer_put_data (cookie, buffer, size);
235 npth_cond_signal (&cookie->wait_data);
237 while (size); /* Until done. */
244 outstream_release_cookie (struct outstream_cookie_s *cookie)
247 if (!cookie->refcount)
249 npth_cond_destroy (&cookie->wait_data);
250 npth_cond_destroy (&cookie->wait_space);
256 /* Closer function for the outstream. This deallocates the cookie if
257 it won't be used anymore. */
259 outstream_cookie_closer (void *cookie_arg)
261 struct outstream_cookie_s *cookie = cookie_arg;
264 return 0; /* Nothing to do. */
266 cookie->eof_seen = 1; /* (only useful if refcount > 1) */
268 assert (cookie->refcount > 0);
269 outstream_release_cookie (cookie);
274 /* The KSBA reader callback which takes the output of the ldap thread
275 form the outstream_cookie_writer and make it available to the ksba
278 outstream_reader_cb (void *cb_value, char *buffer, size_t count,
281 struct outstream_cookie_s *cookie = cb_value;
285 if (!buffer && !count && !r_nread)
286 return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Rewind is not supported. */
290 while (BUFFER_EMPTY(cookie))
292 if (cookie->eof_seen)
293 return gpg_error (GPG_ERR_EOF);
295 /* Wait for data to become available. */
296 npth_cond_wait (&cookie->wait_data, NULL);
299 if (BUFFER_FULL(cookie))
302 nread = buffer_get_data (cookie, buffer, count);
306 npth_cond_signal (&cookie->wait_space);
310 return 0; /* Success. */
314 /* This function is called by ksba_reader_release. */
316 outstream_reader_released (void *cb_value, ksba_reader_t r)
318 struct outstream_cookie_s *cookie = cb_value;
322 assert (cookie->refcount > 0);
323 outstream_release_cookie (cookie);
328 /* This function is to be used to release a context associated with the
329 given reader object. This does not release the reader object, though. */
331 ldap_wrapper_release_context (ksba_reader_t reader)
339 /* Free a NULL terminated array of malloced strings and the array
342 free_arg_list (char **arg_list)
348 for (i=0; arg_list[i]; i++)
355 /* Copy ARGV into a new array and prepend one element as name of the
356 program (which is more or less a stub). We need to allocate all
357 the strings to get ownership of them. */
359 create_arg_list (const char *argv[], char ***r_arg_list)
365 for (i = 0; argv[i]; i++)
367 arg_list = xtrycalloc (i + 2, sizeof *arg_list);
372 arg_list[i] = xtrystrdup ("<ldap-wrapper-thread>");
376 for (j=0; argv[j]; j++)
378 arg_list[i] = xtrystrdup (argv[j]);
384 *r_arg_list = arg_list;
388 err = gpg_error_from_syserror ();
389 log_error (_("error allocating memory: %s\n"), strerror (errno));
390 free_arg_list (arg_list);
397 /* Parameters passed to the wrapper thread. */
398 struct ldap_wrapper_thread_parms
404 /* The thread which runs the LDAP wrapper. */
406 ldap_wrapper_thread (void *opaque)
408 struct ldap_wrapper_thread_parms *parms = opaque;
410 /*err =*/ ldap_wrapper_main (parms->arg_list, parms->outstream);
412 /* FIXME: Do we need to return ERR? */
414 free_arg_list (parms->arg_list);
415 es_fclose (parms->outstream);
422 /* Start a new LDAP thread and returns a new libksba reader
423 object at READER. ARGV is a NULL terminated list of arguments for
424 the wrapper. The function returns 0 on success or an error code. */
426 ldap_wrapper (ctrl_t ctrl, ksba_reader_t *r_reader, const char *argv[])
429 struct ldap_wrapper_thread_parms *parms;
431 es_cookie_io_functions_t outstream_func = { NULL };
432 struct outstream_cookie_s *outstream_cookie;
433 ksba_reader_t reader;
441 parms = xtrycalloc (1, sizeof *parms);
443 return gpg_error_from_syserror ();
445 err = create_arg_list (argv, &parms->arg_list);
452 outstream_cookie = xtrycalloc (1, sizeof *outstream_cookie);
453 if (!outstream_cookie)
455 err = gpg_error_from_syserror ();
456 free_arg_list (parms->arg_list);
460 outstream_cookie->refcount++;
462 res = npth_cond_init (&outstream_cookie->wait_data, NULL);
465 free_arg_list (parms->arg_list);
467 return gpg_error_from_errno (res);
469 res = npth_cond_init (&outstream_cookie->wait_space, NULL);
472 npth_cond_destroy (&outstream_cookie->wait_data);
473 free_arg_list (parms->arg_list);
475 return gpg_error_from_errno (res);
478 err = ksba_reader_new (&reader);
480 err = ksba_reader_set_release_notify (reader,
481 outstream_reader_released,
484 err = ksba_reader_set_cb (reader,
485 outstream_reader_cb, outstream_cookie);
488 log_error (_("error initializing reader object: %s\n"),
490 ksba_reader_release (reader);
491 outstream_release_cookie (outstream_cookie);
492 free_arg_list (parms->arg_list);
498 outstream_func.func_write = outstream_cookie_writer;
499 outstream_func.func_close = outstream_cookie_closer;
500 parms->outstream = es_fopencookie (outstream_cookie, "wb", outstream_func);
501 if (!parms->outstream)
503 err = gpg_error_from_syserror ();
504 ksba_reader_release (reader);
505 outstream_release_cookie (outstream_cookie);
506 free_arg_list (parms->arg_list);
510 outstream_cookie->refcount++;
512 res = npth_attr_init(&tattr);
515 err = gpg_error_from_errno (res);
516 ksba_reader_release (reader);
517 free_arg_list (parms->arg_list);
518 es_fclose (parms->outstream);
522 npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
524 res = npth_create (&thread, &tattr, ldap_wrapper_thread, parms);
525 npth_attr_destroy (&tattr);
528 err = gpg_error_from_errno (res);
529 log_error ("error spawning ldap wrapper thread: %s\n",
533 parms = NULL; /* Now owned by the thread. */
537 free_arg_list (parms->arg_list);
538 es_fclose (parms->outstream);
543 ksba_reader_release (reader);
547 /* Need to wait for the first byte so we are able to detect an empty
548 output and not let the consumer see an EOF without further error
549 indications. The CRL loading logic assumes that after return
550 from this function, a failed search (e.g. host not found ) is
551 indicated right away. */
555 err = read_buffer (reader, &c, 1);
558 ksba_reader_release (reader);
560 if (gpg_err_code (err) == GPG_ERR_EOF)
561 return gpg_error (GPG_ERR_NO_DATA);
565 ksba_reader_unread (reader, &c, 1);