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

« back to all changes in this revision

Viewing changes to gpgme/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
 
#include <ctype.h>
37
 
 
38
 
#include "util.h"
39
 
#include "priv-io.h"
40
 
#include "sema.h"
41
 
#include "ath.h"
42
 
#include "debug.h"
43
 
 
44
 
 
45
 
void
46
 
_gpgme_io_subsystem_init (void)
47
 
{
48
 
  struct sigaction act;
49
 
 
50
 
  sigaction (SIGPIPE, NULL, &act);
51
 
  if (act.sa_handler == SIG_DFL)
52
 
    {
53
 
      act.sa_handler = SIG_IGN;
54
 
      sigemptyset (&act.sa_mask);
55
 
      act.sa_flags = 0;
56
 
      sigaction (SIGPIPE, &act, NULL);
57
 
    }
58
 
}
59
 
 
60
 
 
61
 
/* Write the printable version of FD to the buffer BUF of length
62
 
   BUFLEN.  The printable version is the representation on the command
63
 
   line that the child process expects.  */
64
 
int
65
 
_gpgme_io_fd2str (char *buf, int buflen, int fd)
66
 
{
67
 
  return snprintf (buf, buflen, "%d", fd);
68
 
}
69
 
 
70
 
 
71
 
static struct
72
 
{
73
 
  _gpgme_close_notify_handler_t handler;
74
 
  void *value;
75
 
} notify_table[256];
76
 
 
77
 
 
78
 
int
79
 
_gpgme_io_read (int fd, void *buffer, size_t count)
80
 
{
81
 
  int nread;
82
 
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
83
 
              "buffer=%p, count=%u", buffer, count);
84
 
 
85
 
  do
86
 
    {
87
 
      nread = _gpgme_ath_read (fd, buffer, count);
88
 
    }
89
 
  while (nread == -1 && errno == EINTR);
90
 
 
91
 
  TRACE_LOGBUF (buffer, nread);
92
 
  return TRACE_SYSRES (nread);
93
 
}
94
 
 
95
 
 
96
 
int
97
 
_gpgme_io_write (int fd, const void *buffer, size_t count)
98
 
{
99
 
  int nwritten;
100
 
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
101
 
              "buffer=%p, count=%u", buffer, count);
102
 
  TRACE_LOGBUF (buffer, count);
103
 
 
104
 
  do
105
 
    {
106
 
      nwritten = _gpgme_ath_write (fd, buffer, count);
107
 
    }
108
 
  while (nwritten == -1 && errno == EINTR);
109
 
 
110
 
  return TRACE_SYSRES (nwritten);
111
 
}
112
 
 
113
 
 
114
 
int
115
 
_gpgme_io_pipe (int filedes[2], int inherit_idx)
116
 
{
117
 
  int saved_errno;
118
 
  int err;
119
 
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
120
 
              "inherit_idx=%i (GPGME uses it for %s)",
121
 
              inherit_idx, inherit_idx ? "reading" : "writing");
122
 
 
123
 
  err = pipe (filedes);
124
 
  if (err < 0)
125
 
    return TRACE_SYSRES (err);
126
 
 
127
 
  /* FIXME: Should get the old flags first.  */
128
 
  err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
129
 
  saved_errno = errno;
130
 
  if (err < 0)
131
 
    {
132
 
      close (filedes[0]);
133
 
      close (filedes[1]);
134
 
    }
135
 
  errno = saved_errno;
136
 
  if (err)
137
 
    return TRACE_SYSRES (err);
138
 
 
139
 
  return TRACE_SUC2 ("read=0x%x, write=0x%x", filedes[0], filedes[1]);
140
 
}
141
 
 
142
 
 
143
 
int
144
 
_gpgme_io_close (int fd)
145
 
{
146
 
  int res;
147
 
 
148
 
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
149
 
 
150
 
  if (fd == -1)
151
 
    {
152
 
      errno = EINVAL;
153
 
      return TRACE_SYSRES (-1);
154
 
    }
155
 
 
156
 
  /* First call the notify handler.  */
157
 
  if (fd >= 0 && fd < (int) DIM (notify_table))
158
 
    {
159
 
      if (notify_table[fd].handler)
160
 
        {
161
 
          TRACE_LOG2 ("invoking close handler %p/%p",
162
 
                      notify_table[fd].handler, notify_table[fd].value);
163
 
          notify_table[fd].handler (fd, notify_table[fd].value);
164
 
          notify_table[fd].handler = NULL;
165
 
          notify_table[fd].value = NULL;
166
 
        }
167
 
    }
168
 
  /* Then do the close.  */    
169
 
  res = close (fd);
170
 
  return TRACE_SYSRES (res);
171
 
}
172
 
 
173
 
 
174
 
