This commit is contained in:
2026-03-23 20:54:41 +08:00
commit e13b3650e9
4596 changed files with 1015768 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_ASIO_FWD_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_ASIO_FWD_HPP_
#include <memory>
#include <boost/asio/ts/netfwd.hpp>
namespace boost { namespace asio {
class mutable_buffer;
class mutable_buffers_1;
class const_buffer;
class const_buffers_1;
template<typename Allocator>
class basic_streambuf;
typedef basic_streambuf<std::allocator<char>> streambuf;
template <typename Executor>
class basic_signal_set;
typedef basic_signal_set<any_io_executor> signal_set;
template <typename Handler>
class basic_yield_context;
namespace posix {
template <typename Executor>
class basic_stream_descriptor;
typedef basic_stream_descriptor<any_io_executor> stream_descriptor;
} //posix
} //asio
namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
class async_pipe;
template<typename T>
struct async_in_buffer;
template<int p1, int p2, typename Buffer>
struct async_out_buffer;
template<int p1, int p2, typename Type>
struct async_out_future;
} // posix
} // detail
using ::boost::process::v1::detail::posix::async_pipe;
} // v1
} // process
} // boost
#endif /* BOOST_PROCESS_DETAIL_POSIX_ASIO_FWD_HPP_ */

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_POSIX_ASYNC_HANDLER_HPP_
#define BOOST_PROCESS_POSIX_ASYNC_HANDLER_HPP_
#include <boost/process/v1/detail/posix/handler.hpp>
#include <type_traits>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct require_io_context {};
struct async_handler : handler_base_ext, require_io_context
{
};
template<typename T>
struct is_async_handler : std::is_base_of<async_handler, T> {};
template<typename T>
struct is_async_handler<T&> : std::is_base_of<async_handler, T> {};
template<typename T>
struct is_async_handler<const T&> : std::is_base_of<async_handler, T> {};
template<typename T>
struct does_require_io_context : std::is_base_of<require_io_context, T> {};
template<typename T>
struct does_require_io_context<T&> : std::is_base_of<require_io_context, T> {};
template<typename T>
struct does_require_io_context<const T&> : std::is_base_of<require_io_context, T> {};
}}}}}
#endif /* BOOST_PROCESS_WINDOWS_ASYNC_HANDLER_HPP_ */

View File

@@ -0,0 +1,111 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_ASYNC_IN_HPP
#define BOOST_PROCESS_DETAIL_POSIX_ASYNC_IN_HPP
#include <boost/process/v1/detail/handler_base.hpp>
#include <boost/process/v1/detail/posix/async_handler.hpp>
#include <boost/asio/write.hpp>
#include <boost/process/v1/async_pipe.hpp>
#include <memory>
#include <future>
#include <boost/process/v1/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<typename Buffer>
struct async_in_buffer : ::boost::process::v1::detail::posix::handler_base_ext,
::boost::process::v1::detail::posix::require_io_context,
::boost::process::v1::detail::uses_handles
{
Buffer & buf;
std::shared_ptr<std::promise<void>> promise;
async_in_buffer operator>(std::future<void> & fut)
{
promise = std::make_shared<std::promise<void>>();
fut = promise->get_future(); return std::move(*this);
}
std::shared_ptr<boost::process::v1::async_pipe> pipe;
async_in_buffer(Buffer & buf) : buf(buf)
{
}
template <typename Executor>
inline void on_success(Executor)
{
auto pipe_ = this->pipe;
if (this->promise)
{
auto promise_ = this->promise;
boost::asio::async_write(*pipe_, buf,
[pipe_, promise_](const boost::system::error_code & ec, std::size_t)
{
if (ec && (ec.value() != EBADF) && (ec.value() != EPERM) && (ec.value() != ENOENT))
{
std::error_code e(ec.value(), std::system_category());
promise_->set_exception(std::make_exception_ptr(process_error(e)));
}
else
promise_->set_value();
});
}
else
boost::asio::async_write(*pipe_, buf,
[pipe_](const boost::system::error_code&, std::size_t){});
std::move(*pipe_).source().close();
this->pipe = nullptr;
}
template<typename Executor>
void on_error(Executor &, const std::error_code &) const
{
std::move(*pipe).source().close();
}
template<typename Executor>
void on_setup(Executor & exec)
{
if (!pipe)
pipe = std::make_shared<boost::process::v1::async_pipe>(get_io_context(exec.seq));
}
std::array<int, 3> get_used_handles()
{
if (pipe)
return {STDIN_FILENO, pipe->native_source(), pipe->native_sink()};
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
return {STDIN_FILENO, STDIN_FILENO, STDIN_FILENO};
}
template <typename Executor>
void on_exec_setup(Executor &exec)
{
if (::dup2(pipe->native_source(), STDIN_FILENO) == -1)
exec.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
if (pipe->native_source() != STDIN_FILENO)
::close(pipe->native_source());
::close(pipe->native_sink());
}
};
}}}}}
#endif

View File

@@ -0,0 +1,190 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_ASYNC_OUT_HPP
#define BOOST_PROCESS_DETAIL_POSIX_ASYNC_OUT_HPP
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/asio/read.hpp>
#include <boost/process/v1/async_pipe.hpp>
#include <istream>
#include <memory>
#include <exception>
#include <future>
#include <array>
#include <boost/process/v1/detail/used_handles.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
inline int apply_out_handles(int handle, std::integral_constant<int, 1>, std::integral_constant<int, -1>)
{
return ::dup2(handle, STDOUT_FILENO);
}
inline int apply_out_handles(int handle, std::integral_constant<int, 2>, std::integral_constant<int, -1>)
{
return ::dup2(handle, STDERR_FILENO);
}
inline int apply_out_handles(int handle, std::integral_constant<int, 1>, std::integral_constant<int, 2>)
{
if (::dup2(handle, STDOUT_FILENO) == -1)
return -1;
if (::dup2(handle, STDERR_FILENO) == -1)
return -1;
return 0;
}
template<int p1, int p2, typename Buffer>
struct async_out_buffer : ::boost::process::v1::detail::posix::handler_base_ext,
::boost::process::v1::detail::posix::require_io_context,
::boost::process::v1::detail::uses_handles
{
Buffer & buf;
std::shared_ptr<boost::process::v1::async_pipe> pipe;
std::array<int, 4> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
if (pipe)
return {pipe->native_source(), pipe->native_sink(), pp1, pp2};
else //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
return {pp1, pp2, pp1, pp2};
}
async_out_buffer(Buffer & buf) : buf(buf)
{
}
template <typename Executor>
inline void on_success(Executor &)
{
auto pipe = this->pipe;
boost::asio::async_read(*pipe, buf,
[pipe](const boost::system::error_code&, std::size_t){});
this->pipe = nullptr;
std::move(*pipe).sink().close();
}
template<typename Executor>
void on_error(Executor &, const std::error_code &) const
{
std::move(*pipe).sink().close();
}
template<typename Executor>
void on_setup(Executor & exec)
{
pipe = std::make_shared<boost::process::v1::async_pipe>(get_io_context(exec.seq));
}
template <typename Executor>
void on_exec_setup(Executor &exec)
{
int res = apply_out_handles(pipe->native_sink(),
std::integral_constant<int, p1>(), std::integral_constant<int, p2>());
if (res == -1)
exec.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
::close(pipe->native_sink());
::close(pipe->native_source());
}
};
template<int p1, int p2, typename Type>
struct async_out_future : ::boost::process::v1::detail::posix::handler_base_ext,
::boost::process::v1::detail::posix::require_io_context
{
std::shared_ptr<std::promise<Type>> promise = std::make_shared<std::promise<Type>>();
std::shared_ptr<boost::asio::streambuf> buffer = std::make_shared<boost::asio::streambuf>();
std::shared_ptr<boost::process::v1::async_pipe> pipe;
async_out_future(std::future<Type> & fut)
{
fut = promise->get_future();
}
template <typename Executor>
inline void on_success(Executor &)
{
auto pipe_ = this->pipe;
auto buffer_ = this->buffer;
auto promise_ = this->promise;
boost::asio::async_read(*pipe_, *buffer_,
[pipe_, buffer_, promise_](const boost::system::error_code& ec, std::size_t)
{
if (ec && (ec.value() != ENOENT))
{
std::error_code e(ec.value(), std::system_category());
promise_->set_exception(std::make_exception_ptr(process_error(e)));
}
else
{
std::istream is (buffer_.get());
Type arg;
if (buffer_->size() > 0)
{
arg.resize(buffer_->size());
is.read(&*arg.begin(), buffer_->size());
}
promise_->set_value(std::move(arg));
}
});
std::move(*pipe_).sink().close();
this->pipe = nullptr;
}
template<typename Executor>
void on_error(Executor &, const std::error_code &) const
{
std::move(*pipe).sink().close();
}
template<typename Executor>
void on_setup(Executor & exec)
{
pipe = std::make_shared<boost::process::v1::async_pipe>(get_io_context(exec.seq));
}
template <typename Executor>
void on_exec_setup(Executor &exec)
{
int res = apply_out_handles(pipe->native_sink(),
std::integral_constant<int, p1>(), std::integral_constant<int, p2>());
if (res == -1)
exec.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
::close(pipe->native_sink());
::close(pipe->native_source());
}
};
}}}}}
#endif

View File

