1 // Copyright (c) 2016 Klemens D. Morgenstern
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef BOOST_PROCESS_DETAIL_POSIX_ASYNC_PIPE_HPP_
7 #define BOOST_PROCESS_DETAIL_POSIX_ASYNC_PIPE_HPP_
8 
9 
10 #include <boost/process/detail/posix/basic_pipe.hpp>
11 #include <boost/asio/posix/stream_descriptor.hpp>
12 #include <boost/asio/post.hpp>
13 #include <system_error>
14 #include <string>
15 #include <utility>
16 
17 namespace boost { namespace process { namespace detail { namespace posix {
18 
19 class async_pipe
20 {
21     ::boost::asio::posix::stream_descriptor _source;
22     ::boost::asio::posix::stream_descriptor _sink  ;
23 public:
24     typedef int native_handle_type;
25     typedef ::boost::asio::posix::stream_descriptor handle_type;
26     typedef typename handle_type::executor_type executor_type;
27 
async_pipe(boost::asio::io_context & ios)28     inline async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios) {}
29 
async_pipe(boost::asio::io_context & ios_source,boost::asio::io_context & ios_sink)30     inline async_pipe(boost::asio::io_context & ios_source,
31                       boost::asio::io_context & ios_sink) : _source(ios_source), _sink(ios_sink)
32     {
33         int fds[2];
34         if (::pipe(fds) == -1)
35             boost::process::detail::throw_last_error("pipe(2) failed");
36 
37         _source.assign(fds[0]);
38         _sink  .assign(fds[1]);
39     };
async_pipe(boost::asio::io_context & ios,const std::string & name)40     inline async_pipe(boost::asio::io_context & ios, const std::string & name)
41         : async_pipe(ios, ios, name) {}
42 
43     inline async_pipe(boost::asio::io_context & ios_source,
44                       boost::asio::io_context & io_sink, const std::string & name);
45     inline async_pipe(const async_pipe& lhs);
async_pipe(async_pipe && lhs)46     async_pipe(async_pipe&& lhs)  : _source(std::move(lhs._source)), _sink(std::move(lhs._sink))
47     {
48         lhs._source.assign (-1);
49         lhs._sink  .assign (-1);
50     }
51 
52     template<class CharT, class Traits = std::char_traits<CharT>>
async_pipe(::boost::asio::io_context & ios_source,::boost::asio::io_context & ios_sink,const basic_pipe<CharT,Traits> & p)53     explicit async_pipe(::boost::asio::io_context & ios_source,
54                         ::boost::asio::io_context & ios_sink,
55                          const basic_pipe<CharT, Traits> & p)
56             : _source(ios_source, p.native_source()), _sink(ios_sink, p.native_sink())
57     {
58     }
59 
60     template<class CharT, class Traits = std::char_traits<CharT>>
async_pipe(boost::asio::io_context & ios,const basic_pipe<CharT,Traits> & p)61     explicit async_pipe(boost::asio::io_context & ios, const basic_pipe<CharT, Traits> & p)
62             : async_pipe(ios, ios, p)
63     {
64     }
65 
66     template<class CharT, class Traits = std::char_traits<CharT>>
67     inline async_pipe& operator=(const basic_pipe<CharT, Traits>& p);
68     inline async_pipe& operator=(const async_pipe& rhs);
69 
70     inline async_pipe& operator=(async_pipe&& lhs);
71 
~async_pipe()72     ~async_pipe()
73     {
74         boost::system::error_code ec;
75         close(ec);
76     }
77 
78     template<class CharT, class Traits = std::char_traits<CharT>>
79     inline explicit operator basic_pipe<CharT, Traits>() const;
80 
cancel()81     void cancel()
82     {
83         if (_sink.is_open())
84             _sink.cancel();
85         if (_source.is_open())
86             _source.cancel();
87     }
88 
close()89     void close()
90     {
91         if (_sink.is_open())
92             _sink.close();
93         if (_source.is_open())
94             _source.close();
95     }
close(boost::system::error_code & ec)96     void close(boost::system::error_code & ec)
97     {
98         if (_sink.is_open())
99             _sink.close(ec);
100         if (_source.is_open())
101             _source.close(ec);
102     }
103 
104 
is_open() const105     bool is_open() const
106     {
107         return  _sink.is_open() || _source.is_open();
108     }
async_close()109     void async_close()
110     {
111         if (_sink.is_open())
112             boost::asio::post(_sink.get_executor(),   [this]{_sink.close();});
113         if (_source.is_open())
114             boost::asio::post(_source.get_executor(), [this]{_source.close();});
115     }
116 
117     template<typename MutableBufferSequence>
read_some(const MutableBufferSequence & buffers)118     std::size_t read_some(const MutableBufferSequence & buffers)
119     {
120         return _source.read_some(buffers);
121     }
122     template<typename MutableBufferSequence>
write_some(const MutableBufferSequence & buffers)123     std::size_t write_some(const MutableBufferSequence & buffers)
124     {
125         return _sink.write_some(buffers);
126     }
127 
128     template<typename MutableBufferSequence>
read_some(const MutableBufferSequence & buffers,boost::system::error_code & ec)129     std::size_t read_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
130     {
131         return _source.read_some(buffers, ec);
132     }
133     template<typename MutableBufferSequence>
write_some(const MutableBufferSequence & buffers,boost::system::error_code & ec)134     std::size_t write_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
135     {
136         return _sink.write_some(buffers, ec);
137     }
138 
139 
native_source() const140     native_handle_type native_source() const {return const_cast<boost::asio::posix::stream_descriptor&>(_source).native_handle();}
native_sink() const141     native_handle_type native_sink  () const {return const_cast<boost::asio::posix::stream_descriptor&>(_sink  ).native_handle();}
142 
143     template<typename MutableBufferSequence,
144              typename ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,void (boost::system::error_code,std::size_t))145     BOOST_ASIO_INITFN_RESULT_TYPE(
146           ReadHandler, void(boost::system::error_code, std::size_t))
147       async_read_some(
148         const MutableBufferSequence & buffers,
149               ReadHandler &&handler)
150     {
151         return _source.async_read_some(buffers, std::forward<ReadHandler>(handler));
152     }
153 
154     template<typename ConstBufferSequence,
155              typename WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,void (boost::system::error_code,std::size_t))156     BOOST_ASIO_INITFN_RESULT_TYPE(
157               WriteHandler, void(boost::system::error_code, std::size_t))
158       async_write_some(
159         const ConstBufferSequence & buffers,
160         WriteHandler&& handler)
161     {
162         return _sink.async_write_some(buffers, std::forward<WriteHandler>(handler));
163     }
164 
165 
sink() const166     const handle_type & sink  () const & {return _sink;}
source() const167     const handle_type & source() const & {return _source;}
168 
sink()169     handle_type && sink()  &&  { return std::move(_sink); }
source()170     handle_type && source()&&  { return std::move(_source); }
171 
source(::boost::asio::io_context & ios)172     handle_type source(::boost::asio::io_context& ios) &&
173     {
174         ::boost::asio::posix::stream_descriptor stolen(ios, _source.release());
175         return stolen;
176     }
sink(::boost::asio::io_context & ios)177     handle_type sink  (::boost::asio::io_context& ios) &&
178     {
179         ::boost::asio::posix::stream_descriptor stolen(ios, _sink.release());
180         return stolen;
181     }
182 
source(::boost::asio::io_context & ios) const183     handle_type source(::boost::asio::io_context& ios) const &
184     {
185         auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(_source).native_handle();
186         return ::boost::asio::posix::stream_descriptor(ios, ::dup(source_in));
187     }
sink(::boost::asio::io_context & ios) const188     handle_type sink  (::boost::asio::io_context& ios) const &
189     {
190         auto sink_in = const_cast<::boost::asio::posix::stream_descriptor &>(_sink).native_handle();
191         return ::boost::asio::posix::stream_descriptor(ios, ::dup(sink_in));
192     }
193 };
194 
195 
async_pipe(boost::asio::io_context & ios_source,boost::asio::io_context & ios_sink,const std::string & name)196 async_pipe::async_pipe(boost::asio::io_context & ios_source,
197                        boost::asio::io_context & ios_sink,
198                        const std::string & name) : _source(ios_source), _sink(ios_sink)
199 {
200     auto fifo = mkfifo(name.c_str(), 0666 );
201 
202     if (fifo != 0)
203         boost::process::detail::throw_last_error("mkfifo() failed");
204 
205 
206     int  read_fd = open(name.c_str(), O_RDWR);
207 
208     if (read_fd == -1)
209         boost::process::detail::throw_last_error();
210 
211     int write_fd = dup(read_fd);
212 
213     if (write_fd == -1)
214         boost::process::detail::throw_last_error();
215 
216     _source.assign(read_fd);
217     _sink  .assign(write_fd);
218 }
219 
async_pipe(const async_pipe & p)220 async_pipe::async_pipe(const async_pipe & p) :
221         _source(const_cast<async_pipe&>(p)._source.get_executor()),
222         _sink(  const_cast<async_pipe&>(p)._sink.get_executor())
223 {
224 
225     //cannot get the handle from a const object.
226     auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(_source).native_handle();
227     auto sink_in   = const_cast<::boost::asio::posix::stream_descriptor &>(_sink).native_handle();
228     if (source_in == -1)
229         _source.assign(-1);
230     else
231     {
232         _source.assign(::dup(source_in));
233         if (_source.native_handle()== -1)
234             ::boost::process::detail::throw_last_error("dup()");
235     }
236 
237     if (sink_in   == -1)
238         _sink.assign(-1);
239     else
240     {
241         _sink.assign(::dup(sink_in));
242         if (_sink.native_handle() == -1)
243             ::boost::process::detail::throw_last_error("dup()");
244     }
245 }
246 
operator =(const async_pipe & p)247 async_pipe& async_pipe::operator=(const async_pipe & p)
248 {
249     int source;
250     int sink;
251 
252     //cannot get the handle from a const object.
253     auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(p._source).native_handle();
254     auto sink_in   = const_cast<::boost::asio::posix::stream_descriptor &>(p._sink).native_handle();
255     if (source_in == -1)
256         source = -1;
257     else
258     {
259         source = ::dup(source_in);
260         if (source == -1)
261             ::boost::process::detail::throw_last_error("dup()");
262     }
263 
264     if (sink_in   == -1)
265         sink = -1;
266     else
267     {
268         sink  = ::dup(sink_in);
269         if (sink == -1)
270             ::boost::process::detail::throw_last_error("dup()");
271     }
272     _source.assign(source);
273     _sink.  assign(sink);
274 
275     return *this;
276 }
277 
operator =(async_pipe && lhs)278 async_pipe& async_pipe::operator=(async_pipe && lhs)
279 {
280     std::swap(_source, lhs._source);
281     std::swap(_sink, lhs._sink);
282     return *this;
283 }
284 
285 template<class CharT, class Traits>
operator basic_pipe<CharT,Traits>() const286 async_pipe::operator basic_pipe<CharT, Traits>() const
287 {
288     int source;
289     int sink;
290 
291     //cannot get the handle from a const object.
292     auto source_in = const_cast<::boost::asio::posix::stream_descriptor &>(_source).native_handle();
293     auto sink_in   = const_cast<::boost::asio::posix::stream_descriptor &>(_sink).native_handle();
294 
295 
296     if (source_in == -1)
297         source = -1;
298     else
299     {
300         source = ::dup(source_in);
301         if (source == -1)
302             ::boost::process::detail::throw_last_error("dup()");
303     }
304 
305     if (sink_in   == -1)
306         sink = -1;
307     else
308     {
309         sink = ::dup(sink_in);
310         if (sink == -1)
311             ::boost::process::detail::throw_last_error("dup()");
312     }
313 
314     return basic_pipe<CharT, Traits>{source, sink};
315 }
316 
317 
operator ==(const async_pipe & lhs,const async_pipe & rhs)318 inline bool operator==(const async_pipe & lhs, const async_pipe & rhs)
319 {
320     return compare_handles(lhs.native_source(), rhs.native_source()) &&
321            compare_handles(lhs.native_sink(),   rhs.native_sink());
322 }
323 
operator !=(const async_pipe & lhs,const async_pipe & rhs)324 inline bool operator!=(const async_pipe & lhs, const async_pipe & rhs)
325 {
326     return !compare_handles(lhs.native_source(), rhs.native_source()) ||
327            !compare_handles(lhs.native_sink(),   rhs.native_sink());
328 }
329 
330 template<class Char, class Traits>
operator ==(const async_pipe & lhs,const basic_pipe<Char,Traits> & rhs)331 inline bool operator==(const async_pipe & lhs, const basic_pipe<Char, Traits> & rhs)
332 {
333     return compare_handles(lhs.native_source(), rhs.native_source()) &&
334            compare_handles(lhs.native_sink(),   rhs.native_sink());
335 }
336 
337 template<class Char, class Traits>
operator !=(const async_pipe & lhs,const basic_pipe<Char,Traits> & rhs)338 inline bool operator!=(const async_pipe & lhs, const basic_pipe<Char, Traits> & rhs)
339 {
340     return !compare_handles(lhs.native_source(), rhs.native_source()) ||
341            !compare_handles(lhs.native_sink(),   rhs.native_sink());
342 }
343 
344 template<class Char, class Traits>
operator ==(const basic_pipe<Char,Traits> & lhs,const async_pipe & rhs)345 inline bool operator==(const basic_pipe<Char, Traits> & lhs, const async_pipe & rhs)
346 {
347     return compare_handles(lhs.native_source(), rhs.native_source()) &&
348            compare_handles(lhs.native_sink(),   rhs.native_sink());
349 }
350 
351 template<class Char, class Traits>
operator !=(const basic_pipe<Char,Traits> & lhs,const async_pipe & rhs)352 inline bool operator!=(const basic_pipe<Char, Traits> & lhs, const async_pipe & rhs)
353 {
354     return !compare_handles(lhs.native_source(), rhs.native_source()) ||
355            !compare_handles(lhs.native_sink(),   rhs.native_sink());
356 }
357 
358 }}}}
359 
360 #endif /* INCLUDE_BOOST_PIPE_DETAIL_WINDOWS_ASYNC_PIPE_HPP_ */
361