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