~ubuntu-branches/ubuntu/precise/gnupg2/precise-proposed

« back to all changes in this revision

Viewing changes to common/exechelp.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Mueller
  • Date: 2005-03-29 10:30:32 UTC
  • Revision ID: james.westby@ubuntu.com-20050329103032-sj42n2ain3ipx310
Tags: upstream-1.9.15
ImportĀ upstreamĀ versionĀ 1.9.15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* exechelp.c - fork and exec helpers
 
2
 *      Copyright (C) 2004 Free Software Foundation, Inc.
 
3
 *
 
4
 * This file is part of GnuPG.
 
5
 *
 
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.
 
10
 *
 
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.
 
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
#ifdef USE_GNU_PTH      
 
31
#include <pth.h>
 
32
#endif
 
33
#ifndef HAVE_W32_SYSTEM
 
34
#include <sys/wait.h>
 
35
#endif
 
36
 
 
37
#include "util.h"
 
38
#include "i18n.h"
 
39
#include "exechelp.h"
 
40
 
 
41
/* Define to 1 do enable debugging.  */
 
42
#define DEBUG_W32_SPAWN 1
 
43
 
 
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
/* 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
 
55
   pragma.  */ 
 
56
#ifndef _WIN32
 
57
#pragma weak pth_fork
 
58
#pragma weak pth_waitpid
 
59
#endif
 
60
 
 
61
 
 
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))
 
71
#endif
 
72
 
 
73
 
 
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.  */
 
77
static gpg_error_t
 
78
build_w32_commandline (const char *pgmname, const char **argv, char **cmdline)
 
79
{
 
80
  int i, n;
 
81
  const char *s;
 
82
  char *buf, *p;
 
83
 
 
84
  *cmdline = NULL;
 
85
  n = strlen (pgmname);
 
86
  for (i=0; (s=argv[i]); i++)
 
87
    {
 
88
      n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
 
89
      for (; *s; s++)
 
90
        if (*s == '\"')
 
91
          n++;  /* Need to double inner quotes.  */
 
92
    }
 
93
  n++;
 
94
 
 
95
  buf = p = xtrymalloc (n);
 
96
  if (!buf)
 
97
    return gpg_error_from_errno (errno);
 
98
 
 
99
  /* fixme: PGMNAME may not contain spaces etc. */
 
100
  p = stpcpy (p, pgmname);
 
101
  for (i=0; argv[i]; i++) 
 
102
    {
 
103
      if (!*argv[i]) /* Empty string. */
 
104
        p = stpcpy (p, " \"\"");
 
105
      else if (strpbrk (argv[i], " \t\n\v\f\""))
 
106
        {
 
107
          p = stpcpy (p, " \"");
 
108
          for (s=argv[i]; *s; s++)
 
109
            {
 
110
              *p++ = *s;
 
111
              if (*s == '\"')
 
112
                *p++ = *s;
 
113
            }
 
114
          *p++ = '\"';
 
115
          *p = 0;
 
116
        }
 
117
      else
 
118
        p = stpcpy (stpcpy (p, " "), argv[i]);
 
119
    }
 
120
 
 
121
  *cmdline= buf;
 
122
  return 0;
 
123
}
 
124
#endif /*HAVE_W32_SYSTEM*/
 
125
 
 
126
 
 
127
#ifdef HAVE_W32_SYSTEM
 
128
/* Create  pipe where the write end is inheritable.  */
 
129
static int
 
130
create_inheritable_pipe (int filedes[2])
 
131
{
 
132
  HANDLE r, w, h;
 
133
  SECURITY_ATTRIBUTES sec_attr;
 
134
 
 
135
  memset (&sec_attr, 0, sizeof sec_attr );
 
136
  sec_attr.nLength = sizeof sec_attr;
 
137
  sec_attr.bInheritHandle = FALSE;
 
138
    
 
139
  if (!CreatePipe (&r, &w, &sec_attr, 0))
 
140
    return -1;
 
141
 
 
142
  if (!DuplicateHandle (GetCurrentProcess(), w,
 
143
                        GetCurrentProcess(), &h, 0,
 
144
                        TRUE, DUPLICATE_SAME_ACCESS ))
 
145
    {
 
146
      log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1));
 
147
      CloseHandle (r);
 
148
      CloseHandle (w);
 
149
      return -1;
 
150
    }
 
151
  CloseHandle (w);
 
152
  w = h;
 
153
 
 
154
  filedes[0] = handle_to_fd (r);
 
155
  filedes[1] = handle_to_fd (w);
 
156
  return 0;
 
157
}
 
158
#endif /*HAVE_W32_SYSTEM*/
 
159
 
 
160
 
 
161
 
 
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
 
168
   exec.
 
169
 
 
170
   Returns 0 on success or an error code. */
 
171
gpg_error_t
 
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)
 
