json: Fix t-decrypt-verify.out for GnuPG >= 2.3.
[gpgme.git] / src / posix-io.c
1 /* posix-io.c - Posix I/O functions
2  * Copyright (C) 2000 Werner Koch (dd9jn)
3  * Copyright (C) 2001, 2002, 2004, 2005, 2007, 2010 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, see <https://gnu.org/licenses/>.
19  * SPDX-License-Identifier: LGPL-2.1-or-later
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #ifdef HAVE_STDINT_H
28 # include <stdint.h>
29 #endif
30 #include <string.h>
31 #include <assert.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <fcntl.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #ifdef HAVE_SYS_TIME_H
39 # include <sys/time.h>
40 #endif
41 #ifdef HAVE_SYS_TYPES_H
42 # include <sys/types.h>
43 #endif
44 #include <sys/wait.h>
45 #ifdef HAVE_SYS_UIO_H
46 # include <sys/uio.h>
47 #endif
48 #include <ctype.h>
49 #include <sys/resource.h>
50
51 #ifdef USE_LINUX_GETDENTS
52 # include <sys/syscall.h>
53 # include <sys/types.h>
54 # include <dirent.h>
55 #endif /*USE_LINUX_GETDENTS*/
56
57
58 #include "util.h"
59 #include "priv-io.h"
60 #include "sema.h"
61 #include "ath.h"
62 #include "debug.h"
63
64
65 #ifdef USE_LINUX_GETDENTS
66 /* This is not declared in public headers; getdents64(2) says that we must
67  * define it ourselves.  */
68 struct linux_dirent64
69 {
70   ino64_t d_ino;
71   off64_t d_off;
72   unsigned short d_reclen;
73   unsigned char d_type;
74   char d_name[];
75 };
76
77 # define DIR_BUF_SIZE 1024
78 #endif /*USE_LINUX_GETDENTS*/
79
80
81 /* Return true if FD is valid file descriptor.  */
82 #if 0
83 int
84 _gpgme_is_fd_valid (int fd)
85 {
86   int dir_fd;
87   char dir_buf[DIR_BUF_SIZE];
88   struct linux_dirent64 *dir_entry;
89   int r, pos, x;
90   const char *s;
91   int result = 0;
92
93   dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
94   if (dir_fd != -1)
95     {
96       for (;;)
97         {
98           r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE);
99           if (r == -1)
100             break;  /* Ooops */
101           if (r == 0)
102             break;
103
104           for (pos = 0; pos < r; pos += dir_entry->d_reclen)
105             {
106               dir_entry = (struct linux_dirent64 *) (dir_buf + pos);
107               s = dir_entry->d_name;
108               if (*s < '0' || *s > '9')
109                 continue;
110               /* atoi is not guaranteed to be async-signal-safe.  */
111               for (x = 0; *s >= '0' && *s <= '9'; s++)
112                 x = x * 10 + (*s - '0');
113               if (*s)
114                 continue;  /* Does not look like a file descriptor.  */
115               if (x == fd)
116                 {
117                   result = 1;
118                   goto leave;
119                 }
120             }
121         }
122     leave:
123       close (dir_fd);
124     }
125   return result;
126 }
127 #endif /*0*/
128
129 \f
130 void
131 _gpgme_io_subsystem_init (void)
132 {
133   struct sigaction act;
134
135   sigaction (SIGPIPE, NULL, &act);
136   if (act.sa_handler == SIG_DFL)
137     {
138       act.sa_handler = SIG_IGN;
139       sigemptyset (&act.sa_mask);
140       act.sa_flags = 0;
141       sigaction (SIGPIPE, &act, NULL);
142     }
143 }
144
145
146 /* Write the printable version of FD to the buffer BUF of length
147    BUFLEN.  The printable version is the representation on the command
148    line that the child process expects.  */
149 int
150 _gpgme_io_fd2str (char *buf, int buflen, int fd)
151 {
152   return snprintf (buf, buflen, "%d", fd);
153 }
154
155 \f
156 /* The table to hold notification handlers.  We use a linear search
157    and extend the table as needed.  */
158 struct notify_table_item_s
159 {
160   int fd;  /* -1 indicates an unused entry.  */
161   _gpgme_close_notify_handler_t handler;
162   void *value;
163 };
164 typedef struct notify_table_item_s *notify_table_item_t;
165
166 static notify_table_item_t notify_table;
167 static size_t notify_table_size;
168 DEFINE_STATIC_LOCK (notify_table_lock);
169
170
171 \f
172 int
173 _gpgme_io_read (int fd, void *buffer, size_t count)
174 {
175   int nread;
176   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_read", NULL,
177               "fd=%d buffer=%p count=%zu", fd, buffer, count);
178
179   do
180     {
181       nread = _gpgme_ath_read (fd, buffer, count);
182     }
183   while (nread == -1 && errno == EINTR);
184
185   TRACE_LOGBUFX (buffer, nread);
186   return TRACE_SYSRES (nread);
187 }
188
189
190 int
191 _gpgme_io_write (int fd, const void *buffer, size_t count)
192 {
193   int nwritten;
194   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_write", NULL,
195               "fd=%d buffer=%p count=%zu", fd, buffer, count);
196   TRACE_LOGBUFX (buffer, count);
197
198   do
199     {
200       nwritten = _gpgme_ath_write (fd, buffer, count);
201     }
202   while (nwritten == -1 && errno == EINTR);
203
204   return TRACE_SYSRES (nwritten);
205 }
206
207
208 int
209 _gpgme_io_pipe (int filedes[2], int inherit_idx)
210 {
211   int saved_errno;
212   int err;
213   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_pipe", NULL,
214               "inherit_idx=%i (GPGME uses it for %s)",
215               inherit_idx, inherit_idx ? "reading" : "writing");
216
217   err = pipe (filedes);
218   if (err < 0)
219     return TRACE_SYSRES (err);
220
221   /* FIXME: Should get the old flags first.  */
222   err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
223   saved_errno = errno;
224   if (err < 0)
225     {
226       close (filedes[0]);
227       close (filedes[1]);
228     }
229   errno = saved_errno;
230   if (err)
231     return TRACE_SYSRES (err);
232
233   TRACE_SUC ("read fd=%d write fd=%d", filedes[0], filedes[1]);
234   return 0;
235 }
236
237
238 int
239 _gpgme_io_close (int fd)
240 {
241   int res;
242   _gpgme_close_notify_handler_t handler = NULL;
243   void *handler_value;
244   int idx;
245
246   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", NULL, "fd=%d", fd);
247
248   if (fd == -1)
249     {
250       errno = EINVAL;
251       return TRACE_SYSRES (-1);
252     }
253
254   /* First call the notify handler.  */
255   LOCK (notify_table_lock);
256   for (idx=0; idx < notify_table_size; idx++)
257     {
258       if (notify_table[idx].fd == fd)
259         {
260           handler       = notify_table[idx].handler;
261           handler_value = notify_table[idx].value;
262           notify_table[idx].handler = NULL;
263           notify_table[idx].value = NULL;
264           notify_table[idx].fd = -1; /* Mark slot as free.  */
265           break;
266         }
267     }
268   UNLOCK (notify_table_lock);
269   if (handler)
270     {
271       TRACE_LOG  ("invoking close handler %p/%p", handler, handler_value);
272       handler (fd, handler_value);
273     }
274
275   /* Then do the close.  */
276   res = close (fd);
277   return TRACE_SYSRES (res);
278 }
279
280
281 int
282 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
283                             void *value)
284 {
285   int res = 0;
286   int idx;
287
288   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_set_close_notify", NULL,
289               "fd=%d close_handler=%p/%p", fd, handler, value);
290
291   assert (fd != -1);
292
293   LOCK (notify_table_lock);
294   for (idx=0; idx < notify_table_size; idx++)
295     if (notify_table[idx].fd == -1)
296       break;
297   if (idx == notify_table_size)
298     {
299       /* We need to increase the size of the table.  The approach we
300          take is straightforward to minimize the risk of bugs.  */
301       notify_table_item_t newtbl;
302       size_t newsize = notify_table_size + 64;
303
304       newtbl = calloc (newsize, sizeof *newtbl);
305       if (!newtbl)
306         {
307           res = -1;
308           goto leave;
309         }
310       for (idx=0; idx < notify_table_size; idx++)
311         newtbl[idx] = notify_table[idx];
312       for (; idx < newsize; idx++)
313         {
314           newtbl[idx].fd = -1;
315           newtbl[idx].handler = NULL;
316           newtbl[idx].value = NULL;
317         }
318       free (notify_table);
319       notify_table = newtbl;
320       idx = notify_table_size;
321       notify_table_size = newsize;
322     }
323   notify_table[idx].fd = fd;
324   notify_table[idx].handler = handler;
325   notify_table[idx].value = value;
326
327  leave:
328   UNLOCK (notify_table_lock);
329
330   return TRACE_SYSRES (res);
331 }
332
333
334 int
335 _gpgme_io_set_nonblocking (int fd)
336 {
337   int flags;
338   int res;
339   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", NULL, "fd=%d", fd);
340
341   flags = fcntl (fd, F_GETFL, 0);
342   if (flags == -1)
343     return TRACE_SYSRES (-1);
344   flags |= O_NONBLOCK;
345   res = fcntl (fd, F_SETFL, flags);
346   return TRACE_SYSRES (res);
347 }
348
349
350 static long int
351 get_max_fds (void)
352 {
353   const char *source = NULL;
354   long int fds = -1;
355   int rc;
356
357   /* Under Linux we can figure out the highest used file descriptor by
358    * reading /proc/self/fd.  This is in the common cases much faster
359    * than for example doing 4096 close calls where almost all of them
360    * will fail.
361    *
362    * We can't use the normal opendir/readdir/closedir interface between
363    * fork and exec in a multi-threaded process because opendir uses
364    * malloc and thus a mutex which may deadlock with a malloc in another
365    * thread.  However, the underlying getdents system call is safe.  */
366 #ifdef USE_LINUX_GETDENTS
367   {
368     int dir_fd;
369     char dir_buf[DIR_BUF_SIZE];
370     struct linux_dirent64 *dir_entry;
371     int r, pos;
372     const char *s;
373     int x;
374
375     dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
376     if (dir_fd != -1)
377       {
378         for (;;)
379           {
380             r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE);
381             if (r == -1)
382               {
383                 /* Fall back to other methods.  */
384                 fds = -1;
385                 break;
386               }
387             if (r == 0)
388               break;
389
390             for (pos = 0; pos < r; pos += dir_entry->d_reclen)
391               {
392                 dir_entry = (struct linux_dirent64 *) (dir_buf + pos);
393                 s = dir_entry->d_name;
394                 if (*s < '0' || *s > '9')
395                   continue;
396                 /* atoi is not guaranteed to be async-signal-safe.  */
397                 for (x = 0; *s >= '0' && *s <= '9'; s++)
398                   x = x * 10 + (*s - '0');
399                 if (!*s && x > fds && x != dir_fd)
400                   fds = x;
401               }
402           }
403
404         close (dir_fd);
405       }
406     if (fds != -1)
407       {
408         fds++;
409         source = "/proc";
410       }
411     }
412 #endif /*USE_LINUX_GETDENTS*/
413
414 #ifdef RLIMIT_NOFILE
415   if (fds == -1)
416     {
417       struct rlimit rl;
418       rc = getrlimit (RLIMIT_NOFILE, &rl);
419       if (rc == 0)
420         {
421           source = "RLIMIT_NOFILE";
422           fds = rl.rlim_max;
423         }
424     }
425 #endif
426 #ifdef RLIMIT_OFILE
427   if (fds == -1)
428     {
429       struct rlimit rl;
430       rc = getrlimit (RLIMIT_OFILE, &rl);
431       if (rc == 0)
432         {
433           source = "RLIMIT_OFILE";
434           fds = rl.rlim_max;
435         }
436     }
437 #endif
438 #ifdef _SC_OPEN_MAX
439   if (fds == -1)
440     {
441       long int scres;
442       scres = sysconf (_SC_OPEN_MAX);
443       if (scres >= 0)
444         {
445           source = "_SC_OPEN_MAX";
446           return scres;
447         }
448     }
449 #endif
450 #ifdef OPEN_MAX
451   if (fds == -1)
452     {
453       source = "OPEN_MAX";
454       fds = OPEN_MAX;
455     }
456 #endif
457
458 #if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \
459   && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX)
460 #warning "No known way to get the maximum number of file descriptors."
461 #endif
462   if (fds == -1)
463     {
464       source = "arbitrary";
465       /* Arbitrary limit.  */
466       fds = 1024;
467     }
468
469   /* AIX returns INT32_MAX instead of a proper value.  We assume that
470    * this is always an error and use a more reasonable limit.  */
471 #ifdef INT32_MAX
472   if (fds == INT32_MAX)
473     {
474       source = "aix-fix";
475       fds = 1024;
476     }
477 #endif
478
479   TRACE (DEBUG_SYSIO, "gpgme:max_fds", NULL, "max fds=%ld (%s)", fds, source);
480   return fds;
481 }
482
483
484 int
485 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
486 {
487   int status;
488   pid_t ret;
489
490   *r_status = 0;
491   *r_signal = 0;
492   do
493     ret = _gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG);
494   while (ret == (pid_t)(-1) && errno == EINTR);
495
496   if (ret == pid)
497     {
498       if (WIFSIGNALED (status))
499         {
500           *r_status = 4; /* Need some value here.  */
501           *r_signal = WTERMSIG (status);
502         }
503       else if (WIFEXITED (status))
504         *r_status = WEXITSTATUS (status);
505       else
506         *r_status = 4; /* Oops.  */
507       return 1;
508     }
509   return 0;
510 }
511
512
513 /* Returns 0 on success, -1 on error.  */
514 int
515 _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
516                  struct spawn_fd_item_s *fd_list,
517                  void (*atfork) (void *opaque, int reserved),
518                  void *atforkvalue, pid_t *r_pid)
519 {
520   pid_t pid;
521   int i;
522   int status;
523   int signo;
524
525   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_spawn", NULL,
526               "path=%s", path);
527   i = 0;
528   while (argv[i])
529     {
530       TRACE_LOG  ("argv[%2i] = %s", i, argv[i]);
531       i++;
532     }
533   for (i = 0; fd_list[i].fd != -1; i++)
534     {
535       if (fd_list[i].dup_to == -1)
536         TRACE_LOG  ("fd[%i] = 0x%x", i, fd_list[i].fd);
537       else
538         TRACE_LOG  ("fd[%i] = 0x%x -> 0x%x", i,fd_list[i].fd,fd_list[i].dup_to);
539     }
540
541   pid = fork ();
542   if (pid == -1)
543     return TRACE_SYSRES (-1);
544
545   if (!pid)
546     {
547       /* Intermediate child to prevent zombie processes.  */
548       if ((pid = fork ()) == 0)
549         {
550           /* Child.  */
551           int max_fds = -1;
552           int fd;
553           int seen_stdin = 0;
554           int seen_stdout = 0;
555           int seen_stderr = 0;
556
557           if (atfork)
558             atfork (atforkvalue, 0);
559
560           /* First close all fds which will not be inherited.  If we
561            * have closefrom(2) we first figure out the highest fd we
562            * do not want to close, then call closefrom, and on success
563            * use the regular code to close all fds up to the start
564            * point of closefrom.  Note that Solaris' and FreeBSD's closefrom do
565            * not return errors.  */
566 #ifdef HAVE_CLOSEFROM
567           {
568             fd = -1;
569             for (i = 0; fd_list[i].fd != -1; i++)
570               if (fd_list[i].fd > fd)
571                 fd = fd_list[i].fd;
572             fd++;
573 #if defined(__sun) || defined(__FreeBSD__)
574             closefrom (fd);
575             max_fds = fd;
576 #else /*!__sun */
577             while ((i = closefrom (fd)) && errno == EINTR)
578               ;
579             if (!i || errno == EBADF)
580               max_fds = fd;
581 #endif /*!__sun*/
582           }
583 #endif /*HAVE_CLOSEFROM*/
584           if (max_fds == -1)
585             max_fds = get_max_fds ();
586           for (fd = 0; fd < max_fds; fd++)
587             {
588               for (i = 0; fd_list[i].fd != -1; i++)
589                 if (fd_list[i].fd == fd)
590                   break;
591               if (fd_list[i].fd == -1)
592                 close (fd);
593             }
594
595           /* And now dup and close those to be duplicated.  */
596           for (i = 0; fd_list[i].fd != -1; i++)
597             {
598               int child_fd;
599               int res;
600
601               if (fd_list[i].dup_to != -1)
602                 child_fd = fd_list[i].dup_to;
603               else
604                 child_fd = fd_list[i].fd;
605
606               if (child_fd == 0)
607                 seen_stdin = 1;
608               else if (child_fd == 1)
609                 seen_stdout = 1;
610               else if (child_fd == 2)
611                 seen_stderr = 1;
612
613               if (fd_list[i].dup_to == -1)
614                 continue;
615
616               res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
617               if (res < 0)
618                 {
619 #if 0
620                   /* FIXME: The debug file descriptor is not
621                      dup'ed anyway, so we can't see this.  */
622                   TRACE_LOG  ("dup2 failed in child: %s\n",
623                               strerror (errno));
624 #endif
625                   _exit (8);
626                 }
627
628               close (fd_list[i].fd);
629             }
630
631           if (! seen_stdin || ! seen_stdout || !seen_stderr)
632             {
633               fd = open ("/dev/null", O_RDWR);
634               if (fd == -1)
635                 {
636                   /* The debug file descriptor is not dup'ed, so we
637                      can't do a trace output.  */
638                   _exit (8);
639                 }
640               /* Make sure that the process has connected stdin.  */
641               if (! seen_stdin && fd != 0)
642                 {
643                   if (dup2 (fd, 0) == -1)
644                     _exit (8);
645                 }
646               if (! seen_stdout && fd != 1)
647                 {
648                   if (dup2 (fd, 1) == -1)
649                     _exit (8);
650                 }
651               if (! seen_stderr && fd != 2)
652                 {
653                   if (dup2 (fd, 2) == -1)
654                     _exit (8);
655                 }
656               if (fd != 0 && fd != 1 && fd != 2)
657                 close (fd);
658             }
659
660           execv (path, (char *const *) argv);
661           /* Hmm: in that case we could write a special status code to the
662              status-pipe.  */
663           _exit (8);
664           /* End child.  */
665         }
666       if (pid == -1)
667         _exit (1);
668       else
669         _exit (0);
670     }
671
672   TRACE_LOG  ("waiting for child process pid=%i", pid);
673   _gpgme_io_waitpid (pid, 1, &status, &signo);
674   if (status)
675     return TRACE_SYSRES (-1);
676
677   for (i = 0; fd_list[i].fd != -1; i++)
678     {
679       if (! (flags & IOSPAWN_FLAG_NOCLOSE))
680         _gpgme_io_close (fd_list[i].fd);
681       /* No handle translation.  */
682       fd_list[i].peer_name = fd_list[i].fd;
683     }
684
685   if (r_pid)
686     *r_pid = pid;
687
688   return TRACE_SYSRES (0);
689 }
690
691
692 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
693    nothing to select, > 0 = number of signaled fds.  */
694 int
695 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
696 {
697   fd_set readfds;
698   fd_set writefds;
699   unsigned int i;
700   int any;
701   int max_fd;
702   int n;
703   int count;
704   /* Use a 1s timeout.  */
705   struct timeval timeout = { 1, 0 };
706   void *dbg_help = NULL;
707   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_select", NULL,
708               "nfds=%zu, nonblock=%u", nfds, nonblock);
709
710   FD_ZERO (&readfds);
711   FD_ZERO (&writefds);
712   max_fd = 0;
713   if (nonblock)
714     timeout.tv_sec = 0;
715
716   TRACE_SEQ (dbg_help, "select on [ ");
717
718   any = 0;
719   for (i = 0; i < nfds; i++)
720     {
721       if (fds[i].fd == -1)
722         continue;
723       if (fds[i].for_read)
724         {
725           if (fds[i].fd >= FD_SETSIZE)
726             {
727               TRACE_END (dbg_help, " -BAD- ]");
728               gpg_err_set_errno (EMFILE);
729               return TRACE_SYSRES (-1);
730             }
731           assert (!FD_ISSET (fds[i].fd, &readfds));
732           FD_SET (fds[i].fd, &readfds);
733           if (fds[i].fd > max_fd)
734             max_fd = fds[i].fd;
735           TRACE_ADD1 (dbg_help, "r=%d ", fds[i].fd);
736           any = 1;
737         }
738       else if (fds[i].for_write)
739         {
740           if (fds[i].fd >= FD_SETSIZE)
741             {
742               TRACE_END (dbg_help, " -BAD- ]");
743               gpg_err_set_errno (EMFILE);
744               return TRACE_SYSRES (-1);
745             }
746           assert (!FD_ISSET (fds[i].fd, &writefds));
747           FD_SET (fds[i].fd, &writefds);
748           if (fds[i].fd > max_fd)
749             max_fd = fds[i].fd;
750           TRACE_ADD1 (dbg_help, "w=%d ", fds[i].fd);
751           any = 1;
752         }
753       fds[i].signaled = 0;
754     }
755   TRACE_END (dbg_help, "]");
756   if (!any)
757     return TRACE_SYSRES (0);
758
759   do
760     {
761       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
762                                  &timeout);
763     }
764   while (count < 0 && errno == EINTR);
765   if (count < 0)
766     return TRACE_SYSRES (-1);
767
768   TRACE_SEQ (dbg_help, "select OK [ ");
769   if (TRACE_ENABLED (dbg_help))
770     {
771       for (i = 0; i <= max_fd; i++)
772         {
773           if (FD_ISSET (i, &readfds))
774             TRACE_ADD1 (dbg_help, "r=%d ", i);
775           if (FD_ISSET (i, &writefds))
776             TRACE_ADD1 (dbg_help, "w=%d ", i);
777         }
778       TRACE_END (dbg_help, "]");
779     }
780
781   /* The variable N is used to optimize it a little bit.  */
782   for (n = count, i = 0; i < nfds && n; i++)
783     {
784       if (fds[i].fd == -1)
785         ;
786       else if (fds[i].for_read)
787         {
788           if (FD_ISSET (fds[i].fd, &readfds))
789             {
790               fds[i].signaled = 1;
791               n--;
792             }
793         }
794       else if (fds[i].for_write)
795         {
796           if (FD_ISSET (fds[i].fd, &writefds))
797             {
798               fds[i].signaled = 1;
799               n--;
800             }
801         }
802     }
803   return TRACE_SYSRES (count);
804 }
805
806 \f
807 int
808 _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
809 {
810   int nread;
811   int saved_errno;
812   struct iovec *iov;
813   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_recvmsg", NULL,
814               "fd=%d msg=%p flags=%i", fd, msg, flags);
815
816   nread = 0;
817   iov = msg->msg_iov;
818   while (iov < msg->msg_iov + msg->msg_iovlen)
819     {
820       nread += iov->iov_len;
821       iov++;
822     }
823
824   TRACE_LOG  ("about to receive %d bytes", nread);
825
826   do
827     {
828       nread = _gpgme_ath_recvmsg (fd, msg, flags);
829     }
830   while (nread == -1 && errno == EINTR);
831   saved_errno = errno;
832   if (nread > 0)
833     {
834       int nr = nread;
835
836       iov = msg->msg_iov;
837       while (nr > 0)
838         {
839           int len = nr > iov->iov_len ? iov->iov_len : nr;
840           TRACE_LOGBUFX (msg->msg_iov->iov_base, len);
841           iov++;
842           nr -= len;
843         }
844     }
845   errno = saved_errno;
846   return TRACE_SYSRES (nread);
847 }
848
849
850 int
851 _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
852 {
853   int nwritten;
854   struct iovec *iov;
855   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_sendmsg", NULL,
856               "fd=%d msg=%p flags=%i", fd, msg, flags);
857
858   nwritten = 0;
859   iov = msg->msg_iov;
860   while (iov < msg->msg_iov + msg->msg_iovlen)
861     {
862       nwritten += iov->iov_len;
863       iov++;
864     }
865
866   TRACE_LOG  ("about to receive %d bytes", nwritten);
867   iov = msg->msg_iov;
868   while (nwritten > 0)
869     {
870       int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
871       TRACE_LOGBUFX (msg->msg_iov->iov_base, len);
872       iov++;
873       nwritten -= len;
874     }
875
876   do
877     {
878       nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
879     }
880   while (nwritten == -1 && errno == EINTR);
881   return TRACE_SYSRES (nwritten);
882 }
883
884
885 int
886 _gpgme_io_dup (int fd)
887 {
888   int new_fd;
889
890   do
891     new_fd = dup (fd);
892   while (new_fd == -1 && errno == EINTR);
893
894   TRACE (DEBUG_SYSIO, "_gpgme_io_dup", NULL, "fd=%d -> fd=%d", fd, new_fd);
895
896   return new_fd;
897 }
898
899 \f
900 int
901 _gpgme_io_socket (int domain, int type, int proto)
902 {
903   int res;
904
905   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_socket", NULL,
906               "domain=%d type=%i proto=%i", domain, type, proto);
907
908   res = socket (domain, type, proto);
909
910   return TRACE_SYSRES (res);
911 }
912
913
914 int
915 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
916 {
917   int res;
918
919   TRACE_BEG  (DEBUG_SYSIO, "_gpgme_io_connect", NULL,
920               "fd=%d addr=%p addrlen=%i", fd, addr, addrlen);
921
922   do
923     res = ath_connect (fd, addr, addrlen);
924   while (res == -1 && errno == EINTR);
925
926   return TRACE_SYSRES (res);
927 }