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