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