aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/gpgme/wait.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/gpgme/wait.c')
-rw-r--r--trunk/gpgme/wait.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/trunk/gpgme/wait.c b/trunk/gpgme/wait.c
new file mode 100644
index 00000000..f8cadc00
--- /dev/null
+++ b/trunk/gpgme/wait.c
@@ -0,0 +1,282 @@
+/* wait.c
+ * Copyright (C) 2000 Werner Koch (dd9jn)
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "util.h"
+#include "context.h"
+#include "wait.h"
+
+/* Fixme: implement the following stuff to make the code MT safe.
+ * To avoid the need to link against a specific threads lib, such
+ * an implementation should require the caller to register a function
+ * which does this task.
+ * enter_crit() and leave_crit() are used to embrace an area of code
+ * which should be executed only by one thread at a time.
+ * lock_xxxx() and unlock_xxxx() protect access to an data object.
+ * */
+#define enter_crit() do { } while (0)
+#define leave_crit() do { } while (0)
+#define lock_queue() do { } while (0)
+#define unlock_queue() do { } while (0)
+
+struct wait_queue_item_s {
+ struct wait_queue_item_s *next;
+ volatile int used;
+ volatile int active;
+ int (*handler)(void*,pid_t,int);
+ void *handler_value;
+ pid_t pid;
+ int fd;
+ int inbound; /* this is an inbound data handler fd */
+
+ int exited;
+ int exit_status;
+ int exit_signal;
+
+ GpgmeCtx ctx;
+};
+
+
+static struct wait_queue_item_s wait_queue[SIZEOF_WAIT_QUEUE];
+
+static int the_big_select ( void );
+
+
+static void
+init_wait_queue (void)
+{
+ int i;
+ static int initialized = 0;
+
+ if ( initialized ) /* FIXME: This leads to a race */
+ return;
+
+ lock_queue ();
+ for (i=1; i < SIZEOF_WAIT_QUEUE; i++ )
+ wait_queue[i-1].next = &wait_queue[i];
+ initialized = 1;
+ unlock_queue();
+}
+
+static struct wait_queue_item_s *
+queue_item_from_context ( GpgmeCtx ctx )
+{
+ struct wait_queue_item_s *q;
+
+ for (q=wait_queue; q; q = q->next) {
+ if ( q->used && q->ctx == ctx )
+ return q;
+ }
+ return NULL;
+}
+
+
+/**
+ * gpgme_wait:
+ * @c:
+ * @hang:
+ *
+ * Wait for a finished request, if @c is given the function does only
+ * wait on a finsihed request for that context, otherwise it will return
+ * on any request. When @hang is true the function will wait, otherwise
+ * it will return immediately when there is no pending finished request.
+ *
+ * Return value: Context of the finished request or NULL if @hang is false
+ * and no (or the given) request has finished.
+ **/
+GpgmeCtx
+gpgme_wait ( GpgmeCtx c, int hang )
+{
+ struct wait_queue_item_s *q;
+
+ init_wait_queue ();
+ do {
+ if ( !the_big_select() ) {
+ int status;
+
+ /* We did no read/write - see whether this process is still
+ * alive */
+ assert (c); /* !c is not yet implemented */
+ q = queue_item_from_context ( c );
+ assert (q);
+
+ if ( waitpid ( q->pid, &status, WNOHANG ) == q->pid ) {
+ q->exited = 1;
+ if ( WIFSIGNALED (status) ) {
+ q->exit_status = 4; /* Need some value here */
+ q->exit_signal = WTERMSIG (status);
+ }
+ else if ( WIFEXITED (status) ) {
+ q->exit_status = WEXITSTATUS (status);
+ }
+ else {
+ q->exited++;
+ q->exit_status = 4;
+ }
+ /* okay, the process has terminated - we are ready */
+ hang = 0;
+ }
+ }
+ } while (hang);
+ return c;
+}
+
+
+
+/*
+ * We use this function to do the select stuff for all running
+ * gpgs. A future version might provide a facility to delegate
+ * those selects to the GDK select stuff.
+ * This function must be called only by one thread!!
+ * FIXME: The data structures and algorithms are stupid.
+ * Returns: 0 = nothing to run
+ * 1 = did run something
+ */
+
+static int
+the_big_select ( void )
+{
+ static fd_set readfds;
+ static fd_set writefds;
+ struct wait_queue_item_s *q;
+ int max_fd, n;
+ struct timeval timeout = { 1, 0 }; /* Use a one second timeout */
+
+ FD_ZERO ( &readfds );
+ FD_ZERO ( &writefds );
+ max_fd = 0;
+ lock_queue ();
+ for ( q = wait_queue; q; q = q->next ) {
+ if ( q->used && q->active ) {
+ if (q->inbound) {
+ assert ( !FD_ISSET ( q->fd, &readfds ) );
+ FD_SET ( q->fd, &readfds );
+ }
+ else {
+ assert ( !FD_ISSET ( q->fd, &writefds ) );
+ FD_SET ( q->fd, &writefds );
+ }
+ if ( q->fd > max_fd )
+ max_fd = q->fd;
+ }
+ }
+ unlock_queue ();
+
+
+ n = select ( max_fd+1, &readfds, &writefds, NULL, &timeout );
+ if ( n <= 0 ) {
+ if ( n && errno != EINTR ) {
+ fprintf (stderr, "the_big_select: select failed: %s\n",
+ strerror (errno) );
+ }
+ return 0;
+ }
+
+ /* something has to be done. Go over the queue and call
+ * the handlers */
+ restart:
+ while ( n ) {
+ lock_queue ();
+ for ( q = wait_queue; q; q = q->next ) {
+ if ( q->used && q->active
+ && FD_ISSET (q->fd, q->inbound? &readfds : &writefds ) ) {
+ FD_CLR (q->fd, q->inbound? &readfds : &writefds );
+ assert (n);
+ n--;
+ unlock_queue ();
+ if ( q->handler (q->handler_value, q->pid, q->fd ) )
+ q->active = 0;
+ goto restart;
+ }
+ }
+ unlock_queue ();
+ }
+ return 1;
+}
+
+
+
+/*
+ * called by rungpg.c to register something for select()
+ */
+GpgmeError
+_gpgme_register_pipe_handler( void *opaque,
+ int (*handler)(void*,pid_t,int),
+ void *handler_value,
+ pid_t pid, int fd, int inbound )
+{
+ GpgmeCtx ctx = opaque;
+ struct wait_queue_item_s *q;
+
+ init_wait_queue();
+ assert (opaque);
+ assert (handler);
+
+ lock_queue ();
+ for ( q = wait_queue; q; q = q->next ) {
+ if ( !q->used ) {
+ q->used = 1;
+ q->active = 0;
+ break;
+ }
+ }
+ unlock_queue ();
+ if ( !q )
+ return mk_error (Too_Many_Procs);
+
+ q->fd = fd;
+ q->inbound = inbound;
+ q->handler = handler;
+ q->handler_value = handler_value;
+ q->pid = pid;
+ q->ctx = ctx;
+
+ /* and enable this entry for the next select */
+ q->exited = 0;
+ q->active = 1;
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+