2009-11-05 Marcus Brinkmann <marcus@g10code.de>
[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 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 \f
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 \f
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", 0, "max fds=%i (%s)", fds, source);
277   return fds;
278 }
279
280
281 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[], unsigned int flags,
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
315   (void)flags;
316
317   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
318               "path=%s", path);
319   i = 0;
320   while (argv[i])
321     {
322       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
323       i++;
324     }
325   for (i = 0; fd_list[i].fd != -1; i++)
326     if (fd_list[i].dup_to == -1)
327       TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd);
328     else
329       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
330
331   pid = fork ();
332   if (pid == -1) 
333     return TRACE_SYSRES (-1);
334
335   if (!pid)
336     {
337       /* Intermediate child to prevent zombie processes.  */
338       if ((pid = fork ()) == 0)
339         {
340           int max_fds = get_max_fds ();
341           int fd;
342
343           /* Child.  */
344           int seen_stdin = 0;
345           int seen_stderr = 0;
346
347           /* First close all fds which will not be inherited.  */
348           for (fd = 0; fd < max_fds; fd++)
349             {
350               for (i = 0; fd_list[i].fd != -1; i++)
351                 if (fd_list[i].fd == fd)
352                   break;
353               if (fd_list[i].fd == -1)
354                 close (fd);
355             }
356
357           /* And now dup and close those to be duplicated.  */
358           for (i = 0; fd_list[i].fd != -1; i++)
359             {
360               int child_fd;
361               int res;
362
363               if (fd_list[i].dup_to != -1)
364                 child_fd = fd_list[i].dup_to;
365               else
366                 child_fd = fd_list[i].fd;
367
368               if (child_fd == 0)
369                 seen_stdin = 1;
370               else if (child_fd == 2)
371                 seen_stderr = 1;
372
373               if (fd_list[i].dup_to == -1)
374                 continue;
375
376               res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
377               if (res < 0)
378                 {
379 #if 0
380                   /* FIXME: The debug file descriptor is not
381                      dup'ed anyway, so we can't see this.  */
382                   TRACE_LOG1 ("dup2 failed in child: %s\n",
383                               strerror (errno));
384 #endif
385                   _exit (8);
386                 }
387
388               close (fd_list[i].fd);
389             }
390           
391           if (! seen_stdin || ! seen_stderr)
392             {
393               fd = open ("/dev/null", O_RDWR);
394               if (fd == -1)
395                 {
396 #if 0
397                   /* FIXME: The debug file descriptor is not dup'ed
398                      anyway, so we can't see this.  */
399                   TRACE_LOG1 ("can't open `/dev/null': %s\n",
400                               strerror (errno));
401 #endif
402                   _exit (8);
403                 }
404               /* Make sure that the process has a connected stdin.  */
405               if (! seen_stdin && fd != 0)
406                 {
407                   if (dup2 (fd, 0) == -1)
408                     {
409 #if 0
410                   /* FIXME: The debug file descriptor is not dup'ed
411                      anyway, so we can't see this.  */
412                       TRACE_LOG1 ("dup2(/dev/null, 0) failed: %s\n",
413                                   strerror (errno));
414 #endif
415                       _exit (8);
416                     }
417                 }
418               if (! seen_stderr && fd != 2)
419                 if (dup2 (fd, 2) == -1)
420                   {
421 #if 0
422                     /* FIXME: The debug file descriptor is not dup'ed
423                        anyway, so we can't see this.  */
424                     TRACE_LOG1 ("dup2(dev/null, 2) failed: %s\n",
425                                 strerror (errno));
426 #endif
427                     _exit (8);
428                   }
429               if (fd != 0 && fd != 2)
430                 close (fd);
431             }
432     
433           execv (path, (char *const *) argv);
434           /* Hmm: in that case we could write a special status code to the
435              status-pipe.  */
436 #if 0
437           /* FIXME: The debug file descriptor is not dup'ed anyway, so
438              we can't see this.  */
439           TRACE_LOG1 ("exec of `%s' failed\n", path);
440 #endif
441           _exit (8);
442           /* End child.  */
443         }
444       if (pid == -1)
445         _exit (1);
446       else
447         _exit (0);
448     }
449
450   TRACE_LOG1 ("waiting for child process pid=%i", pid);
451   _gpgme_io_waitpid (pid, 1, &status, &signo);
452   if (status)
453     return TRACE_SYSRES (-1);
454
455   for (i = 0; fd_list[i].fd != -1; i++)
456     {
457       if (! (flags & IOSPAWN_FLAG_NOCLOSE))
458         _gpgme_io_close (fd_list[i].fd);
459       /* No handle translation.  */
460       fd_list[i].peer_name = fd_list[i].fd;
461     }
462
463   if (r_pid)
464     *r_pid = pid;
465
466   return TRACE_SYSRES (0);
467 }
468
469
470 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
471    nothing to select, > 0 = number of signaled fds.  */
472 int
473 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
474 {
475   fd_set readfds;
476   fd_set writefds;
477   unsigned int i;
478   int any;
479   int max_fd;
480   int n;
481   int count;
482   /* Use a 1s timeout.  */
483   struct timeval timeout = { 1, 0 };
484   void *dbg_help = NULL;
485   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
486               "nfds=%u, nonblock=%u", nfds, nonblock);
487
488   FD_ZERO (&readfds);
489   FD_ZERO (&writefds);
490   max_fd = 0;
491   if (nonblock)
492     timeout.tv_sec = 0;
493
494   TRACE_SEQ (dbg_help, "select on [ ");
495
496   any = 0;
497   for (i = 0; i < nfds; i++)
498     {
499       if (fds[i].fd == -1) 
500         continue;
501       if (fds[i].for_read)
502         {
503           assert (!FD_ISSET (fds[i].fd, &readfds));
504           FD_SET (fds[i].fd, &readfds);
505           if (fds[i].fd > max_fd)
506             max_fd = fds[i].fd;
507           TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
508           any = 1;
509         }
510       else if (fds[i].for_write)
511         {
512           assert (!FD_ISSET (fds[i].fd, &writefds));
513           FD_SET (fds[i].fd, &writefds);
514           if (fds[i].fd > max_fd)
515             max_fd = fds[i].fd;
516           TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
517           any = 1;
518         }
519       fds[i].signaled = 0;
520     }
521   TRACE_END (dbg_help, "]"); 
522   if (!any)
523     return TRACE_SYSRES (0);
524
525   do
526     {
527       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
528                                  &timeout);
529     }
530   while (count < 0 && errno == EINTR);
531   if (count < 0)
532     return TRACE_SYSRES (-1);
533
534   TRACE_SEQ (dbg_help, "select OK [ ");
535   if (TRACE_ENABLED (dbg_help))
536     {
537       for (i = 0; i <= max_fd; i++)
538         {
539           if (FD_ISSET (i, &readfds))
540             TRACE_ADD1 (dbg_help, "r0x%x ", i);
541           if (FD_ISSET (i, &writefds))
542             TRACE_ADD1 (dbg_help, "w0x%x ", i);
543         }
544       TRACE_END (dbg_help, "]");
545     }
546     
547   /* The variable N is used to optimize it a little bit.  */
548   for (n = count, i = 0; i < nfds && n; i++)
549     {
550       if (fds[i].fd == -1)
551         ;
552       else if (fds[i].for_read)
553         {
554           if (FD_ISSET (fds[i].fd, &readfds))
555             {
556               fds[i].signaled = 1;
557               n--;
558             }
559         }
560       else if (fds[i].for_write)
561         {
562           if (FD_ISSET (fds[i].fd, &writefds))
563             {
564               fds[i].signaled = 1;
565               n--;
566             }
567         }
568     }
569   return TRACE_SYSRES (count);
570 }
571
572 \f
573 int
574 _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
575 {
576   int nread;
577   int saved_errno;
578   struct iovec *iov;
579   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
580               "msg=%p, flags=%i", msg, flags);
581
582   nread = 0;
583   iov = msg->msg_iov;
584   while (iov < msg->msg_iov + msg->msg_iovlen)
585     {
586       nread += iov->iov_len;
587       iov++;
588     }
589   
590   TRACE_LOG1 ("about to receive %d bytes", nread);
591
592   do
593     {
594       nread = _gpgme_ath_recvmsg (fd, msg, flags);
595     }
596   while (nread == -1 && errno == EINTR);
597   saved_errno = errno;
598   if (nread > 0)
599     {
600       int nr = nread;
601
602       iov = msg->msg_iov;
603       while (nr > 0)
604         {
605           int len = nr > iov->iov_len ? iov->iov_len : nr;
606           TRACE_LOGBUF (msg->msg_iov->iov_base, len);
607           iov++;
608           nr -= len;
609         }
610     }
611   errno = saved_errno;
612   return TRACE_SYSRES (nread);
613 }
614
615
616 int
617 _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
618 {
619   int nwritten;
620   struct iovec *iov;
621   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
622               "msg=%p, flags=%i", msg, flags);
623
624   nwritten = 0;
625   iov = msg->msg_iov;
626   while (iov < msg->msg_iov + msg->msg_iovlen)
627     {
628       nwritten += iov->iov_len;
629       iov++;
630     }
631
632   TRACE_LOG1 ("about to receive %d bytes", nwritten);
633   iov = msg->msg_iov;
634   while (nwritten > 0)
635     {
636       int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
637       TRACE_LOGBUF (msg->msg_iov->iov_base, len);
638       iov++;
639       nwritten -= len;
640     }
641
642   do
643     {
644       nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
645     }
646   while (nwritten == -1 && errno == EINTR);
647   return TRACE_SYSRES (nwritten);
648 }
649
650
651 int
652 _gpgme_io_dup (int fd)
653 {
654   int new_fd = dup (fd);
655
656   TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
657
658   return new_fd;
659 }
660
661 \f
662 int
663 _gpgme_io_socket (int domain, int type, int proto)
664 {
665   int res;
666
667   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
668               "type=%i, proto=%i", type, proto);
669
670   res = socket (domain, type, proto);
671
672   return TRACE_SYSRES (res);
673 }
674
675
676 int
677 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
678 {
679   int res;
680
681   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
682               "addr=%p, addrlen=%i", addr, addrlen);
683
684   res = ath_connect (fd, addr, addrlen);
685
686   return TRACE_SYSRES (res);
687 }