FD are now released and a major bug has been fixed.
[gpgme.git] / gpgme / posix-io.c
1 /* posix-io.c - Posix I/O functions
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #ifndef HAVE_DOSISH_SYSTEM
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <signal.h>
33 #include <fcntl.h>
34 #include "syshdr.h"
35
36 #include "util.h"
37 #include "io.h"
38
39 static struct {
40     void (*handler)(int,void*);
41     void *value;
42 } notify_table[256];
43
44
45 int
46 _gpgme_io_read ( int fd, void *buffer, size_t count )
47 {
48     int nread;
49
50     DEBUG2 ("fd %d: about to read %d bytes\n", fd, (int)count );
51     do {
52         nread = read (fd, buffer, count);
53     } while (nread == -1 && errno == EINTR );
54     DEBUG2 ("fd %d: got %d bytes\n", fd, nread );
55     return nread;
56 }
57
58
59 int
60 _gpgme_io_write ( int fd, const void *buffer, size_t count )
61 {
62     int nwritten;
63
64     DEBUG2 ("fd %d: about to write %d bytes\n", fd, (int)count );
65     do {
66         nwritten = write (fd, buffer, count);
67     } while (nwritten == -1 && errno == EINTR );
68     DEBUG2 ("fd %d:          wrote %d bytes\n", fd, (int)nwritten );
69     return nwritten;
70 }
71
72 int
73 _gpgme_io_pipe ( int filedes[2], int inherit_idx )
74 {
75     /* we don't need inherit_idx in this implementation */
76     return pipe ( filedes );
77 }
78
79 int
80 _gpgme_io_close ( int fd )
81 {
82     if ( fd == -1 )
83         return -1;
84     /* first call the notify handler */
85     DEBUG1 ("closing fd %d", fd );
86     if ( fd >= 0 && fd < DIM (notify_table) ) {
87         if (notify_table[fd].handler) {
88             notify_table[fd].handler (fd, notify_table[fd].value);
89             notify_table[fd].handler = NULL;
90             notify_table[fd].value = NULL;
91         }
92     }
93     /* then do the close */    
94     return close (fd);
95 }
96
97 int
98 _gpgme_io_set_close_notify (int fd, void (*handler)(int, void*), void *value)
99 {
100     assert (fd != -1);
101
102     if ( fd < 0 || fd >= DIM (notify_table) )
103         return -1;
104     DEBUG1 ("set notification for fd %d", fd );
105     notify_table[fd].handler = handler;
106     notify_table[fd].value = value;
107     return 0;
108 }
109
110
111 int
112 _gpgme_io_set_nonblocking ( int fd )
113 {
114     int flags;
115
116     flags = fcntl (fd, F_GETFL, 0);
117     if (flags == -1)
118         return -1;
119     flags |= O_NONBLOCK;
120     return fcntl (fd, F_SETFL, flags);
121 }
122
123
124 int
125 _gpgme_io_spawn ( const char *path, char **argv,
126                   struct spawn_fd_item_s *fd_child_list,
127                   struct spawn_fd_item_s *fd_parent_list )
128 {
129     static volatile int fixed_signals;
130     pid_t pid;
131     int i;
132
133     if ( !fixed_signals ) { 
134         struct sigaction act;
135         
136         sigaction( SIGPIPE, NULL, &act );
137         if( act.sa_handler == SIG_DFL ) {
138             act.sa_handler = SIG_IGN;
139             sigemptyset( &act.sa_mask );
140             act.sa_flags = 0;
141             sigaction( SIGPIPE, &act, NULL);
142         }
143         fixed_signals = 1;
144         /* fixme: This is not really MT safe */
145     }
146
147     
148     pid = fork ();
149     if (pid == -1) 
150         return -1;
151
152     if ( !pid ) { /* child */
153         int duped_stdin = 0;
154         int duped_stderr = 0;
155
156         /* first close all fds which will not be duped */
157         for (i=0; fd_child_list[i].fd != -1; i++ ) {
158             if (fd_child_list[i].dup_to == -1 )
159                 close (fd_child_list[i].fd);
160         }
161         /* and now dup and close the rest */
162         for (i=0; fd_child_list[i].fd != -1; i++ ) {
163             if (fd_child_list[i].dup_to != -1 ) {
164                 if ( dup2 (fd_child_list[i].fd,
165                            fd_child_list[i].dup_to ) == -1 ) {
166                     DEBUG1 ("dup2 failed in child: %s\n", strerror (errno));
167                     _exit (8);
168                 }
169                 if ( fd_child_list[i].dup_to == 0 )
170                     duped_stdin=1;
171                 if ( fd_child_list[i].dup_to == 2 )
172                     duped_stderr=1;
173                 close (fd_child_list[i].fd);
174             }
175         }
176
177         if( !duped_stdin || !duped_stderr ) {
178             int fd = open ( "/dev/null", O_RDWR );
179             if ( fd == -1 ) {
180                 DEBUG1 ("can't open `/dev/null': %s\n", strerror (errno) );
181                 _exit (8);
182             }
183             /* Make sure that the process has a connected stdin */
184             if ( !duped_stdin ) {
185                 if ( dup2 ( fd, 0 ) == -1 ) {
186                     DEBUG1("dup2(/dev/null, 0) failed: %s\n",
187                              strerror (errno) );
188                     _exit (8);
189                 }
190             }
191             if ( !duped_stderr ) {
192                 if ( dup2 ( fd, 2 ) == -1 ) {
193                     DEBUG1 ("dup2(dev/null, 2) failed: %s\n", strerror (errno));
194                     _exit (8);
195                 }
196             }
197             close (fd);
198         }
199
200         execv ( path, argv );
201         /* Hmm: in that case we could write a special status code to the
202          * status-pipe */
203         DEBUG1 ("exec of `%s' failed\n", path );
204         _exit (8);
205     } /* end child */
206     
207     /* .dup_to is not used in the parent list */
208     for (i=0; fd_parent_list[i].fd != -1; i++ ) {
209         close (fd_parent_list[i].fd);
210     }
211
212     return (int)pid;
213 }
214
215
216 int
217 _gpgme_io_waitpid ( int pid, int hang, int *r_status, int *r_signal )
218 {
219     int status;
220
221     *r_status = 0;
222     *r_signal = 0;
223     if ( waitpid ( pid, &status, hang? 0 : WNOHANG ) == pid ) {
224         if ( WIFSIGNALED (status) ) {
225             *r_status = 4; /* Need some value here */
226             *r_signal = WTERMSIG (status);
227         }
228         else if ( WIFEXITED (status) ) {
229             *r_status = WEXITSTATUS (status);
230         }
231         else {
232             *r_status = 4; /* oops */
233         }
234         return 1;
235     }
236     return 0;
237 }
238
239 int
240 _gpgme_io_kill ( int pid, int hard )
241 {
242     return kill ( pid, hard? SIGKILL : SIGTERM );
243 }
244
245
246 /*
247  * Select on the list of fds.
248  * Returns: -1 = error
249  *           0 = timeout or nothing to select
250  *          >0 = number of signaled fds
251  */
252 int
253 _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds )
254 {
255     static fd_set readfds;
256     static fd_set writefds;
257     int any, i, max_fd, n, count;
258     struct timeval timeout = { 1, 0 }; /* Use a 1s timeout */
259     void *dbg_help;
260     
261     FD_ZERO ( &readfds );
262     FD_ZERO ( &writefds );
263     max_fd = 0;
264
265     DEBUG_BEGIN (dbg_help, "gpgme:select on [ ");
266     any = 0;
267     for ( i=0; i < nfds; i++ ) {
268         if ( fds[i].fd == -1 ) 
269             continue;
270         if ( fds[i].for_read ) {
271             assert ( !FD_ISSET ( fds[i].fd, &readfds ) );
272             FD_SET ( fds[i].fd, &readfds );
273             if ( fds[i].fd > max_fd )
274                 max_fd = fds[i].fd;
275             DEBUG_ADD1 (dbg_help, "r%d ", fds[i].fd );
276             any = 1;
277         }
278         else if ( fds[i].for_write ) {
279             assert ( !FD_ISSET ( fds[i].fd, &writefds ) );
280             FD_SET ( fds[i].fd, &writefds );
281             if ( fds[i].fd > max_fd )
282                 max_fd = fds[i].fd;
283             DEBUG_ADD1 (dbg_help, "w%d ", fds[i].fd );
284             any = 1;
285         }
286         fds[i].signaled = 0;
287     }
288     DEBUG_END (dbg_help, "]" );
289     if ( !any )
290         return 0;
291
292     do {
293         count = select ( max_fd+1, &readfds, &writefds, NULL, &timeout );
294     } while ( count < 0 && errno == EINTR);
295     if ( count < 0 ) {
296         DEBUG1 ("_gpgme_io_select failed: %s\n", strerror (errno) );
297         return -1; /* error */
298     }
299
300     DEBUG_BEGIN (dbg_help, "select OK [ " );
301     if (DEBUG_ENABLED(dbg_help)) {
302         for (i=0; i <= max_fd; i++ ) {
303             if (FD_ISSET (i, &readfds) )
304                 DEBUG_ADD1 (dbg_help, "r%d ", i );
305             if (FD_ISSET (i, &writefds) )
306                 DEBUG_ADD1 (dbg_help, "w%d ", i );
307         }
308         DEBUG_END (dbg_help, "]" );
309     }
310     
311     /* n is used to optimize it a little bit */
312     for ( n=count, i=0; i < nfds && n ; i++ ) {
313         if ( fds[i].fd == -1 ) 
314             ;
315         else if ( fds[i].for_read ) {
316             if ( FD_ISSET ( fds[i].fd, &readfds ) ) {
317                 fds[i].signaled = 1;
318                 n--;
319             }
320         }
321         else if ( fds[i].for_write ) {
322             if ( FD_ISSET ( fds[i].fd, &writefds ) ) {
323                 fds[i].signaled = 1;
324                 n--;
325             }
326         }
327     }
328     return count;
329 }
330
331
332 #endif /*!HAVE_DOSISH_SYSTEM*/
333
334
335