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