176
{
 
177
#ifdef HAVE_W32_SYSTEM
 
178
  gpg_error_t err;
 
179
  SECURITY_ATTRIBUTES sec_attr;
 
180
  PROCESS_INFORMATION pi = 
 
181
    {
 
182
      NULL,      /* Returns process handle.  */
 
183
      0,         /* Returns primary thread handle.  */
 
184
      0,         /* Returns pid.  */
 
185
      0          /* Returns tid.  */
 
186
    };
 
187
  STARTUPINFO si;
 
188
  int cr_flags;
 
189
  char *cmdline;
 
190
  int fd, fdout, rp[2];
 
191
 
 
192
  /* Setup return values.  */
 
193
  *statusfile = NULL;
 
194
  *pid = (pid_t)(-1);
 
195
  fflush (infile);
 
196
  rewind (infile);
 
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");
 
201
 
 
202
  /* Prepare security attributes.  */
 
203
  memset (&sec_attr, 0, sizeof sec_attr );
 
204
  sec_attr.nLength = sizeof sec_attr;
 
205
  sec_attr.bInheritHandle = FALSE;
 
206
  
 
207
  /* Build the command line.  */
 
208
  err = build_w32_commandline (pgmname, argv, &cmdline);
 
209
  if (err)
 
210
    return err; 
 
211
 
 
212
  /* Create a pipe.  */
 
213
  if (create_inheritable_pipe (rp))
 
214
    {
 
215
      err = gpg_error (GPG_ERR_GENERAL);
 
216
      log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
 
217
      xfree (cmdline);
 
218
      return err;
 
219
    }
 
220
  
 
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);
 
224
  si.cb = 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]);
 
230
 
 
231
  cr_flags = (CREATE_DEFAULT_ERROR_MODE
 
232
              | GetPriorityClass (GetCurrentProcess ())
 
233
              | CREATE_SUSPENDED); 
 
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.  */
 
245
                      ))
 
246
    {
 
247
      log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
 
248
      xfree (cmdline);
 
249
      CloseHandle (fd_to_handle (rp[0]));
 
250
      CloseHandle (fd_to_handle (rp[1]));
 
251
      return gpg_error (GPG_ERR_GENERAL);
 
252
    }
 
253
  xfree (cmdline);
 
254
  cmdline = NULL;
 
255
 
 
256
  /* Close the other end of the pipe.  */
 
257
  CloseHandle (fd_to_handle (rp[1]));
 
258
  
 
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);
 
263
 
 
264
  /* Process ha been created suspended; resume it now. */
 
265
  ResumeThread (pi.hThread);
 
266
  CloseHandle (pi.hThread); 
 
267
 
 
268
  {
 
269
    int x;
 
270
 
 
271
    x = _open_osfhandle (rp[0], 0);
 
272
    if (x == -1)
 
273
      log_error ("failed to translate osfhandle %p\n", (void*)rp[0] );
 
274
    else 
 
275
      {
 
276
        log_debug ("_open_osfhandle %p yields %d\n", (void*)fd, x );
 
277
        *statusfile = fdopen (x, "r");
 
278
      }
 
279
  }
 
280
  if (!*statusfile)
 
281
    {
 
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);
 
285
      return err;
 
286
    }
 
287
 
 
288
  *pid = handle_to_pid (pi.hProcess);
 
289
  return 0;
 
290
 
 
291
#else /* !HAVE_W32_SYSTEM */
 
292
  gpg_error_t err;
 
293
  int fd, fdout, rp[2];
 
294
 
 
295
  *statusfile = NULL;
 
296
  *pid = (pid_t)(-1);
 
297
  fflush (infile);
 
298
  rewind (infile);
 
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");
 
303
 
 
304
  if (pipe (rp) == -1)
 
305
    {
 
306
      err = gpg_error_from_errno (errno);
 
307
      log_error (_("error creating a pipe: %s\n"), strerror (errno));
 
308
      return err;
 
309
    }
 
310
 
 
311
#ifdef USE_GNU_PTH      
 
312
  *pid = pth_fork? pth_fork () : fork ();
 
313
#else
 
314
  *pid = fork ();
 
315
#endif
 
316
  if (*pid == (pid_t)(-1))
 
317
    {
 
318
      err = gpg_error_from_errno (errno);
 
319
      log_error (_("error forking process: %s\n"), strerror (errno));
 
320
      close (rp[0]);
 
321
      close (rp[1]);
 
322
      return err;
 
323
    }
 
324
 
 
325
  if (!*pid)
 
326
    { 
 
327
      /* Child. */
 
328
      char **arg_list;
 
329
      int n, i, j;
 
330
 
 
331
      /* Create the command line argument array.  */
 
332
      for (i=0; argv[i]; i++)
 
333
        ;
 
334
      arg_list = xcalloc (i+2, sizeof *arg_list);
 
335
      arg_list[0] = strrchr (pgmname, '/');
 
336
      if (arg_list[0])
 
337
        arg_list[0]++;
 
338
      else
 
339
        arg_list[0] = xstrdup (pgmname);
 
340
      for (i=0,j=1; argv[i]; i++, j++)
 
341
        arg_list[j] = (char*)argv[i];
 
342
 
 
343
      /* Connect the infile to stdin. */
 
344
      if (fd != 0 && dup2 (fd, 0) == -1)
 
345
        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
 
346
 
 
347
      /* Connect the outfile to stdout. */
 
348
      if (fdout != 1 && dup2 (fdout, 1) == -1)
 
349
        log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
 
350
      
 
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));
 