@@ -0,0 +1,365 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_ASYNC_PIPE_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_ASYNC_PIPE_HPP_
#include <boost/process/v1/detail/posix/basic_pipe.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/asio/post.hpp>
#include <system_error>
#include <string>
#include <utility>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
class async_pipe
{
::boost::asio::posix::stream_descriptor _source;
::boost::asio::posix::stream_descriptor _sink ;
public:
typedef int native_handle_type;
typedef ::boost::asio::posix::stream_descriptor handle_type;
typedef typename handle_type::executor_type executor_type;
executor_type get_executor()
{
return _source.get_executor();
}
inline async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios) {}
inline async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & ios_sink) : _source(ios_source), _sink(ios_sink)
{
int fds[2];
if (::pipe(fds) == -1)
boost::process::v1::detail::throw_last_error("pipe(2) failed");
_source.assign(fds[0]);
_sink .assign(fds[1]);
};
inline async_pipe(boost::asio::io_context & ios, const std::string & name)
: async_pipe(ios, ios, name) {}
inline async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & io_sink, const std::string & name);
inline async_pipe(const async_pipe& lhs);
async_pipe(async_pipe&& lhs) : _source(std::move(lhs._source)), _sink(std::move(lhs._sink))
{
lhs._source = ::boost::asio::posix::stream_descriptor{lhs._source.get_executor()};
lhs._sink = ::boost::asio::posix::stream_descriptor{lhs._sink. get_executor()};
}
template<class CharT, class Traits = std::char_traits<CharT>>
explicit async_pipe(::boost::asio::io_context & ios_source,
::boost::asio::io_context & ios_sink,
const basic_pipe<CharT, Traits> & p)
: _source(ios_source, p.native_source()), _sink(ios_sink, p.native_sink())
{
}
template<class CharT, class Traits = std::char_traits<CharT>>
explicit async_pipe(boost::asio::io_context & ios, const basic_pipe<CharT, Traits> & p)
: async_pipe(ios, ios, p)
{
}
template<class CharT, class Traits = std::char_traits<CharT>>
inline async_pipe& operator=(const basic_pipe<CharT, Traits>& p);
inline async_pipe& operator=(const async_pipe& rhs);
inline async_pipe& operator=(async_pipe&& lhs);
~async_pipe()
{
boost::system::error_code ec;
close(ec);
}
template<class CharT, class Traits = std::char_traits<CharT>>
inline explicit operator basic_pipe<CharT, Traits>() const;
void cancel()
{
if (_sink.is_open())
_sink.cancel();
if (_source.is_open())
_source.cancel();
}
void close()
{
if (_sink.is_open())
_sink.close();
if (_source.is_open())
_source.close();
}
void close(boost::system::error_code & ec)
{
if (_sink.is_open())
_sink.close(ec);
if (_source.is_open())
_source.close(ec);
}
bool is_open() const
{
return _sink.is_open() || _source.is_open();
}
void async_close()
{
if (_sink.is_open())
boost::asio::post(_sink.get_executor(), [this]{_sink.close();});
if (_source.is_open())
boost::asio::post(_source.get_executor(), [this]{_source.close();});
}
template<typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence & buffers)
{
return _source.read_some(buffers);
}
template<typename MutableBufferSequence>
std::size_t write_some(const MutableBufferSequence & buffers)
{
return _sink.write_some(buffers);
}
template<typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
{
return _source.read_some(buffers, ec);
}
template<typename MutableBufferSequence>
std::size_t write_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
{
return _sink.write_some(buffers, ec);
}
native_handle_type native_source() const {return const_cast<boost::asio::posix::stream_descriptor&>(_source).native_handle();}
native_handle_type native_sink () const {return const_cast<boost::asio::posix::stream_descriptor&>(_sink ).native_handle();}
template<typename MutableBufferSequence,
typename ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(boost::system::error_code, std::size_t))
async_read_some(
const MutableBufferSequence & buffers,
ReadHandler &&handler)
{
return _source.async_read_some(buffers, std::forward<ReadHandler>(handler));
}
template<typename ConstBufferSequence,
typename WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(boost::system::error_code, std::size_t))
async_write_some(
const ConstBufferSequence & buffers,
WriteHandler&& handler)
{
return _sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
}
const handle_type & sink () const & {return _sink;}
const handle_type & source() const & {return _source;}
handle_type && sink() && { return std::move(_sink); }
handle_type && source()&& { return std::move(_source); }
handle_type source(::boost::asio::io_context& ios) &&
{
::boost::asio::posix::stream_descriptor stolen(ios, _source.release());
return stolen;
}
handle_type sink (::boost::asio::io_context& ios) &&
{
::boost::asio::posix::stream_descriptor stolen(ios, _sink.release());
return stolen;
}
handle_type source(::boost::asio::io_context& ios) const &
{
auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(_source).native_handle();
return ::boost::asio::posix::stream_descriptor(ios, ::dup(source_in));
}
handle_type sink (::boost::asio::io_context& ios) const &
{
auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(_sink).native_handle();
return ::boost::asio::posix::stream_descriptor(ios, ::dup(sink_in));
}
};
async_pipe::async_pipe(boost::asio::io_context & ios_source,
boost::asio::io_context & ios_sink,
const std::string & name) : _source(ios_source), _sink(ios_sink)
{
auto fifo = mkfifo(name.c_str(), 0666 );
if (fifo != 0)
boost::process::v1::detail::throw_last_error("mkfifo() failed");
int read_fd = open(name.c_str(), O_RDWR);
if (read_fd == -1)
boost::process::v1::detail::throw_last_error();
int write_fd = dup(read_fd);
if (write_fd == -1)
boost::process::v1::detail::throw_last_error();
_source.assign(read_fd);
_sink .assign(write_fd);
}
async_pipe::async_pipe(const async_pipe & p) :
_source(const_cast<async_pipe&>(p)._source.get_executor()),
_sink( const_cast<async_pipe&>(p)._sink.get_executor())
{
//cannot get the handle from a const object.
auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(_source).native_handle();
auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(_sink).native_handle();
if (source_in == -1)
_source.assign(-1);
else
{
_source.assign(::dup(source_in));
if (_source.native_handle()== -1)
::boost::process::v1::detail::throw_last_error("dup()");
}
if (sink_in == -1)
_sink.assign(-1);
else
{
_sink.assign(::dup(sink_in));
if (_sink.native_handle() == -1)
::boost::process::v1::detail::throw_last_error("dup()");
}
}
async_pipe& async_pipe::operator=(const async_pipe & p)
{
int source;
int sink;
//cannot get the handle from a const object.
auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(p._source).native_handle();
auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(p._sink).native_handle();
if (source_in == -1)
source = -1;
else
{
source = ::dup(source_in);
if (source == -1)
::boost::process::v1::detail::throw_last_error("dup()");
}
if (sink_in == -1)
sink = -1;
else
{
sink = ::dup(sink_in);
if (sink == -1)
::boost::process::v1::detail::throw_last_error("dup()");
}
_source.assign(source);
_sink. assign(sink);
return *this;
}
async_pipe& async_pipe::operator=(async_pipe && lhs)
{
std::swap(_source, lhs._source);
std::swap(_sink, lhs._sink);
return *this;
}
template<class CharT, class Traits>
async_pipe::operator basic_pipe<CharT, Traits>() const
{
int source;
int sink;
//cannot get the handle from a const object.
auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(_source).native_handle();
auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(_sink).native_handle();
if (source_in == -1)
source = -1;
else
{
source = ::dup(source_in);
if (source == -1)
::boost::process::v1::detail::throw_last_error("dup()");
}
if (sink_in == -1)
sink = -1;
else
{
sink = ::dup(sink_in);
if (sink == -1)
::boost::process::v1::detail::throw_last_error("dup()");
}
return basic_pipe<CharT, Traits>{source, sink};
}
inline bool operator==(const async_pipe & lhs, const async_pipe & rhs)
{
return compare_handles(lhs.native_source(), rhs.native_source()) &&
compare_handles(lhs.native_sink(), rhs.native_sink());
}
inline bool operator!=(const async_pipe & lhs, const async_pipe & rhs)
{
return !compare_handles(lhs.native_source(), rhs.native_source()) ||
!compare_handles(lhs.native_sink(), rhs.native_sink());
}
template<class Char, class Traits>
inline bool operator==(const async_pipe & lhs, const basic_pipe<Char, Traits> & rhs)
{
return compare_handles(lhs.native_source(), rhs.native_source()) &&
compare_handles(lhs.native_sink(), rhs.native_sink());
}
template<class Char, class Traits>
inline bool operator!=(const async_pipe & lhs, const basic_pipe<Char, Traits> & rhs)
{
return !compare_handles(lhs.native_source(), rhs.native_source()) ||
!compare_handles(lhs.native_sink(), rhs.native_sink());
}
template<class Char, class Traits>
inline bool operator==(const basic_pipe<Char, Traits> & lhs, const async_pipe & rhs)
{
return compare_handles(lhs.native_source(), rhs.native_source()) &&
compare_handles(lhs.native_sink(), rhs.native_sink());
}
template<class Char, class Traits>
inline bool operator!=(const basic_pipe<Char, Traits> & lhs, const async_pipe & rhs)
{
return !compare_handles(lhs.native_source(), rhs.native_source()) ||
!compare_handles(lhs.native_sink(), rhs.native_sink());
}
}}}}}
#endif /* INCLUDE_BOOST_PIPE_DETAIL_WINDOWS_ASYNC_PIPE_HPP_ */

View File

@@ -0,0 +1,182 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_BASIC_CMD_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_BASIC_CMD_HPP_
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/process/v1/detail/posix/cmd.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/process/v1/shell.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/join.hpp>
#include <string>
#include <vector>
namespace boost
{
namespace process
{
BOOST_PROCESS_V1_INLINE namespace v1
{
namespace detail
{
namespace posix
{
inline std::string build_cmd_shell(const std::string & exe, std::vector<std::string> && data)
{
std::string st = exe;
for (auto & arg : data)
{
boost::replace_all(arg, "\"", "\\\"");
auto it = std::find(arg.begin(), arg.end(), ' ');//contains space?
if (it != arg.end())//ok, contains spaces.
{
//the first one is put directly onto the output,
//because then I don't have to copy the whole string
arg.insert(arg.begin(), '"' );
arg += '"'; //that is the post one.
}
if (!st.empty())//first one does not need a preceding space
st += ' ';
st += arg;
}
return st ;
}
inline std::vector<std::string> build_args(const std::string & data)
{
std::vector<std::string> st;
typedef std::string::const_iterator itr_t;
//normal quotes outside can be stripped, inside ones marked as \" will be replaced.
auto make_entry = [](const itr_t & begin, const itr_t & end)
{
std::string data;
if ((*begin == '"') && (*(end-1) == '"'))
data.assign(begin+1, end-1);
else
data.assign(begin, end);
boost::replace_all(data, "\\\"", "\"");
return data;
};
bool in_quote = false;
auto part_beg = data.cbegin();
auto itr = data.cbegin();
for (; itr != data.cend(); itr++)
{
if (*itr == '"')
in_quote ^= true;
if (!in_quote && (*itr == ' '))
{
//alright, got a space
if ((itr != data.cbegin()) && (*(itr -1) != ' ' ))
st.push_back(make_entry(part_beg, itr));
part_beg = itr+1;
}
}
if (part_beg != itr)
st.emplace_back(make_entry(part_beg, itr));
return st;
}
template<typename Char>
struct exe_cmd_init;
template<>
struct exe_cmd_init<char> : boost::process::v1::detail::api::handler_base_ext
{
exe_cmd_init(const exe_cmd_init & ) = delete;
exe_cmd_init(exe_cmd_init && ) = default;
exe_cmd_init(std::string && exe, std::vector<std::string> && args)
: exe(std::move(exe)), args(std::move(args)) {};
template <class Executor>
void on_setup(Executor& exec)
{
if (exe.empty()) //cmd style
{
if (args.empty())
exec.exe = "";
else
exec.exe = args.front().c_str();
exec.cmd_style = true;
}
else
exec.exe = &exe.front();
cmd_impl = make_cmd();
exec.cmd_line = cmd_impl.data();
}
static exe_cmd_init exe_args(std::string && exe, std::vector<std::string> && args) {return exe_cmd_init(std::move(exe), std::move(args));}
static exe_cmd_init cmd (std::string && cmd)
{
auto args = build_args(cmd);
return exe_cmd_init({}, std::move(args));
}
static exe_cmd_init exe_args_shell(std::string&& exe, std::vector<std::string> && args)
{
auto cmd = build_cmd_shell(std::move(exe), std::move(args));
std::vector<std::string> args_ = {"-c", std::move(cmd)};
std::string sh = shell().string();
return exe_cmd_init(std::move(sh), std::move(args_));
}
static exe_cmd_init cmd_shell(std::string&& cmd)
{
std::vector<std::string> args = {"-c", cmd};
std::string sh = shell().string();
return exe_cmd_init(
std::move(sh),
{std::move(args)});
}
private:
inline std::vector<char*> make_cmd();
std::string exe;
std::vector<std::string> args;
std::vector<char*> cmd_impl;
};
std::vector<char*> exe_cmd_init<char>::make_cmd()
{
// any string must be writable.
static char empty_string[1] = "";
std::vector<char*> vec;
if (!exe.empty())
vec.push_back(exe.empty() ? empty_string : &exe.front());
if (!args.empty()) {
for (auto & v : args)
vec.push_back(v.empty() ? empty_string : &v.front());
}
vec.push_back(nullptr);
return vec;
}
}}}}}
#endif

View File

