1 /* exechelp.c - fork and exec helpers
2 * Copyright (C) 2004, 2007, 2008 g10 Code GmbH
3 *
4 * This file is part of DirMngr.
5 *
6 * DirMngr 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 * DirMngr 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
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assert.h>
28 #include <signal.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31
32 #include <pth.h>
33
34 #ifndef HAVE_W32_SYSTEM
35 #include <sys/wait.h>
36 #endif
37
38 #include "util.h"
39 #include "i18n.h"
40 #include "exechelp.h"
41
42 /* Define to 1 do enable debugging. */
43 #define DEBUG_W32_SPAWN 0
44
45 #ifdef _POSIX_OPEN_MAX
46 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
47 #else
48 #define MAX_OPEN_FDS 20
49 #endif
50
51 #ifdef HAVE_W32_SYSTEM
52 /* We assume that a HANDLE can be represented by an int which should
53 be true for all i386 systems (HANDLE is defined as void *) and
54 these are the only systems for which Windows is available. Further
55 we assume that -1 denotes an invalid handle. */
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 #endif
61
62
63 #ifdef HAVE_W32_SYSTEM
64 /* Helper function to build_w32_commandline. */
65 static char *
build_w32_commandline_copy(char * buffer,const char * string)66 build_w32_commandline_copy (char *buffer, const char *string)
67 {
68 char *p = buffer;
69 const char *s;
70
71 if (!*string) /* Empty string. */
72 p = stpcpy (p, "\"\"");
73 else if (strpbrk (string, " \t\n\v\f\""))
74 {
75 /* Need to do some kind of quoting. */
76 p = stpcpy (p, "\"");
77 for (s=string; *s; s++)
78 {
79 *p++ = *s;
80 if (*s == '\"')
81 *p++ = *s;
82 }
83 *p++ = '\"';
84 *p = 0;
85 }
86 else
87 p = stpcpy (p, string);
88
89 return p;
90 }
91
92 /* Build a command line for use with W32's CreateProcess. On success
93 CMDLINE gets the address of a newly allocated string. */
94 static gpg_error_t
build_w32_commandline(const char * pgmname,const char * const * argv,char ** cmdline)95 build_w32_commandline (const char *pgmname, const char * const *argv,
96 char **cmdline)
97 {
98 int i, n;
99 const char *s;
100 char *buf, *p;
101
102 *cmdline = NULL;
103 n = 0;
104 s = pgmname;
105 n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */
106 for (; *s; s++)
107 if (*s == '\"')
108 n++; /* Need to double inner quotes. */
109 for (i=0; (s=argv[i]); i++)
110 {
111 n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */
112 for (; *s; s++)
113 if (*s == '\"')
114 n++; /* Need to double inner quotes. */
115 }
116 n++;
117
118 buf = p = xtrymalloc (n);
119 if (!buf)
120 return gpg_error_from_syserror ();
121
122 p = build_w32_commandline_copy (p, pgmname);
123 for (i=0; argv[i]; i++)
124 {
125 *p++ = ' ';
126 p = build_w32_commandline_copy (p, argv[i]);
127 }
128
129 *cmdline= buf;
130 return 0;
131 }
132 #endif /*HAVE_W32_SYSTEM*/
133
134
135 #ifndef HAVE_W32_SYSTEM
136 /* The exec core used right after the fork. This will never return. */
137 static void
do_exec(const char * pgmname,char * argv[],int fd_in,int fd_out,int fd_err)138 do_exec (const char *pgmname, char *argv[],
139 int fd_in, int fd_out, int fd_err)
140 {
141 char **arg_list;
142 int n, i, j;
143 int fds[3];
144
145 fds[0] = fd_in;
146 fds[1] = fd_out;
147 fds[2] = fd_err;
148
149 /* Create the command line argument array. */
150 i = 0;
151 if (argv)
152 while (argv[i])
153 i++;
154 arg_list = xcalloc (i+2, sizeof *arg_list);
155 arg_list[0] = strrchr (pgmname, '/');
156 if (arg_list[0])
157 arg_list[0]++;
158 else
159 arg_list[0] = xstrdup (pgmname);
160 if (argv)
161 for (i=0,j=1; argv[i]; i++, j++)
162 arg_list[j] = (char*)argv[i];
163
164 /* Connect the standard files. */
165 for (i = 0; i <= 2; i++)
166 {
167 if (fds[i] == -1)
168 {
169 fds[i] = open ("/dev/null", i ? O_WRONLY : O_RDONLY);
170 if (fds[i] == -1)
171 log_fatal ("failed to open `%s': %s\n",
172 "/dev/null", strerror (errno));
173 }
174 else if (fds[i] != i && dup2 (fds[i], i) == -1)
175 log_fatal ("dup2 std%s failed: %s\n",
176 i==0?"in":i==1?"out":"err", strerror (errno));
177 }
178
179 /* Close all other files. */
180 n = sysconf (_SC_OPEN_MAX);
181 if (n < 0)
182 n = MAX_OPEN_FDS;
183 for (i=3; i < n; i++)
184 close(i);
185 errno = 0;
186
187 execv (pgmname, arg_list);
188
189 /* No way to print anything, as we have closed all streams. */
190 _exit (127);
191 }
192 #endif /*!HAVE_W32_SYSTEM*/
193
194
195 /* Fork and exec the PGMNAME, connect the file descriptor of INFILE to
196 stdin, write the output to OUTFILE, return a new stream in
197 STATUSFILE for stderr and the pid of the process in PID. The
198 arguments for the process are expected in the NULL terminated array
199 ARGV. The program name itself should not be included there. if
200 PREEXEC is not NULL, that function will be called right before the
201 exec.
202
203 Returns 0 on success or an error code. */
204 gpg_error_t
dirmngr_spawn_process(const char * pgmname,char * argv[],int * fdout,int * fderr,pid_t * pid)205 dirmngr_spawn_process (const char *pgmname, char *argv[],
206 int *fdout, int *fderr, pid_t *pid)
207 {
208 #ifdef HAVE_W32_SYSTEM
209 gpg_error_t err;
210 SECURITY_ATTRIBUTES sec_attr;
211 PROCESS_INFORMATION pi =
212 {
213 NULL, /* Returns process handle. */
214 0, /* Returns primary thread handle. */
215 0, /* Returns pid. */
216 0 /* Returns tid. */
217 };
218 STARTUPINFO si;
219 int cr_flags;
220 char *cmdline;
221 int rp_stdout[2];
222 int rp_stderr[2];
223 HANDLE hnul = INVALID_HANDLE_VALUE;
224
225 /* Setup return values. */
226 *fdout = -1;
227 *fderr = -1;
228 *pid = (pid_t)(-1);
229
230 /* Build the command line. */
231 err = build_w32_commandline (pgmname, argv, &cmdline);
232 if (err)
233 return err;
234
235 /* Create a pipe. */
236 /* FIXME: We better replace it by the assuan pipe. */
237 if (pth_pipe (rp_stdout, 1))
238 {
239 err = gpg_error (GPG_ERR_GENERAL);
240 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
241 xfree (cmdline);
242 return err;
243 }
244
245 if (pth_pipe (rp_stderr, 1))
246 {
247 err = gpg_error (GPG_ERR_GENERAL);
248 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
249 xfree (cmdline);
250 CloseHandle (fd_to_handle (rp_stdout[0]));
251 CloseHandle (fd_to_handle (rp_stdout[1]));
252 return err;
253 }
254
255 /* Prepare security attributes. */
256 memset (&sec_attr, 0, sizeof sec_attr);
257 sec_attr.nLength = sizeof sec_attr;
258 sec_attr.bInheritHandle = TRUE;
259 hnul = CreateFile ("nul",
260 GENERIC_READ|GENERIC_WRITE,
261 FILE_SHARE_READ|FILE_SHARE_WRITE,
262 &sec_attr,
263 OPEN_EXISTING,
264 FILE_ATTRIBUTE_NORMAL,
265 NULL);
266 if (hnul == INVALID_HANDLE_VALUE)
267 {
268 err = gpg_error (GPG_ERR_GENERAL);
269 log_error (_("error opening nul: %s\n"), gpg_strerror (err));
270 xfree (cmdline);
271 CloseHandle (fd_to_handle (rp_stdout[0]));
272 CloseHandle (fd_to_handle (rp_stdout[1]));
273 CloseHandle (fd_to_handle (rp_stderr[0]));
274 CloseHandle (fd_to_handle (rp_stderr[1]));
275 return err;
276 }
277
278 /* Prepare security attributes. */
279 memset (&sec_attr, 0, sizeof sec_attr );
280 sec_attr.nLength = sizeof sec_attr;
281 sec_attr.bInheritHandle = FALSE;
282
283 /* Start the process. Note that we can't run the PREEXEC function
284 because this would change our own environment. */
285 memset (&si, 0, sizeof si);
286 si.cb = sizeof (si);
287 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
288 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
289 si.hStdInput = hnul;
290 si.hStdOutput = fd_to_handle (rp_stdout[1]);
291 si.hStdError = fd_to_handle (rp_stderr[1]);
292
293 cr_flags = (CREATE_DEFAULT_ERROR_MODE
294 | GetPriorityClass (GetCurrentProcess ())
295 | CREATE_SUSPENDED);
296 log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
297 if (!CreateProcess (pgmname, /* Program to start. */
298 cmdline, /* Command line arguments. */
299 &sec_attr, /* Process security attributes. */
300 &sec_attr, /* Thread security attributes. */
301 TRUE, /* Inherit handles. */
302 cr_flags, /* Creation flags. */
303 NULL, /* Environment. */
304 NULL, /* Use current drive/directory. */
305 &si, /* Startup information. */
306 &pi /* Returns process information. */
307 ))
308 {
309 log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
310 xfree (cmdline);
311 CloseHandle (fd_to_handle (rp_stdout[0]));
312 CloseHandle (fd_to_handle (rp_stdout[1]));
313 CloseHandle (fd_to_handle (rp_stderr[0]));
314 CloseHandle (fd_to_handle (rp_stderr[1]));
315 CloseHandle (hnul);
316 return gpg_error (GPG_ERR_GENERAL);
317 }
318 xfree (cmdline);
319 cmdline = NULL;
320
321 /* Close the other end of the pipe. */
322 CloseHandle (fd_to_handle (rp_stdout[1]));
323 CloseHandle (fd_to_handle (rp_stderr[1]));
324 CloseHandle (hnul);
325
326 log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
327 " dwProcessID=%d dwThreadId=%d\n",
328 pi.hProcess, pi.hThread,
329 (int) pi.dwProcessId, (int) pi.dwThreadId);
330
331 /* Process has been created suspended; resume it now. */
332 ResumeThread (pi.hThread);
333 CloseHandle (pi.hThread);
334
335 *fdout = handle_to_fd (rp_stdout[0]);
336 *fderr = handle_to_fd (rp_stderr[0]);
337 *pid = handle_to_pid (pi.hProcess);
338 return 0;
339
340 #else /* !HAVE_W32_SYSTEM */
341 gpg_error_t err;
342 int rp_stdout[2];
343 int rp_stderr[2];
344
345 *fdout = -1;
346 *fderr = -1;
347 *pid = (pid_t)(-1);
348
349 if (pipe (rp_stdout) == -1)
350 {
351 err = gpg_error_from_syserror ();
352 log_error (_("error creating a pipe: %s\n"), strerror (errno));
353 return err;
354 }
355
356 if (pipe (rp_stderr) == -1)
357 {
358 err = gpg_error_from_syserror ();
359 log_error (_("error creating a pipe: %s\n"), strerror (errno));
360 close (rp_stdout[0]);
361 close (rp_stdout[1]);
362 return err;
363 }
364
365 *pid = pth_fork ();
366 if (*pid == (pid_t)(-1))
367 {
368 err = gpg_error_from_syserror ();
369 log_error (_("error forking process: %s\n"), strerror (errno));
370 close (rp_stdout[0]);
371 close (rp_stdout[1]);
372 close (rp_stderr[0]);
373 close (rp_stderr[1]);
374 return err;
375 }
376
377 if (!*pid)
378 {
379 /* Run child. */
380 do_exec (pgmname, argv, -1, rp_stdout[1], rp_stderr[1]);
381 /*NOTREACHED*/
382 }
383
384 /* Parent. */
385 close (rp_stdout[1]);
386 close (rp_stderr[1]);
387
388 *fdout = rp_stdout[0];
389 *fderr = rp_stderr[0];
390
391 return 0;
392 #endif /* !HAVE_W32_SYSTEM */
393 }
394
395
396 /* If HANG is true, waits for the process identified by PID to exit.
397 If HANG is false, checks whether the process has terminated.
398 Return values:
399
400 GPG_ERR_NO_ERROR
401 The process exited. The exit code of process is then stored at
402 R_STATUS. An exit code of -1 indicates that the process
403 terminated abnormally (e.g. due to a signal).
404
405 GPG_ERR_TIMEOUT
406 The process is still running (returned only if HANG is false).
407
408 GPG_ERR_INV_VALUE
409 An invalid PID has been specified.
410
411 Other error codes may be returned as well. Unless otherwise noted,
412 -1 will be stored at R_STATUS. */
413 gpg_error_t
dirmngr_wait_process(pid_t pid,int hang,int * r_status)414 dirmngr_wait_process (pid_t pid, int hang, int *r_status)
415 {
416 gpg_err_code_t ec;
417
418 #ifdef HAVE_W32_SYSTEM
419 HANDLE proc = pid_to_handle (pid);
420 int code;
421 DWORD exc;
422
423 *r_status = -1;
424 if (pid == (pid_t)(-1))
425 return gpg_error (GPG_ERR_INV_VALUE);
426
427 /* FIXME: We should do a pth_waitpid here. However this has not yet
428 been implemented. A special W32 pth system call would even be
429 better. */
430 code = WaitForSingleObject (proc, hang ? INFINITE : 0);
431 switch (code)
432 {
433 case WAIT_TIMEOUT:
434 ec = GPG_ERR_TIMEOUT;
435 break;
436
437 case WAIT_FAILED:
438 log_error (_("waiting for process %d to terminate failed: %s\n"),
439 (int)pid, w32_strerror (-1));
440 ec = 0;
441 break;
442
443 case WAIT_OBJECT_0:
444 if (!GetExitCodeProcess (proc, &exc))
445 {
446 log_error (_("error getting exit code of process %d: %s\n"),
447 (int)pid, w32_strerror (-1) );
448 ec = GPG_ERR_GENERAL;
449 }
450 else
451 {
452 *r_status = exc;
453 if (exc)
454 log_error (_("error detected: exit status %d%s\n"),
455 *r_status, "");
456 ec = 0;
457 }
458 break;
459
460 default:
461 log_error ("WaitForSingleObject returned unexpected "
462 "code %d for pid %d\n", code, (int)pid );
463 ec = GPG_ERR_GENERAL;
464 break;
465 }
466
467 #else /* !HAVE_W32_SYSTEM */
468 int i;
469 int status;
470
471 *r_status = -1;
472
473 if (pid == (pid_t)(-1))
474 return gpg_error (GPG_ERR_INV_VALUE);
475
476 i = pth_waitpid (pid, &status, hang? 0 : WNOHANG);
477 if (i == (pid_t)(-1))
478 {
479 ec = gpg_err_code_from_syserror ();
480 log_error (_("waiting for process %d to terminate failed: %s\n"),
481 (int)pid, strerror (errno));
482 }
483 else if (i == 0)
484 {
485 /* The process is still running. */
486 ec = GPG_ERR_TIMEOUT;
487 }
488 else if (WIFEXITED (status))
489 {
490 ec = 0;
491 *r_status = WEXITSTATUS (status);
492 if (*r_status)
493 log_error (_("error detected: exit status %d%s\n"), *r_status,
494 *r_status == 127? _(" (program probably not installed)")
495 /* */ :"");
496 }
497 else
498 {
499 ec = 0;
500 log_error (_("error detected: terminated\n"));
501 }
502 #endif /* !HAVE_W32_SYSTEM */
503
504 return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
505
506 }
507
508
509 /* Kill the program PID with name PGMNAME (only used for
510 diagnostics). */
511 gpg_error_t
dirmngr_kill_process(pid_t pid)512 dirmngr_kill_process (pid_t pid)
513 {
514 #ifdef HAVE_W32_SYSTEM
515 /* FIXME: Implement something. TerminateProcess may compromise the
516 state of global data held by DLLs, but seems our best (or only?)
517 shot. */
518 return 0;
519 #else
520 return kill (pid, SIGTERM);
521 #endif
522 }
523
524
525 gpg_error_t
dirmngr_release_process(pid_t pid)526 dirmngr_release_process (pid_t pid)
527 {
528 #ifdef HAVE_W32_SYSTEM
529 CloseHandle (pid_to_handle (pid));
530 #else
531 (void)pid;
532 #endif
533 return 0;
534 }
535