Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:48:49

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