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