File indexing completed on 2026-08-09 09:01:11
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
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
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
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 & , 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