/* Simplified version of gnupg_spawn_process. This function forks and then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout and ERRFD to stderr (any of them may be -1 to connect them to /dev/null). The arguments for the process are expected in the NULL terminated array ARGV. The program name itself should not be included there. Calling gnupg_wait_process is required. Returns 0 on success or an error code. */ gpg_error_t gnupg_spawn_process_fd (const char *pgmname, const char *argv[], int infd, int outfd, int errfd, pid_t *pid) { gpg_error_t err; PROCESS_INFORMATION pi = {NULL}; char *cmdline; /* Setup return values. */ *pid = (pid_t)(-1); if (infd != -1 || outfd != -1 || errfd != -1) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Build the command line. */ err = build_w32_commandline (argv, 0, 0, 0, &cmdline); if (err) return err; log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); if (!create_process (pgmname, cmdline, &pi)) { log_error ("CreateProcess(fd) failed: %s\n", w32_strerror (-1)); xfree (cmdline); return gpg_error (GPG_ERR_GENERAL); } xfree (cmdline); cmdline = NULL; log_debug ("CreateProcess(fd) 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); *pid = handle_to_pid (pi.hProcess); return 0; }
/* Spawn a new process and immediatley detach from it. The name of the program to exec is PGMNAME and its arguments are in ARGV (the programname is automatically passed as first argument). Environment strings in ENVP are set. An error is returned if pgmname is not executable; to make this work it is necessary to provide an absolute file name. All standard file descriptors are connected to /dev/null. */ gpg_error_t gnupg_spawn_process_detached (const char *pgmname, const char *argv[], const char *envp[] ) { gpg_error_t err; char *cmdline; PROCESS_INFORMATION pi = {NULL }; (void)envp; /* Build the command line. */ err = build_w32_commandline (argv, 0, 0, 0, &cmdline); if (err) return err; /* Note: There is no detached flag under CE. */ log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); if (!create_process (pgmname, cmdline, &pi)) { log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); xfree (cmdline); return gpg_error (GPG_ERR_GENERAL); } xfree (cmdline); cmdline = NULL; log_debug ("CreateProcess(detached) 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); return 0; }
/* Spawn a new process and immediatley detach from it. The name of the program to exec is PGMNAME and its arguments are in ARGV (the programname is automatically passed as first argument). Environment strings in ENVP are set. An error is returned if pgmname is not executable; to make this work it is necessary to provide an absolute file name. All standard file descriptors are connected to /dev/null. */ gpg_error_t gnupg_spawn_process_detached (const char *pgmname, const char *argv[], const char *envp[] ) { 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; /* FIXME: We don't make use of ENVP yet. It is currently only used to pass the GPG_AGENT_INFO variable to gpg-agent. As the default on windows is to use a standard socket, this does not really matter. */ (void)envp; if (access (pgmname, X_OK)) return gpg_error_from_syserror (); /* 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; /* Start the process. */ memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; cr_flags = (CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (GetCurrentProcess ()) | CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS); /* log_debug ("CreateProcess(detached), 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. */ FALSE, /* Inherit handles. */ cr_flags, /* Creation flags. */ NULL, /* Environment. */ NULL, /* Use current drive/directory. */ &si, /* Startup information. */ &pi /* Returns process information. */ )) { log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); xfree (cmdline); return gpg_error (GPG_ERR_GENERAL); } xfree (cmdline); cmdline = NULL; /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */ /* " dwProcessID=%d dwThreadId=%d\n", */ /* pi.hProcess, pi.hThread, */ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ CloseHandle (pi.hThread); return 0; }
/* Simplified version of gnupg_spawn_process. This function forks and then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout and ERRFD to stderr (any of them may be -1 to connect them to /dev/null). The arguments for the process are expected in the NULL terminated array ARGV. The program name itself should not be included there. Calling gnupg_wait_process is required. Returns 0 on success or an error code. */ gpg_error_t gnupg_spawn_process_fd (const char *pgmname, const char *argv[], int infd, int outfd, int errfd, pid_t *pid) { gpg_error_t err; SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; STARTUPINFO si; char *cmdline; int i; HANDLE stdhd[3]; /* Setup return values. */ *pid = (pid_t)(-1); /* 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; memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE; stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd); si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd); si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd); /* 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. */ (CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED | DETACHED_PROCESS), NULL, /* Environment. */ NULL, /* Use current drive/directory. */ &si, /* Startup information. */ &pi /* Returns process information. */ )) { log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); err = gpg_error (GPG_ERR_GENERAL); } else err = 0; xfree (cmdline); for (i=0; i < 3; i++) if (stdhd[i] != INVALID_HANDLE_VALUE) CloseHandle (stdhd[i]); if (err) return err; /* 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); *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; 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; }
int __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* Returns process handle. */ 0, /* Returns primary thread handle. */ 0, /* Returns pid. */ 0 /* Returns tid. */ }; STARTUPINFO si; assuan_fd_t fd; assuan_fd_t *fdp; char *cmdline; HANDLE nullfd = INVALID_HANDLE_VALUE; /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env variable. However this requires us to write a full environment handler, because the strings are expected in sorted order. The suggestion given in the MS Reference Library, to save the old value, change it, create process and restore it, is not thread safe. */ /* Build the command line. */ if (build_w32_commandline (ctx, argv, &cmdline)) return -1; /* Start the process. */ memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES; /* FIXME: Dup to nul if ASSUAN_INVALID_FD. */ si.hStdInput = fd_in; si.hStdOutput = fd_out; /* Dup stderr to /dev/null unless it is in the list of FDs to be passed to the child. */ fd = assuan_fd_from_posix_fd (fileno (stderr)); fdp = fd_child_list; if (fdp) { for (; *fdp != ASSUAN_INVALID_FD && *fdp != fd; fdp++) ; } if (!fdp || *fdp == ASSUAN_INVALID_FD) { nullfd = CreateFileW (L"nul", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (nullfd == INVALID_HANDLE_VALUE) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "can't open `nul': %s", _assuan_w32_strerror (ctx, -1)); _assuan_free (ctx, cmdline); gpg_err_set_errno (EIO); return -1; } si.hStdError = nullfd; } else si.hStdError = fd; /* Note: We inherit all handles flagged as inheritable. This seems to be a security flaw but there seems to be no way of selecting handles to inherit. A fix for this would be to use a helper process like we have in gpgme. */ /* _assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n", */ /* name, cmdline); */ if (!CreateProcess (name, /* Program to start. */ cmdline, /* Command line arguments. */ &sec_attr, /* Process security attributes. */ &sec_attr, /* Thread security attributes. */ TRUE, /* Inherit handles. */ (CREATE_DEFAULT_ERROR_MODE | ((flags & 128)? DETACHED_PROCESS : 0) | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED), /* Creation flags. */ NULL, /* Environment. */ NULL, /* Use current drive/directory. */ &si, /* Startup information. */ &pi /* Returns process information. */ )) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_w32", ctx, "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); _assuan_free (ctx, cmdline); if (nullfd != INVALID_HANDLE_VALUE) CloseHandle (nullfd); gpg_err_set_errno (EIO); return -1; } _assuan_free (ctx, cmdline); if (nullfd != INVALID_HANDLE_VALUE) CloseHandle (nullfd); ResumeThread (pi.hThread); CloseHandle (pi.hThread); /* _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */ /* " dwProcessID=%d dwThreadId=%d\n", */ /* pi.hProcess, pi.hThread, */ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ *r_pid = (pid_t) pi.hProcess; /* No need to modify peer process, as we don't change the handle names. However this also means we are not safe, as we inherit too many handles. Should use approach similar to gpgme and glib using a helper process. */ 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; }
int __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { PROCESS_INFORMATION pi = { NULL, /* Returns process handle. */ 0, /* Returns primary thread handle. */ 0, /* Returns pid. */ 0 /* Returns tid. */ }; assuan_fd_t fd; assuan_fd_t *fdp; assuan_fd_t fd_err; int fd_err_isnull = 0; char *cmdline; /* Dup stderr to /dev/null unless it is in the list of FDs to be passed to the child. Well we don't actually open nul because that is not available on Windows, but use our hack for it. Because an RVID of 0 is an invalid value and HANDLES will never have this value either, we test for this as well. */ /* FIXME: As long as we can't decide whether a handle is a real handler or an rendezvous id we can't do anything with the FD_CHILD_LIST. We can't do much with stderr either, thus we better don't pass stderr to the child at all. If we would do so and it is not a rendezvous id the client would run into problems. */ fd = assuan_fd_from_posix_fd (fileno (stderr)); fdp = fd_child_list; if (fdp) { for (; *fdp != ASSUAN_INVALID_FD && *fdp != 0 && *fdp != fd; fdp++) ; } if (!fdp || *fdp == ASSUAN_INVALID_FD) fd_err_isnull = 1; fd_err = ASSUAN_INVALID_FD; if (build_w32_commandline (ctx, argv, fd_in, fd_out, fd_err, fd_err_isnull, &cmdline)) { return -1; } TRACE2 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "path=`%s' cmdline=`%s'", name, cmdline); { wchar_t *wcmdline, *wname; wcmdline = utf8_to_wchar (cmdline); _assuan_free (ctx, cmdline); if (!wcmdline) return -1; wname = utf8_to_wchar (name); if (!wname) { free_wchar (wcmdline); return -1; } if (!CreateProcess (wname, /* Program to start. */ wcmdline, /* Command line arguments. */ NULL, /* (not supported) */ NULL, /* (not supported) */ FALSE, /* (not supported) */ (CREATE_SUSPENDED), /* Creation flags. */ NULL, /* (not supported) */ NULL, /* (not supported) */ NULL, /* (not supported) */ &pi /* Returns process information.*/ )) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "CreateProcess failed: %s", _assuan_w32_strerror (ctx, -1)); free_wchar (wname); free_wchar (wcmdline); gpg_err_set_errno (EIO); return -1; } free_wchar (wname); free_wchar (wcmdline); } ResumeThread (pi.hThread); TRACE4 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "CreateProcess ready: hProcess=%p hThread=%p" " dwProcessID=%d dwThreadId=%d\n", pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); CloseHandle (pi.hThread); *r_pid = (pid_t) pi.hProcess; return 0; }
int spawn_process_and_wait (const char *pgmname, const char * const *argv) { int 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 proc; int code; DWORD exc; /* Prepare security attributes. */ memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; /* Build the command line. */ cmdline = build_w32_commandline (pgmname, argv); if (! cmdline) return -1; /* Start the process. Note that we can't run the PREEXEC function because this would change our own environment. */ 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 = w32_open_null (0); si.hStdOutput = w32_open_null (1); si.hStdError = w32_open_null (1); cr_flags = (CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED); 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. */ )) { free (cmdline); CloseHandle (si.hStdInput); CloseHandle (si.hStdOutput); CloseHandle (si.hStdError); return -1; } free (cmdline); cmdline = NULL; /* Process has been created suspended; resume it now. */ ResumeThread (pi.hThread); CloseHandle (pi.hThread); /* Wait for it to finish. */ proc = pi.hProcess; err = 0; code = WaitForSingleObject (proc, INFINITE); switch (code) { case WAIT_OBJECT_0: if (! GetExitCodeProcess (proc, &exc)) err = -1; else err = exc; break; default: err = -1; break; } CloseHandle (proc); return err; }