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   int really_close = 1;
143
144   if (fd == -1)
145     return -1;
146   /* First call the notify handler.  */
147   DEBUG1 ("closing fd %d", fd);
148   if (fd >= 0 && fd < (int) DIM (notify_table))
149     {
150       if (notify_table[fd].handler)
151         {
152           really_close = notify_table[fd].handler (fd, notify_table[fd].value);
153           notify_table[fd].handler = NULL;
154           notify_table[fd].value = NULL;
155         }
156     }
157   /* Then do the close.  */    
158   if (really_close)
159     return close (fd);
160
161   return 0;
162 }
163
164
165 int
166 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
167                             void *value)
168 {
169   assert (fd != -1);
170
171   if (fd < 0 || fd >= (int) DIM (notify_table))
172     return -1;
173   DEBUG1 ("set notification for fd %d", fd);
174   notify_table[fd].handler = handler;
175   notify_table[fd].value = value;
176   return 0;
177 }
178
179
180 int
181 _gpgme_io_set_nonblocking (int fd)
182 {
183   int flags;
184
185   flags = fcntl (fd, F_GETFL, 0);
186   if (flags == -1)
187     return -1;
188   flags |= O_NONBLOCK;
189   return fcntl (fd, F_SETFL, flags);
190 }
191
192
193 static int
194 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
195 {
196   int status;
197
198   *r_status = 0;
199   *r_signal = 0;
200   if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
201     {
202       if (WIFSIGNALED (status))
203         {
204           *r_status = 4; /* Need some value here.  */
205           *r_signal = WTERMSIG (status);
206         }
207       else if (WIFEXITED (status))
208         *r_status = WEXITSTATUS (status);
209       else
210         *r_status = 4; /* Oops.  */
211       return 1;
212     }
213   return 0;
214 }
215
216
217 /* Returns 0 on success, -1 on error.  */
218 int
219 _gpgme_io_spawn (const char *path, char **argv,
220                  struct spawn_fd_item_s *fd_child_list,
221                  struct spawn_fd_item_s *fd_parent_list)
222 {
223   pid_t pid;
224   int i;
225   int status, signo;
226
227   pid = fork ();
228   if (pid == -1) 
229     return -1;
230
231   if (!pid)
232     {
233       /* Intermediate child to prevent zombie processes.  */
234       if ((pid = fork ()) == 0)
235         {
236           /* Child.  */
237           int duped_stdin = 0;
238           int duped_stderr = 0;
239
240           /* First close all fds which will not be duped.  */
241           for (i=0; fd_child_list[i].fd != -1; i++)
242             if (fd_child_list[i].dup_to == -1)
243               close (fd_child_list[i].fd);
244
245           /* And now dup and close the rest.  */
246           for (i=0; fd_child_list[i].fd != -1; i++)
247             {
248               if (fd_child_list[i].dup_to != -1)
249                 {
250                   if (dup2 (fd_child_list[i].fd,
251                             fd_child_list[i].dup_to) == -1)
252                     {
253                       DEBUG1 ("dup2 failed in child: %s\n", strerror (errno));
254                       _exit (8);
255                     }
256                   if (fd_child_list[i].dup_to == 0)
257                     duped_stdin=1;
258                   if (fd_child_list[i].dup_to == 2)
259                     duped_stderr=1;
260                   close (fd_child_list[i].fd);
261                 }
262             }
263           
264           if (!duped_stdin || !duped_stderr)
265             {
266               int fd = open ("/dev/null", O_RDWR);
267               if (fd == -1)
268                 {
269                   DEBUG1 ("can't open `/dev/null': %s\n", strerror (errno));
270                   _exit (8);
271                 }
272               /* Make sure that the process has a connected stdin.  */
273               if (!duped_stdin)
274                 {
275                   if (dup2 (fd, 0) == -1)
276                     {
277                       DEBUG1("dup2(/dev/null, 0) failed: %s\n",
278                              strerror (errno));
279                       _exit (8);
280                     }
281                 }
282               if (!duped_stderr)
283                 if (dup2 (fd, 2) == -1)
284                   {
285                     DEBUG1 ("dup2(dev/null, 2) failed: %s\n",
286                             strerror (errno));
287                     _exit (8);
288                   }
289               close (fd);
290             }
291     
292           execv ( path, argv );
293           /* Hmm: in that case we could write a special status code to the
294              status-pipe.  */
295           DEBUG1 ("exec of `%s' failed\n", path);
296           _exit (8);
297         } /* End child.  */
298       if (pid == -1)
299         _exit (1);
300       else
301         _exit (0);
302     }
303     
304   _gpgme_io_waitpid (pid, 1, &status, &signo);
305   if (status)
306     return -1;
307
308   /* .dup_to is not used in the parent list.  */
309   for (i = 0; fd_parent_list[i].fd != -1; i++)
310     _gpgme_io_close (fd_parent_list[i].fd);
311
312   return 0;
313 }
314
315
316 /*
317  * Select on the list of fds.
318  * Returns: -1 = error
319  *           0 = timeout or nothing to select
320  *          >0 = number of signaled fds
321  */
322 int
323 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
324 {
325   fd_set readfds;
326   fd_set writefds;
327   unsigned int i;
328   int any, max_fd, n, count;
329   struct timeval timeout = { 1, 0 }; /* Use a 1s timeout.  */
330   void *dbg_help = NULL;
331
332   FD_ZERO (&readfds);
333   FD_ZERO (&writefds);
334   max_fd = 0;
335   if (nonblock)
336     timeout.tv_sec = 0;
337
338   DEBUG_BEGIN (dbg_help, 3, "gpgme:select on [ ");
339   any = 0;
340   for (i = 0; i < nfds; i++)
341     {
342       if (fds[i].fd == -1) 
343         continue;
344       if (fds[i].frozen)
345         DEBUG_ADD1 (dbg_help, "f%d ", fds[i].fd);
346       else if (fds[i].for_read)
347         {
348           assert (!FD_ISSET (fds[i].fd, &readfds));
349           FD_SET (fds[i].fd, &readfds);
350           if (fds[i].fd > max_fd)
351             max_fd = fds[i].fd;
352           DEBUG_ADD1 (dbg_help, "r%d ", fds[i].fd);
353           any = 1;
354         }
355       else if (fds[i].for_write)
356         {
357           assert (!FD_ISSET (fds[i].fd, &writefds));
358           FD_SET (fds[i].fd, &writefds);
359           if (fds[i].fd > max_fd)
360             max_fd = fds[i].fd;
361           DEBUG_ADD1 (dbg_help, "w%d ", fds[i].fd);
362           any = 1;
363         }
364       fds[i].signaled = 0;
365     }
366   DEBUG_END (dbg_help, "]"); 
367   if (!any)
368     return 0;
369
370   do
371     {
372       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
373                                  &timeout);
374     }
375   while (count < 0 && errno == EINTR);
376   if (count < 0)
377     {
378       int saved_errno = errno;
379       DEBUG1 ("_gpgme_io_select failed: %s\n", strerror (errno));
380       errno = saved_errno;
381       return -1; /* error */
382     }
383
384   DEBUG_BEGIN (dbg_help, 3, "select OK [ ");
385   if (DEBUG_ENABLED (dbg_help))
386     {
387       for (i = 0; i <= max_fd; i++)
388         {
389           if (FD_ISSET (i, &readfds))
390             DEBUG_ADD1 (dbg_help, "r%d ", i);
391           if (FD_ISSET (i, &writefds))
392             DEBUG_ADD1 (dbg_help, "w%d ", i);
393         }
394       DEBUG_END (dbg_help, "]");
395     }
396     
397   /* n is used to optimize it a little bit.  */
398   for (n = count, i = 0; i < nfds && n; i++)
399     {
400       if (fds[i].fd == -1)
401         ;
402       else if (fds[i].for_read)
403         {
404           if (FD_ISSET (fds[i].fd, &readfds))
405             {
406               fds[i].signaled = 1;
407               n--;
408             }
409         }
410       else if (fds[i].for_write)
411         {
412           if (FD_ISSET (fds[i].fd, &writefds))
413             {
414               fds[i].signaled = 1;
415               n--;
416             }
417         }
418     }
419   return count;
420 }
421
422 \f
423 int
424 _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
425 {
426   int nread;
427   int saved_errno;
428   struct iovec *iov;
429
430   nread = 0;
431   iov = msg->msg_iov;
432   while (iov < msg->msg_iov + msg->msg_iovlen)
433     {
434       nread += iov->iov_len;
435       iov++;
436     }
437   
438   DEBUG2 ("fd %d: about to receive %d bytes\n",
439           fd, (int) nread);
440   do
441     {
442       nread = _gpgme_ath_recvmsg (fd, msg, flags);
443     }
444   while (nread == -1 && errno == EINTR);
445   saved_errno = errno;
446   DEBUG2 ("fd %d: got %d bytes\n", fd, nread);
447   if (nread > 0)
448     {
449       int nr = nread;
450
451       iov = msg->msg_iov;
452       while (nr > 0)
453         {
454           int len = nr > iov->iov_len ? iov->iov_len : nr;
455           _gpgme_debug (2, "fd %d: got `%.*s'\n", fd, len,
456                         msg->msg_iov->iov_base);
457           iov++;
458           nr -= len;
459         }
460     }
461   errno = saved_errno;
462   return nread;
463 }
464
465
466 int
467 _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
468 {
469   int saved_errno;
470   int nwritten;
471   struct iovec *iov;
472
473   nwritten = 0;
474   iov = msg->msg_iov;
475   while (iov < msg->msg_iov + msg->msg_iovlen)
476     {
477       nwritten += iov->iov_len;
478       iov++;
479     }
480
481   DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int) nwritten);
482   iov = msg->msg_iov;
483   while (nwritten > 0)
484     {
485       int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
486       _gpgme_debug (2, "fd %d: write `%.*s'\n", fd, len,
487                     msg->msg_iov->iov_base);
488       iov++;
489       nwritten -= len;
490     }
491
492   do
493     {
494       nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
495     }
496   while (nwritten == -1 && errno == EINTR);
497   saved_errno = errno;
498   DEBUG2 ("fd %d:          wrote %d bytes\n", fd, (int) nwritten);
499   errno = saved_errno;
500   return nwritten;
501 }