Merge branch 'master' into javascript-binding
[gpgme.git] / src / posix-io.c
index 6c33d9f..0448d29 100644 (file)
@@ -1,44 +1,58 @@
 /* posix-io.c - Posix I/O functions
    Copyright (C) 2000 Werner Koch (dd9jn)
 /* posix-io.c - Posix I/O functions
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2004, 2005, 2007 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2004, 2005, 2007, 2010 g10 Code GmbH
 
    This file is part of GPGME.
 
    This file is part of GPGME.
+
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
    GPGME is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 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
    Lesser General Public License for more details.
    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
    Lesser General Public License for more details.
-   
+
    You should have received a copy of the GNU Lesser General Public
    You should have received a copy of the GNU Lesser 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.  */
+   License along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 #include <stdio.h>
 #include <stdlib.h>
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 #include <stdio.h>
 #include <stdlib.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
 #include <signal.h>
 #include <fcntl.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>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
 #include <sys/wait.h>
 #ifdef HAVE_SYS_UIO_H
 # include <sys/uio.h>
 #endif
 #include <ctype.h>
 #include <sys/resource.h>
 #include <sys/wait.h>
 #ifdef HAVE_SYS_UIO_H
 # include <sys/uio.h>
 #endif
 #include <ctype.h>
 #include <sys/resource.h>
-#include <unistd.h>
+
+#ifdef USE_LINUX_GETDENTS
+# include <sys/syscall.h>
+# include <sys/types.h>
+# include <dirent.h>
+#endif /*USE_LINUX_GETDENTS*/
+
 
 #include "util.h"
 #include "priv-io.h"
 
 #include "util.h"
 #include "priv-io.h"
@@ -46,6 +60,7 @@
 #include "ath.h"
 #include "debug.h"
 
 #include "ath.h"
 #include "debug.h"
 
+
 \f
 void
 _gpgme_io_subsystem_init (void)
 \f
 void
 _gpgme_io_subsystem_init (void)
@@ -73,13 +88,22 @@ _gpgme_io_fd2str (char *buf, int buflen, int fd)
 }
 
 \f
 }
 
 \f
-static struct
+/* The table to hold notification handlers.  We use a linear search
+   and extend the table as needed.  */
+struct notify_table_item_s
 {
 {
+  int fd;  /* -1 indicates an unused entry.  */
   _gpgme_close_notify_handler_t handler;
   void *value;
   _gpgme_close_notify_handler_t handler;
   void *value;
-} notify_table[256];
+};
+typedef struct notify_table_item_s *notify_table_item_t;
 
 
+static notify_table_item_t notify_table;
+static size_t notify_table_size;
+DEFINE_STATIC_LOCK (notify_table_lock);
 
 
+
+\f
 int
 _gpgme_io_read (int fd, void *buffer, size_t count)
 {
 int
 _gpgme_io_read (int fd, void *buffer, size_t count)
 {
@@ -149,6 +173,9 @@ int
 _gpgme_io_close (int fd)
 {
   int res;
 _gpgme_io_close (int fd)
 {
   int res;
+  _gpgme_close_notify_handler_t handler = NULL;
+  void *handler_value;
+  int idx;
 
   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
 
 
   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
 
@@ -159,18 +186,27 @@ _gpgme_io_close (int fd)
     }
 
   /* First call the notify handler.  */
     }
 
   /* First call the notify handler.  */
-  if (fd >= 0 && fd < (int) DIM (notify_table))
+  LOCK (notify_table_lock);
+  for (idx=0; idx < notify_table_size; idx++)
     {
     {
-      if (notify_table[fd].handler)
-       {
-         TRACE_LOG2 ("invoking close handler %p/%p",
-                     notify_table[fd].handler, notify_table[fd].value);
-         notify_table[fd].handler (fd, notify_table[fd].value);
-         notify_table[fd].handler = NULL;
-         notify_table[fd].value = NULL;
+      if (notify_table[idx].fd == fd)
+        {
+         handler       = notify_table[idx].handler;
+         handler_value = notify_table[idx].value;
+         notify_table[idx].handler = NULL;
+         notify_table[idx].value = NULL;
+         notify_table[idx].fd = -1; /* Mark slot as free.  */
+          break;
         }
     }
         }
     }
-  /* Then do the close.  */    
+  UNLOCK (notify_table_lock);
+  if (handler)
+    {
+      TRACE_LOG2 ("invoking close handler %p/%p", handler, handler_value);
+      handler (fd, handler_value);
+    }
+
+  /* Then do the close.  */
   res = close (fd);
   return TRACE_SYSRES (res);
 }
   res = close (fd);
   return TRACE_SYSRES (res);
 }