int
175
 
_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
176
 
                            void *value)
177
 
{
178
 
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
179
 
              "close_handler=%p/%p", handler, value);
180
 
 
181
 
  assert (fd != -1);
182
 
 
183
 
  if (fd < 0 || fd >= (int) DIM (notify_table))
184
 
    {
185
 
      errno = EINVAL;
186
 
      return TRACE_SYSRES (-1);
187
 
    }
188
 
  notify_table[fd].handler = handler;
189
 
  notify_table[fd].value = value;
190
 
  return TRACE_SYSRES (0);
191
 
}
192
 
 
193
 
 
194
 
int
195
 
_gpgme_io_set_nonblocking (int fd)
196
 
{
197
 
  int flags;
198
 
  int res;
199
 
  TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
200
 
 
201
 
  flags = fcntl (fd, F_GETFL, 0);
202
 
  if (flags == -1)
203
 
    return TRACE_SYSRES (-1);
204
 
  flags |= O_NONBLOCK;
205
 
  res = fcntl (fd, F_SETFL, flags);
206
 
  return TRACE_SYSRES (res);
207
 
}
208
 
 
209
 
 
210
 
static int
211
 
_gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
212
 
{
213
 
  int status;
214
 
 
215
 
  *r_status = 0;
216
 
  *r_signal = 0;
217
 
  if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
218
 
    {
219
 
      if (WIFSIGNALED (status))
220
 
        {
221
 
          *r_status = 4; /* Need some value here.  */
222
 
          *r_signal = WTERMSIG (status);
223
 
        }
224
 
      else if (WIFEXITED (status))
225
 
        *r_status = WEXITSTATUS (status);
226
 
      else
227
 
        *r_status = 4; /* Oops.  */
228
 
      return 1;
229
 
    }
230
 
  return 0;
231
 
}
232
 
 
233
 
 
234
 
/* Returns 0 on success, -1 on error.  */
235
 
int
236
 
_gpgme_io_spawn (const char *path, char **argv,
237
 
                 struct spawn_fd_item_s *fd_child_list,
238
 
                 struct spawn_fd_item_s *fd_parent_list)
239
 
{
240
 
  pid_t pid;
241
 
  int i;
242
 
  int status;
243
 
  int signo;
244
 
  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
245
 
              "path=%s", path);
246
 
  i = 0;
247
 
  while (argv[i])
248
 
    {
249
 
      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
250
 
      i++;
251
 
    }
252
 
  
253
 
  pid = fork ();
254
 
  if (pid == -1) 
255
 
    return TRACE_SYSRES (-1);
256
 
  
257
 
  if (!pid)
