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