auto updated version number.
[gpgme.git] / gpgme / posix-io.c
index dede3f9..b4c3915 100644 (file)
@@ -1,5 +1,6 @@
 /* posix-io.c - Posix I/O functions
  *     Copyright (C) 2000 Werner Koch (dd9jn)
+ *      Copyright (C) 2001, 2002 g10 Code GmbH
  *
  * This file is part of GPGME.
  *
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
+#ifdef HAVE_CONFIG_H
 #include <config.h>
-#ifndef HAVE_DOSISH_SYSTEM
-
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include <sys/time.h>
 #include <sys/types.h>
-#include <unistd.h>
 #include <sys/wait.h>
-#include <signal.h>
-#include <fcntl.h>
 
+#include "util.h"
 #include "io.h"
+#include "sema.h"
+#include "ath.h"
 
-#define DEBUG_SELECT_ENABLED 0
-
-#if DEBUG_SELECT_ENABLED
-# define DEBUG_SELECT(a) fprintf a
-#else
-# define DEBUG_SELECT(a) do { } while(0)
-#endif
-
+static struct
+{
+  void (*handler) (int,void*);
+  void *value;
+} notify_table[256];
 
 int
-_gpgme_io_read ( int fd, void *buffer, size_t count )
+_gpgme_io_read (int fd, void *buffer, size_t count)
 {
-    int nread;
+  int nread;
 
-    do {
-        nread = read (fd, buffer, count);
-    } while (nread == -1 && errno == EINTR );
-    return nread;
+  DEBUG2 ("fd %d: about to read %d bytes\n", fd, (int) count);
+  do
+    {
+      nread = _gpgme_ath_read (fd, buffer, count);
+    }
+  while (nread == -1 && errno == EINTR );
+  DEBUG2 ("fd %d: got %d bytes\n", fd, nread);
+  if (nread > 0)
+    _gpgme_debug (2, "fd %d: got `%.*s'\n", fd, nread, buffer);
+  return nread;
 }
 
 
 int
-_gpgme_io_write ( int fd, const void *buffer, size_t count )
+_gpgme_io_write (int fd, const void *buffer, size_t count)
 {
-    int nwritten;
+  int nwritten;
 
-    do {
-        nwritten = write (fd, buffer, count);
-    } while (nwritten == -1 && errno == EINTR );
-    return nwritten;
+  DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int) count);
+  _gpgme_debug (2, "fd %d: write `%.*s'\n", fd, (int) count, buffer);
+  do
+    {
+      nwritten = _gpgme_ath_write (fd, buffer, count);
+    }
+  while (nwritten == -1 && errno == EINTR);
+  DEBUG2 ("fd %d:          wrote %d bytes\n", fd, (int) nwritten);
+  return nwritten;
 }
 
 int
-_gpgme_io_pipe ( int filedes[2], int inherit_idx )
+_gpgme_io_pipe (int filedes[2], int inherit_idx)
 {
-    /* we don't need inherit_idx in this implementation */
-    return pipe ( filedes );
+  int err;
+
+  err = pipe (filedes);
+  if (err < 0)
+    return err;
+  /* FIXME: Should get the old flags first.  */
+  err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
+  if (err < 0)
+    {
+      close (filedes[0]);
+      close (filedes[1]);
+    }
+  return err;
 }
 
+
 int
-_gpgme_io_close ( int fd )
+_gpgme_io_close (int fd)
 {
-    if ( fd == -1 )
-        return -1;
-    return close (fd);
+  if (fd == -1)
+    return -1;
+  /* First call the notify handler.  */
+  DEBUG1 ("closing fd %d", fd);
+  if (fd >= 0 && fd < DIM (notify_table))
+    {
+      if (notify_table[fd].handler)
+       {
+         notify_table[fd].handler (fd, notify_table[fd].value);
+         notify_table[fd].handler = NULL;
+         notify_table[fd].value = NULL;
+        }
+    }
+  /* Then do the close.  */    
+  return close (fd);
 }
 
+
 int
-_gpgme_io_set_nonblocking ( int fd )
+_gpgme_io_set_close_notify (int fd, void (*handler)(int, void*), void *value)
 {
-    int flags;
+  assert (fd != -1);
+
+  if (fd < 0 || fd >= DIM (notify_table))
+    return -1;
+  DEBUG1 ("set notification for fd %d", fd);
+  notify_table[fd].handler = handler;
+  notify_table[fd].value = value;
+  return 0;
+}
 
