File indexing completed on 2025-01-18 09:50:06
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef BOOST_PROCESS_DETAIL_POSIX_EXECUTOR_HPP
0011 #define BOOST_PROCESS_DETAIL_POSIX_EXECUTOR_HPP
0012
0013 #include <boost/process/detail/child_decl.hpp>
0014 #include <boost/process/error.hpp>
0015 #include <boost/process/pipe.hpp>
0016 #include <boost/process/detail/posix/basic_pipe.hpp>
0017 #include <boost/process/detail/posix/use_vfork.hpp>
0018 #include <boost/fusion/algorithm/iteration/for_each.hpp>
0019 #include <cstdlib>
0020 #include <sys/types.h>
0021 #include <fcntl.h>
0022 #include <errno.h>
0023 #include <unistd.h>
0024
0025 #include <boost/algorithm/string/predicate.hpp>
0026 #include <boost/algorithm/string/split.hpp>
0027 #include <boost/algorithm/string/classification.hpp>
0028
0029 #include <boost/core/ignore_unused.hpp>
0030
0031 namespace boost { namespace process { namespace detail { namespace posix {
0032
0033 template<typename Executor>
0034 struct on_setup_t
0035 {
0036 Executor & exec;
0037 on_setup_t(Executor & exec) : exec(exec) {};
0038 template<typename T>
0039 void operator()(T & t) const
0040 {
0041 if (!exec.error())
0042 t.on_setup(exec);
0043 }
0044 };
0045
0046 template<typename Executor>
0047 struct on_error_t
0048 {
0049 Executor & exec;
0050 const std::error_code & error;
0051 on_error_t(Executor & exec, const std::error_code & error) : exec(exec), error(error) {};
0052 template<typename T>
0053 void operator()(T & t) const
0054 {
0055 t.on_error(exec, error);
0056 }
0057 };
0058
0059 template<typename Executor>
0060 struct on_success_t
0061 {
0062 Executor & exec;
0063 on_success_t(Executor & exec) : exec(exec) {};
0064 template<typename T>
0065 void operator()(T & t) const {t.on_success(exec);}
0066 };
0067
0068
0069
0070 template<typename Executor>
0071 struct on_fork_error_t
0072 {
0073 Executor & exec;
0074 const std::error_code & error;
0075 on_fork_error_t(Executor & exec, const std::error_code & error) : exec(exec), error(error) {};
0076
0077 template<typename T>
0078 void operator()(T & t) const
0079 {
0080 t.on_fork_error(exec, error);
0081 }
0082 };
0083
0084
0085 template<typename Executor>
0086 struct on_exec_setup_t
0087 {
0088 Executor & exec;
0089 on_exec_setup_t(Executor & exec) : exec(exec) {};
0090
0091 template<typename T>
0092 void operator()(T & t) const
0093 {
0094 t.on_exec_setup(exec);
0095 }
0096 };
0097
0098
0099 template<typename Executor>
0100 struct on_exec_error_t
0101 {
0102 Executor & exec;
0103 const std::error_code &ec;
0104 on_exec_error_t(Executor & exec, const std::error_code & error) : exec(exec), ec(error) {};
0105
0106 template<typename T>
0107 void operator()(T & t) const
0108 {
0109 t.on_exec_error(exec, ec);
0110 }
0111 };
0112
0113 template<typename Executor>
0114 struct on_fork_success_t
0115 {
0116 Executor & exec;
0117 on_fork_success_t(Executor & exec) : exec(exec) {};
0118
0119 template<typename T>
0120 void operator()(T & t) const
0121 {
0122 t.on_fork_success(exec);
0123 }
0124 };
0125
0126 template<typename Executor> on_setup_t <Executor> call_on_setup (Executor & exec) {return exec;}
0127 template<typename Executor> on_error_t <Executor> call_on_error (Executor & exec, const std::error_code & ec)
0128 {
0129 return on_error_t<Executor> (exec, ec);
0130 }
0131 template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;}
0132
0133 template<typename Executor> on_fork_error_t <Executor> call_on_fork_error (Executor & exec, const std::error_code & ec)
0134 {
0135 return on_fork_error_t<Executor> (exec, ec);
0136 }
0137
0138
0139 template<typename Executor> on_exec_setup_t <Executor> call_on_exec_setup (Executor & exec) {return exec;}
0140 template<typename Executor> on_exec_error_t <Executor> call_on_exec_error (Executor & exec, const std::error_code & ec)
0141 {
0142 return on_exec_error_t<Executor> (exec, ec);
0143 }
0144
0145
0146 template<typename Sequence>
0147 class executor
0148 {
0149 template<typename HasHandler, typename UseVFork>
0150 void internal_error_handle(const std::error_code&, const char*, HasHandler, boost::mpl::true_, UseVFork) {}
0151
0152 int _pipe_sink = -1;
0153
0154
0155 void write_error(const std::error_code & ec, const char * msg)
0156 {
0157
0158 const auto len = static_cast<int>(std::strlen(msg));
0159 int data[2] = {ec.value(), len + 1};
0160
0161 boost::ignore_unused(::write(_pipe_sink, &data[0], sizeof(int) * 2));
0162 boost::ignore_unused(::write(_pipe_sink, msg, len));
0163 }
0164
0165 void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::false_)
0166 {
0167 if (this->pid == 0)
0168 write_error(ec, msg);
0169 else
0170 {
0171 this->_ec = ec;
0172 this->_msg = msg;
0173 }
0174 }
0175 void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_, boost::mpl::false_)
0176 {
0177 if (this->pid == 0)
0178 write_error(ec, msg);
0179 else
0180 throw process_error(ec, msg);
0181 }
0182
0183
0184 void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::true_)
0185 {
0186 this->_ec = ec;
0187 this->_msg = msg;
0188 }
0189 void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_, boost::mpl::true_)
0190 {
0191 if (this->pid == 0)
0192 {
0193 this->_ec = ec;
0194 this->_msg = msg;
0195 }
0196 else
0197 throw process_error(ec, msg);
0198 }
0199
0200 void check_error(boost::mpl::true_) {};
0201 void check_error(boost::mpl::false_)
0202 {
0203 if (_ec)
0204 throw process_error(_ec, _msg);
0205 }
0206
0207 typedef typename ::boost::process::detail::has_error_handler<Sequence>::type has_error_handler;
0208 typedef typename ::boost::process::detail::has_ignore_error <Sequence>::type has_ignore_error;
0209 typedef typename ::boost::process::detail::posix::shall_use_vfork<Sequence>::type shall_use_vfork;
0210
0211 inline child invoke(boost::mpl::true_ , boost::mpl::true_ );
0212 inline child invoke(boost::mpl::false_, boost::mpl::true_ );
0213 inline child invoke(boost::mpl::true_ , boost::mpl::false_ );
0214 inline child invoke(boost::mpl::false_, boost::mpl::false_ );
0215 void _write_error(int sink)
0216 {
0217 int data[2] = {_ec.value(),static_cast<int>(_msg.size())};
0218 while (::write(sink, &data[0], sizeof(int) *2) == -1)
0219 {
0220 auto err = errno;
0221
0222 if (err == EBADF)
0223 return;
0224 else if ((err != EINTR) && (err != EAGAIN))
0225 break;
0226 }
0227 while (::write(sink, &_msg.front(), _msg.size()) == -1)
0228 {
0229 auto err = errno;
0230
0231 if (err == EBADF)
0232 return;
0233 else if ((err != EINTR) && (err != EAGAIN))
0234 break;
0235 }
0236 }
0237
0238 void _read_error(int source)
0239 {
0240 int data[2];
0241
0242 _ec.clear();
0243 int count = 0;
0244 while ((count = ::read(source, &data[0], sizeof(int) *2 ) ) == -1)
0245 {
0246
0247 auto err = errno;
0248 if ((err != EAGAIN ) && (err != EINTR))
0249 set_error(std::error_code(err, std::system_category()), "Error read pipe");
0250 }
0251 if (count == 0)
0252 return ;
0253
0254 std::error_code ec(data[0], std::system_category());
0255 std::string msg(data[1], ' ');
0256
0257 while (::read(source, &msg.front(), msg.size() ) == -1)
0258 {
0259
0260 auto err = errno;
0261 if ((err == EBADF) || (err == EPERM))
0262 return;
0263
0264 else if ((err != EAGAIN ) && (err != EINTR))
0265 set_error(std::error_code(err, std::system_category()), "Error read pipe");
0266 }
0267 set_error(ec, std::move(msg));
0268 }
0269
0270 std::string prepare_cmd_style_fn;
0271
0272 inline void prepare_cmd_style()
0273 {
0274
0275 prepare_cmd_style_fn = exe;
0276 if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK))
0277 {
0278 const auto * e = ::environ;
0279 while ((e != nullptr) && (*e != nullptr) && !boost::starts_with(*e, "PATH="))
0280 e++;
0281
0282 if ((e != nullptr) && (*e != nullptr))
0283 {
0284 std::vector<std::string> path;
0285
0286 boost::split(path, (*e) + 5, boost::is_any_of(":"));
0287
0288 for (const std::string & pp : path)
0289 {
0290 auto p = pp + "/" + exe;
0291 if (!::access(p.c_str(), X_OK))
0292 {
0293 prepare_cmd_style_fn = p;
0294 break;
0295 }
0296 }
0297 }
0298 }
0299 exe = prepare_cmd_style_fn.c_str();
0300 }
0301
0302 std::error_code _ec;
0303 std::string _msg;
0304 public:
0305 executor(Sequence & seq) : seq(seq)
0306 {
0307 }
0308
0309 child operator()()
0310 {
0311 return invoke(has_ignore_error(), shall_use_vfork());
0312 }
0313
0314
0315 Sequence & seq;
0316 const char * exe = nullptr;
0317 char *const* cmd_line = nullptr;
0318 bool cmd_style = false;
0319 char **env = ::environ;
0320 pid_t pid = -1;
0321 std::shared_ptr<std::atomic<int>> exit_status = std::make_shared<std::atomic<int>>(still_active);
0322
0323 const std::error_code & error() const {return _ec;}
0324
0325 void set_error(const std::error_code &ec, const char* msg)
0326 {
0327 internal_error_handle(ec, msg, has_error_handler(), has_ignore_error(), shall_use_vfork());
0328 }
0329 void set_error(const std::error_code &ec, const std::string &msg) {set_error(ec, msg.c_str());};
0330
0331 std::vector<int> get_used_handles() const
0332 {
0333 if (_pipe_sink == -1)
0334 return {};
0335 else
0336 return {_pipe_sink};
0337 };
0338 };
0339
0340 template<typename Sequence>
0341 child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_)
0342 {
0343 boost::fusion::for_each(seq, call_on_setup(*this));
0344 if (_ec)
0345 return child();
0346 if (cmd_style)
0347 prepare_cmd_style();
0348
0349 this->pid = ::fork();
0350 if (pid == -1)
0351 {
0352 auto ec = boost::process::detail::get_last_error();
0353 boost::fusion::for_each(seq, call_on_fork_error(*this, ec));
0354 return child();
0355 }
0356 else if (pid == 0)
0357 {
0358 boost::fusion::for_each(seq, call_on_exec_setup(*this));
0359 ::execve(exe, cmd_line, env);
0360 auto ec = boost::process::detail::get_last_error();
0361 boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
0362 _exit(EXIT_FAILURE);
0363 }
0364
0365 child c(child_handle(pid), exit_status);
0366
0367 boost::fusion::for_each(seq, call_on_success(*this));
0368
0369 return c;
0370 }
0371
0372 template<typename Sequence>
0373 child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
0374 {
0375 {
0376 struct pipe_guard
0377 {
0378 int p[2];
0379 pipe_guard() : p{-1,-1} {}
0380
0381 ~pipe_guard()
0382 {
0383 if (p[0] != -1)
0384 ::close(p[0]);
0385 if (p[1] != -1)
0386 ::close(p[1]);
0387 }
0388 } p{};
0389
0390 if (::pipe(p.p) == -1)
0391 {
0392 set_error(::boost::process::detail::get_last_error(), "pipe(2) failed");
0393 return child();
0394 }
0395 if (::fcntl(p.p[1], F_SETFD, FD_CLOEXEC) == -1)
0396 {
0397 auto err = ::boost::process::detail::get_last_error();
0398 set_error(err, "fcntl(2) failed");
0399 return child();
0400 }
0401 _ec.clear();
0402 boost::fusion::for_each(seq, call_on_setup(*this));
0403
0404 if (_ec)
0405 {
0406 boost::fusion::for_each(seq, call_on_error(*this, _ec));
0407 return child();
0408 }
0409
0410 if (cmd_style)
0411 prepare_cmd_style();
0412
0413 this->pid = ::fork();
0414 if (pid == -1)
0415 {
0416 _ec = boost::process::detail::get_last_error();
0417 _msg = "fork() failed";
0418 boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
0419 boost::fusion::for_each(seq, call_on_error(*this, _ec));
0420 return child();
0421 }
0422 else if (pid == 0)
0423 {
0424 _pipe_sink = p.p[1];
0425 ::close(p.p[0]);
0426
0427 boost::fusion::for_each(seq, call_on_exec_setup(*this));
0428 ::execve(exe, cmd_line, env);
0429 _ec = boost::process::detail::get_last_error();
0430 _msg = "execve failed";
0431 boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
0432
0433 _write_error(_pipe_sink);
0434 ::close(p.p[1]);
0435
0436 _exit(EXIT_FAILURE);
0437 return child();
0438 }
0439
0440 ::close(p.p[1]);
0441 p.p[1] = -1;
0442 _read_error(p.p[0]);
0443
0444 }
0445 if (_ec)
0446 {
0447
0448 ::waitpid(this->pid, nullptr, WNOHANG);
0449 boost::fusion::for_each(seq, call_on_error(*this, _ec));
0450 return child();
0451 }
0452
0453 child c(child_handle(pid), exit_status);
0454
0455 boost::fusion::for_each(seq, call_on_success(*this));
0456
0457 if (_ec)
0458 {
0459 boost::fusion::for_each(seq, call_on_error(*this, _ec));
0460 return child();
0461 }
0462
0463 return c;
0464 }
0465
0466 #if BOOST_POSIX_HAS_VFORK
0467
0468
0469 template<typename Sequence>
0470 child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::true_)
0471 {
0472 boost::fusion::for_each(seq, call_on_setup(*this));
0473 if (_ec)
0474 return child();
0475 this->pid = ::vfork();
0476 if (pid == -1)
0477 {
0478 auto ec = boost::process::detail::get_last_error();
0479 boost::fusion::for_each(seq, call_on_fork_error(*this, ec));
0480 return child();
0481 }
0482 else if (pid == 0)
0483 {
0484 boost::fusion::for_each(seq, call_on_exec_setup(*this));
0485 ::execve(exe, cmd_line, env);
0486 auto ec = boost::process::detail::get_last_error();
0487 boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
0488 _exit(EXIT_FAILURE);
0489 }
0490 child c(child_handle(pid), exit_status);
0491
0492 boost::fusion::for_each(seq, call_on_success(*this));
0493
0494 return c;
0495 }
0496
0497 template<typename Sequence>
0498 child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
0499 {
0500 boost::fusion::for_each(seq, call_on_setup(*this));
0501
0502 if (_ec)
0503 {
0504 boost::fusion::for_each(seq, call_on_error(*this, _ec));
0505 return child();
0506 }
0507 _ec.clear();
0508 if (cmd_style)
0509 this->prepare_cmd_style();
0510
0511 this->pid = ::vfork();
0512 if (pid == -1)
0513 {
0514 _ec = boost::process::detail::get_last_error();
0515 _msg = "fork() failed";
0516 boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
0517 boost::fusion::for_each(seq, call_on_error(*this, _ec));
0518
0519 return child();
0520 }
0521 else if (pid == 0)
0522 {
0523 boost::fusion::for_each(seq, call_on_exec_setup(*this));
0524
0525 ::execve(exe, cmd_line, env);
0526
0527 _ec = boost::process::detail::get_last_error();
0528 _msg = "execve failed";
0529 boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
0530
0531 _exit(EXIT_FAILURE);
0532 return child();
0533 }
0534 child c(child_handle(pid), exit_status);
0535
0536 check_error(has_error_handler());
0537
0538
0539
0540 if (_ec)
0541 {
0542 ::waitpid(this->pid, nullptr, WNOHANG);
0543 boost::fusion::for_each(seq, call_on_error(*this, _ec));
0544 return child();
0545 }
0546 else
0547 boost::fusion::for_each(seq, call_on_success(*this));
0548
0549 if (_ec)
0550 {
0551 boost::fusion::for_each(seq, call_on_error(*this, _ec));
0552 return child();
0553 }
0554
0555 return c;
0556 }
0557
0558 #endif
0559
0560 template<typename Char, typename Tup>
0561 inline executor<Tup> make_executor(Tup & tup)
0562 {
0563 return executor<Tup>(tup);
0564 }
0565
0566 }}}}
0567
0568 #endif