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