Make sure a spawned process has all standard fds connected.
[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_stdout = 0;
406           int seen_stderr = 0;
407
408           if (atfork)
409             atfork (atforkvalue, 0);
410
411           /* First close all fds which will not be inherited.  */
412           for (fd = 0; fd < max_fds; fd++)
413             {
414               for (i = 0; fd_list[i].fd != -1; i++)
415                 if (fd_list[i].fd == fd)
416                   break;
417               if (fd_list[i].fd == -1)
418                 close (fd);
419             }
420
421           /* And now dup and close those to be duplicated.  */
422           for (i = 0; fd_list[i].fd != -1; i++)
423             {
424               int child_fd;
425               int res;
426
427               if (fd_list[i].dup_to != -1)
428                 child_fd = fd_list[i].dup_to;
429               else
430                 child_fd = fd_list[i].fd;
431
432               if (child_fd == 0)
433                 seen_stdin = 1;
434               else if (child_fd == 1)
435                 seen_stdout = 1;
436               else if (child_fd == 2)
437                 seen_stderr = 1;
438
439               if (fd_list[i].dup_to == -1)
440                 continue;
441
442               res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
443               if (res < 0)
444                 {
445 #if 0
446                   /* FIXME: The debug file descriptor is not
447                      dup'ed anyway, so we can't see this.  */
448                   TRACE_LOG1 ("dup2 failed in child: %s\n",
449                               strerror (errno));
450 #endif
451                   _exit (8);
452                 }
453
454               close (fd_list[i].fd);
455             }
456
457           if (! seen_stdin || ! seen_stdout || !seen_stderr)
458             {
459               fd = open ("/dev/null", O_RDWR);
460               if (fd == -1)
461                 {
462                   /* The debug file descriptor is not dup'ed, so we
463                      can't do a trace output.  */
464                   _exit (8);
465                 }
466               /* Make sure that the process has connected stdin.  */
467               if (! seen_stdin && fd != 0)
468                 {
469                   if (dup2 (fd, 0) == -1)
470                     _exit (8);
471                 }
472               if (! seen_stdout && fd != 1)
473                 {
474                   if (dup2 (fd, 1) == -1)
475                     _exit (8);
476                 }
477               if (! seen_stderr && fd != 2)
478                 {
479                   if (dup2 (fd, 2) == -1)
480                     _exit (8);
481                 }
482               if (fd != 0 && fd != 1 && fd != 2)
483                 close (fd);
484             }
485
486           execv (path, (char *const *) argv);
487           /* Hmm: in that case we could write a special status code to the
488              status-pipe.  */
489           _exit (8);
490           /* End child.  */
491         }
492       if (pid == -1)
493         _exit (1);
494       else
495         _exit (0);
496     }
497
498   TRACE_LOG1 ("waiting for child process pid=%i", pid);
499   _gpgme_io_waitpid (pid, 1, &status, &signo);
500   if (status)
501     return TRACE_SYSRES (-1);
502
503   for (i = 0; fd_list[i].fd != -1; i++)
504     {
505       if (! (flags & IOSPAWN_FLAG_NOCLOSE))
506         _gpgme_io_close (fd_list[i].fd);
507       /* No handle translation.  */
508       fd_list[i].peer_name = fd_list[i].fd;
509     }
510
511   if (r_pid)
512     *r_pid = pid;
513
514   return TRACE_SYSRES (0);
515 }
516
517
518 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
519    nothing to select, > 0 = number of signaled fds.  */
520 int
521 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
522 {
523   fd_set readfds;
524   fd_set writefds;
525   unsigned int i;
526   int any;
527   int max_fd;
528   int n;
529   int count;
530   /* Use a 1s timeout.  */
531   struct timeval timeout = { 1, 0 };
532   void *dbg_help = NULL;
533   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
534               "nfds=%u, nonblock=%u", nfds, nonblock);
535
536   FD_ZERO (&readfds);
537   FD_ZERO (&writefds);
538   max_fd = 0;
539   if (nonblock)
540     timeout.tv_sec = 0;
541
542   TRACE_SEQ (dbg_help, "select on [ ");
543
544   any = 0;
545   for (i = 0; i < nfds; i++)
546     {
547       if (fds[i].fd == -1)
548         continue;
549       if (fds[i].for_read)
550         {
551           assert (!FD_ISSET (fds[i].fd, &readfds));
552           FD_SET (fds[i].fd, &readfds);
553           if (fds[i].fd > max_fd)
554             max_fd = fds[i].fd;
555           TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
556           any = 1;
557         }
558       else if (fds[i].for_write)
559         {
560           assert (!FD_ISSET (fds[i].fd, &writefds));
561           FD_SET (fds[i].fd, &writefds);
562           if (fds[i].fd > max_fd)
563             max_fd = fds[i].fd;
564           TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
565           any = 1;
566         }
567       fds[i].signaled = 0;
568     }
569   TRACE_END (dbg_help, "]");
570   if (!any)
571     return TRACE_SYSRES (0);
572
573   do
574     {
575       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
576                                  &timeout);
577     }
578   while (count < 0 && errno == EINTR);
579   if (count < 0)
580     return TRACE_SYSRES (-1);
581
582   TRACE_SEQ (dbg_help, "select OK [ ");
583   if (TRACE_ENABLED (dbg_help))
584     {
585       for (i = 0; i <= max_fd; i++)
586         {
587           if (FD_ISSET (i, &readfds))
588             TRACE_ADD1 (dbg_help, "r0x%x ", i);
589           if (FD_ISSET (i, &writefds))
590             TRACE_ADD1 (dbg_help, "w0x%x ", i);
591         }
592       TRACE_END (dbg_help, "]");
593     }
594
595   /* The variable N is used to optimize it a little bit.  */
596   for (n = count, i = 0; i < nfds && n; i++)
597     {
598       if (fds[i].fd == -1)
599         ;
600       else if (fds[i].for_read)
601         {
602           if (FD_ISSET (fds[i].fd, &readfds))
603             {
604               fds[i].signaled = 1;
605               n--;
606             }
607         }
608       else if (fds[i].for_write)
609         {
610           if (FD_ISSET (fds[i].fd, &writefds))
611             {
612               fds[i].signaled = 1;
613               n--;
614             }
615         }
616     }
617   return TRACE_SYSRES (count);
618 }
619
620 \f
621 int
622 _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
623 {
624   int nread;
625   int saved_errno;
626   struct iovec *iov;
627   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
628               "msg=%p, flags=%i", msg, flags);
629
630   nread = 0;
631   iov = msg->msg_iov;
632   while (iov < msg->msg_iov + msg->msg_iovlen)
633     {
634       nread += iov->iov_len;
635       iov++;
636     }
637
638   TRACE_LOG1 ("about to receive %d bytes", nread);
639
640   do
641     {
642       nread = _gpgme_ath_recvmsg (fd, msg, flags);
643     }
644   while (nread == -1 && errno == EINTR);
645   saved_errno = errno;
646   if (nread > 0)
647     {
648       int nr = nread;
649
650       iov = msg->msg_iov;
651       while (nr > 0)
652         {
653           int len = nr > iov->iov_len ? iov->iov_len : nr;
654           TRACE_LOGBUF (msg->msg_iov->iov_base, len);
655           iov++;
656           nr -= len;
657         }
658     }
659   errno = saved_errno;
660   return TRACE_SYSRES (nread);
661 }
662
663
664 int
665 _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
666 {
667   int nwritten;
668   struct iovec *iov;
669   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
670               "msg=%p, flags=%i", msg, flags);
671
672   nwritten = 0;
673   iov = msg->msg_iov;
674   while (iov < msg->msg_iov + msg->msg_iovlen)
675     {
676       nwritten += iov->iov_len;
677       iov++;
678     }
679
680   TRACE_LOG1 ("about to receive %d bytes", nwritten);
681   iov = msg->msg_iov;
682   while (nwritten > 0)
683     {
684       int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
685       TRACE_LOGBUF (msg->msg_iov->iov_base, len);
686       iov++;
687       nwritten -= len;
688     }
689
690   do
691     {
692       nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
693     }
694   while (nwritten == -1 && errno == EINTR);
695   return TRACE_SYSRES (nwritten);
696 }
697
698
699 int
700 _gpgme_io_dup (int fd)
701 {
702   int new_fd = dup (fd);
703
704   TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
705
706   return new_fd;
707 }
708
709 \f
710 int
711 _gpgme_io_socket (int domain, int type, int proto)
712 {
713   int res;
714
715   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
716               "type=%i, proto=%i", type, proto);
717
718   res = socket (domain, type, proto);
719
720   return TRACE_SYSRES (res);
721 }
722
723
724 int
725 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
726 {
727   int res;
728
729   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
730               "addr=%p, addrlen=%i", addr, addrlen);
731
732   res = ath_connect (fd, addr, addrlen);
733
734   return TRACE_SYSRES (res);
735 }