Exemplo n.º 1
0
/* Fork and exec the PGMNAME, see exechelp.h for details.  */
gpg_error_t
gnupg_spawn_process (const char *pgmname, const char *argv[],
                     gpg_err_source_t errsource,
                     void (*preexec)(void), unsigned int flags,
                     estream_t infp,
                     estream_t *r_outfp,
                     estream_t *r_errfp,
                     pid_t *pid)
{
  gpg_error_t err;
  SECURITY_ATTRIBUTES sec_attr;
  PROCESS_INFORMATION pi =
    {
      NULL,      /* Returns process handle.  */
      0,         /* Returns primary thread handle.  */
      0,         /* Returns pid.  */
      0          /* Returns tid.  */
    };
  STARTUPINFO si;
  int cr_flags;
  char *cmdline;
  HANDLE inhandle = INVALID_HANDLE_VALUE;
  HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
  HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
  estream_t outfp = NULL;
  estream_t errfp = NULL;
  HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
                      INVALID_HANDLE_VALUE,
                      INVALID_HANDLE_VALUE};
  int i;
  es_syshd_t syshd;

  if (r_outfp)
    *r_outfp = NULL;
  if (r_errfp)
    *r_errfp = NULL;
  *pid = (pid_t)(-1); /* Always required.  */

  if (infp)
    {
      es_fflush (infp);
      es_rewind (infp);
      es_syshd (infp, &syshd);
      switch (syshd.type)
        {
        case ES_SYSHD_FD:
          inhandle = (HANDLE)_get_osfhandle (syshd.u.fd);
          break;
        case ES_SYSHD_SOCK:
          inhandle = (HANDLE)_get_osfhandle (syshd.u.sock);
          break;
        case ES_SYSHD_HANDLE:
          inhandle = syshd.u.handle;
          break;
        default:
          inhandle = INVALID_HANDLE_VALUE;
          break;
        }
      if (inhandle == INVALID_HANDLE_VALUE)
        return gpg_err_make (errsource, GPG_ERR_INV_VALUE);
      /* FIXME: In case we can't get a system handle (e.g. due to
         es_fopencookie we should create a piper and a feeder
         thread.  */
    }

  if (r_outfp)
    {
      if (create_inheritable_pipe (outpipe, 1))
        {
          err = gpg_err_make (errsource, GPG_ERR_GENERAL);
          log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
          return err;
        }

      syshd.type = ES_SYSHD_HANDLE;
      syshd.u.handle = outpipe[0];
      outfp = es_sysopen (&syshd, "r");
      if (!outfp)
        {
          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
          log_error (_("error creating a stream for a pipe: %s\n"),
                     gpg_strerror (err));
          CloseHandle (outpipe[0]);
          CloseHandle (outpipe[1]);
          outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
          return err;
        }
    }

  if (r_errfp)
    {
      if (create_inheritable_pipe (errpipe, 1))
        {
          err = gpg_err_make (errsource, GPG_ERR_GENERAL);
          log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
          return err;
        }

      syshd.type = ES_SYSHD_HANDLE;
      syshd.u.handle = errpipe[0];
      errfp = es_sysopen (&syshd, "r");
      if (!errfp)
        {
          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
          log_error (_("error creating a stream for a pipe: %s\n"),
                     gpg_strerror (err));
          CloseHandle (errpipe[0]);
          CloseHandle (errpipe[1]);
          errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
          if (outfp)
            es_fclose (outfp);
          else if (outpipe[0] != INVALID_HANDLE_VALUE)
            CloseHandle (outpipe[0]);
          if (outpipe[1] != INVALID_HANDLE_VALUE)
            CloseHandle (outpipe[1]);
          return err;
        }
    }

  /* Prepare security attributes.  */
  memset (&sec_attr, 0, sizeof sec_attr );
  sec_attr.nLength = sizeof sec_attr;
  sec_attr.bInheritHandle = FALSE;

  /* Build the command line.  */
  err = build_w32_commandline (pgmname, argv, &cmdline);
  if (err)
    return err;

  if (inhandle != INVALID_HANDLE_VALUE)
    nullhd[0] = w32_open_null (0);
  if (outpipe[1] != INVALID_HANDLE_VALUE)
    nullhd[1] = w32_open_null (0);
  if (errpipe[1] != INVALID_HANDLE_VALUE)
    nullhd[2] = w32_open_null (0);

  /* Start the process.  Note that we can't run the PREEXEC function
     because this might change our own environment. */
  (void)preexec;

  memset (&si, 0, sizeof si);
  si.cb = sizeof (si);
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
  si.hStdInput  =   inhandle == INVALID_HANDLE_VALUE? nullhd[0] : inhandle;
  si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
  si.hStdError  = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];

  cr_flags = (CREATE_DEFAULT_ERROR_MODE
              | ((flags & 128)? DETACHED_PROCESS : 0)
              | GetPriorityClass (GetCurrentProcess ())
              | CREATE_SUSPENDED);
