Fix bug #818.
[gpgme.git] / gpgme / 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", 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 **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, 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 \f
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 }