@@ -0,0 +1,200 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_POSIX_PIPE_HPP
#define BOOST_PROCESS_POSIX_PIPE_HPP
#include <boost/process/v1/filesystem.hpp>
#include <boost/process/v1/detail/posix/compare_handles.hpp>
#include <system_error>
#include <array>
#include <unistd.h>
#include <fcntl.h>
#include <memory>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<class CharT, class Traits = std::char_traits<CharT>>
class basic_pipe
{
int _source = -1;
int _sink = -1;
public:
explicit basic_pipe(int source, int sink) : _source(source), _sink(sink) {}
explicit basic_pipe(int source, int sink, const std::string&) : _source(source), _sink(sink) {}
typedef CharT char_type ;
typedef Traits traits_type;
typedef typename Traits::int_type int_type ;
typedef typename Traits::pos_type pos_type ;
typedef typename Traits::off_type off_type ;
typedef int native_handle_type;
basic_pipe()
{
int fds[2];
if (::pipe(fds) == -1)
boost::process::v1::detail::throw_last_error("pipe(2) failed");
_source = fds[0];
_sink = fds[1];
}
inline basic_pipe(const basic_pipe& rhs);
explicit inline basic_pipe(const std::string& name);
basic_pipe(basic_pipe&& lhs) : _source(lhs._source), _sink(lhs._sink)
{
lhs._source = -1;
lhs._sink = -1;
}
inline basic_pipe& operator=(const basic_pipe& );
basic_pipe& operator=(basic_pipe&& lhs)
{
_source = lhs._source;
_sink = lhs._sink ;
lhs._source = -1;
lhs._sink = -1;
return *this;
}
~basic_pipe()
{
if (_sink != -1)
::close(_sink);
if (_source != -1)
::close(_source);
}
native_handle_type native_source() const {return _source;}
native_handle_type native_sink () const {return _sink;}
void assign_source(native_handle_type h) { _source = h;}
void assign_sink (native_handle_type h) { _sink = h;}
int_type write(const char_type * data, int_type count)
{
ssize_t write_len;
while ((write_len = ::write(_sink, data, count * sizeof(char_type))) == -1)
{
//Try again if interrupted
auto err = errno;
if (err != EINTR)
::boost::process::v1::detail::throw_last_error();
}
return static_cast<int_type>(write_len);
}
int_type read(char_type * data, int_type count)
{
ssize_t read_len;
while ((read_len = ::read(_source, data, count * sizeof(char_type))) == -1)
{
//Try again if interrupted
auto err = errno;
if (err != EINTR)
::boost::process::v1::detail::throw_last_error();
}
return static_cast<int_type>(read_len);
}
bool is_open() const
{
return (_source != -1) ||
(_sink != -1);
}
void close()
{
if (_source != -1)
::close(_source);
if (_sink != -1)
::close(_sink);
_source = -1;
_sink = -1;
}
};
template<class CharT, class Traits>
basic_pipe<CharT, Traits>::basic_pipe(const basic_pipe & rhs)
{
if (rhs._source != -1)
{
_source = ::dup(rhs._source);
if (_source == -1)
::boost::process::v1::detail::throw_last_error("dup() failed");
}
if (rhs._sink != -1)
{
_sink = ::dup(rhs._sink);
if (_sink == -1)
::boost::process::v1::detail::throw_last_error("dup() failed");
}
}
template<class CharT, class Traits>
basic_pipe<CharT, Traits> &basic_pipe<CharT, Traits>::operator=(const basic_pipe & rhs)
{
if (rhs._source != -1)
{
_source = ::dup(rhs._source);
if (_source == -1)
::boost::process::v1::detail::throw_last_error("dup() failed");
}
if (rhs._sink != -1)
{
_sink = ::dup(rhs._sink);
if (_sink == -1)
::boost::process::v1::detail::throw_last_error("dup() failed");
}
return *this;
}
template<class CharT, class Traits>
basic_pipe<CharT, Traits>::basic_pipe(const std::string & name)
{
auto fifo = mkfifo(name.c_str(), 0666 );
if (fifo != 0)
boost::process::v1::detail::throw_last_error("mkfifo() failed");
int read_fd = open(name.c_str(), O_RDWR);
if (read_fd == -1)
boost::process::v1::detail::throw_last_error();
int write_fd = dup(read_fd);
if (write_fd == -1)
boost::process::v1::detail::throw_last_error();
_sink = write_fd;
_source = read_fd;
::unlink(name.c_str());
}
template<class Char, class Traits>
inline bool operator==(const basic_pipe<Char, Traits> & lhs, const basic_pipe<Char, Traits> & rhs)
{
return compare_handles(lhs.native_source(), rhs.native_source()) &&
compare_handles(lhs.native_sink(), rhs.native_sink());
}
template<class Char, class Traits>
inline bool operator!=(const basic_pipe<Char, Traits> & lhs, const basic_pipe<Char, Traits> & rhs)
{
return !compare_handles(lhs.native_source(), rhs.native_source()) ||
!compare_handles(lhs.native_sink(), rhs.native_sink());
}
}}}}}
#endif

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_POSIX_CHILD_HPP
#define BOOST_PROCESS_POSIX_CHILD_HPP
#include <utility>
#include <system_error>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
typedef ::pid_t pid_t;
struct child_handle
{
int pid {-1};
explicit child_handle(int pid) : pid(pid)
{}
child_handle() = default;
~child_handle() = default;
child_handle(const child_handle & c) = delete;
child_handle(child_handle && c) : pid(c.pid)
{
c.pid = -1;
}
child_handle &operator=(const child_handle & c) = delete;
child_handle &operator=(child_handle && c)
{
pid = c.pid;
c.pid = -1;
return *this;
}
int id() const
{
return pid;
}
bool in_group() const {return true;}
bool in_group(std::error_code&) const noexcept {return true;}
typedef int process_handle_t;
process_handle_t process_handle() const { return pid; }
bool valid() const
{
return pid != -1;
}
};
}}}}}
#endif

View File

@@ -0,0 +1,34 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_WINDOWS_INITIALIZERS_CLOSE_IN_HPP
#define BOOST_PROCESS_WINDOWS_INITIALIZERS_CLOSE_IN_HPP
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/process/v1/detail/used_handles.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct close_in : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
template <class Executor>
void on_exec_setup(Executor &e) const
{
if (::close(STDIN_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
}
int get_used_handles() {return STDIN_FILENO;}
};
}}}}}
#endif

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
#define BOOST_PROCESS_DETAIL_POSIX_CLOSE_OUT_HPP
#include <boost/process/v1/detail/used_handles.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <array>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<int p1, int p2>
struct close_out : handler_base_ext
{
template <class Executor>
inline void on_exec_setup(Executor &e) const;
std::array<int, 2> get_used_handles() {return {{p1 != -1 ? p1 : p2, p2 != -1 ? p2 : p1}};}
};
template<>
template<typename Executor>
void close_out<1,-1>::on_exec_setup(Executor &e) const
{
if (::close(STDOUT_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
}
template<>
template<typename Executor>
void close_out<2,-1>::on_exec_setup(Executor &e) const
{
if (::close(STDERR_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
}
template<>
template<typename Executor>
void close_out<1,2>::on_exec_setup(Executor &e) const
{
if (::close(STDOUT_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
if (::close(STDERR_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
}
}}}}}
#endif

View File

@@ -0,0 +1,106 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_CMD_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_CMD_HPP_
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <string>
#include <vector>
namespace boost
{
namespace process
{
BOOST_PROCESS_V1_INLINE namespace v1
{
namespace detail
{
namespace posix
{
template<typename Char>
inline std::vector<std::basic_string<Char>> build_cmd(const std::basic_string<Char> & value)
{
std::vector<std::basic_string<Char>> ret;
bool in_quotes = false;
auto beg = value.begin();
for (auto itr = value.begin(); itr != value.end(); itr++)
{
if (*itr == quote_sign<Char>())
in_quotes = !in_quotes;
if (!in_quotes && (*itr == space_sign<Char>()))
{
if (itr != beg)
{
ret.emplace_back(beg, itr);
beg = itr + 1;
}
}
}
if (beg != value.end())
ret.emplace_back(beg, value.end());
return ret;
}
template<typename Char>
struct cmd_setter_ : handler_base_ext
{
typedef Char value_type;
typedef std::basic_string<value_type> string_type;
cmd_setter_(string_type && cmd_line) : _cmd_line(api::build_cmd(std::move(cmd_line))) {}
cmd_setter_(const string_type & cmd_line) : _cmd_line(api::build_cmd(cmd_line)) {}
template <class Executor>
void on_setup(Executor& exec)
{
exec.exe = _cmd_impl.front();
exec.cmd_line = &_cmd_impl.front();
exec.cmd_style = true;
}
string_type str() const
{
string_type ret;
std::size_t size = 0;
for (auto & cmd : _cmd_line)
size += cmd.size() + 1;
ret.reserve(size -1);
for (auto & cmd : _cmd_line)
{
if (!ret.empty())
ret += equal_sign<Char>();
ret += cmd;
}
return ret;
}
private:
static inline std::vector<Char*> make_cmd(std::vector<string_type> & args);
std::vector<string_type> _cmd_line;
std::vector<Char*> _cmd_impl = make_cmd(_cmd_line);
};
template<typename Char>
std::vector<Char*> cmd_setter_<Char>::make_cmd(std::vector<std::basic_string<Char>> & args)
{
std::vector<Char*> vec;
for (auto & v : args)
vec.push_back(&v.front());
vec.push_back(nullptr);
return vec;
}
}}}}}
#endif

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_COMPARE_HANDLES_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_COMPARE_HANDLES_HPP_
#include <boost/process/v1/detail/config.hpp>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
inline bool compare_handles(int lhs, int rhs)
{
if ((lhs == -1) || (rhs == -1))
return false;
if (lhs == rhs)
return true;
struct stat stat1, stat2;
if(fstat(lhs, &stat1) < 0) ::boost::process::v1::detail::throw_last_error("fstat() failed");
if(fstat(rhs, &stat2) < 0) ::boost::process::v1::detail::throw_last_error("fstat() failed");
return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
}
}}}}}
#endif /* BOOST_PROCESS_DETAIL_POSIX_COMPARE_HANDLES_HPP_ */

View File

@@ -0,0 +1,41 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_ENV_INIT_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_ENV_INIT_HPP_
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/process/v1/environment.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<typename Char>
struct env_init;
template<>
struct env_init<char> : handler_base_ext
{
boost::process::v1::environment env;
env_init(boost::process::v1::environment && env) : env(std::move(env)) {};
env_init(const boost::process::v1::environment & env) : env(env) {};
template <class Executor>
void on_setup(Executor &exec) const
{
exec.env = env._env_impl;
}
};
}}}}}
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_ENV_INIT_HPP_ */

View File

