1 /* gpgme-w32spawn.c - Wrapper to spawn a process under Windows.
2 Copyright (C) 2008 g10 Code GmbH
4 This file is part of GPGME.
6 GPGME is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of
9 the License, or (at your option) any later version.
11 GPGME is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this program; if not, see <http://www.gnu.org/licenses/>.
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
34 #ifdef HAVE_SYS_STAT_H
35 # include <sys/stat.h>
41 /* Flag values as used by gpgme. */
42 #define IOSPAWN_FLAG_ALLOW_SET_FG 1
45 /* Name of this program. */
46 #define PGM "gpgme-w32spawn"
50 struct spawn_fd_item_s
60 build_commandline (char **argv)
67 /* We have to quote some things because under Windows the program
68 parses the commandline and does some unquoting. We enclose the
69 whole argument in double-quotes, and escape literal double-quotes
70 as well as backslashes with a backslash. We end up with a
71 trailing space at the end of the line, but that is harmless. */
72 for (i = 0; argv[i]; i++)
75 /* The leading double-quote. */
79 /* An extra one for each literal that must be escaped. */
80 if (*p == '\\' || *p == '"')
85 /* The trailing double-quote and the delimiter. */
88 /* And a trailing zero. */
94 for (i = 0; argv[i]; i++)
96 char *argvp = argv[i];
101 if (*argvp == '\\' || *argvp == '"')
115 my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
117 SECURITY_ATTRIBUTES sec_attr;
118 PROCESS_INFORMATION pi =
120 NULL, /* returns process handle */
121 0, /* returns primary thread handle */
126 char *envblock = NULL;
127 int cr_flags = CREATE_DEFAULT_ERROR_MODE
128 | GetPriorityClass (GetCurrentProcess ());
132 int duped_stdout = 0;
133 int duped_stderr = 0;
134 HANDLE hnul = INVALID_HANDLE_VALUE;
141 fprintf (stderr, PGM": argv[%2i] = %s\n", i, argv[i]);
145 memset (&sec_attr, 0, sizeof sec_attr);
146 sec_attr.nLength = sizeof sec_attr;
147 sec_attr.bInheritHandle = FALSE;
149 arg_string = build_commandline (argv);
153 memset (&si, 0, sizeof si);
155 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
156 si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
157 si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
158 si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
159 si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
161 fprintf (stderr, PGM": spawning: %s\n", arg_string);
163 for (i = 0; fd_list[i].handle != -1; i++)
165 /* The handle already is inheritable. */
166 if (fd_list[i].dup_to == 0)
168 si.hStdInput = (HANDLE) fd_list[i].peer_name;
170 fprintf (stderr, PGM": dup 0x%x to stdin\n", fd_list[i].peer_name);
172 else if (fd_list[i].dup_to == 1)
174 si.hStdOutput = (HANDLE) fd_list[i].peer_name;
176 fprintf (stderr, PGM": dup 0x%x to stdout\n", fd_list[i].peer_name);
178 else if (fd_list[i].dup_to == 2)
180 si.hStdError = (HANDLE) fd_list[i].peer_name;
182 fprintf (stderr, PGM":dup 0x%x to stderr\n", fd_list[i].peer_name);
186 if (!duped_stdin || !duped_stdout || !duped_stderr)
188 SECURITY_ATTRIBUTES sa;
190 memset (&sa, 0, sizeof sa);
191 sa.nLength = sizeof sa;
192 sa.bInheritHandle = TRUE;
193 hnul = CreateFile ("nul",
194 GENERIC_READ|GENERIC_WRITE,
195 FILE_SHARE_READ|FILE_SHARE_WRITE,
198 FILE_ATTRIBUTE_NORMAL,
200 if (hnul == INVALID_HANDLE_VALUE)
203 /* FIXME: Should translate the error code. */
207 /* Make sure that the process has a connected stdin. */
210 /* Make sure that the process has a connected stdout. */
212 si.hStdOutput = hnul;
213 /* We normally don't want all the normal output. */
218 cr_flags |= CREATE_SUSPENDED;
219 cr_flags |= DETACHED_PROCESS;
220 if (!CreateProcessA (argv[0],
222 &sec_attr, /* process security attributes */
223 &sec_attr, /* thread security attributes */
224 TRUE, /* inherit handles */
225 cr_flags, /* creation flags */
226 envblock, /* environment */
227 NULL, /* use current drive/directory */
228 &si, /* startup information */
229 &pi)) /* returns process information */
232 /* FIXME: Should translate the error code. */
239 /* Close the /dev/nul handle if used. */
240 if (hnul != INVALID_HANDLE_VALUE)
243 for (i = 0; fd_list[i].handle != -1; i++)
244 CloseHandle ((HANDLE) fd_list[i].handle);
246 if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
248 static int initialized;
249 static BOOL (WINAPI * func)(DWORD);
254 /* Available since W2000; thus we dynload it. */
256 handle = LoadLibrary ("user32.dll");
259 func = GetProcAddress (handle, "AllowSetForegroundWindow");
261 FreeLibrary (handle);
267 int rc = func (pi.dwProcessId);
268 fprintf (stderr, PGM": AllowSetForegroundWindow(%d): rc=%d\n",
269 (int)pi.dwProcessId, rc);
273 ResumeThread (pi.hThread);
274 CloseHandle (pi.hThread);
275 CloseHandle (pi.hProcess);
284 translate_get_from_file (const char *trans_file,
285 struct spawn_fd_item_s *fd_list,
286 unsigned int *r_flags)
288 /* Hold roughly MAX_TRANS triplets of 64 bit numbers in hex
289 notation: "0xFEDCBA9876543210". 10*19*4 - 1 = 759. This plans
290 ahead for a time when a HANDLE is 64 bit. */
291 #define BUFFER_MAX 810
293 char line[BUFFER_MAX + 1];
301 fd = open (trans_file, O_RDONLY);
305 /* We always read one line from stdin. */
306 res = read (fd, line, BUFFER_MAX);
311 line[BUFFER_MAX] = '\0';
312 linep = strchr (line, '\n');
315 if (linep > line && linep[-1] == '\r')
321 /* Now start to read mapping pairs. */
322 for (idx = 0; idx < MAX_TRANS; idx++)
330 /* FIXME: Maybe could use scanf. */
331 while (isspace (*((unsigned char *)linep)))
335 if (!idx && *linep == '~')
337 /* Spawn flags have been passed. */
339 *r_flags = strtoul (linep, &tail, 0);
340 if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
344 while (isspace (*((unsigned char *)linep)))
350 from = strtoul (linep, &tail, 0);
351 if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
355 while (isspace (*linep))
359 dup_to = strtol (linep, &tail, 0);
360 if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
364 while (isspace (*linep))
368 to = strtoul (linep, &tail, 0);
369 if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
373 while (isspace (*linep))
377 loc = strtoul (linep, &tail, 0);
378 if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
382 fd_list[idx].handle = from;
383 fd_list[idx].dup_to = dup_to;
384 fd_list[idx].peer_name = to;
385 fd_list[idx].arg_loc = loc;
387 fd_list[idx].handle = -1;
388 fd_list[idx].dup_to = -1;
389 fd_list[idx].peer_name = -1;
390 fd_list[idx].arg_loc = 0;
395 /* Read the translated handles from TRANS_FILE and do a substitution
396 in ARGV. Returns the new argv and the list of substitutions in
397 FD_LIST (which must be MAX_TRANS+1 large). */
399 translate_handles (const char *trans_file, const char * const *argv,
400 struct spawn_fd_item_s *fd_list, unsigned int *r_flags)
407 res = translate_get_from_file (trans_file, fd_list, r_flags);
411 for (idx = 0; argv[idx]; idx++)
413 args = malloc (sizeof (*args) * (idx + 1));
414 for (idx = 0; argv[idx]; idx++)
416 args[idx] = strdup (argv[idx]);
423 for (idx = 0; fd_list[idx].handle != -1; idx++)
428 aidx = fd_list[idx].arg_loc;
434 fprintf (stderr, PGM": translation file does not match args\n");
438 args[aidx] = malloc (sizeof (buf));
439 /* We currently disable translation for stdin/stdout/stderr. We
440 assume that the spawned program handles 0/1/2 specially
441 already. FIXME: Check if this is true. */
442 if (!args[idx] || fd_list[idx].dup_to != -1)
445 /* NOTE: Here is the part where application specific knowledge
446 comes in. GPGME/GnuPG uses two forms of descriptor
447 specification, a plain number and a "-&" form. */
448 if (argv[aidx][0] == '-' && argv[aidx][1] == '&')
449 snprintf (args[aidx], sizeof (buf), "-&%d", fd_list[idx].peer_name);
451 snprintf (args[aidx], sizeof (buf), "%d", fd_list[idx].peer_name);
458 main (int argc, const char * const *argv)
462 struct spawn_fd_item_s fd_list[MAX_TRANS + 1];
471 argv_spawn = translate_handles (argv[1], &argv[2], fd_list, &flags);
478 /* Using execv does not replace the existing program image, but
479 spawns a new one and daemonizes it, confusing the command line
480 interpreter. So we have to use spawnv. */
481 rc = my_spawn (argv_spawn, fd_list, flags);
484 fprintf (stderr, PGM": executing `%s' failed: %s\n",
485 argv[0], strerror (errno));
492 fprintf (stderr, PGM": internal error\n");
493 /* Always try to delete the temporary file. */
496 if (DeleteFile (argv[1]) == 0)
497 fprintf (stderr, PGM": failed to delete %s: ec=%ld\n",
498 argv[1], GetLastError ());