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