File indexing completed on 2025-09-17 08:48:49
0001
0002
0003
0004
0005
0006
0007
0008
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
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
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 & , 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