File indexing completed on 2025-09-18 09:02:56
0001
0002
0003
0004
0005 #ifndef BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER
0006 #define BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER
0007
0008 #include <boost/process/v2/detail/config.hpp>
0009 #include <boost/process/v2/cstring_ref.hpp>
0010 #include <boost/process/v2/posix/detail/close_handles.hpp>
0011 #include <boost/process/v2/detail/throw_error.hpp>
0012 #include <boost/process/v2/detail/utf8.hpp>
0013
0014 #if defined(BOOST_PROCESS_V2_STANDALONE)
0015 #include <asio/execution/executor.hpp>
0016 #include <asio/is_executor.hpp>
0017 #include <asio/execution_context.hpp>
0018 #include <asio/execution/context.hpp>
0019 #include <asio/query.hpp>
0020 #else
0021 #include <boost/asio/execution/executor.hpp>
0022 #include <boost/asio/is_executor.hpp>
0023 #include <boost/asio/execution_context.hpp>
0024 #include <boost/asio/execution/context.hpp>
0025 #include <boost/asio/query.hpp>
0026 #endif
0027
0028 #include <fcntl.h>
0029 #include <sys/types.h>
0030 #include <sys/wait.h>
0031 #include <unistd.h>
0032
0033 #if defined(__APPLE__)
0034 # include <crt_externs.h>
0035 # if !defined(environ)
0036 # define environ (*_NSGetEnviron())
0037 # endif
0038 #elif defined(__MACH__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun)
0039 extern "C" { extern char **environ; }
0040 #endif
0041
0042 BOOST_PROCESS_V2_BEGIN_NAMESPACE
0043
0044 template<typename Executor>
0045 struct basic_process;
0046
0047 namespace posix
0048 {
0049
0050
0051 namespace detail
0052 {
0053
0054 struct base {};
0055 struct derived : base {};
0056
0057 template<typename Launcher, typename Init>
0058 inline error_code invoke_on_setup(Launcher & , const filesystem::path &,
0059 const char * const * (&),
0060 Init && , base && )
0061 {
0062 return error_code{};
0063 }
0064
0065 template<typename Launcher, typename Init>
0066 inline auto invoke_on_setup(Launcher & launcher, const filesystem::path &executable,
0067 const char * const * (&cmd_line),
0068 Init && init, derived && )
0069 -> decltype(init.on_setup(launcher, executable, cmd_line))
0070 {
0071 return init.on_setup(launcher, executable, cmd_line);
0072 }
0073
0074 template<typename Launcher>
0075 inline error_code on_setup(Launcher & , const filesystem::path &,
0076 const char * const * (&))
0077 {
0078 return error_code{};
0079 }
0080
0081 template<typename Launcher, typename Init1, typename ... Inits>
0082 inline error_code on_setup(Launcher & launcher, const filesystem::path &executable,
0083 const char * const * (&cmd_line),
0084 Init1 && init1, Inits && ... inits)
0085 {
0086 auto ec = invoke_on_setup(launcher, executable, cmd_line, init1, derived{});
0087 if (ec)
0088 return ec;
0089 else
0090 return on_setup(launcher, executable, cmd_line, inits...);
0091 }
0092
0093
0094 template<typename Launcher, typename Init>
0095 inline void invoke_on_error(Launcher & , const filesystem::path &,
0096 const char * const * (&),
0097 const error_code & , Init && , base && )
0098 {
0099 }
0100
0101 template<typename Launcher, typename Init>
0102 inline auto invoke_on_error(Launcher & launcher, const filesystem::path &executable,
0103 const char * const * (&cmd_line),
0104 const error_code & ec, Init && init, derived && )
0105 -> decltype(init.on_error(launcher, executable, cmd_line, ec))
0106 {
0107 init.on_error(launcher, executable, cmd_line, ec);
0108 }
0109
0110 template<typename Launcher>
0111 inline void on_error(Launcher & , const filesystem::path &,
0112 const char * const * (&),
0113 const error_code & )
0114 {
0115 }
0116
0117 template<typename Launcher, typename Init1, typename ... Inits>
0118 inline void on_error(Launcher & launcher, const filesystem::path &executable,
0119 const char * const * (&cmd_line),
0120 const error_code & ec,
0121 Init1 && init1, Inits && ... inits)
0122 {
0123 invoke_on_error(launcher, executable, cmd_line, ec, init1, derived{});
0124 on_error(launcher, executable, cmd_line, ec, inits...);
0125 }
0126
0127 template<typename Launcher, typename Init>
0128 inline void invoke_on_success(Launcher & , const filesystem::path &,
0129 const char * const * (&),
0130 Init && , base && )
0131 {
0132 }
0133
0134 template<typename Launcher, typename Init>
0135 inline auto invoke_on_success(Launcher & launcher, const filesystem::path &executable,
0136 const char * const * (&cmd_line),
0137 Init && init, derived && )
0138 -> decltype(init.on_success(launcher, executable, cmd_line))
0139 {
0140 init.on_success(launcher, executable, cmd_line);
0141 }
0142
0143 template<typename Launcher>
0144 inline void on_success(Launcher & , const filesystem::path &,
0145 const char * const * (&))
0146 {
0147 }
0148
0149 template<typename Launcher, typename Init1, typename ... Inits>
0150 inline void on_success(Launcher & launcher, const filesystem::path &executable,
0151 const char * const * (&cmd_line),
0152 Init1 && init1, Inits && ... inits)
0153 {
0154 invoke_on_success(launcher, executable, cmd_line, init1, derived{});
0155 on_success(launcher, executable, cmd_line, inits...);
0156 }
0157
0158 template<typename Launcher, typename Init>
0159 inline void invoke_on_fork_error(Launcher & , const filesystem::path &,
0160 const char * const * (&),
0161 const error_code & , Init && , base && )
0162 {
0163 }
0164
0165 template<typename Launcher, typename Init>
0166 inline auto invoke_on_fork_error(Launcher & launcher, const filesystem::path &executable,
0167 const char * const * (&cmd_line),
0168 const error_code & ec, Init && init, derived && )
0169 -> decltype(init.on_fork_error(launcher, executable, cmd_line, ec))
0170 {
0171 init.on_fork_error(launcher, executable, cmd_line, ec);
0172 }
0173
0174 template<typename Launcher>
0175 inline void on_fork_error(Launcher & , const filesystem::path &,
0176 const char * const * (&),
0177 const error_code & )
0178 {
0179 }
0180
0181 template<typename Launcher, typename Init1, typename ... Inits>
0182 inline void on_fork_error(Launcher & launcher, const filesystem::path &executable,
0183 const char * const * (&cmd_line),
0184 const error_code & ec,
0185 Init1 && init1, Inits && ... inits)
0186 {
0187 invoke_on_fork_error(launcher, executable, cmd_line, ec, init1, derived{});
0188 on_fork_error(launcher, executable, cmd_line, ec, inits...);
0189 }
0190
0191 template<typename Launcher, typename Init>
0192 inline error_code invoke_on_exec_setup(Launcher & , const filesystem::path &,
0193 const char * const * (&),
0194 Init && , base && )
0195 {
0196 return error_code{};
0197 }
0198
0199 template<typename Launcher, typename Init>
0200 inline auto invoke_on_exec_setup(Launcher & launcher, const filesystem::path &executable,
0201 const char * const * (&cmd_line),
0202 Init && init, derived && )
0203 -> decltype(init.on_exec_setup(launcher, executable, cmd_line))
0204 {
0205 return init.on_exec_setup(launcher, executable, cmd_line);
0206 }
0207
0208 template<typename Launcher>
0209 inline error_code on_exec_setup(Launcher & , const filesystem::path &,
0210 const char * const * (&))
0211 {
0212 return error_code{};
0213 }
0214
0215 template<typename Launcher, typename Init1, typename ... Inits>
0216 inline error_code on_exec_setup(Launcher & launcher, const filesystem::path &executable,
0217 const char * const * (&cmd_line),
0218 Init1 && init1, Inits && ... inits)
0219 {
0220 auto ec = invoke_on_exec_setup(launcher, executable, cmd_line, init1, derived{});
0221 if (ec)
0222 return ec;
0223 else
0224 return on_exec_setup(launcher, executable, cmd_line, inits...);
0225 }
0226
0227
0228
0229 template<typename Launcher, typename Init>
0230 inline void invoke_on_exec_error(Launcher & , const filesystem::path &,
0231 const char * const * (&),
0232 const error_code & , Init && , base && )
0233 {
0234 }
0235
0236 template<typename Launcher, typename Init>
0237 inline auto invoke_on_exec_error(Launcher & launcher, const filesystem::path &executable,
0238 const char * const * (&cmd_line),
0239 const error_code & ec, Init && init, derived && )
0240 -> decltype(init.on_exec_error(launcher, executable, cmd_line, ec))
0241 {
0242 init.on_exec_error(launcher, executable, cmd_line, ec);
0243 }
0244
0245 template<typename Launcher>
0246 inline void on_exec_error(Launcher & , const filesystem::path &,
0247 const char * const * (&),
0248 const error_code & )
0249 {
0250 }
0251
0252 template<typename Launcher, typename Init1, typename ... Inits>
0253 inline void on_exec_error(Launcher & launcher, const filesystem::path &executable,
0254 const char * const * (&cmd_line),
0255 const error_code & ec,
0256 Init1 && init1, Inits && ... inits)
0257 {
0258 invoke_on_exec_error(launcher, executable, cmd_line, ec, init1, derived{});
0259 on_exec_error(launcher, executable, cmd_line, ec, inits...);
0260 }
0261 }
0262
0263
0264 struct default_launcher
0265 {
0266
0267 const char * const * env = environ;
0268
0269 int pid = -1;
0270
0271
0272 std::vector<int> fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
0273
0274 default_launcher() = default;
0275
0276 template<typename ExecutionContext, typename Args, typename ... Inits>
0277 auto operator()(ExecutionContext & context,
0278 const typename std::enable_if<std::is_convertible<
0279 ExecutionContext&, net::execution_context&>::value,
0280 filesystem::path >::type & executable,
0281 Args && args,
0282 Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
0283 {
0284 error_code ec;
0285 auto proc = (*this)(context, ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
0286
0287 if (ec)
0288 v2::detail::throw_error(ec, "default_launcher");
0289
0290 return proc;
0291 }
0292
0293
0294 template<typename ExecutionContext, typename Args, typename ... Inits>
0295 auto operator()(ExecutionContext & context,
0296 error_code & ec,
0297 const typename std::enable_if<std::is_convertible<
0298 ExecutionContext&, net::execution_context&>::value,
0299 filesystem::path >::type & executable,
0300 Args && args,
0301 Inits && ... inits ) -> basic_process<typename ExecutionContext::executor_type>
0302 {
0303 return (*this)(context.get_executor(), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
0304 }
0305
0306 template<typename Executor, typename Args, typename ... Inits>
0307 auto operator()(Executor exec,
0308 const typename std::enable_if<
0309 net::execution::is_executor<Executor>::value ||
0310 net::is_executor<Executor>::value,
0311 filesystem::path >::type & executable,
0312 Args && args,
0313 Inits && ... inits ) -> basic_process<Executor>
0314 {
0315 error_code ec;
0316 auto proc = (*this)(std::move(exec), ec, executable, std::forward<Args>(args), std::forward<Inits>(inits)...);
0317
0318 if (ec)
0319 v2::detail::throw_error(ec, "default_launcher");
0320
0321 return proc;
0322 }
0323
0324 template<typename Executor, typename Args, typename ... Inits>
0325 auto operator()(Executor exec,
0326 error_code & ec,
0327 const typename std::enable_if<
0328 net::execution::is_executor<Executor>::value ||
0329 net::is_executor<Executor>::value,
0330 filesystem::path >::type & executable,
0331 Args && args,
0332 Inits && ... inits ) -> basic_process<Executor>
0333 {
0334 auto argv = this->build_argv_(executable, std::forward<Args>(args));
0335 {
0336 pipe_guard pg;
0337 if (::pipe(pg.p))
0338 {
0339 BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
0340 return basic_process<Executor>{exec};
0341 }
0342 if (::fcntl(pg.p[1], F_SETFD, FD_CLOEXEC))
0343 {
0344 BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
0345 return basic_process<Executor>{exec};
0346 }
0347 ec = detail::on_setup(*this, executable, argv, inits ...);
0348 if (ec)
0349 {
0350 detail::on_error(*this, executable, argv, ec, inits...);
0351 return basic_process<Executor>(exec);
0352 }
0353 fd_whitelist.push_back(pg.p[1]);
0354
0355 auto & ctx = net::query(
0356 exec, net::execution::context);
0357 #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
0358 ctx.notify_fork(net::execution_context::fork_prepare);
0359 #endif
0360 pid = ::fork();
0361 if (pid == -1)
0362 {
0363 #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
0364 ctx.notify_fork(net::execution_context::fork_parent);
0365 #endif
0366 detail::on_fork_error(*this, executable, argv, ec, inits...);
0367 detail::on_error(*this, executable, argv, ec, inits...);
0368
0369 BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
0370 return basic_process<Executor>{exec};
0371 }
0372 else if (pid == 0)
0373 {
0374 ::close(pg.p[0]);
0375 #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
0376 ctx.notify_fork(net::execution_context::fork_child);
0377 #endif
0378 ec = detail::on_exec_setup(*this, executable, argv, inits...);
0379 if (!ec)
0380 {
0381 close_all_fds(ec);
0382 }
0383 if (!ec)
0384 ::execve(executable.c_str(), const_cast<char * const *>(argv), const_cast<char * const *>(env));
0385
0386 ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
0387 BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
0388 detail::on_exec_error(*this, executable, argv, ec, inits...);
0389 ::exit(EXIT_FAILURE);
0390 return basic_process<Executor>{exec};
0391 }
0392 #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
0393 ctx.notify_fork(net::execution_context::fork_parent);
0394 #endif
0395 ::close(pg.p[1]);
0396 pg.p[1] = -1;
0397 int child_error{0};
0398 int count = -1;
0399 while ((count = ::read(pg.p[0], &child_error, sizeof(child_error))) == -1)
0400 {
0401 int err = errno;
0402 if ((err != EAGAIN) && (err != EINTR))
0403 {
0404 BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category());
0405 break;
0406 }
0407 }
0408 if (count != 0)
0409 BOOST_PROCESS_V2_ASSIGN_EC(ec, child_error, system_category());
0410
0411 if (ec)
0412 {
0413 detail::on_error(*this, executable, argv, ec, inits...);
0414 do { ::waitpid(pid, nullptr, 0); } while (errno == EINTR);
0415 return basic_process<Executor>{exec};
0416 }
0417 }
0418 basic_process<Executor> proc(exec, pid);
0419 detail::on_success(*this, executable, argv, ec, inits...);
0420 return proc;
0421
0422 }
0423 protected:
0424
0425 void ignore_unused(std::size_t ) {}
0426 void close_all_fds(error_code & ec)
0427 {
0428 std::sort(fd_whitelist.begin(), fd_whitelist.end());
0429 detail::close_all(fd_whitelist, ec);
0430 fd_whitelist = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
0431 }
0432
0433 struct pipe_guard
0434 {
0435 int p[2];
0436 pipe_guard() : p{-1,-1} {}
0437
0438 ~pipe_guard()
0439 {
0440 if (p[0] != -1)
0441 ::close(p[0]);
0442 if (p[1] != -1)
0443 ::close(p[1]);
0444 }
0445 };
0446
0447
0448 std::vector<std::string> argv_buffer_;
0449 std::vector<const char *> argv_;
0450
0451 template<typename Args>
0452 const char * const * build_argv_(const filesystem::path & pt, const Args & args,
0453 typename std::enable_if<
0454 std::is_convertible<
0455 decltype(*std::begin(std::declval<Args>())),
0456 cstring_ref>::value>::type * = nullptr)
0457 {
0458 const auto arg_cnt = std::distance(std::begin(args), std::end(args));
0459 argv_.reserve(arg_cnt + 2);
0460 argv_.push_back(pt.native().data());
0461 for (auto && arg : args)
0462 argv_.push_back(arg.c_str());
0463
0464 argv_.push_back(nullptr);
0465 return argv_.data();
0466 }
0467
0468 const char * const * build_argv_(const filesystem::path &, const char ** argv)
0469 {
0470 return argv;
0471 }
0472
0473 template<typename Args>
0474 const char * const * build_argv_(const filesystem::path & pt, const Args & args,
0475 typename std::enable_if<
0476 !std::is_convertible<
0477 decltype(*std::begin(std::declval<Args>())),
0478 cstring_ref>::value>::type * = nullptr)
0479 {
0480 const auto arg_cnt = std::distance(std::begin(args), std::end(args));
0481 argv_.reserve(arg_cnt + 2);
0482 argv_buffer_.reserve(arg_cnt);
0483 argv_.push_back(pt.native().data());
0484
0485 using char_type = typename decay<decltype((*std::begin(std::declval<Args>()))[0])>::type;
0486
0487 for (basic_string_view<char_type> arg : args)
0488 argv_buffer_.push_back(v2::detail::conv_string<char>(arg.data(), arg.size()));
0489
0490 for (auto && arg : argv_buffer_)
0491 argv_.push_back(arg.c_str());
0492
0493 argv_.push_back(nullptr);
0494 return argv_.data();
0495 }
0496 };
0497
0498
0499 }
0500
0501 BOOST_PROCESS_V2_END_NAMESPACE
0502
0503 #endif