1
/* exechelp.c - fork and exec helpers
2
* Copyright (C) 2004 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
6
* GnuPG 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.
11
* GnuPG 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.
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
33
#ifndef HAVE_W32_SYSTEM
41
/* Define to 1 do enable debugging. */
42
#define DEBUG_W32_SPAWN 1
45
#ifdef _POSIX_OPEN_MAX
46
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
48
#define MAX_OPEN_FDS 20
51
/* We have the usual problem here: Some modules are linked against pth
52
and some are not. However we want to use pth_fork and pth_waitpid
53
here. Using a weak symbol works but is not portable - we should
54
provide a an explicit dummy pth module instead of using the
58
#pragma weak pth_waitpid
62
#ifdef HAVE_W32_SYSTEM
63
/* We assume that a HANDLE can be represented by an int which should
64
be true for all i386 systems (HANDLE is defined as void *) and
65
these are the only systems for which Windows is available. Further
66
we assume that -1 denotes an invalid handle. */
67
# define fd_to_handle(a) ((HANDLE)(a))
68
# define handle_to_fd(a) ((int)(a))
69
# define pid_to_handle(a) ((HANDLE)(a))
70
# define handle_to_pid(a) ((int)(a))
74
#ifdef HAVE_W32_SYSTEM
75
/* Build a command line for use with W32's CreateProcess. On success
76
CMDLINE gets the address of a newly allocated string. */
78
build_w32_commandline (const char *pgmname, const char **argv, char **cmdline)
86
for (i=0; (s=argv[i]); i++)
88
n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
91
n++; /* Need to double inner quotes. */
95
buf = p = xtrymalloc (n);
97
return gpg_error_from_errno (errno);
99
/* fixme: PGMNAME may not contain spaces etc. */
100
p = stpcpy (p, pgmname);
101
for (i=0; argv[i]; i++)
103
if (!*argv[i]) /* Empty string. */
104
p = stpcpy (p, " \"\"");
105
else if (strpbrk (argv[i], " \t\n\v\f\""))
107
p = stpcpy (p, " \"");
108
for (s=argv[i]; *s; s++)
118
p = stpcpy (stpcpy (p, " "), argv[i]);
124
#endif /*HAVE_W32_SYSTEM*/
127
#ifdef HAVE_W32_SYSTEM
128
/* Create pipe where the write end is inheritable. */
130
create_inheritable_pipe (int filedes[2])
133
SECURITY_ATTRIBUTES sec_attr;
135
memset (&sec_attr, 0, sizeof sec_attr );
136
sec_attr.nLength = sizeof sec_attr;
137
sec_attr.bInheritHandle = FALSE;
139
if (!CreatePipe (&r, &w, &sec_attr, 0))
142
if (!DuplicateHandle (GetCurrentProcess(), w,
143
GetCurrentProcess(), &h, 0,
144
TRUE, DUPLICATE_SAME_ACCESS ))
146
log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1));
154
filedes[0] = handle_to_fd (r);
155
filedes[1] = handle_to_fd (w);
158
#endif /*HAVE_W32_SYSTEM*/
162
/* Fork and exec the PGMNAME, connect the file descriptor of INFILE to
163
stdin, write the output to OUTFILE, return a new stream in
164
STATUSFILE for stderr and the pid of the process in PID. The
165
arguments for the process are expected in the NULL terminated array
166
ARGV. The program name itself should not be included there. if
167
PREEXEC is not NULL, that function will be called right before the
170
Returns 0 on success or an error code. */
172
gnupg_spawn_process (const char *pgmname, const char *argv[],
173
FILE *infile, FILE *outfile,
174
void (*preexec)(void),
175
FILE **statusfile, pid_t *pid)
177
#ifdef HAVE_W32_SYSTEM
179
SECURITY_ATTRIBUTES sec_attr;
180
PROCESS_INFORMATION pi =
182
NULL, /* Returns process handle. */
183
0, /* Returns primary thread handle. */
184
0, /* Returns pid. */
190
int fd, fdout, rp[2];
192
/* Setup return values. */
197
fd = _get_osfhandle (fileno (infile));
198
fdout = _get_osfhandle (fileno (outfile));
199
if (fd == -1 || fdout == -1)
200
log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n");
202
/* Prepare security attributes. */
203
memset (&sec_attr, 0, sizeof sec_attr );
204
sec_attr.nLength = sizeof sec_attr;
205
sec_attr.bInheritHandle = FALSE;
207
/* Build the command line. */
208
err = build_w32_commandline (pgmname, argv, &cmdline);
213
if (create_inheritable_pipe (rp))
215
err = gpg_error (GPG_ERR_GENERAL);
216
log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
221
/* Start the process. Note that we can't run the PREEXEC function
222
because this would change our own environment. */
223
memset (&si, 0, sizeof si);
225
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
226
si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
227
si.hStdInput = fd_to_handle (fd);
228
si.hStdOutput = fd_to_handle (fdout);
229
si.hStdError = fd_to_handle (rp[1]);
231
cr_flags = (CREATE_DEFAULT_ERROR_MODE
232
| GetPriorityClass (GetCurrentProcess ())
234
log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
235
if (!CreateProcess (pgmname, /* Program to start. */
236
cmdline, /* Command line arguments. */
237
&sec_attr, /* Process security attributes. */
238
&sec_attr, /* Thread security attributes. */
239
TRUE, /* Inherit handles. */
240
cr_flags, /* Creation flags. */
241
NULL, /* Environment. */
242
NULL, /* Use current drive/directory. */
243
&si, /* Startup information. */
244
&pi /* Returns process information. */
247
log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
249
CloseHandle (fd_to_handle (rp[0]));
250
CloseHandle (fd_to_handle (rp[1]));
251
return gpg_error (GPG_ERR_GENERAL);
256
/* Close the other end of the pipe. */
257
CloseHandle (fd_to_handle (rp[1]));
259
log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
260
" dwProcessID=%d dwThreadId=%d\n",
261
pi.hProcess, pi.hThread,
262
(int) pi.dwProcessId, (int) pi.dwThreadId);
264
/* Process ha been created suspended; resume it now. */
265
ResumeThread (pi.hThread);
266
CloseHandle (pi.hThread);
271
x = _open_osfhandle (rp[0], 0);
273
log_error ("failed to translate osfhandle %p\n", (void*)rp[0] );
276
log_debug ("_open_osfhandle %p yields %d\n", (void*)fd, x );
277
*statusfile = fdopen (x, "r");
282
err = gpg_error_from_errno (errno);
283
log_error (_("can't fdopen pipe for reading: %s\n"), gpg_strerror (err));
284
CloseHandle (pi.hProcess);
288
*pid = handle_to_pid (pi.hProcess);
291
#else /* !HAVE_W32_SYSTEM */
293
int fd, fdout, rp[2];
299
fd = fileno (infile);
300
fdout = fileno (outfile);
301
if (fd == -1 || fdout == -1)
302
log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n");
306
err = gpg_error_from_errno (errno);
307
log_error (_("error creating a pipe: %s\n"), strerror (errno));
312
*pid = pth_fork? pth_fork () : fork ();
316
if (*pid == (pid_t)(-1))
318
err = gpg_error_from_errno (errno);
319
log_error (_("error forking process: %s\n"), strerror (errno));
331
/* Create the command line argument array. */
332
for (i=0; argv[i]; i++)
334
arg_list = xcalloc (i+2, sizeof *arg_list);
335
arg_list[0] = strrchr (pgmname, '/');
339
arg_list[0] = xstrdup (pgmname);
340
for (i=0,j=1; argv[i]; i++, j++)
341
arg_list[j] = (char*)argv[i];
343
/* Connect the infile to stdin. */
344
if (fd != 0 && dup2 (fd, 0) == -1)
345
log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
347
/* Connect the outfile to stdout. */
348
if (fdout != 1 && dup2 (fdout, 1) == -1)
349
log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
351
/* Connect stderr to our pipe. */
352
if (rp[1] != 2 && dup2 (rp[1], 2) == -1)
353
log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
355
/* Close all other files. */
356
n = sysconf (_SC_OPEN_MAX);
359
for (i=3; i < n; i++)
365
execv (pgmname, arg_list);
366
/* No way to print anything, as we have closed all streams. */
373
*statusfile = fdopen (rp[0], "r");
376
err = gpg_error_from_errno (errno);
377
log_error (_("can't fdopen pipe for reading: %s\n"), strerror (errno));
378
kill (*pid, SIGTERM);
384
#endif /* !HAVE_W32_SYSTEM */
388
/* Wait for the process identified by PID to terminate. PGMNAME should
389
be the same as suplieed to the spawn fucntion and is only used for
390
diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for
391
any failures of the spawned program or other error codes.*/
393
gnupg_wait_process (const char *pgmname, pid_t pid)
397
#ifdef HAVE_W32_SYSTEM
398
HANDLE proc = fd_to_handle (pid);
402
if (pid == (pid_t)(-1))
403
return gpg_error (GPG_ERR_INV_VALUE);
405
/* FIXME: We should do a pth_waitpid here. However this has not yet
406
been implemented. A special W32 pth system call would even be
408
code = WaitForSingleObject (proc, INFINITE);
412
log_error (_("waiting for process %d to terminate failed: %s\n"),
413
(int)pid, w32_strerror (-1));
414
ec = GPG_ERR_GENERAL;
418
if (!GetExitCodeProcess (proc, &exc))
420
log_error (_("error getting exit code of process %d: %s\n"),
421
(int)pid, w32_strerror (-1) );
422
ec = GPG_ERR_GENERAL;
426
log_error (_("error running `%s': exit status %d\n"),
428
ec = GPG_ERR_GENERAL;
435
log_error ("WaitForSingleObject returned unexpected "
436
"code %d for pid %d\n", code, (int)pid );
437
ec = GPG_ERR_GENERAL;
441
#else /* !HAVE_W32_SYSTEM */
444
if (pid == (pid_t)(-1))
445
return gpg_error (GPG_ERR_INV_VALUE);
448
i = pth_waitpid ? pth_waitpid (pid, &status, 0) : waitpid (pid, &status, 0);
450
while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
453
if (i == (pid_t)(-1))
455
log_error (_("waiting for process %d to terminate failed: %s\n"),
456
(int)pid, strerror (errno));
457
ec = gpg_err_code_from_errno (errno);
459
else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
461
log_error (_("error running `%s': probably not installed\n"), pgmname);
462
ec = GPG_ERR_CONFIGURATION;
464
else if (WIFEXITED (status) && WEXITSTATUS (status))
466
log_error (_("error running `%s': exit status %d\n"), pgmname,
467
WEXITSTATUS (status));
468
ec = GPG_ERR_GENERAL;
470
else if (!WIFEXITED (status))
472
log_error (_("error running `%s': terminated\n"), pgmname);
473
ec = GPG_ERR_GENERAL;
477
#endif /* !HAVE_W32_SYSTEM */
479
return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);