@@ -180,19 +216,52 @@ int
 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
                            void *value)
 {
 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
                            void *value)
 {
+  int res = 0;
+  int idx;
+
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
              "close_handler=%p/%p", handler, value);
 
   assert (fd != -1);
 
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
              "close_handler=%p/%p", handler, value);
 
   assert (fd != -1);
 
-  if (fd < 0 || fd >= (int) DIM (notify_table))
+  LOCK (notify_table_lock);
+  for (idx=0; idx < notify_table_size; idx++)
+    if (notify_table[idx].fd == -1)
+      break;
+  if (idx == notify_table_size)
     {
     {
-      errno = EINVAL;
-      return TRACE_SYSRES (-1);
+      /* We need to increase the size of the table.  The approach we
+         take is straightforward to minimize the risk of bugs.  */
+      notify_table_item_t newtbl;
+      size_t newsize = notify_table_size + 64;
+
+      newtbl = calloc (newsize, sizeof *newtbl);
+      if (!newtbl)
+        {
+          res = -1;
+          goto leave;
+        }
+      for (idx=0; idx < notify_table_size; idx++)
+        newtbl[idx] = notify_table[idx];
+      for (; idx < newsize; idx++)
+        {
+          newtbl[idx].fd = -1;
+          newtbl[idx].handler = NULL;
+          newtbl[idx].value = NULL;
+        }
+      free (notify_table);
+      notify_table = newtbl;
+      idx = notify_table_size;
+      notify_table_size = newsize;
     }
     }
-  notify_table[fd].handler = handler;
-  notify_table[fd].value = value;
-  return TRACE_SYSRES (0);
+  notify_table[idx].fd = fd;
+  notify_table[idx].handler = handler;
+  notify_table[idx].value = value;
+
+ leave:
+  UNLOCK (notify_table_lock);
+
+  return TRACE_SYSRES (res);
 }
 
 
 }
 
 
@@ -212,23 +281,97 @@ _gpgme_io_set_nonblocking (int fd)
 }
 
 
 }
 
 
