2005-04-14 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 \f
60 static struct
61 {
62   void (*handler) (int,void*);
63   void *value;
64 } notify_table[256];
65
66 int
67 _gpgme_io_read (int fd, void *buffer, size_t count)
68 {
69   int nread;
70   int saved_errno;
71
72   DEBUG2 ("fd %d: about to read %d bytes\n", fd, (int) count);
73   do
74     {
75       nread = _gpgme_ath_read (fd, buffer, count);
76     }
77   while (nread == -1 && errno == EINTR);
78   saved_errno = errno;
79   DEBUG2 ("fd %d: got %d bytes\n", fd, nread);
80   if (nread > 0)
81     _gpgme_debug (2, "fd %d: got `%.*s'\n", fd, nread, buffer);
82   errno = saved_errno;
83   return nread;
84 }
85
86
87 int
88 _gpgme_io_write (int fd, const void *buffer, size_t count)
89 {
90   int saved_errno;
91   int nwritten;
92
93   DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int) count);
94   _gpgme_debug (2, "fd %d: write `%.*s'\n", fd, (int) count, buffer);
95   do
96     {
97       nwritten = _gpgme_ath_write (fd, buffer, count);
98     }
99   while (nwritten == -1 && errno == EINTR);
100   saved_errno = errno;
101   DEBUG2 ("fd %d:          wrote %d bytes\n", fd, (int) nwritten);
102   errno = saved_errno;
103   return nwritten;
104 }
105
106
107 int
108 _gpgme_io_pipe (int filedes[2], int inherit_idx)
109 {
110   int saved_errno;
111   int err;
112
113   err = pipe (filedes);
114   if (err < 0)
115     return err;
116   /* FIXME: Should get the old flags first.  */
117   err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
118   saved_errno = errno;
119   if (err < 0)
120     {
121       close (filedes[0]);
122       close (filedes[1]);
123     }
124   errno = saved_errno;
125   return err;
126 }
127
128
129 int
130 _gpgme_io_close (int fd)
131 {
132   if (fd == -1)
133     return -1;
134   /* First call the notify handler.  */
135   DEBUG1 ("closing fd %d", fd);
136   if (fd >= 0 && fd < (int) DIM (notify_table))
137     {
138       if (notify_table[fd].handler)
139         {
140           notify_table[fd].handler (fd, notify_table[fd].value);
141           notify_table[fd].handler = NULL;
142           notify_table[fd].value = NULL;
143         }
144     }
145   /* Then do the close.  */    
146   return close (fd);
147 }
148
149
150 int
151 _gpgme_io_set_close_notify (int fd, void (*handler)(int, void*), void *value)
152 {
153   assert (fd != -1);
154
155   if (fd < 0 || fd >= (int) DIM (notify_table))
156     return -1;
157   DEBUG1 ("set notification for fd %d", fd);
158   notify_table[fd].handler = handler;
159   notify_table[fd].value = value;
160   return 0;
161 }
162
163
164 int
165 _gpgme_io_set_nonblocking (int fd)
166 {
167   int flags;
168
169   flags = fcntl (fd, F_GETFL, 0);
170   if (flags == -1)
171     return -1;
172   flags |= O_NONBLOCK;
173   return fcntl (fd, F_SETFL, flags);
174 }
175
176
177 /* Returns 0 on success, -1 on error.  */
178 int
179 _gpgme_io_spawn (const char *path, char **argv,
180                  struct spawn_fd_item_s *fd_child_list,
181                  struct spawn_fd_item_s *fd_parent_list)
182 {
183   pid_t pid;
184   int i;
185   int status, signo;
186
187   pid = fork ();
188   if (pid == -1) 
189     return -1;
190
191   if (!pid)
192     {
193       /* Intermediate child to prevent zombie processes.  */
194       if ((pid = fork ()) == 0)
195         {
196           /* Child.  */
197           int duped_stdin = 0;
198           int duped_stderr = 0;
199
200           /* First close all fds which will not be duped.  */
201           for (i=0; fd_child_list[i].fd != -1; i++)
202             if (fd_child_list[i].dup_to == -1)
203               close (fd_child_list[i].fd);
204
205           /* And now dup and close the rest.  */
206           for (i=0; fd_child_list[i].fd != -1; i++)
207             {
208               if (fd_child_list[i].dup_to != -1)
209                 {
210                   if (dup2 (fd_child_list[i].fd,
211                             fd_child_list[i].dup_to) == -1)
212                     {
213                       DEBUG1 ("dup2 failed in child: %s\n", strerror (errno));
214                       _exit (8);
215                     }
216                   if (fd_child_list[i].dup_to == 0)
217                     duped_stdin=1;
218                   if (fd_child_list[i].dup_to == 2)
219                     duped_stderr=1;
220                   close (fd_child_list[i].fd);
221                 }
222             }
223           
224           if (!duped_stdin || !duped_stderr)
225             {
226               int fd = open ("/dev/null", O_RDWR);
227               if (fd == -1)
228                 {
229                   DEBUG1 ("can't open `/dev/null': %s\n", strerror (errno));
230                   _exit (8);
231                 }
232               /* Make sure that the process has a connected stdin.  */
233               if (!duped_stdin)
234                 {
235                   if (dup2 (fd, 0) == -1)
236                     {
237                       DEBUG1("dup2(/dev/null, 0) failed: %s\n",
238                              strerror (errno));
239                       _exit (8);
240                     }
241                 }
242               if (!duped_stderr)
243                 if (dup2 (fd, 2) == -1)
244                   {
245                     DEBUG1 ("dup2(dev/null, 2) failed: %s\n",
246                             strerror (errno));
247                     _exit (8);
248                   }
249               close (fd);
250             }
251     
252           execv ( path, argv );
253           /* Hmm: in that case we could write a special status code to the
254              status-pipe.  */
255           DEBUG1 ("exec of `%s' failed\n", path);
256           _exit (8);
257         } /* End child.  */
258       if (pid == -1)
259         _exit (1);
260       else
261         _exit (0);
262     }
263     
264   _gpgme_io_waitpid (pid, 1, &status, &signo);
265   if (status)
266     return -1;
267
268   /* .dup_to is not used in the parent list.  */
269   for (i = 0; fd_parent_list[i].fd != -1; i++)
270     _gpgme_io_close (fd_parent_list[i].fd);
271
272   return 0;
273 }
274
275
276 int
277 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
278 {
279   int status;
280
281   *r_status = 0;
282   *r_signal = 0;
283   if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
284     {
285       if (WIFSIGNALED (status))
286         {
287           *r_status = 4; /* Need some value here.  */
288           *r_signal = WTERMSIG (status);
289         }
290       else if (WIFEXITED (status))
291         *r_status = WEXITSTATUS (status);
292       else
293         *r_status = 4; /* Oops.  */
294       return 1;
295     }
296   return 0;
297 }
298
299
300 int
301 _gpgme_io_kill (int pid, int hard)
302 {
303   return kill (pid, hard ? SIGKILL : SIGTERM);
304 }
305
306
307 /*
308  * Select on the list of fds.
309  * Returns: -1 = error
310  *           0 = timeout or nothing to select
311  *          >0 = number of signaled fds
312  */
313 int
314 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
315 {
316   fd_set readfds;
317   fd_set writefds;
318   unsigned int i;
319   int any, max_fd, n, count;
320   struct timeval timeout = { 1, 0 }; /* Use a 1s timeout.  */
321   void *dbg_help = NULL;
322
323   FD_ZERO (&readfds);
324   FD_ZERO (&writefds);
325   max_fd = 0;
326   if (nonblock)
327     timeout.tv_sec = 0;
328
329   DEBUG_BEGIN (dbg_help, 3, "gpgme:select on [ ");
330   any = 0;
331   for (i = 0; i < nfds; i++)
332     {
333       if (fds[i].fd == -1) 
334         continue;
335       if (fds[i].frozen)
336         DEBUG_ADD1 (dbg_help, "f%d ", fds[i].fd);
337       else if (fds[i].for_read)
338         {
339           assert (!FD_ISSET (fds[i].fd, &readfds));
340           FD_SET (fds[i].fd, &readfds);
341           if (fds[i].fd > max_fd)
342             max_fd = fds[i].fd;
343           DEBUG_ADD1 (dbg_help, "r%d ", fds[i].fd);
344           any = 1;
345         }
346       else if (fds[i].for_write)
347         {
348           assert (!FD_ISSET (fds[i].fd, &writefds));
349           FD_SET (fds[i].fd, &writefds);
350           if (fds[i].fd > max_fd)
351             max_fd = fds[i].fd;
352           DEBUG_ADD1 (dbg_help, "w%d ", fds[i].fd);
353           any = 1;
354         }
355       fds[i].signaled = 0;
356     }
357   DEBUG_END (dbg_help, "]"); 
358   if (!any)
359     return 0;
360
361   do
362     {
363       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
364                                  &timeout);
365     }
366   while (count < 0 && errno == EINTR);
367   if (count < 0)
368     {
369       int saved_errno = errno;
370       DEBUG1 ("_gpgme_io_select failed: %s\n", strerror (errno));
371       errno = saved_errno;
372       return -1; /* error */
373     }
374
375   DEBUG_BEGIN (dbg_help, 3, "select OK [ ");
376   if (DEBUG_ENABLED (dbg_help))
377     {
378       for (i = 0; i <= max_fd; i++)
379         {
380           if (FD_ISSET (i, &readfds))
381             DEBUG_ADD1 (dbg_help, "r%d ", i);
382           if (FD_ISSET (i, &writefds))
383             DEBUG_ADD1 (dbg_help, "w%d ", i);
384         }
385       DEBUG_END (dbg_help, "]");
386     }
387     
388   /* n is used to optimize it a little bit.  */
389   for (n = count, i = 0; i < nfds && n; i++)
390     {
391       if (fds[i].fd == -1)
392         ;
393       else if (fds[i].for_read)
394         {
395           if (FD_ISSET (fds[i].fd, &readfds))
396             {
397               fds[i].signaled = 1;
398               n--;
399             }
400         }
401       else if (fds[i].for_write)
402         {
403           if (FD_ISSET (fds[i].fd, &writefds))
404             {
405               fds[i].signaled = 1;
406               n--;
407             }
408         }
409     }
410   return count;
411 }