~ubuntu-branches/ubuntu/lucid/gpgme1.0/lucid

« back to all changes in this revision

Viewing changes to src/posix-io.c

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman, Bhavani Shankar, Scott Kitterman
  • Date: 2008-12-31 02:39:33 UTC
  • mfrom: (1.1.6 upstream) (3.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20081231023933-zi5r2w9vf7fdz0x9
Tags: 1.1.8-1ubuntu1
[ Bhavani Shankar ]
* Merge from debian unstable, remaining changes: LP: #311666
  - debian/rules: enable tests

[ Scott Kitterman ]
* Re-enable testsuite on armel since it's no longer a first build 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* posix-io.c - Posix I/O functions
 
2
   Copyright (C) 2000 Werner Koch (dd9jn)
 
3
   Copyright (C) 2001, 2002, 2004, 2005, 2007 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, write to the Free Software
 
19
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
20
   02111-1307, USA.  */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
#include <stdio.h>
 
26
#include <stdlib.h>
 
27
#include <string.h>
 
28
#include <assert.h>
 
29
#include <errno.h>
 
30
#include <signal.h>
 
31
#include <fcntl.h>
 
32
#include <unistd.h>
 
33
#include <sys/time.h>
 
34
#include <sys/types.h>
 
35
#include <sys/wait.h>
 
36
#ifdef HAVE_SYS_UIO_H
 
37
# include <sys/uio.h>
 
38
#endif
 
39
#include <ctype.h>
 
40
#include <sys/resource.h>
 
41
#include <unistd.h>
 
42
 
 
43
#include "util.h"
 
44
#include "priv-io.h"
 
45
#include "sema.h"
 
46
#include "ath.h"
 
47
#include "debug.h"
 
48
 
 
49
 
 
50
void
 
51
_gpgme_io_subsystem_init (void)
 
52
{
 
53
  struct sigaction act;
 
54
 
 
55
  sigaction (SIGPIPE, NULL, &act);
 
56
  if (act.sa_handler == SIG_DFL)
 
57
    {
 
58
      act.sa_handler = SIG_IGN;
 
59
      sigemptyset (&act.sa_mask);
 
60
      act.sa_flags = 0;
 
61
      sigaction (SIGPIPE, &act, NULL);
 
62
    }
 
63
}
 
64
 
 
65
 
 
66
/* Write the printable version of FD to the buffer BUF of length
 
67
   BUFLEN.  The printable version is the representation on the command
 
68
   line that the child process expects.  */
 
69
int
 
70
_gpgme_io_fd2str (char *buf, int buflen, int fd)
 
71
{
 
72
  return snprintf (buf, buflen, "%d", fd);
 
73
}
 
74
 
 
75
 
 
76
static struct
 
77
{
 
78
  _gpgme_close_notify_handler_t handler;
 
79
  void *value;
 
80
} notify_table[256];
 
81
 
 
82
 
 
83
int
 
84
_gpgme_io_read (int fd, void *buffer, size_t count)
 
85
{
 
86
  int nread;
 
87
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
 
88
              "buffer=%p, count=%u", buffer, count);
 
89
 
 
90
  do
 
91
    {
 
92
      nread = _gpgme_ath_read (fd, buffer, count);
 
93
    }
 
94
  while (nread == -1 && errno == EINTR);
 
95
 
 
96
  TRACE_LOGBUF (buffer, nread);
 
97
  return TRACE_SYSRES (nread);
 
98
}
 
99
 
 
100
 
 
101
int
 
102
_gpgme_io_write (int fd, const void *buffer, size_t count)
 
103
{
 
104
  int nwritten;
 
105
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
 
106
              "buffer=%p, count=%u", buffer, count);
 
107
  TRACE_LOGBUF (buffer, count);
 
108
 
 
109
  do
 
110
    {
 
111
      nwritten = _gpgme_ath_write (fd, buffer, count);
 
112
    }
 
113
  while (nwritten == -1 && errno == EINTR);
 
114
 
 
115
  return TRACE_SYSRES (nwritten);
 
116
}
 
117
 
 
118
 
 
119
int
 
120
_gpgme_io_pipe (int filedes[2], int inherit_idx)
 
121
{
 
122
  int saved_errno;
 
123
  int err;
 
124
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
 
125
              "inherit_idx=%i (GPGME uses it for %s)",
 
126
              inherit_idx, inherit_idx ? "reading" : "writing");
 
127
 
 
128
  err = pipe (filedes);
 
129
  if (err < 0)
 
130
    return TRACE_SYSRES (err);
 