+#ifdef USE_LINUX_GETDENTS
+/* This is not declared in public headers; getdents64(2) says that we must
+ * define it ourselves.  */
+struct linux_dirent64
+{
+  ino64_t d_ino;
+  off64_t d_off;
+  unsigned short d_reclen;
+  unsigned char d_type;
+  char d_name[];
+};
+
+# define DIR_BUF_SIZE 1024
+#endif /*USE_LINUX_GETDENTS*/
+
+
 static long int
 get_max_fds (void)
 {
 static long int
 get_max_fds (void)
 {
-  char *source = NULL;
+  const char *source = NULL;
   long int fds = -1;
   int rc;
 
   long int fds = -1;
   int rc;
 
-#ifdef RLIMIT_NOFILE
+  /* Under Linux we can figure out the highest used file descriptor by
+   * reading /proc/self/fd.  This is in the common cases much faster
+   * than for example doing 4096 close calls where almost all of them
+   * will fail.
+   *
+   * We can't use the normal opendir/readdir/closedir interface between
+   * fork and exec in a multi-threaded process because opendir uses
+   * malloc and thus a mutex which may deadlock with a malloc in another
+   * thread.  However, the underlying getdents system call is safe.  */
+#ifdef USE_LINUX_GETDENTS
   {
   {
-    struct rlimit rl;
-    rc = getrlimit (RLIMIT_NOFILE, &rl);
-    if (rc == 0)
+    int dir_fd;
+    char dir_buf[DIR_BUF_SIZE];
+    struct linux_dirent64 *dir_entry;
+    int r, pos;
+    const char *s;
+    int x;
+
+    dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
+    if (dir_fd != -1)
       {
       {
-       source = "RLIMIT_NOFILE";
-       fds = rl.rlim_max;
+        for (;;)
+          {
+            r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE);
+            if (r == -1)
+              {
+                /* Fall back to other methods.  */
+                fds = -1;
+                break;
+              }
+            if (r == 0)
+              break;
+
+            for (pos = 0; pos < r; pos += dir_entry->d_reclen)
+              {
+                dir_entry = (struct linux_dirent64 *) (dir_buf + pos);
+                s = dir_entry->d_name;
+                if (*s < '0' || *s > '9')
+                  continue;
+                /* atoi is not guaranteed to be async-signal-safe.  */
+                for (x = 0; *s >= '0' && *s <= '9'; s++)
+                  x = x * 10 + (*s - '0');
+                if (!*s && x > fds && x != dir_fd)
+                  fds = x;
+              }
+          }
+
+        close (dir_fd);
       }
       }
-  }
+    if (fds != -1)
+      {
+        fds++;
+        source = "/proc";
+      }
+    }
+#endif /*USE_LINUX_GETDENTS*/
+
+#ifdef RLIMIT_NOFILE
+  if (fds == -1)
+    {
+      struct rlimit rl;
+      rc = getrlimit (RLIMIT_NOFILE, &rl);
+      if (rc == 0)
+        {
+          source = "RLIMIT_NOFILE";
+          fds = rl.rlim_max;
+        }
+    }
 #endif
 #ifdef RLIMIT_OFILE
   if (fds == -1)
 #endif
 #ifdef RLIMIT_OFILE
   if (fds == -1)
@@ -273,6 +416,16 @@ get_max_fds (void)
       fds = 1024;
     }
 
       fds = 1024;
     }
 
+  /* AIX returns INT32_MAX instead of a proper value.  We assume that
+   * this is always an error and use a more reasonable limit.  */
+#ifdef INT32_MAX
+  if (fds == INT32_MAX)
+    {
+      source = "aix-fix";
+      fds = 1024;
+    }
+#endif
+
   TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%i (%s)", fds, source);
   return fds;
 }
   TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%i (%s)", fds, source);
   return fds;
 }