354
 
 
355
      /* Close all other files. */
 
356
      n = sysconf (_SC_OPEN_MAX);
 
357
      if (n < 0)
 
358
        n = MAX_OPEN_FDS;
 
359
      for (i=3; i < n; i++)
 
360
        close(i);
 
361
      errno = 0;
 
362
 
 
363
      if (preexec)
 
364
        preexec ();
 
365
      execv (pgmname, arg_list);
 
366
      /* No way to print anything, as we have closed all streams. */
 
367
      _exit (127);
 
368
    }
 
369
 
 
370
  /* Parent. */
 
371
  close (rp[1]);
 
372
 
 
373
  *statusfile = fdopen (rp[0], "r");
 
374
  if (!*statusfile)
 
375
    {
 
376
      err = gpg_error_from_errno (errno);
 
377
      log_error (_("can't fdopen pipe for reading: %s\n"), strerror (errno));
 
378
      kill (*pid, SIGTERM);
 
379
      *pid = (pid_t)(-1);
 
380
      return err;
 
381
    }
 
382
 
 
383
  return 0;
 
384
#endif /* !HAVE_W32_SYSTEM */
 
385
}
 
386
 
 
387
 
 
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.*/
 
392
gpg_error_t
 
393
gnupg_wait_process (const char *pgmname, pid_t pid)
 
394
{
 
395
  gpg_err_code_t ec;
 
396
 
 
397
#ifdef HAVE_W32_SYSTEM
 
398
  HANDLE proc = fd_to_handle (pid);
 
399
  int code;
 
400
  DWORD exc;
 
401
 
 
402
  if (pid == (pid_t)(-1))
 
403
    return gpg_error (GPG_ERR_INV_VALUE);
 
404
 
 
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
 
407
     better.  */
 
408
  code = WaitForSingleObject (proc, INFINITE);
 
409
  switch (code) 
 
410
    {
 
411
      case WAIT_FAILED:
 
412
        log_error (_("waiting for process %d to terminate failed: %s\n"),
 
413
                   (int)pid, w32_strerror (-1));
 
414
        ec = GPG_ERR_GENERAL;
 
415
        break;
 
416
 
 
417
      case WAIT_OBJECT_0:
 
418
        if (!GetExitCodeProcess (proc, &exc))
 
419
          {
 
420
            log_error (_("error getting exit code of process %d: %s\n"),
 
421
                         (int)pid, w32_strerror (-1) );
 
422
            ec = GPG_ERR_GENERAL;
 
423
          }
 
424
        else if (exc)
 
425
          {
 
426
            log_error (_("error running `%s': exit status %d\n"),
 
427
                       pgmname, (int)exc );
 
428
            ec = GPG_ERR_GENERAL;
 
429
          }
 
430
        else
 
431
          ec = 0;
 
432
        break;
 
433
 
 
434
      default:
 
435
        log_error ("WaitForSingleObject returned unexpected "
 
436
                   "code %d for pid %d\n", code, (int)pid );
 
437
        ec = GPG_ERR_GENERAL;
 
438
        break;
 
439
    }
 
440
 
 
441
#else /* !HAVE_W32_SYSTEM */
 
442
  int i, status;
 
443
 
 
444
  if (pid == (pid_t)(-1))
 
445
    return gpg_error (GPG_ERR_INV_VALUE);
 
446
 
 
447
#ifdef USE_GNU_PTH
 
448
  i = pth_waitpid ? pth_waitpid (pid, &status, 0) : waitpid (pid, &status, 0);
 
449
#else
 
450
  while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
 
451
    ;
 
452
#endif
 
453
  if (i == (pid_t)(-1))
 
454
    {
 
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);
 
458
    }
 
459
  else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
 
460
    {
 
461
      log_error (_("error running `%s': probably not installed\n"), pgmname);
 
462
      ec = GPG_ERR_CONFIGURATION;
 
463
    }
 
464
  else if (WIFEXITED (status) && WEXITSTATUS (status))
 
465
    {
 
466
      log_error (_("error running `%s': exit status %d\n"), pgmname,
 
467
                 WEXITSTATUS (status));
 
468
      ec = GPG_ERR_GENERAL;
 
469
    }
 
470
  else if (!WIFEXITED (status))
 
471
    {
 
472
      log_error (_("error running `%s': terminated\n"), pgmname);
 
473
      ec = GPG_ERR_GENERAL;
 
474
    }
 
475
  else 
 
476
    ec = 0;
 
477
#endif /* !HAVE_W32_SYSTEM */
 
478
 
 
479
  return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
 
480
 
 
481
}
 
482