131
 
 
132
  /* FIXME: Should get the old flags first.  */
 
133
  err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
 
134
  saved_errno = errno;
 
135
  if (err < 0)
 
136
    {
 
137
      close (filedes[0]);
 
138
      close (filedes[1]);
 
139
    }
 
140
  errno = saved_errno;
 
141
  if (err)
 
142
    return TRACE_SYSRES (err);
 
143
 
 
144
  return TRACE_SUC2 ("read=0x%x, write=0x%x", filedes[0], filedes[1]);
 
145
}
 
146
 
 
147
 
 
148
int
 
149
_gpgme_io_close (int fd)
 
150
{
 
151
  int res;
 
152
 
 
153
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
 
154
 
 
155
  if (fd == -1)
 
156
    {
 
157
      errno = EINVAL;
 
158
      return TRACE_SYSRES (-1);
 
159
    }
 
160
 
 
161
  /* First call the notify handler.  */
 
162
  if (fd >= 0 && fd < (int) DIM (notify_table))
 
163
    {
 
164
      if (notify_table[fd].handler)
 
165
        {
 
166
          TRACE_LOG2 ("invoking close handler %p/%p",
 
167
                      notify_table[fd].handler, notify_table[fd].value);
 
168
          notify_table[fd].handler (fd, notify_table[fd].value);
 
169
          notify_table[fd].handler = NULL;
 
170
          notify_table[fd].value = NULL;
 
171
        }
 
172
    }
 
173
  /* Then do the close.  */    
 
174
  res = close (fd);
 
175
  return TRACE_SYSRES (res);
 
176
}
 
177
 
 
178
 
 
179
int
 
180
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
 
181
                            void *value)
 
182
{
 
183
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
 
184
              "close_handler=%p/%p", handler, value);
 
185
 
 
186
  assert (fd != -1);
 
187
 
 
188
  if (fd < 0 || fd >= (int) DIM (notify_table))
 
189
    {
 
190
      errno = EINVAL;
 
191
      return TRACE_SYSRES (-1);
 
192
    }
 
193
  notify_table[fd].handler = handler;
 
194
  notify_table[fd].value = value;
 
195
  return TRACE_SYSRES (0);
 
196
}
 
197
 
 
198
 
 
199
int
 
200
_gpgme_io_set_nonblocking (int fd)
 
201
{
 
202
  int flags;
 
203
  int res;
 
204
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
 
205
 
 
206
  flags = fcntl (fd, F_GETFL, 0);
 
207
  if (flags == -1)
 
208
    return TRACE_SYSRES (-1);
 
209
  flags |= O_NONBLOCK;
 
210
  res = fcntl (fd, F_SETFL, flags);
 
211
  return TRACE_SYSRES (res);
 
212
}
 
213
 
 
214
 
 
215
static long int
 
216
get_max_fds (void)
 
217
{
 
218
  char *source = NULL;
 
219
  long int fds = -1;
 
220
  int rc;
 
221
 
 
222
#ifdef RLIMIT_NOFILE
 
223
  {
 
224
    struct rlimit rl;
 
225
    rc = getrlimit (RLIMIT_NOFILE, &rl);
 
226
    if (rc == 0)
 
227
      {
 
228
        source = "RLIMIT_NOFILE";
 
229
        fds = rl.rlim_max;
 
230
      }
 
231
  }
 
232
#endif
 
233
#ifdef RLIMIT_OFILE
 
234
  if (fds == -1)
 
235
    {
 
236
      struct rlimit rl;
 
237
      rc = getrlimit (RLIMIT_OFILE, &rl);
 
238
      if (rc == 0)
 
239
        {
 
240
          source = "RLIMIT_OFILE";
 
241
          fds = rl.rlim_max;
 
242
        }
 
243
    }
 
244
#endif
 
245
#ifdef _SC_OPEN_MAX
 
246
  if (fds == -1)
 
247
    {
 
248
      long int scres;
 
249
      scres = sysconf (_SC_OPEN_MAX);
 
250
      if (scres >= 0)
 
251
        {
 
252
          source = "_SC_OPEN_MAX";
 
253
          return scres;
 
254
        }
 
255
    }
 
256
#endif
 
257
#ifdef OPEN_MAX
 
258
  if (fds == -1)
 
259
    {
 
260
      source = "OPEN_MAX";
 
261
      fds = OPEN_MAX;
 
262
    }
 
263
#endif
 
264
 
 
265
#if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \
 
266
  && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX)
 
267
#warning "No known way to get the maximum number of file descriptors."
 