@@ -282,10 +435,15 @@ int
 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
 {
   int status;
 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
 {
   int status;
+  pid_t ret;
 
   *r_status = 0;
   *r_signal = 0;
 
   *r_status = 0;
   *r_signal = 0;
-  if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
+  do
+    ret = _gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG);
+  while (ret == (pid_t)(-1) && errno == EINTR);
+
+  if (ret == pid)
     {
       if (WIFSIGNALED (status))
        {
     {
       if (WIFSIGNALED (status))
        {
@@ -314,8 +472,6 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
   int status;
   int signo;
 
   int status;
   int signo;
 
-  (void)flags;
-
   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
              "path=%s", path);
   i = 0;
   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
              "path=%s", path);
   i = 0;
@@ -331,7 +487,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
 
   pid = fork ();
       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
 
   pid = fork ();
-  if (pid == -1) 
+  if (pid == -1)
     return TRACE_SYSRES (-1);
 
   if (!pid)
     return TRACE_SYSRES (-1);
 
   if (!pid)
@@ -339,25 +495,50 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
       /* Intermediate child to prevent zombie processes.  */
       if ((pid = fork ()) == 0)
        {
       /* Intermediate child to prevent zombie processes.  */
       if ((pid = fork ()) == 0)
        {
-         int max_fds = get_max_fds ();
-         int fd;
-
          /* Child.  */
          /* Child.  */
+          int max_fds = -1;
+          int fd;
          int seen_stdin = 0;
          int seen_stdin = 0;
+         int seen_stdout = 0;
          int seen_stderr = 0;
 
          if (atfork)
            atfork (atforkvalue, 0);
 
          int seen_stderr = 0;
 
          if (atfork)
            atfork (atforkvalue, 0);
 
-         /* First close all fds which will not be inherited.  */
-         for (fd = 0; fd < max_fds; fd++)
-           {
-             for (i = 0; fd_list[i].fd != -1; i++)
-               if (fd_list[i].fd == fd)
-                 break;
-             if (fd_list[i].fd == -1)
-               close (fd);
-           }
+          /* First close all fds which will not be inherited.  If we
+           * have closefrom(2) we first figure out the highest fd we
+           * do not want to close, then call closefrom, and on success
+           * use the regular code to close all fds up to the start
+           * point of closefrom.  Note that Solaris' and FreeBSD's closefrom do
+           * not return errors.  */
+#ifdef HAVE_CLOSEFROM
+          {
+            fd = -1;
+            for (i = 0; fd_list[i].fd != -1; i++)
+              if (fd_list[i].fd > fd)
+                fd = fd_list[i].fd;
+            fd++;
+#if defined(__sun) || defined(__FreeBSD__)
+            closefrom (fd);
+            max_fds = fd;
+#else /*!__sun */
+            while ((i = closefrom (fd)) && errno == EINTR)
+              ;
+            if (!i || errno == EBADF)
+              max_fds = fd;
+#endif /*!__sun*/
+          }
+#endif /*HAVE_CLOSEFROM*/
+          if (max_fds == -1)
+            max_fds = get_max_fds ();
+          for (fd = 0; fd < max_fds; fd++)
+            {
+              for (i = 0; fd_list[i].fd != -1; i++)
+                if (fd_list[i].fd == fd)
+                  break;
+              if (fd_list[i].fd == -1)
+                close (fd);
+            }
 
          /* And now dup and close those to be duplicated.  */
          for (i = 0; fd_list[i].fd != -1; i++)
 
          /* And now dup and close those to be duplicated.  */
          for (i = 0; fd_list[i].fd != -1; i++)
@@ -372,6 +553,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
 
              if (child_fd == 0)
                seen_stdin = 1;
 
              if (child_fd == 0)
                seen_stdin = 1;
+             else if (child_fd == 1)
+               seen_stdout = 1;
              else if (child_fd == 2)
                seen_stderr = 1;
 
              else if (child_fd == 2)
                seen_stderr = 1;
 
@@ -392,57 +575,39 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
 
              close (fd_list[i].fd);
            }
 
              close (fd_list[i].fd);
            }
-         
-         if (! seen_stdin || ! seen_stderr)
+
+         if (! seen_stdin || ! seen_stdout || !seen_stderr)
            {
              fd = open ("/dev/null", O_RDWR);
              if (fd == -1)
                {
            {
              fd = open ("/dev/null", O_RDWR);
              if (fd == -1)
                {
-#if 0
-                 /* FIXME: The debug file descriptor is not dup'ed
-                    anyway, so we can't see this.  */
-                 TRACE_LOG1 ("can't open `/dev/null': %s\n",
-                             strerror (errno));
-#endif
+                 /* The debug file descriptor is not dup'ed, so we
+                    can't do a trace output.  */
                  _exit (8);
                }
                  _exit (8);
                }
-             /* Make sure that the process has connected stdin.  */
+             /* Make sure that the process has connected stdin.  */
              if (! seen_stdin && fd != 0)
                {
                  if (dup2 (fd, 0) == -1)
              if (! seen_stdin && fd != 0)
                {
                  if (dup2 (fd, 0) == -1)
-                   {
-#if 0
-                 /* FIXME: The debug file descriptor is not dup'ed
-                    anyway, so we can't see this.  */
-                     TRACE_LOG1 ("dup2(/dev/null, 0) failed: %s\n",
-                                 strerror (errno));
-#endif
-                     _exit (8);
-                   }
+                    _exit (8);
                }
                }
+             if (! seen_stdout && fd != 1)
+                {
+                  if (dup2 (fd, 1) == -1)
+                    _exit (8);
+                }
              if (! seen_stderr && fd != 2)
              if (! seen_stderr && fd != 2)
-               if (dup2 (fd, 2) == -1)
-                 {
-#if 0
-                   /* FIXME: The debug file descriptor is not dup'ed
-                      anyway, so we can't see this.  */
-                   TRACE_LOG1 ("dup2(dev/null, 2) failed: %s\n",
-                               strerror (errno));
-#endif
-                   _exit (8);
-                 }
-             if (fd != 0 && fd != 2)
+                {
+                  if (dup2 (fd, 2) == -1)
+                    _exit (8);
+                }
+             if (fd != 0 && fd != 1 && fd != 2)
                close (fd);
            }
                close (fd);
            }
-    
+
          execv (path, (char *const *) argv);
          /* Hmm: in that case we could write a special status code to the
             status-pipe.  */
          execv (path, (char *const *) argv);
          /* Hmm: in that case we could write a special status code to the
             status-pipe.  */
-#if 0
-         /* FIXME: The debug file descriptor is not dup'ed anyway, so
-            we can't see this.  */
-         TRACE_LOG1 ("exec of `%s' failed\n", path);
-#endif
          _exit (8);
          /* End child.  */
        }
          _exit (8);
          /* End child.  */
        }
@@ -501,10 +666,16 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
   any = 0;
   for (i = 0; i < nfds; i++)
     {
   any = 0;
   for (i = 0; i < nfds; i++)
     {
-      if (fds[i].fd == -1) 
+      if (fds[i].fd == -1)
        continue;
       if (fds[i].for_read)
        {
        continue;
       if (fds[i].for_read)
        {
+          if (fds[i].fd >= FD_SETSIZE)
+            {
+              TRACE_END (dbg_help, " -BAD- ]");
+              gpg_err_set_errno (EMFILE);
+              return TRACE_SYSRES (-1);
+            }
          assert (!FD_ISSET (fds[i].fd, &readfds));
          FD_SET (fds[i].fd, &readfds);
          if (fds[i].fd > max_fd)
          assert (!FD_ISSET (fds[i].fd, &readfds));
          FD_SET (fds[i].fd, &readfds);
          if (fds[i].fd > max_fd)
@@ -514,6 +685,12 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
         }
       else if (fds[i].for_write)
        {
         }
       else if (fds[i].for_write)
        {
+          if (fds[i].fd >= FD_SETSIZE)
+            {
+              TRACE_END (dbg_help, " -BAD- ]");
+              gpg_err_set_errno (EMFILE);
+              return TRACE_SYSRES (-1);
+            }
          assert (!FD_ISSET (fds[i].fd, &writefds));
          FD_SET (fds[i].fd, &writefds);
          if (fds[i].fd > max_fd)
          assert (!FD_ISSET (fds[i].fd, &writefds));
          FD_SET (fds[i].fd, &writefds);
          if (fds[i].fd > max_fd)
@@ -523,7 +700,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
         }
       fds[i].signaled = 0;
     }
         }
       fds[i].signaled = 0;
     }
-  TRACE_END (dbg_help, "]"); 
+  TRACE_END (dbg_help, "]");
   if (!any)
     return TRACE_SYSRES (0);
 
   if (!any)
     return TRACE_SYSRES (0);
 
@@ -548,7 +725,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
         }
       TRACE_END (dbg_help, "]");
     }
         }
       TRACE_END (dbg_help, "]");
     }
