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