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_WINDOWS_ASYNC_PIPE_HPP_
7 #define BOOST_PROCESS_DETAIL_WINDOWS_ASYNC_PIPE_HPP_
8 
9 #include <boost/winapi/basic_types.hpp>
10 #include <boost/winapi/pipes.hpp>
11 #include <boost/winapi/handles.hpp>
12 #include <boost/winapi/file_management.hpp>
13 #include <boost/winapi/get_last_error.hpp>
14 #include <boost/winapi/access_rights.hpp>
15 #include <boost/winapi/process.hpp>
16 #include <boost/process/detail/windows/basic_pipe.hpp>
17 #include <boost/asio/post.hpp>
18 #include <boost/asio/windows/stream_handle.hpp>
19 #include <atomic>
20 #include <system_error>
21 #include <string>
22 
23 namespace boost { namespace process { namespace detail { namespace windows {
24 
make_pipe_name()25 inline std::string make_pipe_name()
26 {
27     std::string name = "\\\\.\\pipe\\boost_process_auto_pipe_";
28 
29     auto pid = ::boost::winapi::GetCurrentProcessId();
30 
31     static std::atomic_size_t cnt{0};
32     name += std::to_string(pid);
33     name += "_";
34     name += std::to_string(cnt++);
35 
36     return name;
37 }
38 
39 class async_pipe
40 {
41     ::boost::asio::windows::stream_handle _source;
42     ::boost::asio::windows::stream_handle _sink  ;
43 
44     inline async_pipe(boost::asio::io_context & ios_source,
45                       boost::asio::io_context & ios_sink,
46                       const std::string & name, bool private_);
47 
48 public:
49     typedef ::boost::winapi::HANDLE_ native_handle_type;
50     typedef ::boost::asio::windows::stream_handle   handle_type;
51     typedef typename handle_type::executor_type executor_type;
52 
async_pipe(boost::asio::io_context & ios)53     async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios, make_pipe_name(), true) {}
async_pipe(boost::asio::io_context & ios_source,boost::asio::io_context & ios_sink)54     async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink)
55                 : async_pipe(ios_source, ios_sink, make_pipe_name(), true) {}
56 
async_pipe(boost::asio::io_context & ios,const std::string & name)57     async_pipe(boost::asio::io_context & ios, const std::string & name)
58                 : async_pipe(ios, ios, name, false) {}
59 
async_pipe(boost::asio::io_context & ios_source,boost::asio::io_context & ios_sink,const std::string & name)60     async_pipe(boost::asio::io_context & ios_source, boost::asio::io_context & ios_sink, const std::string & name)
61             : async_pipe(ios_source, ios_sink, name, false) {}
62 
63 
64 
65     inline async_pipe(const async_pipe& rhs);
async_pipe(async_pipe && rhs)66     async_pipe(async_pipe&& rhs)  : _source(std::move(rhs._source)), _sink(std::move(rhs._sink))
67     {
68     }
69     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)70     explicit async_pipe(::boost::asio::io_context & ios_source,
71                         ::boost::asio::io_context & ios_sink,
72                          const basic_pipe<CharT, Traits> & p)
73             : _source(ios_source, p.native_source()), _sink(ios_sink, p.native_sink())
74     {
75     }
76 
77     template<class CharT, class Traits = std::char_traits<CharT>>
async_pipe(boost::asio::io_context & ios,const basic_pipe<CharT,Traits> & p)78     explicit async_pipe(boost::asio::io_context & ios, const basic_pipe<CharT, Traits> & p)
79             : async_pipe(ios, ios, p)
80     {
81     }
82 
83     template<class CharT, class Traits = std::char_traits<CharT>>
84     inline async_pipe& operator=(const basic_pipe<CharT, Traits>& p);
85     inline async_pipe& operator=(const async_pipe& rhs);
86 
87     inline async_pipe& operator=(async_pipe&& rhs);
88 
~async_pipe()89     ~async_pipe()
90     {
91         boost::system::error_code ec;
92         close(ec);
93     }
94 
95     template<class CharT, class Traits = std::char_traits<CharT>>
96     inline explicit operator basic_pipe<CharT, Traits>() const;
97 
cancel()98     void cancel()
99     {
100         if (_sink.is_open())
101             _sink.cancel();
102         if (_source.is_open())
103             _source.cancel();
104     }
105 
close()106     void close()
107     {
108         if (_sink.is_open())
109         {
110             _sink.close();
111             _sink = handle_type(_sink.get_executor());
112         }
113         if (_source.is_open())
114         {
115             _source.close();
116             _source = handle_type(_source.get_executor());
117         }
118     }
close(boost::system::error_code & ec)119     void close(boost::system::error_code & ec)
120     {
121         if (_sink.is_open())
122         {
123             _sink.close(ec);
124             _sink = handle_type(_sink.get_executor());
125         }
126         if (_source.is_open())
127         {
128             _source.close(ec);
129             _source = handle_type(_source.get_executor());
130         }
131     }
132 
is_open() const133     bool is_open() const
134     {
135         return  _sink.is_open() || _source.is_open();
136     }
async_close()137     void async_close()
138     {
139         if (_sink.is_open())
140             boost::asio::post(_sink.get_executor(),   [this]{_sink.close();});
141         if (_source.is_open())
142             boost::asio::post(_source.get_executor(), [this]{_source.close();});
143     }
144 
145     template<typename MutableBufferSequence>
read_some(const MutableBufferSequence & buffers)146     std::size_t read_some(const MutableBufferSequence & buffers)
147     {
148         return _source.read_some(buffers);
149     }
150     template<typename MutableBufferSequence>
write_some(const MutableBufferSequence & buffers)151     std::size_t write_some(const MutableBufferSequence & buffers)
152     {
153         return _sink.write_some(buffers);
154     }
155 
156 
157     template<typename MutableBufferSequence>
read_some(const MutableBufferSequence & buffers,boost::system::error_code & ec)158     std::size_t read_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
159     {
160         return _source.read_some(buffers, ec);
161     }
162     template<typename MutableBufferSequence>
write_some(const MutableBufferSequence & buffers,boost::system::error_code & ec)163     std::size_t write_some(const MutableBufferSequence & buffers, boost::system::error_code & ec) noexcept
164     {
165         return _sink.write_some(buffers, ec);
166     }
167 
native_source() const168     native_handle_type native_source() const {return const_cast<boost::asio::windows::stream_handle&>(_source).native_handle();}
native_sink() const169     native_handle_type native_sink  () const {return const_cast<boost::asio::windows::stream_handle&>(_sink  ).native_handle();}
170 
171     template<typename MutableBufferSequence,
172              typename ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,void (boost::system::error_code,std::size_t))173     BOOST_ASIO_INITFN_RESULT_TYPE(
174           ReadHandler, void(boost::system::error_code, std::size_t))
175       async_read_some(
176         const MutableBufferSequence & buffers,
177               ReadHandler &&handler)
178     {
179         return _source.async_read_some(buffers, std::forward<ReadHandler>(handler));
180     }
181 
182     template<typename ConstBufferSequence,
183              typename WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,void (boost::system::error_code,std::size_t))184     BOOST_ASIO_INITFN_RESULT_TYPE(
185               WriteHandler, void(boost::system::error_code, std::size_t))
186       async_write_some(
187         const ConstBufferSequence & buffers,
188         WriteHandler && handler)
189     {
190         return _sink.async_write_some(buffers,  std::forward<WriteHandler>(handler));
191     }
192 
sink() const193     const handle_type & sink  () const & {return _sink;}
source() const194     const handle_type & source() const & {return _source;}
195 
source()196     handle_type && source() && { return std::move(_source); }
sink()197     handle_type && sink()   && { return std::move(_sink); }
198 
source(::boost::asio::io_context & ios)199     handle_type source(::boost::asio::io_context& ios) &&
200     {
201         ::boost::asio::windows::stream_handle stolen(ios.get_executor(), _source.native_handle());
202         boost::system::error_code ec;
203         _source.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
204         return stolen;
205     }
sink(::boost::asio::io_context & ios)206     handle_type sink  (::boost::asio::io_context& ios) &&
207     {
208         ::boost::asio::windows::stream_handle stolen(ios.get_executor(), _sink.native_handle());
209         boost::system::error_code ec;
210         _sink.assign(::boost::winapi::INVALID_HANDLE_VALUE_, ec);
211         return stolen;
212     }
213 
source(::boost::asio::io_context & ios) const214     handle_type source(::boost::asio::io_context& ios) const &
215     {
216         auto proc = ::boost::winapi::GetCurrentProcess();
217 
218         ::boost::winapi::HANDLE_ source;
219         auto source_in = const_cast<handle_type&>(_source).native_handle();
220         if (source_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
221             source = ::boost::winapi::INVALID_HANDLE_VALUE_;
222         else if (!::boost::winapi::DuplicateHandle(
223                 proc, source_in, proc, &source, 0,
224                 static_cast<::boost::winapi::BOOL_>(true),
225                  ::boost::winapi::DUPLICATE_SAME_ACCESS_))
226             throw_last_error("Duplicate Pipe Failed");
227 
228         return ::boost::asio::windows::stream_handle(ios.get_executor(), source);
229     }
sink(::boost::asio::io_context & ios) const230     handle_type sink  (::boost::asio::io_context& ios) const &
231     {
232         auto proc = ::boost::winapi::GetCurrentProcess();
233 
234         ::boost::winapi::HANDLE_ sink;
235         auto sink_in = const_cast<handle_type&>(_sink).native_handle();
236         if (sink_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
237             sink = ::boost::winapi::INVALID_HANDLE_VALUE_;
238         else if (!::boost::winapi::DuplicateHandle(
239                 proc, sink_in, proc, &sink, 0,
240                 static_cast<::boost::winapi::BOOL_>(true),
241                  ::boost::winapi::DUPLICATE_SAME_ACCESS_))
242             throw_last_error("Duplicate Pipe Failed");
243 
244         return ::boost::asio::windows::stream_handle(ios.get_executor(), sink);
245     }
246 };
247 
async_pipe(const async_pipe & p)248 async_pipe::async_pipe(const async_pipe& p)  :
249     _source(const_cast<handle_type&>(p._source).get_executor()),
250     _sink  (const_cast<handle_type&>(p._sink).get_executor())
251 {
252 
253     auto proc = ::boost::winapi::GetCurrentProcess();
254 
255     ::boost::winapi::HANDLE_ source;
256     ::boost::winapi::HANDLE_ sink;
257 
258     //cannot get the handle from a const object.
259     auto source_in = const_cast<handle_type&>(p._source).native_handle();
260     auto sink_in   = const_cast<handle_type&>(p._sink).native_handle();
261 
262     if (source_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
263         source = ::boost::winapi::INVALID_HANDLE_VALUE_;
264     else if (!::boost::winapi::DuplicateHandle(
265             proc, source_in, proc, &source, 0,
266             static_cast<::boost::winapi::BOOL_>(true),
267              ::boost::winapi::DUPLICATE_SAME_ACCESS_))
268         throw_last_error("Duplicate Pipe Failed");
269 
270     if (sink_in   == ::boost::winapi::INVALID_HANDLE_VALUE_)
271         sink = ::boost::winapi::INVALID_HANDLE_VALUE_;
272     else if (!::boost::winapi::DuplicateHandle(
273             proc, sink_in, proc, &sink, 0,
274             static_cast<::boost::winapi::BOOL_>(true),
275              ::boost::winapi::DUPLICATE_SAME_ACCESS_))
276         throw_last_error("Duplicate Pipe Failed");
277 
278     if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
279         _source.assign(source);
280     if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
281         _sink.  assign(sink);
282 }
283 
284 
async_pipe(boost::asio::io_context & ios_source,boost::asio::io_context & ios_sink,const std::string & name,bool private_)285 async_pipe::async_pipe(boost::asio::io_context & ios_source,
286                        boost::asio::io_context & ios_sink,
287                        const std::string & name, bool private_) : _source(ios_source), _sink(ios_sink)
288 {
289     static constexpr int FILE_FLAG_OVERLAPPED_  = 0x40000000; //temporary
290 
291     ::boost::winapi::HANDLE_ source = ::boost::winapi::create_named_pipe(
292 #if defined(BOOST_NO_ANSI_APIS)
293             ::boost::process::detail::convert(name).c_str(),
294 #else
295             name.c_str(),
296 #endif
297             ::boost::winapi::PIPE_ACCESS_INBOUND_
298             | FILE_FLAG_OVERLAPPED_, //write flag
299             0, private_ ? 1 : ::boost::winapi::PIPE_UNLIMITED_INSTANCES_, 8192, 8192, 0, nullptr);
300 
301 
302     if (source == boost::winapi::INVALID_HANDLE_VALUE_)
303         ::boost::process::detail::throw_last_error("create_named_pipe(" + name + ") failed");
304 
305     _source.assign(source);
306 
307     ::boost::winapi::HANDLE_ sink = boost::winapi::create_file(
308 #if defined(BOOST_NO_ANSI_APIS)
309             ::boost::process::detail::convert(name).c_str(),
310 #else
311             name.c_str(),
312 #endif
313             ::boost::winapi::GENERIC_WRITE_, 0, nullptr,
314             ::boost::winapi::OPEN_EXISTING_,
315             FILE_FLAG_OVERLAPPED_, //to allow read
316             nullptr);
317 
318     if (sink == ::boost::winapi::INVALID_HANDLE_VALUE_)
319         ::boost::process::detail::throw_last_error("create_file() failed");
320 
321     _sink.assign(sink);
322 }
323 
324 template<class CharT, class Traits>
operator =(const basic_pipe<CharT,Traits> & p)325 async_pipe& async_pipe::operator=(const basic_pipe<CharT, Traits> & p)
326 {
327     auto proc = ::boost::winapi::GetCurrentProcess();
328 
329     ::boost::winapi::HANDLE_ source;
330     ::boost::winapi::HANDLE_ sink;
331 
332     //cannot get the handle from a const object.
333     auto source_in = p.native_source();
334     auto sink_in   = p.native_sink();
335 
336     if (source_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
337         source = ::boost::winapi::INVALID_HANDLE_VALUE_;
338     else if (!::boost::winapi::DuplicateHandle(
339             proc, source_in.native_handle(), proc, &source, 0,
340             static_cast<::boost::winapi::BOOL_>(true),
341             ::boost::winapi::DUPLICATE_SAME_ACCESS_))
342         throw_last_error("Duplicate Pipe Failed");
343 
344     if (sink_in   == ::boost::winapi::INVALID_HANDLE_VALUE_)
345         sink = ::boost::winapi::INVALID_HANDLE_VALUE_;
346     else if (!::boost::winapi::DuplicateHandle(
347             proc, sink_in.native_handle(), proc, &sink, 0,
348             static_cast<::boost::winapi::BOOL_>(true),
349             ::boost::winapi::DUPLICATE_SAME_ACCESS_))
350         throw_last_error("Duplicate Pipe Failed");
351 
352     //so we also assign the io_context
353     if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
354         _source.assign(source);
355 
356     if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
357         _sink.assign(sink);
358 
359     return *this;
360 }
361 
operator =(const async_pipe & p)362 async_pipe& async_pipe::operator=(const async_pipe & p)
363 {
364     auto proc = ::boost::winapi::GetCurrentProcess();
365 
366     ::boost::winapi::HANDLE_ source;
367     ::boost::winapi::HANDLE_ sink;
368 
369     //cannot get the handle from a const object.
370     auto &source_in = const_cast<::boost::asio::windows::stream_handle &>(p._source);
371     auto &sink_in   = const_cast<::boost::asio::windows::stream_handle &>(p._sink);
372 
373     source_in.get_executor();
374 
375     if (source_in.native_handle() == ::boost::winapi::INVALID_HANDLE_VALUE_)
376         source = ::boost::winapi::INVALID_HANDLE_VALUE_;
377     else if (!::boost::winapi::DuplicateHandle(
378             proc, source_in.native_handle(), proc, &source, 0,
379             static_cast<::boost::winapi::BOOL_>(true),
380              ::boost::winapi::DUPLICATE_SAME_ACCESS_))
381         throw_last_error("Duplicate Pipe Failed");
382 
383     if (sink_in.native_handle()   == ::boost::winapi::INVALID_HANDLE_VALUE_)
384         sink = ::boost::winapi::INVALID_HANDLE_VALUE_;
385     else if (!::boost::winapi::DuplicateHandle(
386             proc, sink_in.native_handle(), proc, &sink, 0,
387             static_cast<::boost::winapi::BOOL_>(true),
388              ::boost::winapi::DUPLICATE_SAME_ACCESS_))
389         throw_last_error("Duplicate Pipe Failed");
390 
391     //so we also assign the io_context
392     if (source != ::boost::winapi::INVALID_HANDLE_VALUE_)
393         _source = ::boost::asio::windows::stream_handle(source_in.get_executor(), source);
394     else
395         _source = ::boost::asio::windows::stream_handle(source_in.get_executor());
396 
397     if (sink != ::boost::winapi::INVALID_HANDLE_VALUE_)
398         _sink   = ::boost::asio::windows::stream_handle(source_in.get_executor(), sink);
399     else
400         _sink   = ::boost::asio::windows::stream_handle(source_in.get_executor());
401 
402     return *this;
403 }
404 
operator =(async_pipe && rhs)405 async_pipe& async_pipe::operator=(async_pipe && rhs)
406 {
407     _source = std::move(rhs._source);
408     _sink = std::move(rhs._sink);
409     return *this;
410 }
411 
412 template<class CharT, class Traits>
operator basic_pipe<CharT,Traits>() const413 async_pipe::operator basic_pipe<CharT, Traits>() const
414 {
415     auto proc = ::boost::winapi::GetCurrentProcess();
416 
417     ::boost::winapi::HANDLE_ source;
418     ::boost::winapi::HANDLE_ sink;
419 
420     //cannot get the handle from a const object.
421     auto source_in = const_cast<::boost::asio::windows::stream_handle &>(_source).native_handle();
422     auto sink_in   = const_cast<::boost::asio::windows::stream_handle &>(_sink).native_handle();
423 
424     if (source_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
425         source = ::boost::winapi::INVALID_HANDLE_VALUE_;
426     else if (!::boost::winapi::DuplicateHandle(
427             proc, source_in, proc, &source, 0,
428             static_cast<::boost::winapi::BOOL_>(true),
429              ::boost::winapi::DUPLICATE_SAME_ACCESS_))
430         throw_last_error("Duplicate Pipe Failed");
431 
432     if (sink_in == ::boost::winapi::INVALID_HANDLE_VALUE_)
433         sink = ::boost::winapi::INVALID_HANDLE_VALUE_;
434     else if (!::boost::winapi::DuplicateHandle(
435             proc, sink_in, proc, &sink, 0,
436             static_cast<::boost::winapi::BOOL_>(true),
437              ::boost::winapi::DUPLICATE_SAME_ACCESS_))
438         throw_last_error("Duplicate Pipe Failed");
439 
440     return basic_pipe<CharT, Traits>{source, sink};
441 }
442 
operator ==(const async_pipe & lhs,const async_pipe & rhs)443 inline bool operator==(const async_pipe & lhs, const async_pipe & rhs)
444 {
445     return compare_handles(lhs.native_source(), rhs.native_source()) &&
446            compare_handles(lhs.native_sink(),   rhs.native_sink());
447 }
448 
operator !=(const async_pipe & lhs,const async_pipe & rhs)449 inline bool operator!=(const async_pipe & lhs, const async_pipe & rhs)
450 {
451     return !compare_handles(lhs.native_source(), rhs.native_source()) ||
452            !compare_handles(lhs.native_sink(),   rhs.native_sink());
453 }
454 
455 template<class Char, class Traits>
operator ==(const async_pipe & lhs,const basic_pipe<Char,Traits> & rhs)456 inline bool operator==(const async_pipe & lhs, const basic_pipe<Char, Traits> & rhs)
457 {
458     return compare_handles(lhs.native_source(), rhs.native_source()) &&
459            compare_handles(lhs.native_sink(),   rhs.native_sink());
460 }
461 
462 template<class Char, class Traits>
operator !=(const async_pipe & lhs,const basic_pipe<Char,Traits> & rhs)463 inline bool operator!=(const async_pipe & lhs, const basic_pipe<Char, Traits> & rhs)
464 {
465     return !compare_handles(lhs.native_source(), rhs.native_source()) ||
466            !compare_handles(lhs.native_sink(),   rhs.native_sink());
467 }
468 
469 template<class Char, class Traits>
operator ==(const basic_pipe<Char,Traits> & lhs,const async_pipe & rhs)470 inline bool operator==(const basic_pipe<Char, Traits> & lhs, const async_pipe & rhs)
471 {
472     return compare_handles(lhs.native_source(), rhs.native_source()) &&
473            compare_handles(lhs.native_sink(),   rhs.native_sink());
474 }
475 
476 template<class Char, class Traits>
operator !=(const basic_pipe<Char,Traits> & lhs,const async_pipe & rhs)477 inline bool operator!=(const basic_pipe<Char, Traits> & lhs, const async_pipe & rhs)
478 {
479     return !compare_handles(lhs.native_source(), rhs.native_source()) ||
480            !compare_handles(lhs.native_sink(),   rhs.native_sink());
481 }
482 
483 }}}}
484 
485 #endif /* INCLUDE_BOOST_PIPE_DETAIL_WINDOWS_ASYNC_PIPE_HPP_ */
486