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