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