258
 
    {
259
 
      /* Intermediate child to prevent zombie processes.  */
260
 
      if ((pid = fork ()) == 0)
261
 
        {
262
 
          /* Child.  */
263
 
          int duped_stdin = 0;
264
 
          int duped_stderr = 0;
265
 
 
266
 
          /* First close all fds which will not be duped.  */
267
 
          for (i=0; fd_child_list[i].fd != -1; i++)
268
 
            if (fd_child_list[i].dup_to == -1)
269
 
              close (fd_child_list[i].fd);
270
 
 
271
 
          /* And now dup and close the rest.  */
272
 
          for (i=0; fd_child_list[i].fd != -1; i++)
273
 
            {
274
 
              if (fd_child_list[i].dup_to != -1)
275
 
                {
276
 
                  if (dup2 (fd_child_list[i].fd,
277
 
                            fd_child_list[i].dup_to) == -1)
278
 
                    {
279
 
#if 0
280
 
                      /* FIXME: The debug file descriptor is not
281
 
                         dup'ed anyway, so we can't see this.  */
282
 
                      TRACE_LOG1 ("dup2 failed in child: %s\n",
283
 
                                  strerror (errno));
284
 
#endif
285
 
                      _exit (8);
286
 
                    }
287
 
                  if (fd_child_list[i].dup_to == 0)
288
 
                    duped_stdin=1;
289
 
                  if (fd_child_list[i].dup_to == 2)
290
 
                    duped_stderr=1;
291
 
                  close (fd_child_list[i].fd);
292
 
                }
293
 
            }
294
 
          
295
 
          if (!duped_stdin || !duped_stderr)
296
 
            {
297
 
              int fd = open ("/dev/null", O_RDWR);
298
 
              if (fd == -1)
299
 
                {
300
 
#if 0
301
 
                  /* FIXME: The debug file descriptor is not dup'ed
302
 
                     anyway, so we can't see this.  */
303
 
                  TRACE_LOG1 ("can't open `/dev/null': %s\n",
304
 
                              strerror (errno));
305
 
#endif
306
 
                  _exit (8);
307
 
                }
308
 
              /* Make sure that the process has a connected stdin.  */
309
 
              if (!duped_stdin)
310
 
                {
311
 
                  if (dup2 (fd, 0) == -1)
312
 
                    {
313
 
#if 0
314
 
                  /* FIXME: The debug file descriptor is not dup'ed
315
 
                     anyway, so we can't see this.  */
316
 
                      TRACE_LOG1 ("dup2(/dev/null, 0) failed: %s\n",
317
 
                                  strerror (errno));
318
 
#endif
319
 
                      _exit (8);
320
 
                    }
321
 
                }
322
 
              if (!duped_stderr)
323
 
                if (dup2 (fd, 2) == -1)
324
 
                  {
325
 
#if 0
326
 
                    /* FIXME: The debug file descriptor is not dup'ed
327
 
                       anyway, so we can't see this.  */
328
 
                    TRACE_LOG1 ("dup2(dev/null, 2) failed: %s\n",
329
 
                                strerror (errno));
330
 
#endif
331
 
                    _exit (8);
332
 
                  }
333
 
              close (fd);
334
 
            }
335
 
    
336
 
          execv ( path, argv );
337
 
          /* Hmm: in that case we could write a special status code to the
338
 
             status-pipe.  */
339
 
#if 0
340
 
          /* FIXME: The debug file descriptor is not dup'ed anyway, so
341
 
             we can't see this.  */
342
 
          TRACE_LOG1 ("exec of `%s' failed\n", path);
343
 
#endif
344
 
          _exit (8);
345
 
        } /* End child.  */
346
 
      if (pid == -1)
347
 
        _exit (1);
348
 
      else
349
 
        _exit (0);
350
 
    }
351
 
 
352
 
  TRACE_LOG1 ("waiting for child process pid=%i", pid);
353
 
  _gpgme_io_waitpid (pid, 1, &status, &signo);
354
 
  if (status)
355
 
    return TRACE_SYSRES (-1);
356
 
 
357
 
  /* .dup_to is not used in the parent list.  */
358
 
  for (i = 0; fd_parent_list[i].fd != -1; i++)
359
 
    _gpgme_io_close (fd_parent_list[i].fd);
360
 
 
361
 
  return TRACE_SYSRES (0);
362
 
}
363
 
 
364
 
 
365
 
/* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
366
 
   nothing to select, > 0 = number of signaled fds.  */
367
 
int
368
 
_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
369
 
{
370
 
  fd_set readfds;
371
 
  fd_set writefds;
372
 
  unsigned int i;
373
 
  int any;
374
 
  int max_fd;
375
 
  int n;
376
 
  int count;
377
 
  /* Use a 1s timeout.  */
378
 
  struct timeval timeout = { 1, 0 };
379
 
  void *dbg_help = NULL;
380
 
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
381
 
              "nfds=%u, nonblock=%u", nfds, nonblock);
382
 
 
383
 
  FD_ZERO (&readfds);
384
 
  FD_ZERO (&writefds);
385
 
  max_fd = 0;
386
 
  if (nonblock)
387
 
    timeout.tv_sec = 0;
388
 
 
389
 
  TRACE_SEQ (dbg_help, "select on [ ");
390
 
 
391
 
  any = 0;
392
 
  for (i = 0; i < nfds; i++)
393
 
    {
394
 
      if (fds[i].fd == -1) 
395
 
        continue;
396
 
      if (fds[i].for_read)
397
 
        {
398
 
          assert (!FD_ISSET (fds[i].fd, &readfds));
399
 
          FD_SET (fds[i].fd, &readfds);
400
 
          if (fds[i].fd > max_fd)
401
 
            max_fd = fds[i].fd;
402
 
          TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
403
 
          any = 1;
404
 
        }
405
 
      else if (fds[i].for_write)
406
 
        {
407
 
          assert (!FD_ISSET (fds[i].fd, &writefds));
408
 
          FD_SET (fds[i].fd, &writefds);
409
 
          if (fds[i].fd > max_fd)
410
 
            max_fd = fds[i].fd;
411
 
          TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
412
 
          any = 1;
413
 
        }
414
 
      fds[i].signaled = 0;
415
 
    }