268
#endif
 
269
  if (fds == -1)
 
270
    {
 
271
      source = "arbitrary";
 
272
      /* Arbitrary limit.  */
 
273
      fds = 1024;
 
274
    }
 
275
 
 
276
  TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", NULL, "max fds=%i (%s)", fds, source);
 
277
  return fds;
 
278
}
 
279
 
 
280
 
 
281
static int
 
282
_gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
 
283
{
 
284
  int status;
 
285
 
 
286
  *r_status = 0;
 
287
  *r_signal = 0;
 
288
  if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
 
289
    {
 
290
      if (WIFSIGNALED (status))
 
291
        {
 
292
          *r_status = 4; /* Need some value here.  */
 
293
          *r_signal = WTERMSIG (status);
 
294
        }
 
295
      else if (WIFEXITED (status))
 
296
        *r_status = WEXITSTATUS (status);
 
297
      else
 
298
        *r_status = 4; /* Oops.  */
 
299
      return 1;
 
300
    }
 
301
  return 0;
 
302
}
 
303
 
 
304
 
 
305
/* Returns 0 on success, -1 on error.  */
 
306
int
 
307
_gpgme_io_spawn (const char *path, char *const argv[],
 
308
                 struct spawn_fd_item_s *fd_list, pid_t *r_pid)
 
309
{
 
310
  pid_t pid;
 
311
  int i;
 
312
  int status;
 
313
  int signo;
 
314
  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
 
315
              "path=%s", path);
 
316
  i = 0;
 
317
  while (argv[i])
 
318
    {
 
319
      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
 
320
      i++;
 
321
    }
 
322
  for (i = 0; fd_list[i].fd != -1; i++)
 
323
    if (fd_list[i].dup_to == -1)
 
324
      TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd);
 
325
    else
 
326
      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
 
327
 
 
328
  pid = fork ();
 
329
  if (pid == -1) 
 
330
    return TRACE_SYSRES (-1);
 
331
 
 
332
  if (!pid)
 
333
    {
 
334
      /* Intermediate child to prevent zombie processes.  */
 
335
      if ((pid = fork ()) == 0)
 
336
        {
 
337
          int max_fds = get_max_fds ();
 
338
          int fd;
 
339
 
 
340
          /* Child.  */
 
341
          int seen_stdin = 0;
 
342
          int seen_stderr = 0;
 
343
 
 
344
          /* First close all fds which will not be inherited.  */
 
345
          for (fd = 0; fd < max_fds; fd++)
 
346
            {
 
347
              for (i = 0; fd_list[i].fd != -1; i++)
 
348
                if (fd_list[i].fd == fd)
 
349
                  break;
 
350
              if (fd_list[i].fd == -1)
 
351
                close (fd);
 
352
            }
 
353
 
 
354
          /* And now dup and close those to be duplicated.  */
 
355
          for (i = 0; fd_list[i].fd != -1; i++)
 
356
            {
 
357
              int child_fd;
 
358
              int res;
 
359
 
 
360
              if (fd_list[i].dup_to != -1)
 
361
                child_fd = fd_list[i].dup_to;
 
362
              else
 
363
                child_fd = fd_list[i].fd;
 
364
 
 
365
              if (child_fd == 0)
 
366
                seen_stdin = 1;
 
367
              else if (child_fd == 2)
 
368
                seen_stderr = 1;
 
369
 
 
370
              if (fd_list[i].dup_to == -1)
 
371
                continue;
 
372
 
 
373
              res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
 
374
              if (res < 0)
 
375
                {
 
376
#if 0
 
377
                  /* FIXME: The debug file descriptor is not
 
378
                     dup'ed anyway, so we can't see this.  */
 
379
                  TRACE_LOG1 ("dup2 failed in child: %s\n",
 
380
                              strerror (errno));
 
381
#endif
 
382
                  _exit (8);
 
383
                }
 
384
 
 
385
              close (fd_list[i].fd);
 
386
            }
 
387
          
 
388
          if (! seen_stdin || ! seen_stderr)
 
389
            {
 
390
              fd = open ("/dev/null", O_RDWR);
 
391
              if (fd == -1)
 
392
                {
 
393
#if 0
 
394
                  /* FIXME: The debug file descriptor is not dup'ed
 
395
                     anyway, so we can't see this.  */
 
396
                  TRACE_LOG1 ("can't open `/dev/null': %s\n",
 
397
                              strerror (errno));
 
398
#endif
 
399
                  _exit (8);
 
400
                }
 
401
              /* Make sure that the process has a connected stdin.  */
 
402
              if (! seen_stdin && fd != 0)
 
403
                {
 
404
                  if (dup2 (fd, 0) == -1)
 
405
                    {
 
406
#if 0
 
407
                  /* FIXME: The debug file descriptor is not dup'ed
 
408
                     anyway, so we can't see this.  */
 
409
                      TRACE_LOG1 ("dup2(/dev/null, 0) failed: %s\n",
 
410
                                  strerror (errno));
 
411
#endif
 
412
                      _exit (8);
 
413
                    }
 
414
                }
 
415
              if (! seen_stderr && fd != 2)
 
416
                if (dup2 (fd, 2) == -1)
 
417
                  {
 
418
#if 0
 
419
                    /* FIXME: The debug file descriptor is not dup'ed
 
420
                       anyway, so we can't see this.  */
 
421
                    TRACE_LOG1 ("dup2(dev/null, 2) failed: %s\n",
 
422
                                strerror (errno));
 
423
#endif
 
424
                    _exit (8);
 
425
                  }
 
426
              if (fd != 0 && fd != 2)
 
427
                close (fd);
 
428
            }
 
429
    
 
430
          execv (path, (char *const *) argv);
 
431
          /* Hmm: in that case we could write a special status code to the
 
432
             status-pipe.  */
 
433
#if 0
 
434
          /* FIXME: The debug file descriptor is not dup'ed anyway, so
 
435
             we can't see this.  */
 
436
          TRACE_LOG1 ("exec of `%s' failed\n", path);
 
437
#endif
 
438
          _exit (8);
 
439
          /* End child.  */
 
440
        }
 
441
      if (pid == -1)
 
442
        _exit (1);
 
443
      else
 
444
        _exit (0);
 
445
    }
 