@@ -0,0 +1,327 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_ENVIRONMENT_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_ENVIRONMENT_HPP_
#include <string>
#include <vector>
#include <unordered_map>
#include <boost/process/v1/detail/config.hpp>
#include <algorithm>
#include <cstdlib>
#include <boost/process/v1/locale.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<typename Char>
class native_environment_impl
{
static std::vector<std::basic_string<Char>> _load()
{
std::vector<std::basic_string<Char>> val;
auto p = environ;
while (*p != nullptr)
{
std::string str = *p;
val.push_back(::boost::process::v1::detail::convert(str));
p++;
}
return val;
}
static std::vector<Char*> _load_var(std::vector<std::basic_string<Char>> & vec)
{
std::vector<Char*> val;
val.resize(vec.size() + 1);
std::transform(vec.begin(), vec.end(), val.begin(),
[](std::basic_string<Char> & str)
{
return &str.front();
});
val.back() = nullptr;
return val;
}
std::vector<std::basic_string<Char>> _buffer = _load();
std::vector<Char*> _impl = _load_var(_buffer);
public:
using char_type = Char;
using pointer_type = const char_type*;
using string_type = std::basic_string<char_type>;
using native_handle_type = char_type **;
void reload()
{
_buffer = _load();
_impl = _load_var(_buffer);
_env_impl = _impl.data();
}
string_type get(const pointer_type id) { return get(string_type(id)); }
void set(const pointer_type id, const pointer_type value)
{
set(string_type(id), string_type(value));
}
void reset(const pointer_type id) { reset(string_type(id)); }
string_type get(const string_type & id)
{
std::string id_c = ::boost::process::v1::detail::convert(id);
std::string g = ::getenv(id_c.c_str());
return ::boost::process::v1::detail::convert(g.c_str());
}
void set(const string_type & id, const string_type & value)
{
std::string id_c = ::boost::process::v1::detail::convert(id.c_str());
std::string value_c = ::boost::process::v1::detail::convert(value.c_str());
auto res = ::setenv(id_c.c_str(), value_c.c_str(), true);
if (res != 0)
boost::process::v1::detail::throw_last_error();
}
void reset(const string_type & id)
{
std::string id_c = ::boost::process::v1::detail::convert(id.c_str());
auto res = ::unsetenv(id_c.c_str());
if (res != 0)
::boost::process::v1::detail::throw_last_error();
}
native_environment_impl() = default;
native_environment_impl(const native_environment_impl& ) = delete;
native_environment_impl(native_environment_impl && ) = default;
native_environment_impl & operator=(const native_environment_impl& ) = delete;
native_environment_impl & operator=(native_environment_impl && ) = default;
native_handle_type _env_impl = _impl.data();
native_handle_type native_handle() const {return _env_impl;}
};
template<>
class native_environment_impl<char>
{
public:
using char_type = char;
using pointer_type = const char_type*;
using string_type = std::basic_string<char_type>;
using native_handle_type = char_type **;
void reload() {this->_env_impl = environ;}
string_type get(const pointer_type id) { return getenv(id); }
void set(const pointer_type id, const pointer_type value)
{
auto res = ::setenv(id, value, 1);
if (res != 0)
boost::process::v1::detail::throw_last_error();
reload();
}
void reset(const pointer_type id)
{
auto res = ::unsetenv(id);
if (res != 0)
boost::process::v1::detail::throw_last_error();
reload();
}
string_type get(const string_type & id) {return get(id.c_str());}
void set(const string_type & id, const string_type & value) {set(id.c_str(), value.c_str()); }
void reset(const string_type & id) {reset(id.c_str());}
native_environment_impl() = default;
native_environment_impl(const native_environment_impl& ) = delete;
native_environment_impl(native_environment_impl && ) = default;
native_environment_impl & operator=(const native_environment_impl& ) = delete;
native_environment_impl & operator=(native_environment_impl && ) = default;
native_handle_type _env_impl = environ;
native_handle_type native_handle() const {return environ;}
};
template<typename Char>
struct basic_environment_impl
{
std::vector<std::basic_string<Char>> _data {};
static std::vector<Char*> _load_var(std::vector<std::basic_string<Char>> & data);
std::vector<Char*> _env_arr{_load_var(_data)};
public:
using char_type = Char;
using pointer_type = const char_type*;
using string_type = std::basic_string<char_type>;
using native_handle_type = Char**;
void reload()
{
_env_arr = _load_var(_data);
_env_impl = _env_arr.data();
}
string_type get(const pointer_type id) {return get(string_type(id));}
void set(const pointer_type id, const pointer_type value) {set(string_type(id), value);}
void reset(const pointer_type id) {reset(string_type(id));}
string_type get(const string_type & id);
void set(const string_type & id, const string_type & value);
void reset(const string_type & id);
basic_environment_impl(const native_environment_impl<Char> & nei);
basic_environment_impl() = default;
basic_environment_impl(const basic_environment_impl& rhs)
: _data(rhs._data)
{
}
basic_environment_impl(basic_environment_impl && ) = default;
basic_environment_impl & operator=(const basic_environment_impl& rhs)
{
_data = rhs._data;
_env_arr = _load_var(_data);
_env_impl = &*_env_arr.begin();
return *this;
}
basic_environment_impl & operator=(basic_environment_impl && ) = default;
template<typename CharR>
explicit inline basic_environment_impl(
const basic_environment_impl<CharR>& rhs,
const ::boost::process::v1::codecvt_type & cv = ::boost::process::v1::codecvt())
: _data(rhs._data.size())
{
std::transform(rhs._data.begin(), rhs._data.end(), _data.begin(),
[&](const std::basic_string<CharR> & st)
{
return ::boost::process::v1::detail::convert(st, cv);
}
);
reload();
}
template<typename CharR>
basic_environment_impl & operator=(const basic_environment_impl<CharR>& rhs)
{
_data = ::boost::process::v1::detail::convert(rhs._data);
_env_arr = _load_var(&*_data.begin());
_env_impl = &*_env_arr.begin();
return *this;
}
Char ** _env_impl = &*_env_arr.data();
native_handle_type native_handle() const {return &_data.front();}
};
template<typename Char>
basic_environment_impl<Char>::basic_environment_impl(const native_environment_impl<Char> & nei)
{
auto beg = nei.native_handle();
auto end = beg;
while (*end != nullptr)
end++;
this->_data.assign(beg, end);
reload();
}
template<typename Char>
inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;
return std::equal(id.begin(), id.end(), st.begin()) && (st[id.size()] == equal_sign<Char>());
}
);
if (itr == _data.end())
{
return "";
}
else return
itr->data() + id.size(); //id=Thingy -> +2 points to T
}
template<typename Char>
inline void basic_environment_impl<Char>::set(const string_type &id, const string_type &value)
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;
return std::equal(id.begin(), id.end(), st.begin()) && (st[id.size()] == equal_sign<Char>());
}
);
if (itr != _data.end())
*itr = id + equal_sign<Char>() + value;
else
_data.push_back(id + equal_sign<Char>() + value);
reload();
}
template<typename Char>
inline void basic_environment_impl<Char>::reset(const string_type &id)
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;
return std::equal(id.begin(), id.end(), st.begin()) && (st[id.size()] == equal_sign<Char>());
}
);
if (itr != _data.end())
{
_data.erase(itr);//and remove it
}
reload();
}
template<typename Char>
std::vector<Char*> basic_environment_impl<Char>::_load_var(std::vector<std::basic_string<Char>> & data)
{
std::vector<Char*> ret;
ret.reserve(data.size() +1);
for (auto & val : data)
{
if (val.empty())
val.push_back(0);
ret.push_back(&val.front());
}
ret.push_back(nullptr);
return ret;
}
template<typename T> constexpr T env_seperator();
template<> constexpr char env_seperator() {return ':'; }
template<> constexpr wchar_t env_seperator() {return L':'; }
typedef int native_handle_t;
inline int get_id() {return getpid(); }
inline int native_handle() {return getpid(); }
}
}
}
}
}
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_ */

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_EXE_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_EXE_HPP_
#include <boost/process/v1/detail/config.hpp>
namespace boost
{
namespace process
{
BOOST_PROCESS_V1_INLINE namespace v1
{
namespace detail
{
namespace posix
{
template<class StringType, class Executor>
inline void apply_exe(const StringType & exe, Executor & e)
{
e.exe = exe.c_str();
}
}
}
}
}
}
#endif /* INCLUDE_BOOST_PROCESS_WINDOWS_ARGS_HPP_ */

View File

@@ -0,0 +1,573 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_EXECUTOR_HPP
#define BOOST_PROCESS_DETAIL_POSIX_EXECUTOR_HPP
#include <boost/process/v1/detail/child_decl.hpp>
#include <boost/process/v1/error.hpp>
#include <boost/process/v1/pipe.hpp>
#include <boost/process/v1/detail/posix/basic_pipe.hpp>
#include <boost/process/v1/detail/posix/use_vfork.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <cstdlib>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/core/ignore_unused.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<typename Executor>
struct on_setup_t
{
Executor & exec;
on_setup_t(Executor & exec) : exec(exec) {};
template<typename T>
void operator()(T & t) const
{
if (!exec.error())
t.on_setup(exec);
}
};
template<typename Executor>
struct on_error_t
{
Executor & exec;
const std::error_code & error;
on_error_t(Executor & exec, const std::error_code & error) : exec(exec), error(error) {};
template<typename T>
void operator()(T & t) const
{
t.on_error(exec, error);
}
};
template<typename Executor>
struct on_success_t
{
Executor & exec;
on_success_t(Executor & exec) : exec(exec) {};
template<typename T>
void operator()(T & t) const {t.on_success(exec);}
};
template<typename Executor>
struct on_fork_error_t
{
Executor & exec;
const std::error_code & error;
on_fork_error_t(Executor & exec, const std::error_code & error) : exec(exec), error(error) {};
template<typename T>
void operator()(T & t) const
{
t.on_fork_error(exec, error);
}
};
template<typename Executor>
struct on_exec_setup_t
{
Executor & exec;
on_exec_setup_t(Executor & exec) : exec(exec) {};
template<typename T>
void operator()(T & t) const
{
t.on_exec_setup(exec);
}
};
template<typename Executor>
struct on_exec_error_t
{
Executor & exec;
const std::error_code &ec;
on_exec_error_t(Executor & exec, const std::error_code & error) : exec(exec), ec(error) {};
template<typename T>
void operator()(T & t) const
{
t.on_exec_error(exec, ec);
}
};
template<typename Executor>
struct on_fork_success_t
{
Executor & exec;
on_fork_success_t(Executor & exec) : exec(exec) {};
template<typename T>
void operator()(T & t) const
{
t.on_fork_success(exec);
}
};
template<typename Executor> on_setup_t <Executor> call_on_setup (Executor & exec) {return exec;}
template<typename Executor> on_error_t <Executor> call_on_error (Executor & exec, const std::error_code & ec)
{
return on_error_t<Executor> (exec, ec);
}
template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;}
template<typename Executor> on_fork_error_t <Executor> call_on_fork_error (Executor & exec, const std::error_code & ec)
{
return on_fork_error_t<Executor> (exec, ec);
}
template<typename Executor> on_exec_setup_t <Executor> call_on_exec_setup (Executor & exec) {return exec;}
template<typename Executor> on_exec_error_t <Executor> call_on_exec_error (Executor & exec, const std::error_code & ec)
{
return on_exec_error_t<Executor> (exec, ec);
}
template<typename Sequence>
class executor
{
template<typename HasHandler, typename UseVFork>
void internal_error_handle(const std::error_code&, const char*, HasHandler, boost::mpl::true_, UseVFork) {}
int _pipe_sink = -1;
void write_error(const std::error_code & ec, const char * msg)
{
//I am the child
const auto len = static_cast<int>(std::strlen(msg));
int data[2] = {ec.value(), len + 1};
boost::ignore_unused(::write(_pipe_sink, &data[0], sizeof(int) * 2));
boost::ignore_unused(::write(_pipe_sink, msg, len));
}
void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::false_)
{
if (this->pid == 0) //on the fork.
write_error(ec, msg);
else
{
this->_ec = ec;
this->_msg = msg;
}
}
void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_, boost::mpl::false_)
{
if (this->pid == 0)
write_error(ec, msg);
else
throw process_error(ec, msg);
}
void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::true_)
{
this->_ec = ec;
this->_msg = msg;
}
void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_, boost::mpl::true_)
{
if (this->pid == 0)
{
this->_ec = ec;
this->_msg = msg;
}
else
throw process_error(ec, msg);
}
void check_error(boost::mpl::true_) {};
void check_error(boost::mpl::false_)
{
if (_ec)
throw process_error(_ec, _msg);
}
typedef typename ::boost::process::v1::detail::has_error_handler<Sequence>::type has_error_handler;
typedef typename ::boost::process::v1::detail::has_ignore_error <Sequence>::type has_ignore_error;
typedef typename ::boost::process::v1::detail::posix::shall_use_vfork<Sequence>::type shall_use_vfork;
inline child invoke(boost::mpl::true_ , boost::mpl::true_ );
inline child invoke(boost::mpl::false_, boost::mpl::true_ );
inline child invoke(boost::mpl::true_ , boost::mpl::false_ );
inline child invoke(boost::mpl::false_, boost::mpl::false_ );
void _write_error(int sink)
{
int data[2] = {_ec.value(),static_cast<int>(_msg.size())};
while (::write(sink, &data[0], sizeof(int) *2) == -1)
{
auto err = errno;
if (err == EBADF)
return;
else if ((err != EINTR) && (err != EAGAIN))
break;
}
while (::write(sink, &_msg.front(), _msg.size()) == -1)
{
auto err = errno;
if (err == EBADF)
return;
else if ((err != EINTR) && (err != EAGAIN))
break;
}
}
void _read_error(int source)
{
int data[2];
_ec.clear();
int count = 0;
while ((count = ::read(source, &data[0], sizeof(int) *2 ) ) == -1)
{
//actually, this should block until it's read.
auto err = errno;
if ((err != EAGAIN ) && (err != EINTR))
set_error(std::error_code(err, std::system_category()), "Error read pipe");
}
if (count == 0)
return ;
std::error_code ec(data[0], std::system_category());
std::string msg(data[1], ' ');
while (::read(source, &msg.front(), msg.size() ) == -1)
{
//actually, this should block until it's read.
auto err = errno;
if ((err == EBADF) || (err == EPERM))//that should occur on success, therefore return.
return;
//EAGAIN not yet forked, EINTR interrupted, i.e. try again
else if ((err != EAGAIN ) && (err != EINTR))
set_error(std::error_code(err, std::system_category()), "Error read pipe");
}
set_error(ec, std::move(msg));
}
std::string prepare_cmd_style_fn; //buffer
inline void prepare_cmd_style() //this does what execvpe does - but we execute it in the father process, to avoid allocations.
{
//use my own implementation
prepare_cmd_style_fn = exe;
if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK))
{
const auto * e = environ;
while ((e != nullptr) && (*e != nullptr) && !boost::starts_with(*e, "PATH="))
e++;
if ((e != nullptr) && (*e != nullptr))
{
std::vector<std::string> path;
//the beginning of the string contains "PATH="
boost::split(path, (*e) + 5, boost::is_any_of(":"));
for (const std::string & pp : path)
{
auto p = pp + "/" + exe;
if (!::access(p.c_str(), X_OK))
{
prepare_cmd_style_fn = p;
break;
}
}
}
}
exe = prepare_cmd_style_fn.c_str();
}
std::error_code _ec;
std::string _msg;
public:
executor(Sequence & seq) : seq(seq)
{
}
child operator()()
{
return invoke(has_ignore_error(), shall_use_vfork());
}
Sequence & seq;
const char * exe = nullptr;
char *const* cmd_line = nullptr;
bool cmd_style = false;
char **env = environ;
pid_t pid = -1;
std::shared_ptr<std::atomic<int>> exit_status = std::make_shared<std::atomic<int>>(still_active);
const std::error_code & error() const {return _ec;}
void set_error(const std::error_code &ec, const char* msg)
{
internal_error_handle(ec, msg, has_error_handler(), has_ignore_error(), shall_use_vfork());
}
void set_error(const std::error_code &ec, const std::string &msg) {set_error(ec, msg.c_str());};
std::vector<int> get_used_handles() const
{
if (_pipe_sink == -1)
return {};
else
return {_pipe_sink};
};
};
template<typename Sequence>
child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore errors
{
boost::fusion::for_each(seq, call_on_setup(*this));
if (_ec)
return child();
if (cmd_style)
prepare_cmd_style();
this->pid = ::fork();
if (pid == -1)
{
auto ec = boost::process::v1::detail::get_last_error();
boost::fusion::for_each(seq, call_on_fork_error(*this, ec));
return child();
}
else if (pid == 0)
{
boost::fusion::for_each(seq, call_on_exec_setup(*this));
::execve(exe, cmd_line, env);
auto ec = boost::process::v1::detail::get_last_error();
boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
_exit(EXIT_FAILURE);
}
child c(child_handle(pid), exit_status);
boost::fusion::for_each(seq, call_on_success(*this));
return c;
}
template<typename Sequence>
child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
{
{
struct pipe_guard
{
int p[2];
pipe_guard() : p{-1,-1} {}
~pipe_guard()
{
if (p[0] != -1)
::close(p[0]);
if (p[1] != -1)
::close(p[1]);
}
} p{};
if (::pipe(p.p) == -1)
{
set_error(::boost::process::v1::detail::get_last_error(), "pipe(2) failed");
return child();
}
if (::fcntl(p.p[1], F_SETFD, FD_CLOEXEC) == -1)
{
auto err = ::boost::process::v1::detail::get_last_error();
set_error(err, "fcntl(2) failed");//this might throw, so we need to be sure our pipe is safe.
return child();
}
_pipe_sink = p.p[1];
_ec.clear();
boost::fusion::for_each(seq, call_on_setup(*this));
if (_ec)
{
boost::fusion::for_each(seq, call_on_error(*this, _ec));
_pipe_sink = -1;
return child();
}
if (cmd_style)
prepare_cmd_style();
this->pid = ::fork();
if (pid == -1)
{
_ec = boost::process::v1::detail::get_last_error();
_msg = "fork() failed";
boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
boost::fusion::for_each(seq, call_on_error(*this, _ec));
_pipe_sink = -1;
return child();
}
else if (pid == 0)
{
::close(p.p[0]);
boost::fusion::for_each(seq, call_on_exec_setup(*this));
::execve(exe, cmd_line, env);
_ec = boost::process::v1::detail::get_last_error();
_msg = "execve failed";
boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
_write_error(_pipe_sink);
::close(p.p[1]);
_exit(EXIT_FAILURE);
return child();
}
::close(p.p[1]);
p.p[1] = -1;
_pipe_sink = -1;
_read_error(p.p[0]);
}
if (_ec)
{
//if an error occurred we need to reap the child process
::waitpid(this->pid, nullptr, WNOHANG);
boost::fusion::for_each(seq, call_on_error(*this, _ec));
return child();
}
child c(child_handle(pid), exit_status);
boost::fusion::for_each(seq, call_on_success(*this));
if (_ec)
{
boost::fusion::for_each(seq, call_on_error(*this, _ec));
return child();
}
return c;
}
#if BOOST_POSIX_HAS_VFORK
template<typename Sequence>
child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::true_) //ignore errors
{
boost::fusion::for_each(seq, call_on_setup(*this));
if (_ec)
return child();
this->pid = ::vfork();
if (pid == -1)
{
auto ec = boost::process::v1::detail::get_last_error();
boost::fusion::for_each(seq, call_on_fork_error(*this, ec));
return child();
}
else if (pid == 0)
{
boost::fusion::for_each(seq, call_on_exec_setup(*this));
::execve(exe, cmd_line, env);
auto ec = boost::process::v1::detail::get_last_error();
boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
_exit(EXIT_FAILURE);
}
child c(child_handle(pid), exit_status);
boost::fusion::for_each(seq, call_on_success(*this));
return c;
}
template<typename Sequence>
child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
{
boost::fusion::for_each(seq, call_on_setup(*this));
if (_ec)
{
boost::fusion::for_each(seq, call_on_error(*this, _ec));
return child();
}
_ec.clear();
if (cmd_style)
this->prepare_cmd_style();
this->pid = ::vfork();
if (pid == -1)
{
_ec = boost::process::v1::detail::get_last_error();
_msg = "fork() failed";
boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
boost::fusion::for_each(seq, call_on_error(*this, _ec));
return child();
}
else if (pid == 0)
{
boost::fusion::for_each(seq, call_on_exec_setup(*this));
::execve(exe, cmd_line, env);
_ec = boost::process::v1::detail::get_last_error();
_msg = "execve failed";
boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
_exit(EXIT_FAILURE);
return child();
}
child c(child_handle(pid), exit_status);
check_error(has_error_handler());
if (_ec)
{
::waitpid(this->pid, nullptr, WNOHANG);
boost::fusion::for_each(seq, call_on_error(*this, _ec));
return child();
}
else
boost::fusion::for_each(seq, call_on_success(*this));
if (_ec)
{
boost::fusion::for_each(seq, call_on_error(*this, _ec));
return child();
}
return c;
}
#endif
template<typename Char, typename Tup>
inline executor<Tup> make_executor(Tup & tup)
{
return executor<Tup>(tup);
}
}}}}}
#endif

