39e5082657305451c311ec55a28c7f0a588fa0f7
[gpgme.git] / gpgme / w32-io.c
1 /* w32-io.c - W32 API 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 #ifdef 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 <unistd.h>
32 #include <signal.h>
33 #include <fcntl.h>
34 #include <windows.h>
35
36 #include "util.h"
37 #include "io.h"
38
39 #define DEBUG_SELECT_ENABLED 1
40
41 #if DEBUG_SELECT_ENABLED
42 # define DEBUG_SELECT(a) fprintf a
43 #else
44 # define DEBUG_SELECT(a) do { } while(0)
45 #endif
46
47
48
49 /* 
50  * We assume that a HANDLE can be represented by an int which should be true
51  * for all i386 systems (HANDLE is defined as void *) and these are the only
52  * systems for which Windows is available.
53  * Further we assume that -1 denotes an invalid handle.
54  */
55
56 #define fd_to_handle(a)  ((HANDLE)(a))
57 #define handle_to_fd(a)  ((int)(a))
58 #define pid_to_handle(a) ((HANDLE)(a))
59 #define handle_to_pid(a) ((int)(a))
60
61
62 int
63 _gpgme_io_read ( int fd, void *buffer, size_t count )
64 {
65     int nread = 0;
66     HANDLE h = fd_to_handle (fd);
67
68     if ( !ReadFile ( h, buffer, count, &nread, NULL) ) {
69         fprintf (stderr, "** ReadFile failed: ec=%d\n", (int)GetLastError ());
70         return -1;
71     }
72
73     return nread;
74 }
75
76
77 int
78 _gpgme_io_write ( int fd, const void *buffer, size_t count )
79 {
80     int nwritten;
81     HANDLE h = fd_to_handle (fd);
82
83     if ( !WriteFile ( h, buffer, count, &nwritten, NULL) ) {
84         fprintf (stderr, "** WriteFile failed: ec=%d\n", (int)GetLastError ());
85         return -1;
86     }
87
88     return nwritten;
89 }
90
91 int
92 _gpgme_io_pipe ( int filedes[2] )
93 {
94     HANDLE r, w;
95     
96     if (!CreatePipe ( &r, &w, NULL, 0))
97         return -1;
98     filedes[0] = handle_to_fd (r);
99     filedes[1] = handle_to_fd (w);
100     return 0;
101 }
102
103 int
104 _gpgme_io_close ( int fd )
105 {
106     if ( fd == -1 )
107         return -1;
108     return CloseHandle (fd_to_handle(fd)) ? 0 : -1;
109 }
110
111
112 int
113 _gpgme_io_set_nonblocking ( int fd )
114 {
115     return 0;
116 }
117
118
119 static char *
120 build_commandline ( char **argv )
121 {
122     int i, n = 0;
123     char *buf, *p;
124
125     /* FIXME: we have to quote some things because under Windows the 
126      * program parses the commandline and does some unquoting */
127     for (i=0; argv[i]; i++)
128         n += strlen (argv[i]) + 1;
129     n += 5;                     /* "gpg " */
130     buf = p = xtrymalloc (n);
131     if ( !buf )
132         return NULL;
133     p = stpcpy (p, "gpg");
134     for (i = 0; argv[i]; i++)
135         p = stpcpy (stpcpy (p, " "), argv[i]);
136
137     return buf;
138 }
139
140
141 int
142 _gpgme_io_spawn ( const char *path, char **argv,
143                   struct spawn_fd_item_s *fd_child_list,
144                   struct spawn_fd_item_s *fd_parent_list )
145 {
146     SECURITY_ATTRIBUTES sec_attr;
147     PROCESS_INFORMATION pi = {
148         NULL,      /* returns process handle */
149         0,         /* returns primary thread handle */
150         0,         /* returns pid */
151         0         /* returns tid */
152     };
153     STARTUPINFO si = {
154         0, NULL, NULL, NULL,
155         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156         NULL, NULL, NULL, NULL
157     };
158     char *envblock = NULL;
159     int cr_flags = CREATE_DEFAULT_ERROR_MODE
160                  | GetPriorityClass (GetCurrentProcess ());
161     int i, rc;
162     char *arg_string;
163     HANDLE save_stdout;
164     HANDLE outputfd[2], statusfd[2], inputfd[2];
165
166     sec_attr.nLength = sizeof (sec_attr);
167     sec_attr.bInheritHandle = FALSE;
168     sec_attr.lpSecurityDescriptor = NULL;
169
170
171     arg_string = build_commandline ( argv );
172     if (!arg_string )
173         return -1; 
174
175     si.cb = sizeof (si);
176     si.dwFlags = STARTF_USESTDHANDLES;
177     si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
178     si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
179     si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
180     if (!SetHandleInformation (si.hStdOutput,
181                                HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
182         fprintf (stderr, "** SHI 1 failed: ec=%d\n", (int) GetLastError ());
183     }
184     if (!SetHandleInformation (si.hStdError,
185                                HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
186         fprintf (stderr, "** SHI 2 failed: ec=%d\n", (int) GetLastError ());
187     }
188     
189
190     fputs ("** CreateProcess ...\n", stderr);
191     fprintf (stderr, "** args=`%s'\n", arg_string);
192     fflush (stderr);
193     if ( !CreateProcessA (GPG_PATH,
194                           arg_string,
195                           &sec_attr,     /* process security attributes */
196                           &sec_attr,     /* thread security attributes */
197                           TRUE,          /* inherit handles */
198                           cr_flags,      /* creation flags */
199                           envblock,      /* environment */
200                           NULL,          /* use current drive/directory */
201                           &si,           /* startup information */
202                           &pi            /* returns process information */
203         ) ) {
204         fprintf (stderr, "** CreateProcess failed: ec=%d\n",
205                  (int) GetLastError ());
206         fflush (stderr);
207         xfree (arg_string);
208         return -1;
209     }
210
211     /* .dup_to is not used in the parent list */
212     for (i=0; fd_parent_list[i].fd != -1; i++ ) {
213         CloseHandle ( fd_to_handle (fd_parent_list[i].fd) );
214     }
215
216     fprintf (stderr, "** CreateProcess ready\n");
217     fprintf (stderr, "**   hProcess=%p  hThread=%p\n",
218              pi.hProcess, pi.hThread);
219     fprintf (stderr, "**   dwProcessID=%d dwThreadId=%d\n",
220              (int) pi.dwProcessId, (int) pi.dwThreadId);
221     fflush (stderr);
222
223     return handle_to_pid (pi.hProcess);
224 }
225
226
227 int
228 _gpgme_io_waitpid ( int pid, int hang, int *r_status, int *r_signal )
229 {
230     HANDLE proc = fd_to_handle (pid);
231     int code, exc, ret = 0;
232
233     *r_status = 0;
234     *r_signal = 0;
235     code = WaitForSingleObject ( proc, hang? INFINITE : NULL );
236     switch (code) {
237       case WAIT_FAILED:
238         fprintf (stderr, "** WFSO pid=%d failed: %d\n",
239                  (int)pid, (int)GetLastError () );
240         break;
241
242       case WAIT_OBJECT_0:
243         if (!GetExitCodeProcess (proc, &exc)) {
244             fprintf (stderr, "** GECP pid=%d failed: ec=%d\n",
245                      (int)pid, (int)GetLastError () );
246             *r_status = 4; 
247         }
248         else {
249             fprintf (stderr, "** GECP pid=%d exit code=%d\n",
250                         (int)pid,  exc);
251             *r_status = exc;
252         }
253         ret = 1;
254         break;
255
256       case WAIT_TIMEOUT:
257         fprintf (stderr, "** WFSO pid=%d timed out\n", (int)pid);
258         break;
259
260       default:
261         fprintf (stderr, "** WFSO pid=%d returned %d\n", (int)pid, code );
262         break;
263     }
264     return ret;
265 }
266
267
268 /*
269  * Select on the list of fds.
270  * Returns: -1 = error
271  *           0 = timeout or nothing to select
272  *          >0 = number of signaled fds
273  */
274 int
275 _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds )
276 {
277     HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
278     int code, nwait;
279     int i, any, ret;
280
281     DEBUG_SELECT ((stderr, "gpgme:select on [ "));
282     any = 0;
283     nwait = 0;
284     for ( i=0; i < nfds; i++ ) {
285         if ( fds[i].fd == -1 ) 
286             continue;
287         if ( fds[i].for_read || fds[i].for_write ) {
288             if ( nwait >= DIM (waitbuf) ) {
289                 DEBUG_SELECT ((stderr, "oops ]\n" ));
290                 fprintf (stderr, "** Too many objects for WFMO!\n" );
291                 return -1;
292             }
293             else {
294                 waitbuf[nwait++] = fd_to_handle (fds[i].fd);
295                 DEBUG_SELECT ((stderr, "%c%d ",
296                                fds[i].for_read? 'r':'w',fds[i].fd ));
297                 any = 1;
298             }
299         }
300         fds[i].signaled = 0;
301     }
302     DEBUG_SELECT ((stderr, "]\n" ));
303     if (!any)
304         return 0;
305
306     ret = 0;
307     code = WaitForMultipleObjects ( nwait, waitbuf, 0, 1000 );
308     if (code == WAIT_FAILED ) {
309         fprintf (stderr, "** WFMO failed: %d\n",  (int)GetLastError () );
310         ret = -1;
311     }
312     else if ( code == WAIT_TIMEOUT ) {
313         fprintf (stderr, "** WFMO timed out\n" );
314     }  
315     else if ( code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait ) {
316         /* This WFMO is a really silly function:  It does return either
317          * the index of the signaled object or if 2 objects have been
318          * signalled at the same time, the index of the object with the
319          * lowest object is returned - so and how do we find out
320          * how many objects have been signaled???.
321          * The only solution I can imagine is to test each object starting
322          * with the returned index individually - how dull.
323          */
324         any = 0;
325         for (i=code - WAIT_OBJECT_0; i < nwait; i++ ) {
326             if (WaitForSingleObject ( waitbuf[i], NULL ) == WAIT_OBJECT_0) {
327                 fds[i].signaled = 1;
328                 any = 1;
329             }
330         }
331         if (any)
332             ret = 1;
333         else {
334             fprintf (stderr,
335                      "** Oops: No signaled objects found after WFMO\n");
336             ret = -1;
337         }
338     }
339     else {
340         fprintf (stderr, "** WFMO returned %d\n", code );
341         ret = -1;
342     }
343
344     return ret;
345 }
346
347 #endif /*HAVE_DOSISH_SYSTEM*/
348
349
350
351
352
353
354
355
356