Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // Copyright (c) 2022 Klemens D. Morgenstern
0002 //
0003 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0004 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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 /// The default launcher for processes on windows.
0293 struct default_launcher
0294 {
0295     /// The pointer to the environment forwarded to the subprocess.
0296     const char * const * env = ::environ;
0297     /// The pid of the subprocess - will be assigned after fork.
0298     int pid = -1;
0299 
0300     /// The whitelist for file descriptors.
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     //if we need to allocate something
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 //BOOST_PROCESS_V2_POSIX_DEFAULT_LAUNCHER