-    flags = fcntl (fd, F_GETFL, 0);
-    if (flags == -1)
-        return -1;
-    flags |= O_NONBLOCK;
-    return fcntl (fd, F_SETFL, flags);
+
+int
+_gpgme_io_set_nonblocking (int fd)
+{
+  int flags;
+
+  flags = fcntl (fd, F_GETFL, 0);
+  if (flags == -1)
+    return -1;
+  flags |= O_NONBLOCK;
+  return fcntl (fd, F_SETFL, flags);
 }
 
 
 int
-_gpgme_io_spawn ( const char *path, char **argv,
-                  struct spawn_fd_item_s *fd_child_list,
-                  struct spawn_fd_item_s *fd_parent_list )
+_gpgme_io_spawn (const char *path, char **argv,
+                struct spawn_fd_item_s *fd_child_list,
+                struct spawn_fd_item_s *fd_parent_list)
 {
-    pid_t pid;
-    int i;
-    
-    pid = fork ();
-    if (pid == -1) 
-        return -1;
-
-    if ( !pid ) { /* child */
-        int duped_stdin = 0;
-        int duped_stderr = 0;
-
-        /* first close all fds which will not be duped */
-        for (i=0; fd_child_list[i].fd != -1; i++ ) {
-            if (fd_child_list[i].dup_to == -1 )
-                close (fd_child_list[i].fd);
+  static int fixed_signals;
+  DEFINE_STATIC_LOCK (fixed_signals_lock);
+  pid_t pid;
+  int i;
+
+  LOCK (fixed_signals_lock);
+  if (!fixed_signals)
+    { 
+      struct sigaction act;
+        
+      sigaction (SIGPIPE, NULL, &act);
+      if (act.sa_handler == SIG_DFL)
+       {
+         act.sa_handler = SIG_IGN;
+         sigemptyset (&act.sa_mask);
+         act.sa_flags = 0;
+         sigaction (SIGPIPE, &act, NULL);
         }
-        /* and now dup and close the rest */
-        for (i=0; fd_child_list[i].fd != -1; i++ ) {
-            if (fd_child_list[i].dup_to != -1 ) {
-                if ( dup2 (fd_child_list[i].fd,
-                           fd_child_list[i].dup_to ) == -1 ) {
-                    fprintf (stderr, "dup2 failed in child: %s\n",
-                             strerror (errno));
-                    _exit (8);
+      fixed_signals = 1;
+    }
+  UNLOCK (fixed_signals_lock);
+
+  pid = fork ();
+  if (pid == -1) 
+    return -1;
+
+  if (!pid)
+    {
+      /* Child.  */
+      int duped_stdin = 0;
+      int duped_stderr = 0;
+
+      /* First close all fds which will not be duped.  */
+      for (i=0; fd_child_list[i].fd != -1; i++)
+       if (fd_child_list[i].dup_to == -1)
+         close (fd_child_list[i].fd);
+
+      /* And now dup and close the rest.  */
+      for (i=0; fd_child_list[i].fd != -1; i++)
+       {
+         if (fd_child_list[i].dup_to != -1)
+           {
+             if (dup2 (fd_child_list[i].fd,
+                        fd_child_list[i].dup_to) == -1)
+               {
+                 DEBUG1 ("dup2 failed in child: %s\n", strerror (errno));
+                 _exit (8);
                 }
-                if ( fd_child_list[i].dup_to == 0 )
-                    duped_stdin=1;
-                if ( fd_child_list[i].dup_to == 2 )
-                    duped_stderr=1;
-                close (fd_child_list[i].fd);
+             if (fd_child_list[i].dup_to == 0)
+               duped_stdin=1;
+             if (fd_child_list[i].dup_to == 2)
+               duped_stderr=1;
+             close (fd_child_list[i].fd);
             }
         }
 
-        if( !duped_stdin || !duped_stderr ) {
-            int fd = open ( "/dev/null", O_RDONLY );
-            if ( fd == -1 ) {
-                fprintf (stderr,"can't open `/dev/null': %s\n",
-                         strerror (errno) );
-                _exit (8);
+      if (!duped_stdin || !duped_stderr)
+       {
+         int fd = open ("/dev/null", O_RDWR);
+         if (fd == -1)
+           {
+             DEBUG1 ("can't open `/dev/null': %s\n", strerror (errno));
+             _exit (8);
             }
-            /* Make sure that the process has a connected stdin */
-            if ( !duped_stdin ) {
-                if ( dup2 ( fd, 0 ) == -1 ) {
-                    fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n",
-                             strerror (errno) );
-                    _exit (8);
+         /* Make sure that the process has a connected stdin.  */
+         if (!duped_stdin)
+           {
+             if (dup2 (fd, 0) == -1)
+               {
+                 DEBUG1("dup2(/dev/null, 0) failed: %s\n",
+                        strerror (errno));
+                 _exit (8);
                 }
             }
-            /* We normally don't want all the normal output */
-            if ( !duped_stderr ) {
-                if (!getenv ("GPGME_DEBUG") ) {
-                    if ( dup2 ( fd, 2 ) == -1 ) {
-                        fprintf (stderr,"dup2(dev/null, 2) failed: %s\n",
-                                 strerror (errno) );
-                        _exit (8);
-                    }
-                }
-            }
-            close (fd);
-        }
-
-        execv ( path, argv );
-        /* Hmm: in that case we could write a special status code to the
-         * status-pipe */
-        fprintf (stderr,"exec of `%s' failed\n", path );
-        _exit (8);
-    } /* end child */
+         if (!duped_stderr)
+           if (dup2 (fd, 2) == -1)
+             {
+               DEBUG1 ("dup2(dev/null, 2) failed: %s\n", strerror (errno));
+               _exit (8);
+             }
+         close (fd);
+       }
     
-    /* .dup_to is not used in the parent list */
-    for (i=0; fd_parent_list[i].fd != -1; i++ ) {
-        close (fd_parent_list[i].fd);
-    }
+      execv ( path, argv );
+      /* Hmm: in that case we could write a special status code to the
+        status-pipe.  */
+      DEBUG1 ("exec of `%s' failed\n", path);
+      _exit (8);
+    } /* End child.  */
+    
+  /* .dup_to is not used in the parent list.  */
+  for (i=0; fd_parent_list[i].fd != -1; i++)
+    close (fd_parent_list[i].fd);
 
-    return (int)pid;
+  return (int) pid;
 }
 
 
 int
-_gpgme_io_waitpid ( int pid, int hang, int *r_status, int *r_signal )
+_gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
 {
-    int status;
-
-    *r_status = 0;
-    *r_signal = 0;
-    if ( waitpid ( pid, &status, hang? 0 : WNOHANG ) == pid ) {
-        if ( WIFSIGNALED (status) ) {
-            *r_status = 4; /* Need some value here */
-            *r_signal = WTERMSIG (status);
-        }
-        else if ( WIFEXITED (status) ) {
-            *r_status = WEXITSTATUS (status);
-        }
-        else {
-            *r_status = 4; /* oops */
-        }
-        return 1;
+  int status;
+
+  *r_status = 0;
+  *r_signal = 0;
+  if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
+    {
+      if (WIFSIGNALED (status))
+       {
+         *r_status = 4; /* Need some value here.  */
+         *r_signal = WTERMSIG (status);
+       }
+      else if (WIFEXITED (status))
+       *r_status = WEXITSTATUS (status);
+      else
+       *r_status = 4; /* Oops.  */
+      return 1;
     }
-    return 0;
+  return 0;
+}
+
+
+int
+_gpgme_io_kill (int pid, int hard)
+{
+  return kill (pid, hard ? SIGKILL : SIGTERM);
 }
 
 
@@ -208,85 +287,95 @@ _gpgme_io_waitpid ( int pid, int hang, int *r_status, int *r_signal )
  *          >0 = number of signaled fds
  */
 int
-_gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds )
+_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds)
 {
-    static fd_set readfds;
-    static fd_set writefds;
-    int any, i, max_fd, n, count;
-    struct timeval timeout = { 1, 0 }; /* Use a one second timeout */
-    
-    FD_ZERO ( &readfds );
-    FD_ZERO ( &writefds );
-    max_fd = 0;
-
-    DEBUG_SELECT ((stderr, "gpgme:select on [ "));
-    any = 0;
-    for ( i=0; i < nfds; i++ ) {
-        if ( fds[i].fd == -1 ) 
-            continue;
-        if ( fds[i].for_read ) {
-            assert ( !FD_ISSET ( fds[i].fd, &readfds ) );
-            FD_SET ( fds[i].fd, &readfds );
-            if ( fds[i].fd > max_fd )
-                max_fd = fds[i].fd;
-            DEBUG_SELECT ((stderr, "r%d ", fds[i].fd ));
-            any = 1;
+  fd_set readfds;
+  fd_set writefds;
+  int any, i, max_fd, n, count;
+  struct timeval timeout = { 1, 0 }; /* Use a 1s timeout.  */
+  void *dbg_help = NULL;
+
+  FD_ZERO (&readfds);
+  FD_ZERO (&writefds);
+  max_fd = 0;
+
+  DEBUG_BEGIN (dbg_help, 3, "gpgme:select on [ ");
+  any = 0;
+  for (i = 0; i < nfds; i++)
+    {
+      if (fds[i].fd == -1) 
+       continue;
+      if (fds[i].frozen)
+       DEBUG_ADD1 (dbg_help, "f%d ", fds[i].fd);
+      else if (fds[i].for_read)
+       {
+         assert (!FD_ISSET (fds[i].fd, &readfds));
+         FD_SET (fds[i].fd, &readfds);
+         if (fds[i].fd > max_fd)
+           max_fd = fds[i].fd;
+         DEBUG_ADD1 (dbg_help, "r%d ", fds[i].fd);
+         any = 1;
         }
-        else if ( fds[i].for_write ) {
-            assert ( !FD_ISSET ( fds[i].fd, &writefds ) );
-            FD_SET ( fds[i].fd, &writefds );
-            if ( fds[i].fd > max_fd )
-                max_fd = fds[i].fd;
-            DEBUG_SELECT ((stderr, "w%d ", fds[i].fd ));
-            any = 1;
+      else if (fds[i].for_write)
+       {
+         assert (!FD_ISSET (fds[i].fd, &writefds));
+         FD_SET (fds[i].fd, &writefds);
+         if (fds[i].fd > max_fd)
+           max_fd = fds[i].fd;
+         DEBUG_ADD1 (dbg_help, "w%d ", fds[i].fd);
+         any = 1;
         }
-        fds[i].signaled = 0;
+      fds[i].signaled = 0;
     }
-    DEBUG_SELECT ((stderr, "]\n" ));
-    if ( !any )
-        return 0;
-
-    do {
-        count = select ( max_fd+1, &readfds, &writefds, NULL, &timeout );
-    } while ( count < 0 && errno == EINTR);
-    if ( count < 0 ) {
-        fprintf (stderr, "_gpgme_io_select failed: %s\n", strerror (errno) );
-        return -1; /* error */
+  DEBUG_END (dbg_help, "]"); 
+  if (!any)
+    return 0;
+
+  do
+    {
+      count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL, &timeout);
+    }
+  while (count < 0 && errno == EINTR);
+  if (count < 0)
+    {
+      DEBUG1 ("_gpgme_io_select failed: %s\n", strerror (errno));
+      return -1; /* error */
     }
 
-#if DEBUG_SELECT_ENABLED
-    fprintf (stderr, "gpgme:select OK [ " );
-    for (i=0; i <= max_fd; i++ ) {
-        if (FD_ISSET (i, &readfds) )
-            fprintf (stderr, "r%d ", i );
-        if (FD_ISSET (i, &writefds) )
-            fprintf (stderr, "w%d ", i );
+  DEBUG_BEGIN (dbg_help, 3, "select OK [ ");
+  if (DEBUG_ENABLED (dbg_help))
+    {
+      for (i = 0; i <= max_fd; i++)
+       {
+         if (FD_ISSET (i, &readfds))
+           DEBUG_ADD1 (dbg_help, "r%d ", i);
+         if (FD_ISSET (i, &writefds))
+           DEBUG_ADD1 (dbg_help, "w%d ", i);
+        }
+      DEBUG_END (dbg_help, "]");
     }
-    fprintf (stderr, "]\n" );
-#endif
     
-    /* n is used to optimize it a little bit */
-    for ( n=count, i=0; i < nfds && n ; i++ ) {
-        if ( fds[i].fd == -1 ) 
-            ;
-        else if ( fds[i].for_read ) {
-            if ( FD_ISSET ( fds[i].fd, &readfds ) ) {
-                fds[i].signaled = 1;
-                n--;
+  /* n is used to optimize it a little bit.  */
+  for (n = count, i = 0; i < nfds && n; i++)
+    {
+      if (fds[i].fd == -1)
+       ;
+      else if (fds[i].for_read)
+       {
+         if (FD_ISSET (fds[i].fd, &readfds))
+           {
+             fds[i].signaled = 1;
+             n--;
             }
         }
-        else if ( fds[i].for_write ) {
-            if ( FD_ISSET ( fds[i].fd, &writefds ) ) {
-                fds[i].signaled = 1;
-                n--;
+      else if (fds[i].for_write)
+       {
+         if (FD_ISSET (fds[i].fd, &writefds))
+           {
+             fds[i].signaled = 1;
+             n--;
             }
         }
     }
-    return count;
+  return count;
 }
-
-
-#endif /*!HAVE_DOSISH_SYSTEM*/
-
-
-