416
 
  TRACE_END (dbg_help, "]"); 
417
 
  if (!any)
418
 
    return TRACE_SYSRES (0);
419
 
 
420
 
  do
421
 
    {
422
 
      count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
423
 
                                 &timeout);
424
 
    }
425
 
  while (count < 0 && errno == EINTR);
426
 
  if (count < 0)
427
 
    return TRACE_SYSRES (-1);
428
 
 
429
 
  TRACE_SEQ (dbg_help, "select OK [ ");
430
 
  if (TRACE_ENABLED (dbg_help))
431
 
    {
432
 
      for (i = 0; i <= max_fd; i++)
433
 
        {
434
 
          if (FD_ISSET (i, &readfds))
435
 
            TRACE_ADD1 (dbg_help, "r0x%x ", i);
436
 
          if (FD_ISSET (i, &writefds))
437
 
            TRACE_ADD1 (dbg_help, "w0x%x ", i);
438
 
        }
439
 
      TRACE_END (dbg_help, "]");
440
 
    }
441
 
    
442
 
  /* The variable N is used to optimize it a little bit.  */
443
 
  for (n = count, i = 0; i < nfds && n; i++)
444
 
    {
445
 
      if (fds[i].fd == -1)
446
 
        ;
447
 
      else if (fds[i].for_read)
448
 
        {
449
 
          if (FD_ISSET (fds[i].fd, &readfds))
450
 
            {
451
 
              fds[i].signaled = 1;
452
 
              n--;
453
 
            }
454
 
        }
455
 
      else if (fds[i].for_write)
456
 
        {
457
 
          if (FD_ISSET (fds[i].fd, &writefds))
458
 
            {
459
 
              fds[i].signaled = 1;
460
 
              n--;
461
 
            }
462
 
        }
463
 
    }
464
 
  return TRACE_SYSRES (count);
465
 
}
466
 
 
467
 
 
468
 
int
469
 
_gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
470
 
{
471
 
  int nread;
472
 
  int saved_errno;
473
 
  struct iovec *iov;
474
 
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
475
 
              "msg=%p, flags=%i", msg, flags);
476
 
 
477
 
  nread = 0;
478
 
  iov = msg->msg_iov;
479
 
  while (iov < msg->msg_iov + msg->msg_iovlen)
480
 
    {
481
 
      nread += iov->iov_len;
482
 
      iov++;
483
 
    }
484
 
  
485
 
  TRACE_LOG1 ("about to receive %d bytes", nread);
486
 
 
487
 
  do
488
 
    {
489
 
      nread = _gpgme_ath_recvmsg (fd, msg, flags);
490
 
    }
491
 
  while (nread == -1 && errno == EINTR);
492
 
  saved_errno = errno;
493
 
  if (nread > 0)
494
 
    {
495
 
      int nr = nread;
496
 
 
497
 
      iov = msg->msg_iov;
498
 
      while (nr > 0)
499
 
        {
500
 
          int len = nr > iov->iov_len ? iov->iov_len : nr;
501
 
          TRACE_LOGBUF (msg->msg_iov->iov_base, len);
502
 
          iov++;
503
 
          nr -= len;
504
 
        }
505
 
    }
506
 
  errno = saved_errno;
507
 
  return TRACE_SYSRES (nread);
508
 
}
509
 
 
510
 
 
511
 
int
512
 
_gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
513
 
{
514
 
  int nwritten;
515
 
  struct iovec *iov;
516
 
  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
517
 
              "msg=%p, flags=%i", msg, flags);
518
 
 
519
 
  nwritten = 0;
520
 
  iov = msg->msg_iov;
521
 
  while (iov < msg->msg_iov + msg->msg_iovlen)
522
 
    {
523
 
      nwritten += iov->iov_len;
524
 
      iov++;
525
 
    }
526
 
 
527
 
  TRACE_LOG1 ("about to receive %d bytes", nwritten);
528
 
  iov = msg->msg_iov;
529
 
  while (nwritten > 0)
530
 
    {
531
 
      int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
532
 
      TRACE_LOGBUF (msg->msg_iov->iov_base, len);
533
 
      iov++;
534
 
      nwritten -= len;
535
 
    }
536
 
 
537
 
  do
538
 
    {
539
 
      nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
540
 
    }
541
 
  while (nwritten == -1 && errno == EINTR);
542
 
  return TRACE_SYSRES (nwritten);
543
 
}
544
 
 
545
 
 
546
 
int
547
 
_gpgme_io_dup (int fd)
548
 
{
549
 
  return dup (fd);
550
 
}