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