-    
+
   /* The variable N is used to optimize it a little bit.  */
   for (n = count, i = 0; i < nfds && n; i++)
     {
   /* The variable N is used to optimize it a little bit.  */
   for (n = count, i = 0; i < nfds && n; i++)
     {
@@ -591,7 +768,7 @@ _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
       nread += iov->iov_len;
       iov++;
     }
       nread += iov->iov_len;
       iov++;
     }
-  
+
   TRACE_LOG1 ("about to receive %d bytes", nread);
 
   do
   TRACE_LOG1 ("about to receive %d bytes", nread);
 
   do
@@ -656,7 +833,11 @@ _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
 int
 _gpgme_io_dup (int fd)
 {
 int
 _gpgme_io_dup (int fd)
 {
-  int new_fd = dup (fd);
+  int new_fd;
+
+  do
+    new_fd = dup (fd);
+  while (new_fd == -1 && errno == EINTR);
 
   TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
 
 
   TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
 
@@ -686,7 +867,9 @@ _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
              "addr=%p, addrlen=%i", addr, addrlen);
 
   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
              "addr=%p, addrlen=%i", addr, addrlen);
 
-  res = ath_connect (fd, addr, addrlen);
+  do
+    res = ath_connect (fd, addr, addrlen);
+  while (res == -1 && errno == EINTR);
 
   return TRACE_SYSRES (res);
 }
 
   return TRACE_SYSRES (res);
 }