3479483e5097130db548c2acde5d7028365c908a
[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 static int
178 _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
179 {
180   int status;
181
182   *r_status = 0;
183   *r_signal = 0;
184   if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
185     {
186       if (WIFSIGNALED (status))
187         {
188           *r_status = 4; /* Need some value here.  */
189           *r_signal = WTERMSIG (status);
190         }
191       else if (WIFEXITED (status))
192         *r_status = WEXITSTATUS (status);
193       else
194         *r_status = 4; /* Oops.  */
195       return 1;
196     }
197   return 0;
198 }
199
200
201 /* Returns 0 on success, -1 on error.  */
202 int
203 _gpgme_io_spawn (const char *path, char **argv,
204                  struct spawn_fd_item_s *fd_child_list,
205                  struct spawn_fd_item_s *fd_parent_list)
206 {
207   pid_t pid;
208   int i;
209   int status, signo;
210
211   pid = fork ();
212   if (pid == -1) 
213     return -1;
214
215   if (!pid)
216     {
217       /* Intermediate child to prevent zombie processes.  */
218       if ((pid = fork ()) == 0)
219         {
220           /* Child.  */
221           int duped_stdin = 0;
222           int duped_stderr = 0;
223
224           /* First close all fds which will not be duped.  */
225           for (i=0; fd_child_list[i].fd != -1; i++)
226             if (fd_child_list[i].dup_to == -1)
227               close (fd_child_list[i].fd);
228
229           /* And now dup and close the rest.  */
230           for (i=0; fd_child_list[i].fd != -1; i++)
231             {
232               if (fd_child_list[i].dup_to != -1)
233                 {
234                   if (dup2 (fd_child_list[i].fd,
235                             fd_child_list[i].dup_to) == -1)
236                     {
237                       DEBUG1 ("dup2 failed in child: %s\n", strerror (errno));
238                       _exit (8);
239                     }
240                   if (fd_child_list[i].dup_to == 0)
241                     duped_stdin=1;
242                   if (fd_child_list[i].dup_to == 2)
243                     duped_stderr=1;
244                   close (fd_child_list[i].fd);
245                 }
246             }
247           
248           if (!duped_stdin || !duped_stderr)
249             {
250               int fd = open ("/dev/null", O_RDWR);
251               if (fd == -1)
252                 {
253                   DEBUG1 ("can't open `/dev/null': %s\n", strerror (errno));
254                   _exit (8);
255                 }
256               /* Make sure that the process has a connected stdin.  */
257               if (!duped_stdin)
258                 {
259                   if (dup2 (fd, 0) == -1)
260                     {
261                       DEBUG1("dup2(/dev/null, 0) failed: %s\n",
262                              strerror (errno));
263                       _exit (8);
264                     }
265                 }
266               if (!duped_stderr)
267                 if (dup2 (fd, 2) == -1)
268                   {
269                     DEBUG1 ("dup2(dev/null, 2) failed: %s\n",
270                             strerror (errno));
271                     _exit (8);
272                   }
273               close (fd);
274             }
275     
276           execv ( path, argv );
277           /* Hmm: in that case we could write a special status code to the
278              status-pipe.  */
279           DEBUG1 ("exec of `%s' failed\n", path);
280           _exit (8);
281         } /* End child.  */
282       if (pid == -1)
283         _exit (1);
284       else
285         _exit (0);
286     }
287     
288   _gpgme_io_waitpid (pid, 1, &status, &signo);
289   if (status)
290     return -1;
291
292   /* .dup_to is not used in the parent list.  */
293   for (i = 0; fd_parent_list[i].fd != -1; i++)
294     _gpgme_io_close (fd_parent_list[i].fd);
295
296   return 0;
297 }
298
299
300 /*
301  * Select on the list of fds.
302  * Returns: -1 = error
303  *           0 = timeout or nothing to select
304  *          >0 = number of signaled fds
305  */
306 int
307 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
308 {
309   fd_set readfds;
310   fd_set writefds;
311   unsigned int i;
312   int any, max_fd, n, count;
313   struct timeval timeout = { 1, 0 }; /* Use a 1s timeout.  */
314   void *dbg_help = NULL;
315
316   FD_ZERO (&readfds);
317   FD_ZERO (&writefds);
318   max_fd = 0;
319   if (nonblock)
320     timeout.tv_sec = 0;
321
322   DEBUG_BEGIN (dbg_help, 3, "gpgme:select on [ ");
323   any = 0;
324   for (i = 0; i < nfds; i++)
325     {
326       if (fds[i].fd == -1) 
327         continue;
328       if (fds[i].frozen)
329         DEBUG_ADD1 (dbg_help, "f%d ", fds[i].fd);
330       else if (fds[i].for_read)
331         {
332           assert (!FD_ISSET (fds[i].fd, &readfds));
333           FD_SET (fds[i].fd, &readfds);
334           if (fds[i].fd > max_fd)
335             max_fd = fds[i].fd;
336           DEBUG_ADD1 (dbg_help, "r%d ", fds[i].fd);
337           any = 1;
338         }
339       else if (fds[i].for_write)
340         {
341           assert (!FD_ISSET (fds[i].fd, &writefds));
342           FD_SET (fds[i].fd, &writefds);
343           if (fds[i].fd > max_fd)
344             max_fd = fds[i].fd;
345           DEBUG_ADD1 (dbg_help, "w%d ", fds[i].fd);
346           any = 1;
347         }
348       fds[i].signaled = 0;
349     }
350   DEBUG_END (dbg_help, "]"); 
351   if (!any)
352     return 0;
353
354   do
355     {
356       count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
357                                  &timeout);
358     }
359   while (count < 0 && errno == EINTR);
360   if (count < 0)
361     {
362       int saved_errno = errno;
363       DEBUG1 ("_gpgme_io_select failed: %s\n", strerror (errno));
364       errno = saved_errno;
365       return -1; /* error */
366     }
367
368   DEBUG_BEGIN (dbg_help, 3, "select OK [ ");
369   if (DEBUG_ENABLED (dbg_help))
370     {
371       for (i = 0; i <= max_fd; i++)
372         {
373           if (FD_ISSET (i, &readfds))
374             DEBUG_ADD1 (dbg_help, "r%d ", i);
375           if (FD_ISSET (i, &writefds))
376             DEBUG_ADD1 (dbg_help, "w%d ", i);
377         }
378       DEBUG_END (dbg_help, "]");
379     }
380     
381   /* n is used to optimize it a little bit.  */
382   for (n = count, i = 0; i < nfds && n; i++)
383     {
384       if (fds[i].fd == -1)
385         ;
386       else if (fds[i].for_read)
387         {
388           if (FD_ISSET (fds[i].fd, &readfds))
389             {
390               fds[i].signaled = 1;
391               n--;
392             }
393         }
394       else if (fds[i].for_write)
395         {
396           if (FD_ISSET (fds[i].fd, &writefds))
397             {
398               fds[i].signaled = 1;
399               n--;
400             }
401         }
402     }
403   return count;
404 }