You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
584 lines
22 KiB
584 lines
22 KiB
11 years ago
|
//
|
||
|
// Boost.Process
|
||
|
// ~~~~~~~~~~~~~
|
||
|
//
|
||
|
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
|
||
|
// Copyright (c) 2008, 2009 Boris Schaeling
|
||
|
//
|
||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||
|
//
|
||
|
|
||
|
/**
|
||
|
* \file boost/process/operations.hpp
|
||
|
*
|
||
|
* Provides miscellaneous free functions.
|
||
|
*/
|
||
|
|
||
|
#ifndef BOOST_PROCESS_OPERATIONS_HPP
|
||
|
#define BOOST_PROCESS_OPERATIONS_HPP
|
||
|
|
||
|
#include <boost/process/config.hpp>
|
||
|
|
||
|
#if defined(BOOST_POSIX_API)
|
||
|
# include <boost/process/detail/posix_ops.hpp>
|
||
|
# include <stdlib.h>
|
||
|
# include <unistd.h>
|
||
|
# if defined(__CYGWIN__)
|
||
|
# include <boost/scoped_array.hpp>
|
||
|
# include <sys/cygwin.h>
|
||
|
# endif
|
||
|
#elif defined(BOOST_WINDOWS_API)
|
||
|
# include <boost/process/detail/win32_ops.hpp>
|
||
|
# include <boost/algorithm/string/predicate.hpp>
|
||
|
# include <windows.h>
|
||
|
#else
|
||
|
# error "Unsupported platform."
|
||
|
#endif
|
||
|
|
||
|
#include <boost/process/child.hpp>
|
||
|
#include <boost/process/stream_behavior.hpp>
|
||
|
#include <boost/process/status.hpp>
|
||
|
#include <boost/process/detail/file_handle.hpp>
|
||
|
#include <boost/process/detail/pipe.hpp>
|
||
|
#include <boost/process/detail/stream_info.hpp>
|
||
|
#include <boost/filesystem/path.hpp>
|
||
|
#include <boost/algorithm/string/predicate.hpp>
|
||
|
#include <boost/system/system_error.hpp>
|
||
|
#include <boost/throw_exception.hpp>
|
||
|
#include <boost/scoped_array.hpp>
|
||
|
#include <boost/assert.hpp>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
#include <stdexcept>
|
||
|
#include <cstddef>
|
||
|
|
||
|
namespace boost {
|
||
|
namespace process {
|
||
|
|
||
|
/**
|
||
|
* Locates the executable program \a file in all the directory components
|
||
|
* specified in \a path. If \a path is empty, the value of the PATH
|
||
|
* environment variable is used.
|
||
|
*
|
||
|
* The path variable is interpreted following the same conventions used
|
||
|
* to parse the PATH environment variable in the underlying platform.
|
||
|
*
|
||
|
* \throw boost::filesystem::filesystem_error If the file cannot be found
|
||
|
* in the path.
|
||
|
*/
|
||
|
inline std::string find_executable_in_path(const std::string &file, std::string path = "")
|
||
|
{
|
||
|
#if defined(BOOST_POSIX_API)
|
||
|
BOOST_ASSERT(file.find('/') == std::string::npos);
|
||
|
#elif defined(BOOST_WINDOWS_API)
|
||
|
BOOST_ASSERT(file.find_first_of("\\/") == std::string::npos);
|
||
|
#endif
|
||
|
|
||
|
std::string result;
|
||
|
|
||
|
#if defined(BOOST_POSIX_API)
|
||
|
if (path.empty())
|
||
|
{
|
||
|
const char *envpath = ::getenv("PATH");
|
||
|
// if (!envpath)
|
||
|
// boost::throw_exception(boost::filesystem::filesystem_error("boost::process::find_executable_in_path: retrieving PATH failed", file, boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory)));
|
||
|
|
||
|
path = envpath;
|
||
|
}
|
||
|
BOOST_ASSERT(!path.empty());
|
||
|
|
||
|
#if defined(__CYGWIN__)
|
||
|
if (!::cygwin_posix_path_list_p(path.c_str()))
|
||
|
{
|
||
|
int size = ::cygwin_win32_to_posix_path_list_buf_size(path.c_str());
|
||
|
boost::scoped_array<char> cygpath(new char[size]);
|
||
|
::cygwin_win32_to_posix_path_list(path.c_str(), cygpath.get());
|
||
|
path = cygpath.get();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
std::string::size_type pos1 = 0, pos2;
|
||
|
do
|
||
|
{
|
||
|
pos2 = path.find(':', pos1);
|
||
|
std::string dir = (pos2 != std::string::npos) ? path.substr(pos1, pos2 - pos1) : path.substr(pos1);
|
||
|
std::string f = dir + (boost::algorithm::ends_with(dir, "/") ? "" : "/") + file;
|
||
|
if (!::access(f.c_str(), X_OK))
|
||
|
result = f;
|
||
|
pos1 = pos2 + 1;
|
||
|
} while (pos2 != std::string::npos && result.empty());
|
||
|
#elif defined(BOOST_WINDOWS_API)
|
||
|
const char *exts[] = { "", ".exe", ".com", ".bat", NULL };
|
||
|
const char **ext = exts;
|
||
|
while (*ext)
|
||
|
{
|
||
|
char buf[MAX_PATH];
|
||
|
char *dummy;
|
||
|
DWORD size = ::SearchPathA(path.empty() ? NULL : path.c_str(), file.c_str(), *ext, MAX_PATH, buf, &dummy);
|
||
|
BOOST_ASSERT(size < MAX_PATH);
|
||
|
if (size > 0)
|
||
|
{
|
||
|
result = buf;
|
||
|
break;
|
||
|
}
|
||
|
++ext;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// if (result.empty())
|
||
|
// boost::throw_exception(boost::filesystem::filesystem_error("boost::process::find_executable_in_path: file not found", file, boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory)));
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Extracts the program name from a given executable.
|
||
|
*
|
||
|
* \return The program name. On Windows the program name
|
||
|
* is returned without a file extension.
|
||
|
*/
|
||
|
inline std::string executable_to_progname(const std::string &exe)
|
||
|
{
|
||
|
std::string::size_type begin = 0;
|
||
|
std::string::size_type end = std::string::npos;
|
||
|
|
||
|
#if defined(BOOST_POSIX_API)
|
||
|
std::string::size_type slash = exe.rfind('/');
|
||
|
#elif defined(BOOST_WINDOWS_API)
|
||
|
std::string::size_type slash = exe.find_last_of("/\\");
|
||
|
#endif
|
||
|
if (slash != std::string::npos)
|
||
|
begin = slash + 1;
|
||
|
|
||
|
#if defined(BOOST_WINDOWS_API)
|
||
|
if (exe.size() > 4 &&
|
||
|
(boost::algorithm::iends_with(exe, ".exe") || boost::algorithm::iends_with(exe, ".com") || boost::algorithm::iends_with(exe, ".bat")))
|
||
|
end = exe.size() - 4;
|
||
|
#endif
|
||
|
|
||
|
return exe.substr(begin, end - begin);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts a new child process.
|
||
|
*
|
||
|
* Launches a new process based on the binary image specified by the
|
||
|
* executable, the set of arguments to be passed to it and several
|
||
|
* parameters that describe the execution context.
|
||
|
*
|
||
|
* \remark Blocking remarks: This function may block if the device
|
||
|
* holding the executable blocks when loading the image. This might
|
||
|
* happen if, e.g., the binary is being loaded from a network share.
|
||
|
*
|
||
|
* \return A handle to the new child process.
|
||
|
*/
|
||
|
template <class Executable, class Arguments, class Context>
|
||
|
inline child launch(const Executable &exe, const Arguments &args, const Context &ctx)
|
||
|
{
|
||
|
detail::file_handle fhstdin, fhstdout, fhstderr;
|
||
|
|
||
|
BOOST_ASSERT(!args.empty());
|
||
|
BOOST_ASSERT(!ctx.work_directory.empty());
|
||
|
|
||
|
#if defined(BOOST_POSIX_API)
|
||
|
detail::info_map infoin, infoout;
|
||
|
|
||
|
if (ctx.stdin_behavior.get_type() != stream_behavior::close)
|
||
|
{
|
||
|
detail::stream_info si = detail::stream_info(ctx.stdin_behavior, false);
|
||
|
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si));
|
||
|
}
|
||
|
|
||
|
if (ctx.stdout_behavior.get_type() != stream_behavior::close)
|
||
|
{
|
||
|
detail::stream_info si = detail::stream_info(ctx.stdout_behavior, true);
|
||
|
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si));
|
||
|
}
|
||
|
|
||
|
if (ctx.stderr_behavior.get_type() != stream_behavior::close)
|
||
|
{
|
||
|
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
|
||
|
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
|
||
|
}
|
||
|
|
||
|
detail::posix_setup s;
|
||
|
s.work_directory = ctx.work_directory;
|
||
|
|
||
|
child::id_type pid = detail::posix_start(exe, args, ctx.environment, infoin, infoout, s);
|
||
|
|
||
|
if (ctx.stdin_behavior.get_type() == stream_behavior::capture)
|
||
|
{
|
||
|
fhstdin = detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false);
|
||
|
BOOST_ASSERT(fhstdin.valid());
|
||
|
}
|
||
|
|
||
|
if (ctx.stdout_behavior.get_type() == stream_behavior::capture)
|
||
|
{
|
||
|
fhstdout = detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true);
|
||
|
BOOST_ASSERT(fhstdout.valid());
|
||
|
}
|
||
|
|
||
|
if (ctx.stderr_behavior.get_type() == stream_behavior::capture)
|
||
|
{
|
||
|
fhstderr = detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true);
|
||
|
BOOST_ASSERT(fhstderr.valid());
|
||
|
}
|
||
|
|
||
|
return child(pid, fhstdin, fhstdout, fhstderr);
|
||
|
#elif defined(BOOST_WINDOWS_API)
|
||
|
detail::stream_info behin = detail::stream_info(ctx.stdin_behavior, false);
|
||
|
if (behin.type_ == detail::stream_info::use_pipe)
|
||
|
fhstdin = behin.pipe_->wend();
|
||
|
detail::stream_info behout = detail::stream_info(ctx.stdout_behavior, true);
|
||
|
if (behout.type_ == detail::stream_info::use_pipe)
|
||
|
fhstdout = behout.pipe_->rend();
|
||
|
detail::stream_info beherr = detail::stream_info(ctx.stderr_behavior, true);
|
||
|
if (beherr.type_ == detail::stream_info::use_pipe)
|
||
|
fhstderr = beherr.pipe_->rend();
|
||
|
|
||
|
STARTUPINFOA si;
|
||
|
::ZeroMemory(&si, sizeof(si));
|
||
|
si.cb = sizeof(si);
|
||
|
|
||
|
detail::win32_setup s;
|
||
|
s.work_directory = ctx.work_directory;
|
||
|
s.startupinfo = &si;
|
||
|
|
||
|
PROCESS_INFORMATION pi = detail::win32_start(exe, args, ctx.environment, behin, behout, beherr, s);
|
||
|
|
||
|
if (!::CloseHandle(pi.hThread))
|
||
|
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch: CloseHandle failed"));
|
||
|
|
||
|
return child(pi.dwProcessId, fhstdin, fhstdout, fhstderr, detail::file_handle(pi.hProcess));
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Launches a shell-based command.
|
||
|
*
|
||
|
* Executes the given command through the default system shell. The
|
||
|
* command is subject to pattern expansion, redirection and pipelining.
|
||
|
* The shell is launched as described by the parameters in the execution
|
||
|
* context.
|
||
|
*
|
||
|
* This function behaves similarly to the system(3) system call. In a
|
||
|
* POSIX system, the command is fed to /bin/sh whereas under a Windows
|
||
|
* system, it is fed to cmd.exe. It is difficult to write portable
|
||
|
* commands as the first parameter, but this function comes in handy in
|
||
|
* multiple situations.
|
||
|
*/
|
||
|
template <class Context>
|
||
|
inline child launch_shell(const std::string &command, const Context &ctx)
|
||
|
{
|
||
|
std::string exe;
|
||
|
std::vector<std::string> args;
|
||
|
|
||
|
#if defined(BOOST_POSIX_API)
|
||
|
exe = "/bin/sh";
|
||
|
args.push_back("sh");
|
||
|
args.push_back("-c");
|
||
|
args.push_back(command);
|
||
|
#elif defined(BOOST_WINDOWS_API)
|
||
|
char sysdir[MAX_PATH];
|
||
|
UINT size = ::GetSystemDirectoryA(sysdir, sizeof(sysdir));
|
||
|
if (!size)
|
||
|
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_shell: GetWindowsDirectory failed"));
|
||
|
BOOST_ASSERT(size < MAX_PATH);
|
||
|
|
||
|
exe = std::string(sysdir) + (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe");
|
||
|
args.push_back("cmd");
|
||
|
args.push_back("/c");
|
||
|
args.push_back(command);
|
||
|
#endif
|
||
|
|
||
|
return launch(exe, args, ctx);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Launches a pipelined set of child processes.
|
||
|
*
|
||
|
* Given a collection of pipeline_entry objects describing how to launch
|
||
|
* a set of child processes, spawns them all and connects their inputs and
|
||
|
* outputs in a way that permits pipelined communication.
|
||
|
*
|
||
|
* \pre Let 1..N be the processes in the collection: the input behavior of
|
||
|
* the 2..N processes must be set to close_stream().
|
||
|
* \pre Let 1..N be the processes in the collection: the output behavior of
|
||
|
* the 1..N-1 processes must be set to close_stream().
|
||
|
* \remark Blocking remarks: This function may block if the device holding
|
||
|
* the executable of one of the entries blocks when loading the
|
||
|
* image. This might happen if, e.g., the binary is being loaded
|
||
|
* from a network share.
|
||
|
* \return A set of Child objects that represent all the processes spawned
|
||
|
* by this call. You should use wait_children() to wait for their
|
||
|
* termination.
|
||
|
*/
|
||
|
template <class Entries>
|
||
|
inline children launch_pipeline(const Entries &entries)
|
||
|
{
|
||
|
BOOST_ASSERT(entries.size() >= 2);
|
||
|
|
||
|
children cs;
|
||
|
detail::file_handle fhinvalid;
|
||
|
|
||
|
boost::scoped_array<detail::pipe> pipes(new detail::pipe[entries.size() - 1]);
|
||
|
|
||
|
#if defined(BOOST_POSIX_API)
|
||
|
{
|
||
|
typename Entries::size_type i = 0;
|
||
|
const typename Entries::value_type::context_type &ctx = entries[i].context;
|
||
|
|
||
|
detail::info_map infoin, infoout;
|
||
|
|
||
|
if (ctx.stdin_behavior.get_type() != stream_behavior::close)
|
||
|
{
|
||
|
detail::stream_info si = detail::stream_info(ctx.stdin_behavior, false);
|
||
|
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si));
|
||
|
}
|
||
|
|
||
|
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
|
||
|
detail::stream_info si2(close_stream(), true);
|
||
|
si2.type_ = detail::stream_info::use_handle;
|
||
|
si2.handle_ = pipes[i].wend().release();
|
||
|
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si2));
|
||
|
|
||
|
if (ctx.stderr_behavior.get_type() != stream_behavior::close)
|
||
|
{
|
||
|
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
|
||
|
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
|
||
|
}
|
||
|
|
||
|
detail::posix_setup s;
|
||
|
s.work_directory = ctx.work_directory;
|
||
|
|
||
|
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
|
||
|
|
||
|
detail::file_handle fhstdin;
|
||
|
|
||
|
if (ctx.stdin_behavior.get_type() == stream_behavior::capture)
|
||
|
{
|
||
|
fhstdin = detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false);
|
||
|
BOOST_ASSERT(fhstdin.valid());
|
||
|
}
|
||
|
|
||
|
cs.push_back(child(pid, fhstdin, fhinvalid, fhinvalid));
|
||
|
}
|
||
|
|
||
|
for (typename Entries::size_type i = 1; i < entries.size() - 1; ++i)
|
||
|
{
|
||
|
const typename Entries::value_type::context_type &ctx = entries[i].context;
|
||
|
detail::info_map infoin, infoout;
|
||
|
|
||
|
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
|
||
|
detail::stream_info si1(close_stream(), false);
|
||
|
si1.type_ = detail::stream_info::use_handle;
|
||
|
si1.handle_ = pipes[i - 1].rend().release();
|
||
|
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si1));
|
||
|
|
||
|
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
|
||
|
detail::stream_info si2(close_stream(), true);
|
||
|
si2.type_ = detail::stream_info::use_handle;
|
||
|
si2.handle_ = pipes[i].wend().release();
|
||
|
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si2));
|
||
|
|
||
|
if (ctx.stderr_behavior.get_type() != stream_behavior::close)
|
||
|
{
|
||
|
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
|
||
|
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
|
||
|
}
|
||
|
|
||
|
detail::posix_setup s;
|
||
|
s.work_directory = ctx.work_directory;
|
||
|
|
||
|
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
|
||
|
|
||
|
cs.push_back(child(pid, fhinvalid, fhinvalid, fhinvalid));
|
||
|
}
|
||
|
|
||
|
{
|
||
|
typename Entries::size_type i = entries.size() - 1;
|
||
|
const typename Entries::value_type::context_type &ctx = entries[i].context;
|
||
|
|
||
|
detail::info_map infoin, infoout;
|
||
|
|
||
|
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
|
||
|
detail::stream_info si1(close_stream(), false);
|
||
|
si1.type_ = detail::stream_info::use_handle;
|
||
|
si1.handle_ = pipes[i - 1].rend().release();
|
||
|
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si1));
|
||
|
|
||
|
if (ctx.stdout_behavior.get_type() != stream_behavior::close)
|
||
|
{
|
||
|
detail::stream_info si = detail::stream_info(ctx.stdout_behavior, true);
|
||
|
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si));
|
||
|
}
|
||
|
|
||
|
if (ctx.stderr_behavior.get_type() != stream_behavior::close)
|
||
|
{
|
||
|
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
|
||
|
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
|
||
|
}
|
||
|
|
||
|
detail::posix_setup s;
|
||
|
s.work_directory = ctx.work_directory;
|
||
|
|
||
|
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
|
||
|
|
||
|
detail::file_handle fhstdout, fhstderr;
|
||
|
|
||
|
if (ctx.stdout_behavior.get_type() == stream_behavior::capture)
|
||
|
{
|
||
|
fhstdout = detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true);
|
||
|
BOOST_ASSERT(fhstdout.valid());
|
||
|
}
|
||
|
|
||
|
if (ctx.stderr_behavior.get_type() == stream_behavior::capture)
|
||
|
{
|
||
|
fhstderr = detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true);
|
||
|
BOOST_ASSERT(fhstderr.valid());
|
||
|
}
|
||
|
|
||
|
cs.push_back(child(pid, fhinvalid, fhstdout, fhstderr));
|
||
|
}
|
||
|
#elif defined(BOOST_WINDOWS_API)
|
||
|
STARTUPINFOA si;
|
||
|
detail::win32_setup s;
|
||
|
s.startupinfo = &si;
|
||
|
|
||
|
{
|
||
|
typename Entries::size_type i = 0;
|
||
|
const typename Entries::value_type::context_type &ctx = entries[i].context;
|
||
|
|
||
|
detail::stream_info sii = detail::stream_info(ctx.stdin_behavior, false);
|
||
|
detail::file_handle fhstdin;
|
||
|
if (sii.type_ == detail::stream_info::use_pipe)
|
||
|
fhstdin = sii.pipe_->wend();
|
||
|
|
||
|
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
|
||
|
detail::stream_info sio(close_stream(), true);
|
||
|
sio.type_ = detail::stream_info::use_handle;
|
||
|
sio.handle_ = pipes[i].wend().release();
|
||
|
|
||
|
detail::stream_info sie(ctx.stderr_behavior, true);
|
||
|
|
||
|
s.work_directory = ctx.work_directory;
|
||
|
|
||
|
::ZeroMemory(&si, sizeof(si));
|
||
|
si.cb = sizeof(si);
|
||
|
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
|
||
|
|
||
|
if (!::CloseHandle(pi.hThread))
|
||
|
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
|
||
|
|
||
|
cs.push_back(child(pi.dwProcessId, fhstdin, fhinvalid, fhinvalid, detail::file_handle(pi.hProcess)));
|
||
|
}
|
||
|
|
||
|
for (typename Entries::size_type i = 1; i < entries.size() - 1; ++i)
|
||
|
{
|
||
|
const typename Entries::value_type::context_type &ctx = entries[i].context;
|
||
|
|
||
|
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
|
||
|
detail::stream_info sii(close_stream(), false);
|
||
|
sii.type_ = detail::stream_info::use_handle;
|
||
|
sii.handle_ = pipes[i - 1].rend().release();
|
||
|
|
||
|
detail::stream_info sio(close_stream(), true);
|
||
|
sio.type_ = detail::stream_info::use_handle;
|
||
|
sio.handle_ = pipes[i].wend().release();
|
||
|
|
||
|
detail::stream_info sie(ctx.stderr_behavior, true);
|
||
|
|
||
|
s.work_directory = ctx.work_directory;
|
||
|
|
||
|
::ZeroMemory(&si, sizeof(si));
|
||
|
si.cb = sizeof(si);
|
||
|
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
|
||
|
|
||
|
if (!::CloseHandle(pi.hThread))
|
||
|
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
|
||
|
|
||
|
cs.push_back(child(pi.dwProcessId, fhinvalid, fhinvalid, fhinvalid, detail::file_handle(pi.hProcess)));
|
||
|
}
|
||
|
|
||
|
{
|
||
|
typename Entries::size_type i = entries.size() - 1;
|
||
|
const typename Entries::value_type::context_type &ctx = entries[i].context;
|
||
|
|
||
|
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
|
||
|
detail::stream_info sii(close_stream(), false);
|
||
|
sii.type_ = detail::stream_info::use_handle;
|
||
|
sii.handle_ = pipes[i - 1].rend().release();
|
||
|
|
||
|
detail::file_handle fhstdout, fhstderr;
|
||
|
|
||
|
detail::stream_info sio(ctx.stdout_behavior, true);
|
||
|
if (sio.type_ == detail::stream_info::use_pipe)
|
||
|
fhstdout = sio.pipe_->rend();
|
||
|
detail::stream_info sie(ctx.stderr_behavior, true);
|
||
|
if (sie.type_ == detail::stream_info::use_pipe)
|
||
|
fhstderr = sie.pipe_->rend();
|
||
|
|
||
|
s.work_directory = ctx.work_directory;
|
||
|
|
||
|
::ZeroMemory(&si, sizeof(si));
|
||
|
si.cb = sizeof(si);
|
||
|
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
|
||
|
|
||
|
if (!::CloseHandle(pi.hThread))
|
||
|
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
|
||
|
|
||
|
cs.push_back(child(pi.dwProcessId, fhinvalid, fhstdout, fhstderr, detail::file_handle(pi.hProcess)));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return cs;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Waits for a collection of children to terminate.
|
||
|
*
|
||
|
* Given a collection of Child objects (such as std::vector<child> or
|
||
|
* the convenience children type), waits for the termination of all of
|
||
|
* them.
|
||
|
*
|
||
|
* \remark Blocking remarks: This call blocks if any of the children
|
||
|
* processes in the collection has not finalized execution and
|
||
|
* waits until it terminates.
|
||
|
*
|
||
|
* \return The exit status of the first process that returns an error
|
||
|
* code or, if all of them executed correctly, the exit status
|
||
|
* of the last process in the collection.
|
||
|
*/
|
||
|
template <class Children>
|
||
|
inline status wait_children(Children &cs)
|
||
|
{
|
||
|
BOOST_ASSERT(cs.size() >= 2);
|
||
|
|
||
|
typename Children::iterator it = cs.begin();
|
||
|
while (it != cs.end())
|
||
|
{
|
||
|
const status s = it->wait();
|
||
|
++it;
|
||
|
if (it == cs.end())
|
||
|
return s;
|
||
|
else if (!s.exited() || s.exit_status() != EXIT_SUCCESS)
|
||
|
{
|
||
|
while (it != cs.end())
|
||
|
{
|
||
|
it->wait();
|
||
|
++it;
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOST_ASSERT(false);
|
||
|
return cs.begin()->wait();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|