2007-09-27 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / 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 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, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <ctype.h>
37
38 #include "util.h"
39 #include "priv-io.h"
40 #include "sema.h"
41 #include "ath.h"
42 #include "debug.h"
43
44 \f
45 void
46 _gpgme_io_subsystem_init (void)
47 {
48   struct sigaction act;
49
50   sigaction (SIGPIPE, NULL, &act);
51   if (act.sa_handler == SIG_DFL)
52     {
53       act.sa_handler = SIG_IGN;
54       sigemptyset (&act.sa_mask);
55       act.sa_flags = 0;
56       sigaction (SIGPIPE, &act, NULL);
57     }
58 }
59
60
61 /* Write the printable version of FD to the buffer BUF of length
62    BUFLEN.  The printable version is the representation on the command
63    line that the child process expects.  */
64 int
65 _gpgme_io_fd2str (char *buf, int buflen, int fd)
66 {
67   return snprintf (buf, buflen, "%d", fd);
68 }
69
70 \f
71 static struct
72 {
73   _gpgme_close_notify_handler_t handler;
74   void *value;
75 } notify_table[256];
76
77
78 int
79 _gpgme_io_read (int fd, void *buffer, size_t count)
80 {
81   int nread;
82   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
83               "buffer=%p, count=%u", buffer, count);
84
85   do
86     {
87       nread = _gpgme_ath_read (fd, buffer, count);
88     }
89   while (nread == -1 && errno == EINTR);
90
91   TRACE_LOGBUF (buffer, nread);
92   return TRACE_SYSRES (nread);
93 }
94
95
96 int
97 _gpgme_io_write (int fd, const void *buffer, size_t count)
98 {
99   int nwritten;
100   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
101               "buffer=%p, count=%u", buffer, count);
102   TRACE_LOGBUF (buffer, count);
103
104   do
105     {
106       nwritten = _gpgme_ath_write (fd, buffer, count);
107     }
108   while (nwritten == -1 && errno == EINTR);
109
110   return TRACE_SYSRES (nwritten);
111 }
112
113
114 int
115 _gpgme_io_pipe (int filedes[2], int inherit_idx)
116 {
117   int saved_errno;
118   int err;
119   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
120               "inherit_idx=%i (GPGME uses it for %s)",
121               inherit_idx, inherit_idx ? "reading" : "writing");
122
123   err = pipe (filedes);
124   if (err < 0)
125     return TRACE_SYSRES (err);
126
127   /* FIXME: Should get the old flags first.  */
128   err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
129   saved_errno = errno;
130   if (err < 0)
131     {
132       close (filedes[0]);
133       close (filedes[1]);
134     }
135   errno = saved_errno;
136   if (err)
137     return TRACE_SYSRES (err);
138
139   return TRACE_SUC2 ("read=0x%x, write=0x%x", filedes[0], filedes[1]);
140 }
141
142
143 int
144 _gpgme_io_close (int fd)
145 {
146   int res;
147
148   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
149
150   if (fd == -1)
151     {
152       errno = EINVAL;
153       return TRACE_SYSRES (-1);
154     }
155
156   /* First call the notify handler.  */
157   if (fd >= 0 && fd < (int) DIM (notify_table))
158     {
159       if (notify_table[fd].handler)
160         {
161           TRACE_LOG2 ("invoking close handler %p/%p",
162                       notify_table[fd].handler, notify_table[fd].value);
163           notify_table[fd].handler (fd, notify_table[fd].value);
164           notify_table[fd].handler = NULL;
165           notify_table[fd].value = NULL;
166         }
167     }
168   /* Then do the close.  */    
169   res = close (fd);
170   return TRACE_SYSRES (res);
171 }
172
173
174 int
175 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
176                             void *value)
177 {
178   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
179               "close_handler=%p/%p", handler, value);
180
181   assert (fd != -1);
182
183   if (fd < 0 || fd >= (int) DIM (notify_table))
184     {
185       errno = EINVAL;
186       return TRACE_SYSRES (-1);
187     }
188   notify_table[fd].handler = handler;
189   notify_table[fd].value = value;
190   return TRACE_SYSRES (0);
191 }
192
193
194 int
195 _gpgme_io_set_nonblocking (int fd)
196 {
197   int flags;
198   int res;
199   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
200
201   flags = fcntl (fd, F_GETFL, 0);
202   if (flags == -1)
203     return TRACE_SYSRES (-1);
204   flags |= O_NONBLOCK;
205   res = fcntl (fd, F_SETFL, flags);
206   return TRACE_SYSRES (res);
207 }
208
209
210 static int
211 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
212 {
213   int status;
214
215   *r_status = 0;
216   *r_signal = 0;
217   if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
218     {
219       if (WIFSIGNALED (status))
220         {
221           *r_status = 4; /* Need some value here.  */
222           *r_signal = WTERMSIG (status);
223         }
224       else if (WIFEXITED (status))
225         *r_status = WEXITSTATUS (status);
226       else
227         *r_status = 4; /* Oops.  */
228       return 1;
229     }
230   return 0;
231 }
232
233
234 /* Returns 0 on success, -1 on error.  */
235 int
236 _gpgme_io_spawn (const char *path, char **argv,
237                  struct spawn_fd_item_s *fd_child_list,
238                  struct spawn_fd_item_s *fd_parent_list)
239 {
240   pid_t pid;
241   int i;
242   int status;
243   int signo;
244   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
245               "path=%s", path);
246   i = 0;
247   while (argv[i])
248     {
249       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
250       i++;
251     }
252   
253   pid = fork ();
254   if (pid == -1) 
255     return TRACE_SYSRES (-1);
256   
257   if (!pid)
258     {
259       /* Intermediate child to prevent zombie processes.  */
260       if ((pid = fork ()) == 0)
261         {
262           /* Child.  */
263           int duped_stdin = 0;
264           int duped_stderr = 0;
265
266           /* First close all fds which will not be duped.  */
267           for (i=0; fd_child_list[i].fd != -1; i++)
268             if (fd_child_list[i].dup_to == -1)
269               close (fd_child_list[i].fd);
270
271           /* And now dup and close the rest.  */
272           for (i=0; fd_child_list[i].fd != -1; i++)
273             {
274               if (fd_child_list[i].dup_to != -1)
275                 {
276                   if (dup2 (fd_child_list[i].fd,
277                             fd_child_list[i].dup_to) == -1)
278                     {
279 #if 0
280                       /* FIXME: The debug file descriptor is not
281                          dup'ed anyway, so we can't see this.  */
282                       TRACE_LOG1 ("dup2 failed in child: %s\n",
283                                   strerror (errno));
284 #endif
285                       _exit (8);
286                     }
287                   if (fd_child_list[i].dup_to == 0)
288                     duped_stdin=1;
289                   if (fd_child_list[i].dup_to == 2)
290                     duped_stderr=1;
291                   close (fd_child_list[i].fd);
292                 }
293             }
294           
295           if (!duped_stdin || !duped_stderr)
296             {
297               int fd = open ("/dev/null", O_RDWR);
298               if (fd == -1)
299                 {
300 #if 0
301                   /* FIXME: The debug file descriptor is not dup'ed
302                      anyway, so we can't see this.  */
303                   TRACE_LOG1 ("can't open `/dev/null': %s\n",
304                               strerror (errno));
305 #endif
306                   _exit (8);
307                 }
308               /* Make sure that the process has a connected stdin.  */
309               if (!duped_stdin)
310                 {
311                   if (dup2 (fd, 0) == -1)
312                     {
313 #if 0
314                   /* FIXME: The debug file descriptor is not dup'ed
315                      anyway, so we can't see this.  */
316                       TRACE_LOG1 ("dup2(/dev/null, 0) failed: %s\n",
317                                   strerror (errno));
318 #endif
319                       _exit (8);
320                     }
321                 }
322               if (!duped_stderr)
323                 if (dup2 (fd, 2) == -1)
324                   {
325 #if 0
326                     /* FIXME: The debug file descriptor is not dup'ed
327                        anyway, so we can't see this.  */
328                     TRACE_LOG1 ("dup2(dev/null, 2) failed: %s\n",
329                                 strerror (errno));
330 #endif
331                     _exit (8);
332                   }
333               close (fd);
334             }
335     
336           execv ( path, argv );
337           /* Hmm: in that case we could write a special status code to the
338              status-pipe.  */
339 #if 0
340           /* FIXME: The debug file descriptor is not dup'ed anyway, so
341              we can't see this.  */
342           TRACE_LOG1 ("exec of `%s' failed\n", path);
343 #endif
344           _exit (8);
345         } /* End child.  */
346       if (pid == -1)
347         _exit (1);
348       else
349         _exit (0);
350     }
351
352   TRACE_LOG1 ("waiting for child process pid=%i", pid);
353   _gpgme_io_waitpid (pid, 1, &status, &signo);
354   if (status)
355     return TRACE_SYSRES (-1);
356
357   /* .dup_to is not used in the parent list.  */
358   for (i = 0; fd_parent_list[i].fd != -1; i++)
359     _gpgme_io_close (fd_parent_list[i].fd);
360
361   return TRACE_SYSRES (0);
362 }
363
364
365 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
366    nothing to select, > 0 = number of signaled fds.  */
367 int
368 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
369 {
370   fd_set readfds;
371   fd_set writefds;
372   unsigned int i;
373   int any;
374   int max_fd;
375   int n;
376   int count;
377   /* Use a 1s timeout.  */
378   struct timeval timeout = { 1, 0 };
379   void *dbg_help = NULL;
380   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
381               "nfds=%u, nonblock=%u", nfds, nonblock);
382
383   FD_ZERO (&readfds);
384   FD_ZERO (&writefds);
385   max_fd = 0;
386   if (nonblock)
387     timeout.tv_sec = 0;
388
389   TRACE_SEQ (dbg_help, "select on [ ");
390
391   any = 0;
392   for (i = 0; i < nfds; i++)
393     {
394       if (fds[i].fd == -1) 
395         continue;
396       if (fds[i].frozen)
397         TRACE_ADD1 (dbg_help, "f0x%x ", fds[i].fd);
398       else if (fds[i].for_read)
399         {
400           assert (!FD_ISSET (fds[i].fd, &readfds));
401           FD_SET (fds[i].fd, &readfds);
402           if (fds[i].fd > max_fd)
403             max_fd = fds[i].fd;
404           TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
405           any = 1;
406         }
407       else if (fds[i].for_write)
408         {
409           assert (!FD_ISSET (fds[i].fd, &writefds));
410           FD_SET (fds[i].fd, &writefds);
411           if (fds[i].fd > max_fd)
412             max_fd = fds[i].fd;
413           TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
414           any = 1;
415         }
416       fds[i].signaled = 0;
417     }
418   TRACE_END (dbg_help, "]"); 
419   if (!any)
420     return TRACE_SYSRES (0);
421
422   do
423     {
424       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
425                                  &timeout);
426     }
427   while (count < 0 && errno == EINTR);
428   if (count < 0)
429     return TRACE_SYSRES (-1);
430
431   TRACE_SEQ (dbg_help, "select OK [ ");
432   if (TRACE_ENABLED (dbg_help))
433     {
434       for (i = 0; i <= max_fd; i++)
435         {
436           if (FD_ISSET (i, &readfds))
437             TRACE_ADD1 (dbg_help, "r0x%x ", i);
438           if (FD_ISSET (i, &writefds))
439             TRACE_ADD1 (dbg_help, "w0x%x ", i);
440         }
441       TRACE_END (dbg_help, "]");
442     }
443     
444   /* The variable N is used to optimize it a little bit.  */
445   for (n = count, i = 0; i < nfds && n; i++)
446     {
447       if (fds[i].fd == -1)
448         ;
449       else if (fds[i].for_read)
450         {
451           if (FD_ISSET (fds[i].fd, &readfds))
452             {
453               fds[i].signaled = 1;
454               n--;
455             }
456         }
457       else if (fds[i].for_write)
458         {
459           if (FD_ISSET (fds[i].fd, &writefds))
460             {
461               fds[i].signaled = 1;
462               n--;
463             }
464         }
465     }
466   return TRACE_SYSRES (count);
467 }
468
469 \f
470 int
471 _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
472 {
473   int nread;
474   int saved_errno;
475   struct iovec *iov;
476   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
477               "msg=%p, flags=%i", msg, flags);
478
479   nread = 0;
480   iov = msg->msg_iov;
481   while (iov < msg->msg_iov + msg->msg_iovlen)
482     {
483       nread += iov->iov_len;
484       iov++;
485     }
486   
487   TRACE_LOG1 ("about to receive %d bytes", nread);
488
489   do
490     {
491       nread = _gpgme_ath_recvmsg (fd, msg, flags);
492     }
493   while (nread == -1 && errno == EINTR);
494   saved_errno = errno;
495   if (nread > 0)
496     {
497       int nr = nread;
498
499       iov = msg->msg_iov;
500       while (nr > 0)
501         {
502           int len = nr > iov->iov_len ? iov->iov_len : nr;
503           TRACE_LOGBUF (msg->msg_iov->iov_base, len);
504           iov++;
505           nr -= len;
506         }
507     }
508   errno = saved_errno;
509   return TRACE_SYSRES (nread);
510 }
511
512
513 int
514 _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
515 {
516   int nwritten;
517   struct iovec *iov;
518   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
519               "msg=%p, flags=%i", msg, flags);
520
521   nwritten = 0;
522   iov = msg->msg_iov;
523   while (iov < msg->msg_iov + msg->msg_iovlen)
524     {
525       nwritten += iov->iov_len;
526       iov++;
527     }
528
529   TRACE_LOG1 ("about to receive %d bytes", nwritten);
530   iov = msg->msg_iov;
531   while (nwritten > 0)
532     {
533       int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
534       TRACE_LOGBUF (msg->msg_iov->iov_base, len);
535       iov++;
536       nwritten -= len;
537     }
538
539   do
540     {
541       nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
542     }
543   while (nwritten == -1 && errno == EINTR);
544   return TRACE_SYSRES (nwritten);
545 }
546
547
548 int
549 _gpgme_io_dup (int fd)
550 {
551   return dup (fd);
552 }