/*   log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline); */
  if (!CreateProcess (pgmname,       /* Program to start.  */
                      cmdline,       /* Command line arguments.  */
                      &sec_attr,     /* Process security attributes.  */
                      &sec_attr,     /* Thread security attributes.  */
                      TRUE,          /* Inherit handles.  */
                      cr_flags,      /* Creation flags.  */
                      NULL,          /* Environment.  */
                      NULL,          /* Use current drive/directory.  */
                      &si,           /* Startup information. */
                      &pi            /* Returns process information.  */
                      ))
    {
      log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
      xfree (cmdline);
      if (outfp)
        es_fclose (outfp);
      else if (outpipe[0] != INVALID_HANDLE_VALUE)
        CloseHandle (outpipe[0]);
      if (outpipe[1] != INVALID_HANDLE_VALUE)
        CloseHandle (outpipe[1]);
      if (errfp)
        es_fclose (errfp);
      else if (errpipe[0] != INVALID_HANDLE_VALUE)
        CloseHandle (errpipe[0]);
      if (errpipe[1] != INVALID_HANDLE_VALUE)
        CloseHandle (errpipe[1]);
      return gpg_err_make (errsource, GPG_ERR_GENERAL);
    }
  xfree (cmdline);
  cmdline = NULL;

  /* Close the inherited handles to /dev/null.  */
  for (i=0; i < DIM (nullhd); i++)
    if (nullhd[i] != INVALID_HANDLE_VALUE)
      CloseHandle (nullhd[i]);

  /* Close the inherited ends of the pipes.  */
  if (outpipe[1] != INVALID_HANDLE_VALUE)
    CloseHandle (outpipe[1]);
  if (errpipe[1] != INVALID_HANDLE_VALUE)
    CloseHandle (errpipe[1]);

  /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
  /*            " dwProcessID=%d dwThreadId=%d\n", */
  /*            pi.hProcess, pi.hThread, */
  /*            (int) pi.dwProcessId, (int) pi.dwThreadId); */
  /* log_debug ("                     outfp=%p errfp=%p\n", outfp, errfp); */

  /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
     invalid argument error if we pass it the correct processID.  As a
     workaround we use -1 (ASFW_ANY).  */
  if ( (flags & 64) )
    gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);

  /* Process has been created suspended; resume it now. */
  ResumeThread (pi.hThread);
  CloseHandle (pi.hThread);

  if (r_outfp)
    *r_outfp = outfp;
  if (r_errfp)
    *r_errfp = errfp;

  *pid = handle_to_pid (pi.hProcess);
  return 0;

}
Exemplo n.º 2
0
/* Fork and exec the PGMNAME, see exechelp.h for details.  */
gpg_error_t
gnupg_spawn_process (const char *pgmname, const char *argv[],
                     gpg_err_source_t errsource,
                     void (*preexec)(void), unsigned int flags,
                     estream_t infp,
                     estream_t *r_outfp,
                     estream_t *r_errfp,
                     pid_t *pid)
{
  gpg_error_t err;
  PROCESS_INFORMATION pi = {NULL };
  char *cmdline;
  es_syshd_t syshd;
  struct {
    HANDLE hd;
    int rvid;
  } inpipe = {INVALID_HANDLE_VALUE, 0};
  struct {
    HANDLE hd;
    int rvid;
  } outpipe = {INVALID_HANDLE_VALUE, 0};
  struct {
    HANDLE hd;
    int rvid;
  } errpipe = {INVALID_HANDLE_VALUE, 0};
  estream_t outfp = NULL;
  estream_t errfp = NULL;

  (void)preexec;
  (void)flags;

  /* Setup return values.  */
  if (r_outfp)
    *r_outfp = NULL;
  if (r_errfp)
    *r_errfp = NULL;
  *pid = (pid_t)(-1); /* Always required.  */

  log_debug ("%s: enter\n", __func__);
  if (infp)
    {
      es_fflush (infp);
      es_rewind (infp);

      /* Create a pipe to copy our infile to the stdin of the child
         process.  On success inpipe.hd is owned by the feeder.  */
      inpipe.hd = _assuan_w32ce_prepare_pipe (&inpipe.rvid, 1);
      if (inpipe.hd == INVALID_HANDLE_VALUE)
        {
          log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
                     w32_strerror (-1));
          gpg_err_set_errno (EIO);
          return gpg_error_from_syserror ();
        }
      log_debug ("%s: inpipe %p created; hd=%p rvid=%d\n", __func__,
                 infp, inpipe.hd, inpipe.rvid);
      err = start_feeder (infp, inpipe.hd, 1);
      if (err)
        {
          log_error ("error spawning feeder: %s\n", gpg_strerror (err));
          CloseHandle (inpipe.hd);
          return err;
        }
      inpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the feeder.  */
      log_debug ("%s: inpipe %p created; feeder started\n", __func__,
                 infp);
    }

  if (r_outfp)
    {
      /* Create a pipe to make the stdout of the child process
         available as a stream.  */
      outpipe.hd = _assuan_w32ce_prepare_pipe (&outpipe.rvid, 0);
      if (outpipe.hd == INVALID_HANDLE_VALUE)
        {
          log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
                     w32_strerror (-1));
          gpg_err_set_errno (EIO);
          /* Fixme release other stuff/kill feeder.  */
          return gpg_error_from_syserror ();
        }
      syshd.type = ES_SYSHD_HANDLE;
      syshd.u.handle = outpipe.hd;
      err = 0;
      outfp = es_sysopen (&syshd, "r");
      if (!outfp)
        {
          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
          log_error ("error opening pipe stream: %s\n", gpg_strerror (err));
          CloseHandle (outpipe.hd);
          return err;
        }
      log_debug ("%s: outpipe %p created; hd=%p rvid=%d\n", __func__,
                 outfp, outpipe.hd, outpipe.rvid);
      outpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the OUTFP.  */
    }

  if (r_errfp)
    {
      /* Create a pipe to make the stderr of the child process
         available as a stream.  */
      errpipe.hd = _assuan_w32ce_prepare_pipe (&errpipe.rvid, 0);
      if (errpipe.hd == INVALID_HANDLE_VALUE)
        {
          log_error ("_assuan_w32ce_prepare_pipe failed: %s\n",
                     w32_strerror (-1));
          gpg_err_set_errno (EIO);
          /* Fixme release other stuff/kill feeder.  */
          return gpg_error_from_syserror ();
        }
      syshd.type = ES_SYSHD_HANDLE;
      syshd.u.handle = errpipe.hd;
      err = 0;
      errfp = es_sysopen (&syshd, "r");
      if (!errfp)
        {
          err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
          log_error ("error opening pipe stream: %s\n", gpg_strerror (err));
          CloseHandle (errpipe.hd);
          return err;
        }
      log_debug ("%s: errpipe %p created; hd=%p rvid=%d\n", __func__,
                 errfp, errpipe.hd, errpipe.rvid);
      errpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the ERRFP.  */
    }



  /* Build the command line.  */
  err = build_w32_commandline (argv, inpipe.rvid, outpipe.rvid, errpipe.rvid,
                               &cmdline);
  if (err)
    {
      /* Fixme release other stuff/kill feeder.  */
      CloseHandle (errpipe.hd);
      return err;
    }

  log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline);
  if (!create_process (pgmname, cmdline, &pi))
    {
      log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
      xfree (cmdline);
      /* Fixme release other stuff/kill feeder.  */
      CloseHandle (errpipe.hd);
      return gpg_error (GPG_ERR_GENERAL);
    }
  xfree (cmdline);
  cmdline = NULL;

  /* Note: The other end of the pipe is a rendezvous id and thus there
     is no need for a close.  */

  log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
             " dwProcessID=%d dwThreadId=%d\n",
             pi.hProcess, pi.hThread,
             (int) pi.dwProcessId, (int) pi.dwThreadId);


  /* Process has been created suspended; resume it now. */
  ResumeThread (pi.hThread);
  CloseHandle (pi.hThread);

  if (r_outfp)
    *r_outfp = outfp;
  if (r_errfp)
    *r_errfp = errfp;
  *pid = handle_to_pid (pi.hProcess);
  return 0;
}