Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:50:13

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