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