/* 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; }
/* 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; }