View File

@@ -0,0 +1,102 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_FD_HPP
#define BOOST_PROCESS_DETAIL_POSIX_FD_HPP
#include <boost/process/v1/detail/posix/handler.hpp>
#include <unistd.h>
#include <boost/process/v1/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct close_fd_ : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
close_fd_(int fd) : fd_(fd) {}
template <class PosixExecutor>
void on_exec_setup(PosixExecutor& e) const
{
if (::close(fd_) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
}
int get_used_handles() {return fd_;}
private:
int fd_;
};
template <class Range>
struct close_fds_ : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
public:
close_fds_(const Range &fds) : fds_(fds) {}
template <class PosixExecutor>
void on_exec_setup(PosixExecutor& e) const
{
for (auto & fd_ : fds_)
if (::close(fd_) == -1)
{
e.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
break;
}
}
Range& get_used_handles() {return fds_;}
private:
Range fds_;
};
template <class FileDescriptor>
struct bind_fd_ : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
public:
bind_fd_(int id, const FileDescriptor &fd) : id_(id), fd_(fd) {}
template <class PosixExecutor>
void on_exec_setup(PosixExecutor& e) const
{
if (::dup2(fd_, id_) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
std::array<int, 2> get_used_handles() {return {id_, fd_};}
private:
int id_;
FileDescriptor fd_;
};
struct fd_
{
constexpr fd_() {};
close_fd_ close(int _fd) const {return close_fd_(_fd);}
close_fds_<std::vector<int>> close(const std::initializer_list<int> & vec) const {return std::vector<int>(vec);}
template<typename Range>
close_fds_<Range> close(const Range & r) const {return r;}
template <class FileDescriptor>
bind_fd_<FileDescriptor> bind(int id, const FileDescriptor & fd) const {return {id, fd};}
};
}}}}}
#endif

View File

@@ -0,0 +1,89 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_FILE_DESCRIPTOR_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_FILE_DESCRIPTOR_HPP_
#include <fcntl.h>
#include <string>
#include <boost/process/v1/filesystem.hpp>
#include <boost/core/exchange.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct file_descriptor
{
enum mode_t
{
read = 1,
write = 2,
read_write = 3
};
file_descriptor() = default;
explicit file_descriptor(const boost::process::v1::filesystem::path& p, mode_t mode = read_write)
: file_descriptor(p.native(), mode)
{
}
explicit file_descriptor(const std::string & path , mode_t mode = read_write)
: file_descriptor(path.c_str(), mode) {}
explicit file_descriptor(const char* path, mode_t mode = read_write)
: _handle(create_file(path, mode))
{
}
file_descriptor(const file_descriptor & ) = delete;
file_descriptor(file_descriptor &&other)
: _handle(boost::exchange(other._handle, -1))
{
}
file_descriptor& operator=(const file_descriptor & ) = delete;
file_descriptor& operator=(file_descriptor &&other)
{
if (this != &other)
{
if (_handle != -1)
::close(_handle);
_handle = boost::exchange(other._handle, -1);
}
return *this;
}
~file_descriptor()
{
if (_handle != -1)
::close(_handle);
}
int handle() const { return _handle;}
private:
static int create_file(const char* name, mode_t mode )
{
switch(mode)
{
case read:
return ::open(name, O_RDONLY);
case write:
return ::open(name, O_WRONLY | O_CREAT, 0660);
case read_write:
return ::open(name, O_RDWR | O_CREAT, 0660);
default:
return -1;
}
}
int _handle = -1;
};
}}}}}
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_FILE_DESCRIPTOR_HPP_ */

View File

@@ -0,0 +1,46 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_FILE_IN_HPP
#define BOOST_PROCESS_DETAIL_POSIX_FILE_IN_HPP
#include <boost/process/v1/pipe.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/process/v1/detail/posix/file_descriptor.hpp>
#include <boost/process/v1/detail/used_handles.hpp>
#include <cstdio>
#include <unistd.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct file_in : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
file_descriptor file;
int handle = file.handle();
std::array<int, 2> get_used_handles()
{
return {{STDIN_FILENO, handle}};
}
template<typename T>
file_in(T&& t) : file(std::forward<T>(t)) {}
file_in(FILE * f) : handle(fileno(f)) {}
template <class WindowsExecutor>
void on_exec_setup(WindowsExecutor &e) const
{
if (::dup2(handle, STDIN_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
};
}}}}}
#endif

View File