446
 
 
447
  TRACE_LOG1 ("waiting for child process pid=%i", pid);
 
448
  _gpgme_io_waitpid (pid, 1, &status, &signo);
 
449
  if (status)
 
450
    return TRACE_SYSRES (-1);
 
451
 
 
452
  for (i = 0; fd_list[i].fd != -1; i++)
 
453
    {
 
454
      _gpgme_io_close (fd_list[i].fd);
 
455
      /* No handle translation.  */
 
456
      fd_list[i].peer_name = fd_list[i].fd;
 
457
    }
 
458
 
 
459
  if (r_pid)
 
460
    *r_pid = pid;
 
461
 
 
462
  return TRACE_SYSRES (0);
 
463
}
 
464
 
 
465
 
 
466
/* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
 
467
   nothing to select, > 0 = number of signaled fds.  */
 
468
int
 
469
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
 
470
{
 
471
  fd_set readfds;
 
472
  fd_set writefds;
 
473
  unsigned int i;
 
474
  int any;
 
475
  int max_fd;
 
476
  int n;
 
477
  int count;
 
478
  /* Use a 1s timeout.  */
 
479
  struct timeval timeout = { 1, 0 };
 
480
  void *dbg_help = NULL;
 
481
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
 
482
              "nfds=%u, nonblock=%u", nfds, nonblock);
 
483
 
 
484
  FD_ZERO (&readfds);
 
485
  FD_ZERO (&writefds);
 
486
  max_fd = 0;
 
487
  if (nonblock)
 
488
    timeout.tv_sec = 0;
 
489
 
 
490
  TRACE_SEQ (dbg_help, "select on [ ");
 
491
 
 
492
  any = 0;
 
493
  for (i = 0; i < nfds; i++)
 
494
    {
 
495
      if (fds[i].fd == -1) 
 
496
        continue;
 
497
      if (fds[i].for_read)
 
498
        {
 
499
          assert (!FD_ISSET (fds[i].fd, &readfds));
 
500
          FD_SET (fds[i].fd, &readfds);
 
501
          if (fds[i].fd > max_fd)
 
502
            max_fd = fds[i].fd;
 
503
          TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
 
504
          any = 1;
 
505
        }
 
506
      else if (fds[i].for_write)
 
507
        {
 
508
          assert (!FD_ISSET (fds[i].fd, &writefds));
 
509
          FD_SET (fds[i].fd, &writefds);
 
510
          if (fds[i].fd > max_fd)
 
511
            max_fd = fds[i].fd;
 
512
          TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
 
513
          any = 1;
 
514
        }
 
515
      fds[i].signaled = 0;
 
516
    }
 
517
  TRACE_END (dbg_help, "]"); 
 
518
  if (!any)
 
519
    return TRACE_SYSRES (0);
 
