Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-09 09:01:11

0001 //
0002 // process/stdio.hpp
0003 // ~~~~~~~~
0004 //
0005 // Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0009 
0010 #ifndef BOOST_PROCESS_V2_STDIO_HPP
0011 #define BOOST_PROCESS_V2_STDIO_HPP
0012 
0013 #include <boost/process/v2/detail/config.hpp>
0014 #include <boost/process/v2/detail/last_error.hpp>
0015 #include <boost/process/v2/default_launcher.hpp>
0016 #include <cstddef>
0017 #if defined(BOOST_PROCESS_V2_STANDALONE)
0018 #include <asio/connect_pipe.hpp>
0019 #else
0020 #include <boost/asio/connect_pipe.hpp>
0021 #endif
0022 
0023 #if defined(BOOST_PROCESS_V2_POSIX)
0024 #include <fcntl.h>
0025 #include <stdio.h>
0026 #include <unistd.h>
0027 #endif
0028 
0029 #if defined(BOOST_PROCESS_V2_WINDOWS)
0030 #include <io.h>
0031 #endif
0032 
0033 BOOST_PROCESS_V2_BEGIN_NAMESPACE
0034 
0035 
0036 template<typename T>
0037 struct is_readable_pipe : std::false_type
0038 {
0039 };
0040 
0041 template<typename Executor>
0042 struct is_readable_pipe<asio::basic_readable_pipe<Executor>> : std::true_type
0043 {
0044 };
0045 
0046 
0047 template<typename T>
0048 struct is_writable_pipe : std::false_type
0049 {
0050 };
0051 
0052 template<typename Executor>
0053 struct is_writable_pipe<asio::basic_writable_pipe<Executor>> : std::true_type
0054 {
0055 };
0056 
0057 namespace detail
0058 {
0059 
0060 
0061 #if defined(BOOST_PROCESS_V2_WINDOWS)
0062 
0063 struct handle_closer
0064 {
0065   handle_closer() = default;
0066   handle_closer(bool close) : close(close) {}
0067   handle_closer(DWORD flags) : close(false), flags{flags} {}
0068 
0069 
0070   void operator()(HANDLE h) const
0071   {
0072     if (close)
0073       ::CloseHandle(h);
0074     else if (flags != 0xFFFFFFFFu)
0075       ::SetHandleInformation(h, 0xFFFFFFFFu, flags);
0076 
0077   }
0078 
0079   bool close{false};
0080   DWORD flags{0xFFFFFFFFu};
0081 };
0082 
0083 template<DWORD Target>
0084 struct process_io_binding
0085 {
0086   HANDLE prepare()
0087   {
0088     auto hh =  h.get();
0089     ::SetHandleInformation(hh, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
0090     return hh;
0091   }
0092 
0093   std::unique_ptr<void, handle_closer> h{::GetStdHandle(Target), false};
0094 
0095   static DWORD get_flags(HANDLE h)
0096   {
0097     DWORD res;
0098     if (!::GetHandleInformation(h, &res))
0099     {
0100       error_code ec;
0101       BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
0102       throw system_error(ec, "get_flags");
0103     }
0104     return res;
0105   }
0106 
0107   process_io_binding() = default;
0108 
0109   template<typename Stream>
0110   process_io_binding(Stream && str, decltype(std::declval<Stream>().native_handle())* = nullptr)
0111       : process_io_binding(str.native_handle())
0112   {}
0113 
0114   process_io_binding(FILE * f) : process_io_binding(reinterpret_cast<HANDLE>(::_get_osfhandle(_fileno(f)))) {}
0115   process_io_binding(HANDLE h) : h{h, get_flags(h)} {}
0116   process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("NUL")) {}
0117   template<typename T, typename = typename std::enable_if<std::is_same<T, filesystem::path>::value>::type>
0118   process_io_binding(const T & pth)
0119     : h(::CreateFileW(
0120         pth.c_str(),
0121         Target == STD_INPUT_HANDLE ? GENERIC_READ : GENERIC_WRITE,
0122         FILE_SHARE_READ | FILE_SHARE_WRITE,
0123         nullptr,
0124         OPEN_ALWAYS,
0125         FILE_ATTRIBUTE_NORMAL,
0126         nullptr
0127         ), true)
0128   {
0129   }
0130 
0131 
0132   template<typename ReadablePipe>
0133   process_io_binding(ReadablePipe & pipe,
0134                      typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STD_INPUT_HANDLE>::type * = nullptr)
0135   {
0136     net::detail::native_pipe_handle p[2];
0137     error_code ec;
0138     net::detail::create_pipe(p, ec);
0139     if (ec)
0140       detail::throw_error(ec, "create_pipe");
0141       
0142     h = std::unique_ptr<void, handle_closer>{p[1], true};
0143     pipe.assign(p[0]);
0144   }
0145 
0146 
0147   template<typename WritablePipe>
0148   process_io_binding(WritablePipe & pipe,
0149                      typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STD_INPUT_HANDLE>::type * = nullptr)
0150   {
0151     net::detail::native_pipe_handle p[2];
0152     error_code ec;
0153     net::detail::create_pipe(p, ec);
0154     if (ec)
0155       detail::throw_error(ec, "create_pipe");
0156 
0157     h = std::unique_ptr<void, handle_closer>{p[0], true};
0158     pipe.assign(p[1]);
0159   }
0160 };
0161 
0162 typedef process_io_binding<STD_INPUT_HANDLE>  process_input_binding;
0163 typedef process_io_binding<STD_OUTPUT_HANDLE> process_output_binding;
0164 typedef process_io_binding<STD_ERROR_HANDLE>  process_error_binding;
0165 
0166 #else
0167 
0168 template<int Target>
0169 struct process_io_binding
0170 {
0171   constexpr static int target = Target;
0172   int fd{target};
0173   bool fd_needs_closing{false};
0174   error_code ec;
0175 
0176   ~process_io_binding()
0177   {
0178     if (fd_needs_closing)
0179       ::close(fd);
0180   }
0181 
0182   process_io_binding() = default;
0183   process_io_binding(const process_io_binding &) = delete;
0184   process_io_binding & operator=(const process_io_binding &) = delete;
0185 
0186   process_io_binding(process_io_binding && other) noexcept
0187           : fd(other.fd), fd_needs_closing(other.fd_needs_closing), ec(other.ec)
0188   {
0189     other.fd = target;
0190     other.fd_needs_closing = false;
0191     other.ec = {};
0192   }
0193 
0194   process_io_binding & operator=(process_io_binding && other) noexcept
0195   {
0196     if (fd_needs_closing)
0197       ::close(fd);
0198 
0199     fd = other.fd;
0200     fd_needs_closing = other.fd_needs_closing;
0201     ec = other.ec;
0202 
0203     other.fd = target;
0204     other.fd_needs_closing = false;
0205     other.ec = {};
0206     return *this;
0207   }
0208 
0209   template<typename Stream>
0210   process_io_binding(Stream && str, decltype(std::declval<Stream>().native_handle()) * = nullptr)
0211           : process_io_binding(str.native_handle())
0212   {}
0213 
0214   process_io_binding(FILE * f) : process_io_binding(fileno(f)) {}
0215   process_io_binding(int fd) : fd(fd) {}
0216   process_io_binding(std::nullptr_t) : process_io_binding(filesystem::path("/dev/null")) {}
0217   process_io_binding(const filesystem::path & pth)
0218           : fd(::open(pth.c_str(),
0219                       Target == STDIN_FILENO ? O_RDONLY : (O_WRONLY | O_CREAT),
0220                       0660)), fd_needs_closing(true)
0221   {
0222   }
0223 
0224   template<typename ReadablePipe>
0225   process_io_binding(ReadablePipe & readable_pipe,
0226                      typename std::enable_if<is_readable_pipe<ReadablePipe>::value && Target != STDIN_FILENO>::type * = nullptr)
0227   {
0228     net::detail::native_pipe_handle p[2];
0229     net::detail::create_pipe(p, ec);
0230     if (ec)
0231       detail::throw_error(ec, "create_pipe");
0232 
0233     fd = p[1];
0234     if (::fcntl(p[0], F_SETFD, FD_CLOEXEC) == -1)
0235     {
0236       BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
0237       return ;
0238     }
0239     fd_needs_closing = true;
0240     readable_pipe.assign(p[0], ec);
0241   }
0242 
0243 
0244   template<typename WritablePipe>
0245   process_io_binding(WritablePipe & writable_pipe,
0246                      typename std::enable_if<is_writable_pipe<WritablePipe>::value && Target == STDIN_FILENO>::type * = nullptr)
0247   {
0248     net::detail::native_pipe_handle p[2];
0249     error_code ec;
0250     net::detail::create_pipe(p, ec);
0251     if (ec)
0252       detail::throw_error(ec, "create_pipe");
0253 
0254     fd = p[0];
0255     if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
0256     {
0257       BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
0258       return ;
0259     }
0260     fd_needs_closing = true;
0261     writable_pipe.assign(p[1], ec);
0262   }
0263 
0264   error_code on_setup(posix::default_launcher &,
0265                       const filesystem::path &, const char * const *)
0266   {
0267       return ec;
0268   }
0269 
0270   error_code on_exec_setup(posix::default_launcher &,
0271                            const filesystem::path &, const char * const *)
0272   {
0273     if (::dup2(fd, target) == -1)
0274       return get_last_error();
0275     else
0276       return error_code();
0277   }
0278 };
0279 
0280 typedef process_io_binding<STDIN_FILENO>  process_input_binding;
0281 typedef process_io_binding<STDOUT_FILENO> process_output_binding;
0282 typedef process_io_binding<STDERR_FILENO> process_error_binding;
0283 
0284 #endif
0285 
0286 }
0287 
0288 
0289 /// The initializer for the stdio of a subprocess
0290 /** The subprocess initializer has three members:
0291  * 
0292  *  - in for stdin
0293  *  - out for stdout
0294  *  - err for stderr
0295  * 
0296  * If the initializer is present all three will be set for the subprocess.
0297  * By default they will inherit the stdio handles from the parent process. 
0298  * This means that this will forward stdio to the subprocess:
0299  * 
0300  * @code {.cpp}
0301  * asio::io_context ctx;
0302  * v2::process proc(ctx, "/bin/bash", {}, v2::process_stdio{});
0303  * @endcode
0304  * 
0305  * No constructors are provided in order to support designated initializers
0306  * in later version of C++.
0307  * 
0308  * * @code {.cpp}
0309  * asio::io_context ctx;
0310  * /// C++17
0311  * v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{.err=nullptr});
0312  * /// C++11 & C++14
0313  * v2::process proc17(ctx, "/bin/bash", {}, v2::process_stdio{ {}, {}, nullptr});
0314  *                                                        stdin ^  ^ stderr
0315  * @endcode
0316  * 
0317  * Valid initializers for any stdio are:
0318  * 
0319  *  - `std::nullptr_t` assigning a null-device
0320  *  - `FILE*` any open file, including `stdin`, `stdout` and `stderr`
0321  *  - a filesystem::path, which will open a readable or writable depending on the direction of the stream
0322  *  - `native_handle` any native file handle (`HANDLE` on windows) or file descriptor (`int` on posix)
0323  *  - any io-object with a .native_handle() function that is compatible with the above. E.g. a asio::ip::tcp::socket
0324  *  - an asio::basic_writeable_pipe for stdin or asio::basic_readable_pipe for stderr/stdout. 
0325  * 
0326  * 
0327  */ 
0328 struct process_stdio
0329 {
0330   detail::process_input_binding in;
0331   detail::process_output_binding out;
0332   detail::process_error_binding err;
0333 
0334 #if defined(BOOST_PROCESS_V2_WINDOWS)
0335   error_code on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &)
0336   {
0337     launcher.startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
0338     launcher.startup_info.StartupInfo.hStdInput  = in.prepare();
0339     launcher.startup_info.StartupInfo.hStdOutput = out.prepare();
0340     launcher.startup_info.StartupInfo.hStdError  = err.prepare();
0341     launcher.inherited_handles.reserve(launcher.inherited_handles.size() + 3);
0342     launcher.inherited_handles.push_back(launcher.startup_info.StartupInfo.hStdInput);
0343     launcher.inherited_handles.push_back(launcher.startup_info.StartupInfo.hStdOutput);
0344     launcher.inherited_handles.push_back(launcher.startup_info.StartupInfo.hStdError);
0345     return error_code {};
0346   };
0347 #else
0348   error_code on_exec_setup(posix::default_launcher & /*launcher*/, const filesystem::path &, const char * const *)
0349   {
0350     if (::dup2(in.fd, in.target) == -1)
0351       return error_code(errno, system_category());
0352 
0353     if (::dup2(out.fd, out.target) == -1)
0354       return error_code(errno, system_category());
0355 
0356     if (::dup2(err.fd, err.target) == -1)
0357       return error_code(errno, system_category());
0358 
0359     return error_code {};
0360   };
0361 #endif
0362 
0363 };
0364 
0365 BOOST_PROCESS_V2_END_NAMESPACE
0366 
0367 #endif //  BOOST_PROCESS_V2_STDIO_HPP