@@ -0,0 +1,73 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_POSIX_FILE_OUT_HPP
#define BOOST_PROCESS_POSIX_FILE_OUT_HPP
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/process/v1/detail/posix/file_descriptor.hpp>
#include <boost/process/v1/detail/used_handles.hpp>
#include <unistd.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<int p1, int p2>
struct file_out : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
file_descriptor file;
int handle = file.handle();
template<typename T>
file_out(T&& t) : file(std::forward<T>(t), file_descriptor::write), handle(file.handle()) {}
file_out(FILE * f) : handle(fileno(f)) {}
std::array<int, 3> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
return {handle, pp1, pp2};
}
template <typename Executor>
void on_exec_setup(Executor &e) const;
};
template<>
template<typename Executor>
void file_out<1,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(handle, STDOUT_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
template<>
template<typename Executor>
void file_out<2,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(handle, STDERR_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
template<>
template<typename Executor>
void file_out<1,2>::on_exec_setup(Executor &e) const
{
if (::dup2(handle, STDOUT_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
if (::dup2(handle, STDERR_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
}}}}}
#endif

View File

@@ -0,0 +1,94 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_GROUP_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_GROUP_HPP_
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/child_handle.hpp>
#include <system_error>
#include <unistd.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct group_handle
{
pid_t grp = -1;
typedef pid_t handle_t;
handle_t handle() const { return grp; }
explicit group_handle(handle_t h) :
grp(h)
{
}
group_handle() = default;
~group_handle() = default;
group_handle(const group_handle & c) = delete;
group_handle(group_handle && c) : grp(c.grp)
{
c.grp = -1;
}
group_handle &operator=(const group_handle & c) = delete;
group_handle &operator=(group_handle && c)
{
grp = c.grp;
c.grp = -1;
return *this;
}
void add(handle_t proc)
{
if (::setpgid(proc, grp))
throw_last_error();
}
void add(handle_t proc, std::error_code & ec) noexcept
{
if (::setpgid(proc, grp))
ec = get_last_error();
}
bool has(handle_t proc)
{
return ::getpgid(proc) == grp;
}
bool has(handle_t proc, std::error_code &) noexcept
{
return ::getpgid(proc) == grp;
}
bool valid() const
{
return grp != -1;
}
};
inline void terminate(group_handle &p, std::error_code &ec) noexcept
{
if (::killpg(p.grp, SIGKILL) == -1)
ec = boost::process::v1::detail::get_last_error();
else
ec.clear();
p.grp = -1;
}
inline void terminate(group_handle &p)
{
std::error_code ec;
terminate(p, ec);
boost::process::v1::detail::throw_error(ec, "killpg(2) failed in terminate");
}
inline bool in_group()
{
return true;
}
}}}}}
#endif /* BOOST_PROCESS_DETAIL_WINDOWS_GROUP_HPP_ */

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_GROUP_REF_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_GROUP_REF_HPP_
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/group_handle.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <unistd.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 {
namespace detail { namespace posix {
struct group_ref : handler_base_ext
{
group_handle & grp;
explicit group_ref(group_handle & g) :
grp(g)
{}
template <class Executor>
void on_exec_setup(Executor&) const
{
if (grp.grp == -1)
::setpgid(0, 0);
else
::setpgid(0, grp.grp);
}
template <class Executor>
void on_success(Executor& exec) const
{
if (grp.grp == -1)
grp.grp = exec.pid;
}
};
}}}}}
#endif /* BOOST_PROCESS_DETAIL_POSIX_GROUP_REF_HPP_ */

View File

@@ -0,0 +1,74 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_HANDLER_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_HANDLER_HPP_
#include <boost/process/v1/detail/handler_base.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
//does not extend anything.
struct handler_base_ext : handler_base
{
template<typename Executor>
void on_fork_error (Executor &, const std::error_code&) const {}
template<typename Executor>
void on_exec_setup (Executor &) const {}
template<typename Executor>
void on_exec_error (Executor &, const std::error_code&) const {}
};
template <class Handler>
struct on_fork_error_ : handler_base_ext
{
explicit on_fork_error_(Handler handler) : handler_(handler) {}
template <class Executor>
void on_fork_error(Executor &e, const std::error_code &ec) const
{
handler_(e, ec);
}
private:
Handler handler_;
};
template <class Handler>
struct on_exec_setup_ : handler_base_ext
{
explicit on_exec_setup_(Handler handler) : handler_(handler) {}
template <class Executor>
void on_exec_setup(Executor &e) const
{
handler_(e);
}
private:
Handler handler_;
};
template <class Handler>
struct on_exec_error_ : handler_base_ext
{
explicit on_exec_error_(Handler handler) : handler_(handler) {}
template <class Executor>
void on_exec_error(Executor &e, const std::error_code &ec) const
{
handler_(e, ec);
}
private:
Handler handler_;
};
}}}}}
#endif /* BOOST_PROCESS_DETAIL_POSIX_HANDLER_HPP_ */

View File

@@ -0,0 +1,148 @@
// Copyright (c) 2019 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_HANDLES_HPP_
#include <vector>
#include <system_error>
#include <dirent.h>
#include <sys/stat.h>
#include <algorithm>
#include <memory>
#include <cstdlib>
#include <boost/process/v1/detail/posix/handler.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
using native_handle_type = int;
inline std::vector<native_handle_type> get_handles(std::error_code & ec)
{
std::vector<native_handle_type> res;
std::unique_ptr<DIR, void(*)(DIR*)> dir{::opendir("/dev/fd"), +[](DIR* p){::closedir(p);}};
if (!dir)
{
ec = ::boost::process::v1::detail::get_last_error();
return {};
}
else
ec.clear();
auto my_fd = dirfd(dir.get());
struct ::dirent * ent_p;
while ((ent_p = readdir(dir.get())) != nullptr)
{
if (ent_p->d_name[0] == '.')
continue;
const auto conv = std::atoi(ent_p->d_name);
if (conv == 0 && (ent_p->d_name[0] != '0' && ent_p->d_name[1] != '\0'))
continue;
if (conv == my_fd)
continue;
res.push_back(conv);
}
return res;
}
inline std::vector<native_handle_type> get_handles()
{
std::error_code ec;
auto res = get_handles(ec);
if (ec)
boost::process::v1::detail::throw_error(ec, "open_dir(\"/dev/fd\") failed");
return res;
}
inline bool is_stream_handle(native_handle_type handle, std::error_code & ec)
{
struct ::stat stat_;
if (::fstat(handle, &stat_) != 0)
{
ec = ::boost::process::v1::detail::get_last_error();
}
else
ec.clear();
return S_ISCHR (stat_.st_mode) //This macro returns non-zero if the file is a character special file (a device like a terminal).
|| S_ISBLK (stat_.st_mode) // This macro returns non-zero if the file is a block special file (a device like a disk).
|| S_ISREG (stat_.st_mode) // This macro returns non-zero if the file is a regular file.
|| S_ISFIFO (stat_.st_mode) // This macro returns non-zero if the file is a FIFO special file, or a pipe. See section 15. Pipes and FIFOs.
|| S_ISSOCK (stat_.st_mode) ;// This macro returns non-zero if the file is a socket. See section 16. Sockets.;
}
inline bool is_stream_handle(native_handle_type handle)
{
std::error_code ec;
auto res = is_stream_handle(handle, ec);
if (ec)
boost::process::v1::detail::throw_error(ec, "fstat() failed");
return res;
}
struct limit_handles_ : handler_base_ext
{
limit_handles_() {}
~limit_handles_() {}
mutable std::vector<int> used_handles;
template<typename Executor>
void on_setup(Executor & exec) const
{
used_handles = get_used_handles(exec);
}
template<typename Executor>
void on_exec_setup(Executor & exec) const
{
auto dir = ::opendir("/dev/fd");
if (!dir)
{
exec.set_error(::boost::process::v1::detail::get_last_error(), "opendir(\"/dev/fd\")");
return;
}
auto my_fd = dirfd(dir);
struct ::dirent * ent_p;
while ((ent_p = readdir(dir)) != nullptr)
{
if (ent_p->d_name[0] == '.')
continue;
const auto conv = std::atoi(ent_p->d_name);
if ((conv == my_fd) || (conv == -1))
continue;
if (std::find(used_handles.begin(), used_handles.end(), conv) != used_handles.end())
continue;
if (::close(conv) != 0)
{
exec.set_error(::boost::process::v1::detail::get_last_error(), "close() failed");
return;
}
}
::closedir(dir);
}
};
}}}}}
#endif //PROCESS_HANDLES_HPP

View File

@@ -0,0 +1,125 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_POSIX_IO_CONTEXT_REF_HPP_
#define BOOST_PROCESS_POSIX_IO_CONTEXT_REF_HPP_
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/process/v1/detail/posix/async_handler.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/algorithm/transformation/filter_if.hpp>
#include <boost/fusion/algorithm/transformation/transform.hpp>
#include <boost/fusion/view/transform_view.hpp>
#include <boost/fusion/container/vector/convert.hpp>
#include <boost/process/v1/detail/posix/sigchld_service.hpp>
#include <boost/process/v1/detail/posix/is_running.hpp>
#include <functional>
#include <type_traits>
#include <memory>
#include <vector>
#include <sys/wait.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<typename Executor>
struct on_exit_handler_transformer
{
Executor & exec;
on_exit_handler_transformer(Executor & exec) : exec(exec) {}
template<typename Sig>
struct result;
template<typename T>
struct result<on_exit_handler_transformer<Executor>(T&)>
{
typedef typename T::on_exit_handler_t type;
};
template<typename T>
auto operator()(T& t) const -> typename T::on_exit_handler_t
{
return t.on_exit_handler(exec);
}
};
template<typename Executor>
struct async_handler_collector
{
Executor & exec;
std::vector<std::function<void(int, const std::error_code & ec)>> &handlers;
async_handler_collector(Executor & exec,
std::vector<std::function<void(int, const std::error_code & ec)>> &handlers)
: exec(exec), handlers(handlers) {}
template<typename T>
void operator()(T & t) const
{
handlers.push_back(t.on_exit_handler(exec));
}
};
//Also set's up waiting for the exit, so it can close async stuff.
struct io_context_ref : handler_base_ext
{
io_context_ref(boost::asio::io_context & ios) : ios(ios)
{
}
boost::asio::io_context &get() {return ios;};
template <class Executor>
void on_success(Executor& exec)
{
ios.notify_fork(boost::asio::io_context::fork_parent);
//must be on the heap, so I can move it into the lambda.
auto asyncs = boost::fusion::filter_if<
is_async_handler<
typename std::remove_reference< boost::mpl::_ > ::type
>>(exec.seq);
//ok, check if there are actually any.
if (boost::fusion::empty(asyncs))
return;
std::vector<std::function<void(int, const std::error_code & ec)>> funcs;
funcs.reserve(boost::fusion::size(asyncs));
boost::fusion::for_each(asyncs, async_handler_collector<Executor>(exec, funcs));
auto & es = exec.exit_status;
auto wh = [funcs, es](int val, const std::error_code & ec)
{
es->store(val);
for (auto & func : funcs)
func(::boost::process::v1::detail::posix::eval_exit_status(val), ec);
};
sigchld_service.async_wait(exec.pid, std::move(wh));
}
template<typename Executor>
void on_setup (Executor &) const {/*ios.notify_fork(boost::asio::io_context::fork_prepare);*/}
template<typename Executor>
void on_exec_setup (Executor &) const {/*ios.notify_fork(boost::asio::io_context::fork_child);*/}
template <class Executor>
void on_error(Executor&, const std::error_code &) const {/*ios.notify_fork(boost::asio::io_context::fork_parent);*/}
private:
boost::asio::io_context &ios;
boost::process::v1::detail::posix::sigchld_service &sigchld_service = boost::asio::use_service<boost::process::v1::detail::posix::sigchld_service>(ios);
};
}}}}}
#endif /* BOOST_PROCESS_WINDOWS_IO_CONTEXT_REF_HPP_ */

View File

@@ -0,0 +1,80 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_IS_RUNNING_HPP
#define BOOST_PROCESS_DETAIL_POSIX_IS_RUNNING_HPP
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/child_handle.hpp>
#include <system_error>
#include <sys/wait.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
// Use the "stopped" state (WIFSTOPPED) to indicate "not terminated".
// This bit arrangement of status codes is not guaranteed by POSIX, but (according to comments in
// the glibc <bits/waitstatus.h> header) is the same across systems in practice.
constexpr int still_active = 0x017f;
static_assert(WIFSTOPPED(still_active), "Expected still_active to indicate WIFSTOPPED");
static_assert(!WIFEXITED(still_active), "Expected still_active to not indicate WIFEXITED");
static_assert(!WIFSIGNALED(still_active), "Expected still_active to not indicate WIFSIGNALED");
static_assert(!WIFCONTINUED(still_active), "Expected still_active to not indicate WIFCONTINUED");
inline bool is_running(int code)
{
return !WIFEXITED(code) && !WIFSIGNALED(code);
}
inline bool is_running(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
{
int status;
auto ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == -1)
{
if (errno != ECHILD) //because it no child is running, then this one isn't either, obviously.
ec = ::boost::process::v1::detail::get_last_error();
return false;
}
else if (ret == 0)
return true;
else
{
ec.clear();
if (!is_running(status))
exit_code = status;
return false;
}
}
inline bool is_running(const child_handle &p, int & exit_code)
{
std::error_code ec;
bool b = is_running(p, exit_code, ec);
boost::process::v1::detail::throw_error(ec, "waitpid(2) failed in is_running");
return b;
}
inline int eval_exit_status(int code)
{
if (WIFEXITED(code))
{
return WEXITSTATUS(code);
}
else if (WIFSIGNALED(code))
{
return WTERMSIG(code);
}
else
{
return code;
}
}
}}}}}
#endif

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_NULL_IN_HPP
#define BOOST_PROCESS_DETAIL_POSIX_NULL_IN_HPP
#include <boost/process/v1/pipe.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/process/v1/detail/posix/file_descriptor.hpp>
#include <unistd.h>
#include <boost/process/v1/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct null_in : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
file_descriptor source{"/dev/null", file_descriptor::read};
std::array<int, 2> get_used_handles()
{
return {{STDIN_FILENO, source.handle()}};
}
public:
template <class Executor>
void on_exec_setup(Executor &e) const
{
if (::dup2(source.handle(), STDIN_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
};
}}}}}
#endif

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_POSIX_PIPE_OUT_HPP
#define BOOST_PROCESS_POSIX_PIPE_OUT_HPP
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/process/v1/detail/posix/file_descriptor.hpp>
#include <boost/process/v1/detail/used_handles.hpp>
#include <unistd.h>
#include <array>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<int p1, int p2>
struct null_out : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
file_descriptor sink{"/dev/null", file_descriptor::write};
template <typename Executor>
void on_exec_setup(Executor &e) const;
std::array<int, 3> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
return {sink.handle(), pp1, pp2};
}
};
template<>
template<typename Executor>
void null_out<1,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink.handle(), STDOUT_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
template<>
template<typename Executor>
void null_out<2,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink.handle(), STDERR_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
template<>
template<typename Executor>
void null_out<1,2>::on_exec_setup(Executor &e) const
{
if (::dup2(sink.handle(), STDOUT_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
if (::dup2(sink.handle(), STDERR_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
}
}}}}}
#endif

