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