core: Fix possible deadlock due to get_max_fds.
[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           int max_fds = get_max_fds ();
463           int fd;
464
465           /* Child.  */
466           int seen_stdin = 0;
467           int seen_stdout = 0;
468           int seen_stderr = 0;
469
470           if (atfork)
471             atfork (atforkvalue, 0);
472
473           /* First close all fds which will not be inherited.  */
474           for (fd = 0; fd < max_fds; fd++)
475             {
476               for (i = 0; fd_list[i].fd != -1; i++)
477                 if (fd_list[i].fd == fd)
478                   break;
479               if (fd_list[i].fd == -1)
480                 close (fd);
481             }
482
483           /* And now dup and close those to be duplicated.  */
484           for (i = 0; fd_list[i].fd != -1; i++)
485             {
486               int child_fd;
487               int res;
488
489               if (fd_list[i].dup_to != -1)
490                 child_fd = fd_list[i].dup_to;
491               else
492                 child_fd = fd_list[i].fd;
493
494               if (child_fd == 0)
495                 seen_stdin = 1;
496               else if (child_fd == 1)
497                 seen_stdout = 1;
498               else if (child_fd == 2)
499                 seen_stderr = 1;
500
501               if (fd_list[i].dup_to == -1)
502                 continue;
503
504               res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
505               if (res < 0)
506                 {
507 #if 0
508                   /* FIXME: The debug file descriptor is not
509                      dup'ed anyway, so we can't see this.  */
510                   TRACE_LOG1 ("dup2 failed in child: %s\n",
511                               strerror (errno));
512 #endif
513                   _exit (8);
514                 }
515
516               close (fd_list[i].fd);
517             }
518
519           if (! seen_stdin || ! seen_stdout || !seen_stderr)
520             {
521               fd = open ("/dev/null", O_RDWR);
522               if (fd == -1)
523                 {
524                   /* The debug file descriptor is not dup'ed, so we
525                      can't do a trace output.  */
526                   _exit (8);
527                 }
528               /* Make sure that the process has connected stdin.  */
529               if (! seen_stdin && fd != 0)
530                 {
531                   if (dup2 (fd, 0) == -1)
532                     _exit (8);
533                 }
534               if (! seen_stdout && fd != 1)
535                 {
536                   if (dup2 (fd, 1) == -1)
537                     _exit (8);
538                 }
539               if (! seen_stderr && fd != 2)
540                 {
541                   if (dup2 (fd, 2) == -1)
542                     _exit (8);
543                 }
544               if (fd != 0 && fd != 1 && fd != 2)
545                 close (fd);
546             }
547
548           execv (path, (char *const *) argv);
549           /* Hmm: in that case we could write a special status code to the
550              status-pipe.  */
551           _exit (8);
552           /* End child.  */
553         }
554       if (pid == -1)
555         _exit (1);
556       else
557         _exit (0);
558     }
559
560   TRACE_LOG1 ("waiting for child process pid=%i", pid);
561   _gpgme_io_waitpid (pid, 1, &status, &signo);
562   if (status)
563     return TRACE_SYSRES (-1);
564
565   for (i = 0; fd_list[i].fd != -1; i++)
566     {
567       if (! (flags & IOSPAWN_FLAG_NOCLOSE))
568         _gpgme_io_close (fd_list[i].fd);
569       /* No handle translation.  */
570       fd_list[i].peer_name = fd_list[i].fd;
571     }
572
573   if (r_pid)
574     *r_pid = pid;
575
576   return TRACE_SYSRES (0);
577 }
578
579
580 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
581    nothing to select, > 0 = number of signaled fds.  */
582 int
583 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
584 {
585   fd_set readfds;
586   fd_set writefds;
587   unsigned int i;
588   int any;
589   int max_fd;
590   int n;
591   int count;
592   /* Use a 1s timeout.  */
593   struct timeval timeout = { 1, 0 };
594   void *dbg_help = NULL;
595   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
596               "nfds=%u, nonblock=%u", nfds, nonblock);
597
598   FD_ZERO (&readfds);
599   FD_ZERO (&writefds);
600   max_fd = 0;
601   if (nonblock)
602     timeout.tv_sec = 0;
603
604   TRACE_SEQ (dbg_help, "select on [ ");
605
606   any = 0;
607   for (i = 0; i < nfds; i++)
608     {
609       if (fds[i].fd == -1)
610         continue;
611       if (fds[i].for_read)
612         {
613           if (fds[i].fd >= FD_SETSIZE)
614             {
615               TRACE_END (dbg_help, " -BAD- ]");
616               gpg_err_set_errno (EMFILE);
617               return TRACE_SYSRES (-1);
618             }
619           assert (!FD_ISSET (fds[i].fd, &readfds));
620           FD_SET (fds[i].fd, &readfds);
621           if (fds[i].fd > max_fd)
622             max_fd = fds[i].fd;
623           TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
624           any = 1;
625         }
626       else if (fds[i].for_write)
627         {
628           if (fds[i].fd >= FD_SETSIZE)
629             {
630               TRACE_END (dbg_help, " -BAD- ]");
631               gpg_err_set_errno (EMFILE);
632               return TRACE_SYSRES (-1);
633             }
634           assert (!FD_ISSET (fds[i].fd, &writefds));
635           FD_SET (fds[i].fd, &writefds);
636           if (fds[i].fd > max_fd)
637             max_fd = fds[i].fd;
638           TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
639           any = 1;
640         }
641       fds[i].signaled = 0;
642     }
643   TRACE_END (dbg_help, "]");
644   if (!any)
645     return TRACE_SYSRES (0);
646
647   do
648     {
649       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
650                                  &timeout);
651     }
652   while (count < 0 && errno == EINTR);
653   if (count < 0)
654     return TRACE_SYSRES (-1);
655
656   TRACE_SEQ (dbg_help, "select OK [ ");
657   if (TRACE_ENABLED (dbg_help))
658     {
659       for (i = 0; i <= max_fd; i++)
660         {
661           if (FD_ISSET (i, &readfds))
662             TRACE_ADD1 (dbg_help, "r0x%x ", i);
663           if (FD_ISSET (i, &writefds))
664             TRACE_ADD1 (dbg_help, "w0x%x ", i);
665         }
666       TRACE_END (dbg_help, "]");
667     }
668
669   /* The variable N is used to optimize it a little bit.  */
670   for (n = count, i = 0; i < nfds && n; i++)
671     {
672       if (fds[i].fd == -1)
673         ;
674       else if (fds[i].for_read)
675         {
676           if (FD_ISSET (fds[i].fd, &readfds))
677             {
678               fds[i].signaled = 1;
679               n--;
680             }
681         }
682       else if (fds[i].for_write)
683         {
684           if (FD_ISSET (fds[i].fd, &writefds))
685             {
686               fds[i].signaled = 1;
687               n--;
688             }
689         }
690     }
691   return TRACE_SYSRES (count);
692 }
693
694 \f
695 int
696 _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
697 {
698   int nread;
699   int saved_errno;
700   struct iovec *iov;
701   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
702               "msg=%p, flags=%i", msg, flags);
703
704   nread = 0;
705   iov = msg->msg_iov;
706   while (iov < msg->msg_iov + msg->msg_iovlen)
707     {
708       nread += iov->iov_len;
709       iov++;
710     }
711
712   TRACE_LOG1 ("about to receive %d bytes", nread);
713
714   do
715     {
716       nread = _gpgme_ath_recvmsg (fd, msg, flags);
717     }
718   while (nread == -1 && errno == EINTR);
719   saved_errno = errno;
720   if (nread > 0)
721     {
722       int nr = nread;
723
724       iov = msg->msg_iov;
725       while (nr > 0)
726         {
727           int len = nr > iov->iov_len ? iov->iov_len : nr;
728           TRACE_LOGBUF (msg->msg_iov->iov_base, len);
729           iov++;
730           nr -= len;
731         }
732     }
733   errno = saved_errno;
734   return TRACE_SYSRES (nread);
735 }
736
737
738 int
739 _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
740 {
741   int nwritten;
742   struct iovec *iov;
743   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
744               "msg=%p, flags=%i", msg, flags);
745
746   nwritten = 0;
747   iov = msg->msg_iov;
748   while (iov < msg->msg_iov + msg->msg_iovlen)
749     {
750       nwritten += iov->iov_len;
751       iov++;
752     }
753
754   TRACE_LOG1 ("about to receive %d bytes", nwritten);
755   iov = msg->msg_iov;
756   while (nwritten > 0)
757     {
758       int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
759       TRACE_LOGBUF (msg->msg_iov->iov_base, len);
760       iov++;
761       nwritten -= len;
762     }
763
764   do
765     {
766       nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
767     }
768   while (nwritten == -1 && errno == EINTR);
769   return TRACE_SYSRES (nwritten);
770 }
771
772
773 int
774 _gpgme_io_dup (int fd)
775 {
776   int new_fd;
777
778   do
779     new_fd = dup (fd);
780   while (new_fd == -1 && errno == EINTR);
781
782   TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
783
784   return new_fd;
785 }
786
787 \f
788 int
789 _gpgme_io_socket (int domain, int type, int proto)
790 {
791   int res;
792
793   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
794               "type=%i, proto=%i", type, proto);
795
796   res = socket (domain, type, proto);
797
798   return TRACE_SYSRES (res);
799 }
800
801
802 int
803 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
804 {
805   int res;
806
807   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
808               "addr=%p, addrlen=%i", addr, addrlen);
809
810   do
811     res = ath_connect (fd, addr, addrlen);
812   while (res == -1 && errno == EINTR);
813
814   return TRACE_SYSRES (res);
815 }