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