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