core: Do not crash if CMS plaintext is ignored
[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; getdents64(2) says that we must
286  * define it ourselves.  */
287 struct linux_dirent64
288 {
289   ino64_t d_ino;
290   off64_t d_off;
291   unsigned short d_reclen;
292   unsigned char d_type;
293   char d_name[];
294 };
295
296 # define DIR_BUF_SIZE 1024
297 #endif /*USE_LINUX_GETDENTS*/
298
299
300 static long int
301 get_max_fds (void)
302 {
303   const char *source = NULL;
304   long int fds = -1;
305   int rc;
306
307   /* Under Linux we can figure out the highest used file descriptor by
308    * reading /proc/self/fd.  This is in the common cases much faster
309    * than for example doing 4096 close calls where almost all of them
310    * will fail.
311    *
312    * We can't use the normal opendir/readdir/closedir interface between
313    * fork and exec in a multi-threaded process because opendir uses
314    * malloc and thus a mutex which may deadlock with a malloc in another
315    * thread.  However, the underlying getdents system call is safe.  */
316 #ifdef USE_LINUX_GETDENTS
317   {
318     int dir_fd;
319     char dir_buf[DIR_BUF_SIZE];
320     struct linux_dirent64 *dir_entry;
321     int r, pos;
322     const char *s;
323     int x;
324
325     dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
326     if (dir_fd != -1)
327       {
328         for (;;)
329           {
330             r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE);
331             if (r == -1)
332               {
333                 /* Fall back to other methods.  */
334                 fds = -1;
335                 break;
336               }
337             if (r == 0)
338               break;
339
340             for (pos = 0; pos < r; pos += dir_entry->d_reclen)
341               {
342                 dir_entry = (struct linux_dirent64 *) (dir_buf + pos);
343                 s = dir_entry->d_name;
344                 if (*s < '0' || *s > '9')
345                   continue;
346                 /* atoi is not guaranteed to be async-signal-safe.  */
347                 for (x = 0; *s >= '0' && *s <= '9'; s++)
348                   x = x * 10 + (*s - '0');
349                 if (!*s && x > fds && x != dir_fd)
350                   fds = x;
351               }
352           }
353
354         close (dir_fd);
355       }
356     if (fds != -1)
357       {
358         fds++;
359         source = "/proc";
360       }
361     }
362 #endif /*USE_LINUX_GETDENTS*/
363
364 #ifdef RLIMIT_NOFILE
365   if (fds == -1)
366     {
367       struct rlimit rl;
368       rc = getrlimit (RLIMIT_NOFILE, &rl);
369       if (rc == 0)
370         {
371           source = "RLIMIT_NOFILE";
372           fds = rl.rlim_max;
373         }
374     }
375 #endif
376 #ifdef RLIMIT_OFILE
377   if (fds == -1)
378     {
379       struct rlimit rl;
380       rc = getrlimit (RLIMIT_OFILE, &rl);
381       if (rc == 0)
382         {
383           source = "RLIMIT_OFILE";
384           fds = rl.rlim_max;
385         }
386     }
387 #endif
388 #ifdef _SC_OPEN_MAX
389   if (fds == -1)
390     {
391       long int scres;
392       scres = sysconf (_SC_OPEN_MAX);
393       if (scres >= 0)
394         {
395           source = "_SC_OPEN_MAX";
396           return scres;
397         }
398     }
399 #endif
400 #ifdef OPEN_MAX
401   if (fds == -1)
402     {
403       source = "OPEN_MAX";
404       fds = OPEN_MAX;
405     }
406 #endif
407
408 #if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \
409   && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX)
410 #warning "No known way to get the maximum number of file descriptors."
411 #endif
412   if (fds == -1)
413     {
414       source = "arbitrary";
415       /* Arbitrary limit.  */
416       fds = 1024;
417     }
418
419   /* AIX returns INT32_MAX instead of a proper value.  We assume that
420    * this is always an error and use a more reasonable limit.  */
421 #ifdef INT32_MAX
422   if (fds == INT32_MAX)
423     {
424       source = "aix-fix";
425       fds = 1024;
426     }
427 #endif
428
429   TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%i (%s)", fds, source);
430   return fds;
431 }
432
433
434 int
435 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
436 {
437   int status;
438   pid_t ret;
439
440   *r_status = 0;
441   *r_signal = 0;
442   do
443     ret = _gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG);
444   while (ret == (pid_t)(-1) && errno == EINTR);
445
446   if (ret == pid)
447     {
448       if (WIFSIGNALED (status))
449         {
450           *r_status = 4; /* Need some value here.  */
451           *r_signal = WTERMSIG (status);
452         }
453       else if (WIFEXITED (status))
454         *r_status = WEXITSTATUS (status);
455       else
456         *r_status = 4; /* Oops.  */
457       return 1;
458     }
459   return 0;
460 }
461
462
463 /* Returns 0 on success, -1 on error.  */
464 int
465 _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
466                  struct spawn_fd_item_s *fd_list,
467                  void (*atfork) (void *opaque, int reserved),
468                  void *atforkvalue, pid_t *r_pid)
469 {
470   pid_t pid;
471   int i;
472   int status;
473   int signo;
474
475   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
476               "path=%s", path);
477   i = 0;
478   while (argv[i])
479     {
480       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
481       i++;
482     }
483   for (i = 0; fd_list[i].fd != -1; i++)
484     if (fd_list[i].dup_to == -1)
485       TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd);
486     else
487       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
488
489   pid = fork ();
490   if (pid == -1)
491     return TRACE_SYSRES (-1);
492
493   if (!pid)
494     {
495       /* Intermediate child to prevent zombie processes.  */
496       if ((pid = fork ()) == 0)
497         {
498           /* Child.  */
499           int max_fds = -1;
500           int fd;
501           int seen_stdin = 0;
502           int seen_stdout = 0;
503           int seen_stderr = 0;
504
505           if (atfork)
506             atfork (atforkvalue, 0);
507
508           /* First close all fds which will not be inherited.  If we
509            * have closefrom(2) we first figure out the highest fd we
510            * do not want to close, then call closefrom, and on success
511            * use the regular code to close all fds up to the start
512            * point of closefrom.  Note that Solaris' and FreeBSD's closefrom do
513            * not return errors.  */
514 #ifdef HAVE_CLOSEFROM
515           {
516             fd = -1;
517             for (i = 0; fd_list[i].fd != -1; i++)
518               if (fd_list[i].fd > fd)
519                 fd = fd_list[i].fd;
520             fd++;
521 #if defined(__sun) || defined(__FreeBSD__)
522             closefrom (fd);
523             max_fds = fd;
524 #else /*!__sun */
525             while ((i = closefrom (fd)) && errno == EINTR)
526               ;
527             if (!i || errno == EBADF)
528               max_fds = fd;
529 #endif /*!__sun*/
530           }
531 #endif /*HAVE_CLOSEFROM*/
532           if (max_fds == -1)
533             max_fds = get_max_fds ();
534           for (fd = 0; fd < max_fds; fd++)
535             {
536               for (i = 0; fd_list[i].fd != -1; i++)
537                 if (fd_list[i].fd == fd)
538                   break;
539               if (fd_list[i].fd == -1)
540                 close (fd);
541             }
542
543           /* And now dup and close those to be duplicated.  */
544           for (i = 0; fd_list[i].fd != -1; i++)
545             {
546               int child_fd;
547               int res;
548
549               if (fd_list[i].dup_to != -1)
550                 child_fd = fd_list[i].dup_to;
551               else
552                 child_fd = fd_list[i].fd;
553
554               if (child_fd == 0)
555                 seen_stdin = 1;
556               else if (child_fd == 1)
557                 seen_stdout = 1;
558               else if (child_fd == 2)
559                 seen_stderr = 1;
560
561               if (fd_list[i].dup_to == -1)
562                 continue;
563
564               res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
565               if (res < 0)
566                 {
567 #if 0
568                   /* FIXME: The debug file descriptor is not
569                      dup'ed anyway, so we can't see this.  */
570                   TRACE_LOG1 ("dup2 failed in child: %s\n",
571                               strerror (errno));
572 #endif
573                   _exit (8);
574                 }
575
576               close (fd_list[i].fd);
577             }
578
579           if (! seen_stdin || ! seen_stdout || !seen_stderr)
580             {
581               fd = open ("/dev/null", O_RDWR);
582               if (fd == -1)
583                 {
584                   /* The debug file descriptor is not dup'ed, so we
585                      can't do a trace output.  */
586                   _exit (8);
587                 }
588               /* Make sure that the process has connected stdin.  */
589               if (! seen_stdin && fd != 0)
590                 {
591                   if (dup2 (fd, 0) == -1)
592                     _exit (8);
593                 }
594               if (! seen_stdout && fd != 1)
595                 {
596                   if (dup2 (fd, 1) == -1)
597                     _exit (8);
598                 }
599               if (! seen_stderr && fd != 2)
600                 {
601                   if (dup2 (fd, 2) == -1)
602                     _exit (8);
603                 }
604               if (fd != 0 && fd != 1 && fd != 2)
605                 close (fd);
606             }
607
608           execv (path, (char *const *) argv);
609           /* Hmm: in that case we could write a special status code to the
610              status-pipe.  */
611           _exit (8);
612           /* End child.  */
613         }
614       if (pid == -1)
615         _exit (1);
616       else
617         _exit (0);
618     }
619
620   TRACE_LOG1 ("waiting for child process pid=%i", pid);
621   _gpgme_io_waitpid (pid, 1, &status, &signo);
622   if (status)
623     return TRACE_SYSRES (-1);
624
625   for (i = 0; fd_list[i].fd != -1; i++)
626     {
627       if (! (flags & IOSPAWN_FLAG_NOCLOSE))
628         _gpgme_io_close (fd_list[i].fd);
629       /* No handle translation.  */
630       fd_list[i].peer_name = fd_list[i].fd;
631     }
632
633   if (r_pid)
634     *r_pid = pid;
635
636   return TRACE_SYSRES (0);
637 }
638
639
640 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
641    nothing to select, > 0 = number of signaled fds.  */
642 int
643 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
644 {
645   fd_set readfds;
646   fd_set writefds;
647   unsigned int i;
648   int any;
649   int max_fd;
650   int n;
651   int count;
652   /* Use a 1s timeout.  */
653   struct timeval timeout = { 1, 0 };
654   void *dbg_help = NULL;
655   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
656               "nfds=%u, nonblock=%u", nfds, nonblock);
657
658   FD_ZERO (&readfds);
659   FD_ZERO (&writefds);
660   max_fd = 0;
661   if (nonblock)
662     timeout.tv_sec = 0;
663
664   TRACE_SEQ (dbg_help, "select on [ ");
665
666   any = 0;
667   for (i = 0; i < nfds; i++)
668     {
669       if (fds[i].fd == -1)
670         continue;
671       if (fds[i].for_read)
672         {
673           if (fds[i].fd >= FD_SETSIZE)
674             {
675               TRACE_END (dbg_help, " -BAD- ]");
676               gpg_err_set_errno (EMFILE);
677               return TRACE_SYSRES (-1);
678             }
679           assert (!FD_ISSET (fds[i].fd, &readfds));
680           FD_SET (fds[i].fd, &readfds);
681           if (fds[i].fd > max_fd)
682             max_fd = fds[i].fd;
683           TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
684           any = 1;
685         }
686       else if (fds[i].for_write)
687         {
688           if (fds[i].fd >= FD_SETSIZE)
689             {
690               TRACE_END (dbg_help, " -BAD- ]");
691               gpg_err_set_errno (EMFILE);
692               return TRACE_SYSRES (-1);
693             }
694           assert (!FD_ISSET (fds[i].fd, &writefds));
695           FD_SET (fds[i].fd, &writefds);
696           if (fds[i].fd > max_fd)
697             max_fd = fds[i].fd;
698           TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
699           any = 1;
700         }
701       fds[i].signaled = 0;
702     }
703   TRACE_END (dbg_help, "]");
704   if (!any)
705     return TRACE_SYSRES (0);
706
707   do
708     {
709       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
710                                  &timeout);
711     }
712   while (count < 0 && errno == EINTR);
713   if (count < 0)
714     return TRACE_SYSRES (-1);
715
716   TRACE_SEQ (dbg_help, "select OK [ ");
717   if (TRACE_ENABLED (dbg_help))
718     {
719       for (i = 0; i <= max_fd; i++)
720         {
721           if (FD_ISSET (i, &readfds))
722             TRACE_ADD1 (dbg_help, "r0x%x ", i);
723           if (FD_ISSET (i, &writefds))
724             TRACE_ADD1 (dbg_help, "w0x%x ", i);
725         }
726       TRACE_END (dbg_help, "]");
727     }
728
729   /* The variable N is used to optimize it a little bit.  */
730   for (n = count, i = 0; i < nfds && n; i++)
731     {
732       if (fds[i].fd == -1)
733         ;
734       else if (fds[i].for_read)
735         {
736           if (FD_ISSET (fds[i].fd, &readfds))
737             {
738               fds[i].signaled = 1;
739               n--;
740             }
741         }
742       else if (fds[i].for_write)
743         {
744           if (FD_ISSET (fds[i].fd, &writefds))
745             {
746               fds[i].signaled = 1;
747               n--;
748             }
749         }
750     }
751   return TRACE_SYSRES (count);
752 }
753
754 \f
755 int
756 _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
757 {
758   int nread;
759   int saved_errno;
760   struct iovec *iov;
761   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
762               "msg=%p, flags=%i", msg, flags);
763
764   nread = 0;
765   iov = msg->msg_iov;
766   while (iov < msg->msg_iov + msg->msg_iovlen)
767     {
768       nread += iov->iov_len;
769       iov++;
770     }
771
772   TRACE_LOG1 ("about to receive %d bytes", nread);
773
774   do
775     {
776       nread = _gpgme_ath_recvmsg (fd, msg, flags);
777     }
778   while (nread == -1 && errno == EINTR);
779   saved_errno = errno;
780   if (nread > 0)
781     {
782       int nr = nread;
783
784       iov = msg->msg_iov;
785       while (nr > 0)
786         {
787           int len = nr > iov->iov_len ? iov->iov_len : nr;
788           TRACE_LOGBUF (msg->msg_iov->iov_base, len);
789           iov++;
790           nr -= len;
791         }
792     }
793   errno = saved_errno;
794   return TRACE_SYSRES (nread);
795 }
796
797
798 int
799 _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
800 {
801   int nwritten;
802   struct iovec *iov;
803   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
804               "msg=%p, flags=%i", msg, flags);
805
806   nwritten = 0;
807   iov = msg->msg_iov;
808   while (iov < msg->msg_iov + msg->msg_iovlen)
809     {
810       nwritten += iov->iov_len;
811       iov++;
812     }
813
814   TRACE_LOG1 ("about to receive %d bytes", nwritten);
815   iov = msg->msg_iov;
816   while (nwritten > 0)
817     {
818       int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
819       TRACE_LOGBUF (msg->msg_iov->iov_base, len);
820       iov++;
821       nwritten -= len;
822     }
823
824   do
825     {
826       nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
827     }
828   while (nwritten == -1 && errno == EINTR);
829   return TRACE_SYSRES (nwritten);
830 }
831
832
833 int
834 _gpgme_io_dup (int fd)
835 {
836   int new_fd;
837
838   do
839     new_fd = dup (fd);
840   while (new_fd == -1 && errno == EINTR);
841
842   TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
843
844   return new_fd;
845 }
846
847 \f
848 int
849 _gpgme_io_socket (int domain, int type, int proto)
850 {
851   int res;
852
853   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
854               "type=%i, proto=%i", type, proto);
855
856   res = socket (domain, type, proto);
857
858   return TRACE_SYSRES (res);
859 }
860
861
862 int
863 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
864 {
865   int res;
866
867   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
868               "addr=%p, addrlen=%i", addr, addrlen);
869
870   do
871     res = ath_connect (fd, addr, addrlen);
872   while (res == -1 && errno == EINTR);
873
874   return TRACE_SYSRES (res);
875 }