520
 
 
521
  do
 
522
    {
 
523
      count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
 
524
                                 &timeout);
 
525
    }
 
526
  while (count < 0 && errno == EINTR);
 
527
  if (count < 0)
 
528
    return TRACE_SYSRES (-1);
 
529
 
 
530
  TRACE_SEQ (dbg_help, "select OK [ ");
 
531
  if (TRACE_ENABLED (dbg_help))
 
532
    {
 
533
      for (i = 0; i <= max_fd; i++)
 
534
        {
 
535
          if (FD_ISSET (i, &readfds))
 
536
            TRACE_ADD1 (dbg_help, "r0x%x ", i);
 
537
          if (FD_ISSET (i, &writefds))
 
538
            TRACE_ADD1 (dbg_help, "w0x%x ", i);
 
539
        }
 
540
      TRACE_END (dbg_help, "]");
 
541
    }
 
542
    
 
543
  /* The variable N is used to optimize it a little bit.  */
 
544
  for (n = count, i = 0; i < nfds && n; i++)
 
545
    {
 
546
      if (fds[i].fd == -1)
 
547
        ;
 
548
      else if (fds[i].for_read)
 
549
        {
 
550
          if (FD_ISSET (fds[i].fd, &readfds))
 
551
            {
 
552
              fds[i].signaled = 1;
 
553
              n--;
 
554
            }
 
555
        }
 
556
      else if (fds[i].for_write)
 
557
        {
 
558
          if (FD_ISSET (fds[i].fd, &writefds))
 
559
            {
 
560
              fds[i].signaled = 1;
 
561
              n--;
 
562
            }
 
563
        }
 
564
    }
 
565
  return TRACE_SYSRES (count);
 
566
}
 
567
 
 
568
 
 
569
int
 
570
_gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
 
571
{
 
572
  int nread;
 
573
  int saved_errno;
 
574
  struct iovec *iov;
 
575
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
 
576
              "msg=%p, flags=%i", msg, flags);
 
577
 
 
578
  nread = 0;
 
579
  iov = msg->msg_iov;
 
580
  while (iov < msg->msg_iov + msg->msg_iovlen)
 
581
    {
 
582
      nread += iov->iov_len;
 
583
      iov++;
 
584
    }
 
585
  
 
586
  TRACE_LOG1 ("about to receive %d bytes", nread);
 
587
 
 
588
  do
 
589
    {
 
590
      nread = _gpgme_ath_recvmsg (fd, msg, flags);
 
591
    }
 
592
  while (nread == -1 && errno == EINTR);
 
593
  saved_errno = errno;
 
594
  if (nread > 0)
 
595
    {
 
596
      int nr = nread;
 
597
 
 
598
      iov = msg->msg_iov;
 
599
      while (nr > 0)
 
600
        {
 
601
          int len = nr > iov->iov_len ? iov->iov_len : nr;
 
602
          TRACE_LOGBUF (msg->msg_iov->iov_base, len);
 
603
          iov++;
 
604
          nr -= len;
 
605
        }
 
606
    }
 
607
  errno = saved_errno;
 
608
  return TRACE_SYSRES (nread);
 
609
}
 
610
 
 
611
 
 
612
int
 
613
_gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
 
614
{
 
615
  int nwritten;
 
616
  struct iovec *iov;
 
617
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
 
618
              "msg=%p, flags=%i", msg, flags);
 
619
 
 
620
  nwritten = 0;
 
621
  iov = msg->msg_iov;
 
622
  while (iov < msg->msg_iov + msg->msg_iovlen)
 
623
    {
 
624
      nwritten += iov->iov_len;
 
625
      iov++;
 
626
    }
 
627
 
 
628
  TRACE_LOG1 ("about to receive %d bytes", nwritten);
 
629
  iov = msg->msg_iov;
 
630
  while (nwritten > 0)
 
631
    {
 
632
      int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
 
633
      TRACE_LOGBUF (msg->msg_iov->iov_base, len);
 
634
      iov++;
 
635
      nwritten -= len;
 
636
    }
 
637
 
 
638
  do
 
639
    {
 
640
      nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
 
641
    }
 
642
  while (nwritten == -1 && errno == EINTR);
 
643
  return TRACE_SYSRES (nwritten);
 
644
}
 
645
 
 
646
 
 
647
int
 
648
_gpgme_io_dup (int fd)
 
649
{
 
650
  int new_fd = dup (fd);
 
651
 
 
652
  TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
 
653
 
 
654
  return new_fd;
 
655
}