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