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