View File

@@ -0,0 +1,48 @@
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_POSIX_ON_EXIT_HPP_
#define BOOST_PROCESS_POSIX_ON_EXIT_HPP_
#include <boost/asio/execution.hpp>
#include <boost/process/v1/async.hpp>
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/handler_base.hpp>
#include <boost/process/v1/detail/posix/async_handler.hpp>
#include <system_error>
#include <functional>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail {
template<typename Tuple>
inline asio::io_context& get_io_context(const Tuple & tup);
namespace posix {
struct on_exit_ : boost::process::v1::detail::posix::async_handler
{
std::function<void(int, const std::error_code&)> handler;
on_exit_(const std::function<void(int, const std::error_code&)> & handler) : handler(handler)
{
}
template<typename Executor>
std::function<void(int, const std::error_code&)> on_exit_handler(Executor& exec)
{
auto v = boost::asio::prefer(boost::process::v1::detail::get_io_context(exec.seq).get_executor(),
boost::asio::execution::outstanding_work.tracked);
auto handler_ = this->handler;
return
[handler_, v](int exit_code, const std::error_code & ec)
{
handler_(exit_code, ec);
};
}
};
}}}}}
#endif /* BOOST_PROCESS_POSIX_ON_EXIT_HPP_ */

View File

@@ -0,0 +1,98 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_POSIX_PIPE_IN_HPP
#define BOOST_PROCESS_POSIX_PIPE_IN_HPP
#include <boost/process/v1/pipe.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <unistd.h>
#include <boost/process/v1/detail/used_handles.hpp>
#include <array>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct pipe_in : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
int source;
int sink; //opposite end
pipe_in(int sink, int source) : source(source), sink(sink) {}
std::array<int, 3> get_used_handles()
{
return {{STDIN_FILENO, source, sink}};
}
template<typename T>
pipe_in(T & p) : source(p.native_source()), sink(p.native_sink())
{
p.assign_source(-1);
}
template<typename Executor>
void on_error(Executor &, const std::error_code &) const
{
::close(source);
}
template<typename Executor>
void on_success(Executor &) const
{
::close(source);
}
template <class Executor>
void on_exec_setup(Executor &e) const
{
if (::dup2(source, STDIN_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
if (source != STDIN_FILENO)
::close(source);
::close(sink);
}
};
class async_pipe;
struct async_pipe_in : public pipe_in
{
async_pipe &pipe;
template<typename AsyncPipe>
async_pipe_in(AsyncPipe & p) : pipe_in(p.native_sink(), p.native_source()), pipe(p)
{
}
template<typename Pipe, typename Executor>
static void close(Pipe & pipe, Executor &)
{
boost::system::error_code ec;
std::move(pipe).source().close(ec);
}
template<typename Executor>
void on_error(Executor & exec, const std::error_code &)
{
close(pipe, exec);
}
template<typename Executor>
void on_success(Executor &exec)
{
close(pipe, exec);
}
};
}}}}}
#endif

View File

@@ -0,0 +1,131 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_PIPE_OUT_HPP
#define BOOST_PROCESS_DETAIL_POSIX_PIPE_OUT_HPP
#include <boost/process/v1/pipe.hpp>
#include <boost/process/v1/detail/posix/handler.hpp>
#include <unistd.h>
#include <array>
#include <boost/process/v1/detail/used_handles.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<int p1, int p2>
struct pipe_out : handler_base_ext, ::boost::process::v1::detail::uses_handles
{
int sink;
int source; //opposite end
std::array<int, 4> get_used_handles()
{
const auto pp1 = p1 != -1 ? p1 : p2;
const auto pp2 = p2 != -1 ? p2 : p1;
return {source, sink, pp1, pp2};
}
pipe_out(int sink, int source) : sink(sink), source(source) {}
template<typename T>
pipe_out(T & p) : sink(p.native_sink()), source(p.native_source())
{
p.assign_sink(-1);
}
template<typename Executor>
void on_error(Executor &, const std::error_code &) const
{
::close(sink);
}
template<typename Executor>
void on_success(Executor &) const
{
::close(sink);
}
template <typename Executor>
void on_exec_setup(Executor &e) const;
};
template<>
template<typename Executor>
void pipe_out<1,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDOUT_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
if (sink != STDOUT_FILENO)
::close(sink);
::close(source);
}
template<>
template<typename Executor>
void pipe_out<2,-1>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDERR_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
if (sink != STDOUT_FILENO)
::close(sink);
::close(source);
}
template<>
template<typename Executor>
void pipe_out<1,2>::on_exec_setup(Executor &e) const
{
if (::dup2(sink, STDOUT_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
if (::dup2(sink, STDERR_FILENO) == -1)
e.set_error(::boost::process::v1::detail::get_last_error(), "dup2() failed");
if ((sink != STDOUT_FILENO) && (sink != STDERR_FILENO))
::close(sink);
::close(source);
}
class async_pipe;
template<int p1, int p2>
struct async_pipe_out : public pipe_out<p1, p2>
{
async_pipe &pipe;
template<typename AsyncPipe>
async_pipe_out(AsyncPipe & p) : pipe_out<p1, p2>(p.native_sink(), p.native_source()), pipe(p)
{
}
template<typename Pipe, typename Executor>
static void close(Pipe & pipe, Executor &)
{
boost::system::error_code ec;
std::move(pipe).sink().close(ec);
}
template<typename Executor>
void on_error(Executor & exec, const std::error_code &)
{
close(pipe, exec);
}
template<typename Executor>
void on_success(Executor &exec)
{
close(pipe, exec);
}
};
}}}}}
#endif

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_POSIX_SEARCH_PATH_HPP
#define BOOST_PROCESS_POSIX_SEARCH_PATH_HPP
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/filesystem.hpp>
#include <boost/tokenizer.hpp>
#include <string>
#include <stdexcept>
#include <stdlib.h>
#include <unistd.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
inline boost::process::v1::filesystem::path search_path(
const boost::process::v1::filesystem::path &filename,
const std::vector<boost::process::v1::filesystem::path> &path)
{
for (const boost::process::v1::filesystem::path & pp : path)
{
auto p = pp / filename;
#if defined(BOOST_PROCESS_USE_STD_FS)
std::error_code ec;
#else
boost::system::error_code ec;
#endif
bool file = boost::process::v1::filesystem::is_regular_file(p, ec);
if (!ec && file && ::access(p.c_str(), X_OK) == 0)
return p;
}
return "";
}
}}}}}
#endif

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_POSIX_SHELL_PATH_HPP
#define BOOST_PROCESS_POSIX_SHELL_PATH_HPP
#include <boost/process/v1/detail/config.hpp>
#include <boost/system/error_code.hpp>
#include <boost/process/v1/filesystem.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
inline boost::process::v1::filesystem::path shell_path()
{
return "/bin/sh";
}
inline boost::process::v1::filesystem::path shell_path(std::error_code &ec)
{
ec.clear();
return "/bin/sh";
}
}}}}}
#endif

View File

@@ -0,0 +1,152 @@
// Copyright (c) 2017 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/consign.hpp>
#include <boost/asio/append.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/strand.hpp>
#include <boost/optional.hpp>
#include <signal.h>
#include <functional>
#include <sys/wait.h>
#include <list>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
class sigchld_service : public boost::asio::detail::service_base<sigchld_service>
{
boost::asio::strand<boost::asio::io_context::executor_type> _strand{get_io_context().get_executor()};
boost::asio::signal_set _signal_set{get_io_context(), SIGCHLD};
std::list<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;
inline void _handle_signal(const boost::system::error_code & ec);
struct initiate_async_wait_op
{
sigchld_service * self;
template<typename Initiation>
void operator()(Initiation && init, ::pid_t pid)
{
// check if the child actually is running first
int status;
auto pid_res = ::waitpid(pid, &status, WNOHANG);
if (pid_res < 0)
{
auto ec = get_last_error();
boost::asio::post(
self->_strand,
asio::append(std::forward<Initiation>(init), pid_res, ec));
}
else if ((pid_res == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
boost::asio::post(
self->_strand,
boost::asio::append(std::forward<Initiation>(init), status, std::error_code{}));
else //still running
{
sigchld_service * self_ = self;
if (self->_receivers.empty())
self->_signal_set.async_wait(
boost::asio::bind_executor(
self->_strand,
[self_](const boost::system::error_code &ec, int)
{
self_->_handle_signal(ec);
}));
self->_receivers.emplace_back(pid, init);
}
}
};
public:
sigchld_service(boost::asio::io_context & io_context)
: boost::asio::detail::service_base<sigchld_service>(io_context)
{
}
template <typename SignalHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(SignalHandler,
void (int, std::error_code))
async_wait(::pid_t pid, SignalHandler && handler)
{
return boost::asio::async_initiate<
SignalHandler,
void(int, std::error_code)>(
initiate_async_wait_op{this}, handler, pid);
}
void shutdown() override
{
_receivers.clear();
}
void cancel()
{
_signal_set.cancel();
}
void cancel(boost::system::error_code & ec)
{
_signal_set.cancel(ec);
}
};
void sigchld_service::_handle_signal(const boost::system::error_code & ec)
{
std::error_code ec_{ec.value(), std::system_category()};
if (ec_)
{
for (auto & r : _receivers)
r.second(-1, ec_);
return;
}
for (auto & r : _receivers) {
int status;
int pid = ::waitpid(r.first, &status, WNOHANG);
if (pid < 0) {
// error (eg: the process no longer exists)
r.second(-1, get_last_error());
r.first = 0; // mark for deletion
} else if (pid == r.first) {
r.second(status, ec_);
r.first = 0; // mark for deletion
}
// otherwise the process is still around
}
_receivers.erase(std::remove_if(_receivers.begin(), _receivers.end(),
[](const std::pair<::pid_t, std::function<void(int, std::error_code)>> & p)
{
return p.first == 0;
}),
_receivers.end());
if (!_receivers.empty())
{
_signal_set.async_wait(
[this](const boost::system::error_code & ec, int)
{
boost::asio::post(_strand, [this, ec]{this->_handle_signal(ec);});
});
}
}
}
}
}
}
}
#endif

View File

