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
);