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