@@ -0,0 +1,79 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
// Copyright (c) 2016 Klemens D. Morgenstern
//
// 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)
#ifndef BOOST_PROCESS_POSIX_SIGNAL_HPP
#define BOOST_PROCESS_POSIX_SIGNAL_HPP
#include <boost/process/v1/detail/posix/handler.hpp>
#include <signal.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
#if defined(__GLIBC__)
using sighandler_t = ::sighandler_t;
#else
using sighandler_t = void(*)(int);
#endif
struct sig_init_ : handler_base_ext
{
sig_init_ (sighandler_t handler) : _handler(handler) {}
template <class PosixExecutor>
void on_exec_setup(PosixExecutor&)
{
_old = ::signal(SIGCHLD, _handler);
}
template <class Executor>
void on_error(Executor&, const std::error_code &)
{
if (!_reset)
{
::signal(SIGCHLD, _old);
_reset = true;
}
}
template <class Executor>
void on_success(Executor&)
{
if (!_reset)
{
::signal(SIGCHLD, _old);
_reset = true;
}
}
private:
bool _reset = false;
::boost::process::v1::detail::posix::sighandler_t _old{0};
::boost::process::v1::detail::posix::sighandler_t _handler{0};
};
struct sig_
{
constexpr sig_() {}
sig_init_ operator()(::boost::process::v1::detail::posix::sighandler_t h) const {return h;}
sig_init_ operator= (::boost::process::v1::detail::posix::sighandler_t h) const {return h;}
sig_init_ dfl() const {return SIG_DFL;}
sig_init_ ign() const {return SIG_IGN;}
};
}}}}}
#endif

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_START_DIR_HPP
#define BOOST_PROCESS_DETAIL_POSIX_START_DIR_HPP
#include <boost/process/v1/detail/posix/handler.hpp>
#include <string>
#include <unistd.h>
#include <boost/core/ignore_unused.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
template<typename Char>
struct start_dir_init : handler_base_ext
{
typedef Char value_type;
typedef std::basic_string<value_type> string_type;
start_dir_init(string_type s) : s_(std::move(s)) {}
template <class PosixExecutor>
void on_exec_setup(PosixExecutor&) const
{
boost::ignore_unused(::chdir(s_.c_str()));
}
const string_type & str() const {return s_;}
private:
string_type s_;
};
}}}}}
#endif

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_TERMINATE_HPP
#define BOOST_PROCESS_DETAIL_POSIX_TERMINATE_HPP
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/child_handle.hpp>
#include <system_error>
#include <signal.h>
#include <sys/wait.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
inline void terminate(const child_handle &p, std::error_code &ec) noexcept
{
if (::kill(p.pid, SIGKILL) == -1)
ec = boost::process::v1::detail::get_last_error();
else
ec.clear();
int status;
::waitpid(p.pid, &status, 0); //should not be WNOHANG, since that would allow zombies.
}
inline void terminate(const child_handle &p)
{
std::error_code ec;
terminate(p, ec);
boost::process::v1::detail::throw_error(ec, "kill(2) failed");
}
}}}}}
#endif

View File

@@ -0,0 +1,33 @@
/*
* use_vfork.hpp
*
* Created on: 17.06.2016
* Author: klemens
*/
#ifndef BOOST_PROCESS_DETAIL_POSIX_USE_VFORK_HPP_
#define BOOST_PROCESS_DETAIL_POSIX_USE_VFORK_HPP_
#include <boost/process/v1/detail/posix/handler.hpp>
#include <boost/fusion/sequence/intrinsic/has_key.hpp>
#include <boost/fusion/container/set/convert.hpp>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
struct use_vfork_ : handler_base_ext
{
constexpr use_vfork_(){};
};
template<typename Sequence>
struct shall_use_vfork
{
typedef typename boost::fusion::result_of::as_set<Sequence>::type set_type;
typedef typename boost::fusion::result_of::has_key<set_type, const use_vfork_&>::type type;
};
}}}}}
#endif /* BOOST_PROCESS_DETAIL_POSIX_USE_VFORK_HPP_ */

View File

@@ -0,0 +1,241 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_WAIT_FOR_EXIT_HPP
#define BOOST_PROCESS_DETAIL_POSIX_WAIT_FOR_EXIT_HPP
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/child_handle.hpp>
#include <system_error>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
inline void wait(const child_handle &p, int & exit_code, std::error_code &ec) noexcept
{
pid_t ret;
int status;
do
{
ret = ::waitpid(p.pid, &status, 0);
}
while (((ret == -1) && (errno == EINTR)) ||
(ret != -1 && !WIFEXITED(status) && !WIFSIGNALED(status)));
if (ret == -1)
ec = boost::process::v1::detail::get_last_error();
else
{
ec.clear();
exit_code = status;
}
}
inline void wait(const child_handle &p, int & exit_code) noexcept
{
std::error_code ec;
wait(p, exit_code, ec);
boost::process::v1::detail::throw_error(ec, "waitpid(2) failed in wait");
}
template< class Clock, class Duration >
inline bool wait_until(
const child_handle &p,
int & exit_code,
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
::sigset_t sigset;
//I need to set the signal, because it might be ignore / default, in which case sigwait might not work.
using _signal_t = void(*)(int);
static thread_local _signal_t sigchld_handler = SIG_DFL;
struct signal_interceptor_t
{
static void handler_func(int val)
{
if ((sigchld_handler != SIG_DFL) && (sigchld_handler != SIG_IGN))
sigchld_handler(val);
}
signal_interceptor_t() { sigchld_handler = ::signal(SIGCHLD, &handler_func); }
~signal_interceptor_t() { ::signal(SIGCHLD, sigchld_handler); sigchld_handler = SIG_DFL;}
} signal_interceptor{};
if (sigemptyset(&sigset) != 0)
{
ec = get_last_error();
return false;
}
if (sigaddset(&sigset, SIGCHLD) != 0)
{
ec = get_last_error();
return false;
}
auto get_timespec =
[](const Duration & dur)
{
::timespec ts;
ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(dur).count();
ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count() % 1000000000;
return ts;
};
int ret;
int status{0};
struct ::sigaction old_sig;
if (-1 == ::sigaction(SIGCHLD, nullptr, &old_sig))
{
ec = get_last_error();
return false;
}
bool timed_out;
#if defined(BOOST_POSIX_HAS_SIGTIMEDWAIT)
do
{
auto ts = get_timespec(time_out - Clock::now());
auto ret_sig = ::sigtimedwait(&sigset, nullptr, &ts);
errno = 0;
ret = ::waitpid(p.pid, &status, WNOHANG);
if ((ret_sig == SIGCHLD) && (old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
if (ret == 0)
{
timed_out = Clock::now() >= time_out;
if (timed_out)
return false;
}
}
while ((ret == 0) ||
(((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
#else
//if we do not have sigtimedwait, we fork off a child process to get the signal in time
pid_t timeout_pid = ::fork();
if (timeout_pid == -1)
{
ec = boost::process::v1::detail::get_last_error();
return true;
}
else if (timeout_pid == 0)
{
auto ts = get_timespec(time_out - Clock::now());
::timespec rem;
while (ts.tv_sec > 0 || ts.tv_nsec > 0)
{
if (::nanosleep(&ts, &rem) != 0)
{
auto err = errno;
if ((err == EINVAL) || (err == EFAULT))
break;
}
ts = get_timespec(time_out - Clock::now());
}
::exit(0);
}
struct child_cleaner_t
{
pid_t pid;
~child_cleaner_t()
{
int res;
::kill(pid, SIGKILL);
::waitpid(pid, &res, 0);
}
};
child_cleaner_t child_cleaner{timeout_pid};
do
{
int sig_{0};
if ((::waitpid(timeout_pid, &status, WNOHANG) != 0)
&& (WIFEXITED(status) || WIFSIGNALED(status)))
return false;
ret = ::sigwait(&sigset, &sig_);
errno = 0;
if ((sig_ == SIGCHLD) &&
(old_sig.sa_handler != SIG_DFL) && (old_sig.sa_handler != SIG_IGN))
old_sig.sa_handler(ret);
ret = ::waitpid(p.pid, &status, WNOHANG);
if (ret == 0) // == > is running
{
timed_out = Clock::now() >= time_out;
if (timed_out)
return false;
}
}
while ((ret == 0) ||
(((ret == -1) && errno == EINTR) ||
((ret != -1) && !WIFEXITED(status) && !WIFSIGNALED(status))));
#endif
if (ret == -1)
ec = boost::process::v1::detail::get_last_error();
else
{
ec.clear();
exit_code = status;
}
return true;
}
template< class Clock, class Duration >
inline bool wait_until(
const child_handle &p,
int & exit_code,
const std::chrono::time_point<Clock, Duration>& time_out)
{
std::error_code ec;
bool b = wait_until(p, exit_code, time_out, ec);
boost::process::v1::detail::throw_error(ec, "waitpid(2) failed in wait_until");
return b;
}
template< class Rep, class Period >
inline bool wait_for(
const child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time,
std::error_code & ec) noexcept
{
return wait_until(p, exit_code, std::chrono::steady_clock::now() + rel_time, ec);
}
template< class Rep, class Period >
inline bool wait_for(
const child_handle &p,
int & exit_code,
const std::chrono::duration<Rep, Period>& rel_time)
{
std::error_code ec;
bool b = wait_for(p, exit_code, rel_time, ec);
boost::process::v1::detail::throw_error(ec, "waitpid(2) failed in wait_for");
return b;
}
}}}}}
#endif

View File

@@ -0,0 +1,124 @@
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
// Copyright (c) 2009 Boris Schaeling
// Copyright (c) 2010 Felipe Tanus, Boris Schaeling
// Copyright (c) 2011, 2012 Jeff Flinn, 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)
#ifndef BOOST_PROCESS_DETAIL_POSIX_WAIT_GROUP_HPP
#define BOOST_PROCESS_DETAIL_POSIX_WAIT_GROUP_HPP
#include <boost/process/v1/detail/config.hpp>
#include <boost/process/v1/detail/posix/group_handle.hpp>
#include <chrono>
#include <system_error>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
namespace boost { namespace process { BOOST_PROCESS_V1_INLINE namespace v1 { namespace detail { namespace posix {
inline void wait(const group_handle &p, std::error_code &ec) noexcept
{
pid_t ret;
siginfo_t status;
do
{
ret = ::waitpid(-p.grp, &status.si_status, 0);
if (ret == -1)
{
ec = get_last_error();
return;
}
//ECHILD --> no child processes left.
ret = ::waitid(P_PGID, p.grp, &status, WEXITED | WNOHANG);
}
while ((ret != -1) || (errno != ECHILD));
if (errno != ECHILD)
ec = boost::process::v1::detail::get_last_error();
else
ec.clear();
}
inline void wait(const group_handle &p) noexcept
{
std::error_code ec;
wait(p, ec);
boost::process::v1::detail::throw_error(ec, "waitpid(2) failed in wait");
}
template< class Clock, class Duration >
inline bool wait_until(
const group_handle &p,
const std::chrono::time_point<Clock, Duration>& time_out,
std::error_code & ec) noexcept
{
::siginfo_t siginfo;
bool timed_out = false;
int ret;
::timespec sleep_interval;
sleep_interval.tv_sec = 0;
sleep_interval.tv_nsec = 100000000;
while (!(timed_out = (Clock::now() > time_out)))
{
ret = ::waitid(P_PGID, p.grp, &siginfo, WEXITED | WSTOPPED | WNOHANG);
if (ret == -1)
{
if ((errno == ECHILD) || (errno == ESRCH))
{
ec.clear();
return true;
}
ec = boost::process::v1::detail::get_last_error();
return false;
}
//we can wait, because unlike in the wait_for_exit, we have no race condition regarding eh exit code.
::nanosleep(&sleep_interval, nullptr);
}
return !timed_out;
}
template< class Clock, class Duration >
inline bool wait_until(
const group_handle &p,
const std::chrono::time_point<Clock, Duration>& time_out) noexcept
{
std::error_code ec;
bool b = wait_until(p, time_out, ec);
boost::process::v1::detail::throw_error(ec, "waitpid(2) failed in wait_until");
return b;
}
template< class Rep, class Period >
inline bool wait_for(
const group_handle &p,
const std::chrono::duration<Rep, Period>& rel_time,
std::error_code & ec) noexcept
{
return wait_until(p, std::chrono::steady_clock::now() + rel_time, ec);
}
template< class Rep, class Period >
inline bool wait_for(
const group_handle &p,
const std::chrono::duration<Rep, Period>& rel_time) noexcept
{
std::error_code ec;
bool b = wait_for(p, rel_time, ec);
boost::process::v1::detail::throw_error(ec, "waitpid(2) failed in wait_for");
return b;
}
}}}}}
#endif