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