1 // Copyright (c) 2006, 2007 Julio M. Merino Vidal
2 // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling
3 // Copyright (c) 2009 Boris Schaeling
4 // Copyright (c) 2010 Felipe Tanus, Boris Schaeling
5 // Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 
10 #ifndef BOOST_PROCESS_DETAIL_POSIX_ASYNC_OUT_HPP
11 #define BOOST_PROCESS_DETAIL_POSIX_ASYNC_OUT_HPP
12 
13 
14 #include <boost/process/detail/posix/handler.hpp>
15 #include <boost/asio/posix/stream_descriptor.hpp>
16 #include <boost/asio/read.hpp>
17 #include <boost/process/async_pipe.hpp>
18 #include <istream>
19 #include <memory>
20 #include <exception>
21 #include <future>
22 #include <array>
23 #include <boost/process/detail/used_handles.hpp>
24 
25 namespace boost { namespace process { namespace detail { namespace posix {
26 
27 
apply_out_handles(int handle,std::integral_constant<int,1>,std::integral_constant<int,-1>)28 inline int apply_out_handles(int handle, std::integral_constant<int, 1>, std::integral_constant<int, -1>)
29 {
30     return ::dup2(handle, STDOUT_FILENO);
31 }
32 
apply_out_handles(int handle,std::integral_constant<int,2>,std::integral_constant<int,-1>)33 inline int apply_out_handles(int handle, std::integral_constant<int, 2>, std::integral_constant<int, -1>)
34 {
35     return ::dup2(handle, STDERR_FILENO);
36 }
37 
apply_out_handles(int handle,std::integral_constant<int,1>,std::integral_constant<int,2>)38 inline int apply_out_handles(int handle, std::integral_constant<int, 1>, std::integral_constant<int, 2>)
39 {
40     if (::dup2(handle, STDOUT_FILENO) == -1)
41         return -1;
42     if (::dup2(handle, STDERR_FILENO) == -1)
43         return -1;
44 
45     return 0;
46 }
47 
48 template<int p1, int p2, typename Buffer>
49 struct async_out_buffer : ::boost::process::detail::posix::handler_base_ext,
50                           ::boost::process::detail::posix::require_io_context,
51                           ::boost::process::detail::uses_handles
52 {
53     Buffer & buf;
54 
55     std::shared_ptr<boost::process::async_pipe> pipe;
56 
get_used_handlesboost::process::detail::posix::async_out_buffer57     std::array<int, 4> get_used_handles()
58     {
59         const auto pp1 = p1 != -1 ? p1 : p2;
60         const auto pp2 = p2 != -1 ? p2 : p1;
61 
62         if (pipe)
63             return {pipe->native_source(), pipe->native_sink(), pp1, pp2};
64         else  //if pipe is not constructed, limit_ds is invoked before -> this also means on_exec_setup gets invoked before.
65             return {pp1, pp2, pp1, pp2};
66     }
67 
68 
async_out_bufferboost::process::detail::posix::async_out_buffer69     async_out_buffer(Buffer & buf) : buf(buf)
70     {
71     }
72 
73     template <typename Executor>
on_successboost::process::detail::posix::async_out_buffer74     inline void on_success(Executor &exec)
75     {
76         auto  pipe              = this->pipe;
77         boost::asio::async_read(*pipe, buf,
78                 [pipe](const boost::system::error_code&, std::size_t){});
79 
80         this->pipe = nullptr;
81         std::move(*pipe).sink().close();
82     }
83 
84     template<typename Executor>
on_errorboost::process::detail::posix::async_out_buffer85     void on_error(Executor &, const std::error_code &) const
86     {
87         std::move(*pipe).sink().close();
88     }
89 
90     template<typename Executor>
on_setupboost::process::detail::posix::async_out_buffer91     void on_setup(Executor & exec)
92     {
93         pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
94     }
95 
96 
97     template <typename Executor>
on_exec_setupboost::process::detail::posix::async_out_buffer98     void on_exec_setup(Executor &exec)
99     {
100         int res = apply_out_handles(pipe->native_sink(),
101                       std::integral_constant<int, p1>(), std::integral_constant<int, p2>());
102         if (res == -1)
103             exec.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
104 
105         ::close(pipe->native_sink());
106         ::close(pipe->native_source());
107 
108     }
109 };
110 
111 
112 
113 
114 template<int p1, int p2, typename Type>
115 struct async_out_future : ::boost::process::detail::posix::handler_base_ext,
116                           ::boost::process::detail::posix::require_io_context
117 {
118     std::shared_ptr<std::promise<Type>> promise = std::make_shared<std::promise<Type>>();
119 
120     std::shared_ptr<boost::asio::streambuf> buffer = std::make_shared<boost::asio::streambuf>();
121 
122     std::shared_ptr<boost::process::async_pipe> pipe;
123 
async_out_futureboost::process::detail::posix::async_out_future124     async_out_future(std::future<Type> & fut)
125     {
126         fut = promise->get_future();
127     }
128     template <typename Executor>
on_successboost::process::detail::posix::async_out_future129     inline void on_success(Executor &)
130     {
131         auto pipe_ = this->pipe;
132 
133         auto buffer_  = this->buffer;
134         auto promise_ = this->promise;
135 
136         boost::asio::async_read(*pipe_, *buffer_,
137                 [pipe_, buffer_, promise_](const boost::system::error_code& ec, std::size_t)
138                 {
139                     if (ec && (ec.value() != ENOENT))
140                     {
141                         std::error_code e(ec.value(), std::system_category());
142                         promise_->set_exception(std::make_exception_ptr(process_error(e)));
143                     }
144                     else
145                     {
146                         std::istream is (buffer_.get());
147                         Type arg;
148                         arg.resize(buffer_->size());
149                         is.read(&*arg.begin(), buffer_->size());
150                         promise_->set_value(std::move(arg));
151                     }
152                 });
153 
154         std::move(*pipe_).sink().close();
155         this->pipe = nullptr;
156     }
157 
158     template<typename Executor>
on_errorboost::process::detail::posix::async_out_future159     void on_error(Executor &, const std::error_code &) const
160     {
161         std::move(*pipe).sink().close();
162     }
163 
164     template<typename Executor>
on_setupboost::process::detail::posix::async_out_future165     void on_setup(Executor & exec)
166     {
167         pipe = std::make_shared<boost::process::async_pipe>(get_io_context(exec.seq));
168     }
169 
170     template <typename Executor>
on_exec_setupboost::process::detail::posix::async_out_future171     void on_exec_setup(Executor &exec)
172     {
173 
174         int res = apply_out_handles(pipe->native_sink(),
175                       std::integral_constant<int, p1>(), std::integral_constant<int, p2>());
176         if (res == -1)
177             exec.set_error(::boost::process::detail::get_last_error(), "dup2() failed");
178 
179         ::close(pipe->native_sink());
180         ::close(pipe->native_source());
181     }
182 
183 };
184 
185 }}}}
186 
187 #endif
188