// Copyright (c) 2022 Klemens D. Morgenstern // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_HPP #define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_HPP #include #include #include #include #include #include #include #include #if defined(BOOST_PROCESS_V2_STANDALONE) #include #include #include #include #else #include #include #include #include #endif BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace detail { template struct basic_process_handle_fd { using native_handle_type = int; typedef Executor executor_type; executor_type get_executor() { return descriptor_.get_executor(); } /// Rebinds the process_handle to another executor. template struct rebind_executor { /// The socket type when rebound to the specified executor. typedef basic_process_handle_fd other; }; template basic_process_handle_fd(ExecutionContext &context, typename std::enable_if< std::is_convertible::value>::type * = nullptr) : pid_(-1), descriptor_(context) { } basic_process_handle_fd(executor_type executor) : pid_(-1), descriptor_(executor) { } basic_process_handle_fd(executor_type executor, pid_type pid) : pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0)) { } basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle) : pid_(pid), descriptor_(executor, process_handle) { } basic_process_handle_fd(basic_process_handle_fd &&handle) : pid_(handle.pid_), descriptor_(std::move(handle.descriptor_)) { handle.pid_ = -1; } template basic_process_handle_fd(basic_process_handle_fd &&handle) : pid_(handle.pid_), descriptor_(std::move(handle.descriptor_)) { handle.pid_ = -1; } basic_process_handle_fd& operator=(basic_process_handle_fd &&handle) { pid_ = handle.pid_; descriptor_ = std::move(handle.descriptor_); handle.pid_ = -1; return *this; } pid_type id() const { return pid_; } void terminate_if_running(error_code &) { if (pid_ <= 0) return; if (::waitpid(pid_, nullptr, WNOHANG) == 0) { ::kill(pid_, SIGKILL); ::waitpid(pid_, nullptr, 0); } } void terminate_if_running() { if (pid_ <= 0) return; if (::waitpid(pid_, nullptr, WNOHANG) == 0) { ::kill(pid_, SIGKILL); ::waitpid(pid_, nullptr, 0); } } void wait(native_exit_code_type &exit_status, error_code &ec) { if (pid_ <= 0) return; while (::waitpid(pid_, &exit_status, 0) < 0) { if (errno != EINTR) { ec = get_last_error(); break; } } } void wait(native_exit_code_type &exit_status) { if (pid_ <= 0) return; error_code ec; wait(exit_status, ec); if (ec) detail::throw_error(ec, "wait(pid)"); } void interrupt(error_code &ec) { if (pid_ <= 0) return; if (::kill(pid_, SIGINT) == -1) ec = get_last_error(); } void interrupt() { if (pid_ <= 0) return; error_code ec; interrupt(ec); if (ec) detail::throw_error(ec, "interrupt"); } void request_exit(error_code &ec) { if (pid_ <= 0) return; if (::kill(pid_, SIGTERM) == -1) ec = get_last_error(); } void request_exit() { if (pid_ <= 0) return; error_code ec; request_exit(ec); if (ec) detail::throw_error(ec, "request_exit"); } void terminate(native_exit_code_type &exit_status, error_code &ec) { if (pid_ <= 0) return; if (::kill(pid_, SIGKILL) == -1) ec = get_last_error(); } void terminate(native_exit_code_type &exit_status) { if (pid_ <= 0) return; error_code ec; terminate(exit_status, ec); if (ec) detail::throw_error(ec, "terminate"); } bool running(native_exit_code_type &exit_code, error_code & ec) { if (pid_ <= 0) return false; int code = 0; int res = ::waitpid(pid_, &code, WNOHANG); if (res == -1) ec = get_last_error(); else if (res == 0) return true; else ec.clear(); if (res == 0) return true; else { exit_code = code; return false; } } bool running(native_exit_code_type &exit_code) { if (pid_ <= 0) return false; error_code ec; bool res = running(exit_code, ec); if (ec) detail::throw_error(ec, "is_running"); return res; } bool is_open() const { return pid_ != -1; } template BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type)) async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type)) { return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose( async_wait_op_{descriptor_, pid_}, handler, descriptor_); } private: template friend struct basic_process_handle_fd; pid_type pid_ = -1; BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_stream_descriptor descriptor_; struct async_wait_op_ { BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_descriptor &descriptor; pid_type pid_; template void operator()(Self &&self) { error_code ec; native_exit_code_type exit_code{}; int wait_res = -1; if (pid_ <= 0) // error, complete early ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor; else { wait_res = ::waitpid(pid_, &exit_code, WNOHANG); if (wait_res == -1) ec = get_last_error(); } if (!ec && (wait_res == 0)) { descriptor.async_wait( BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::descriptor_base::wait_read, std::move(self)); return; } struct completer { error_code ec; native_exit_code_type code; typename std::decay::type self; void operator()() { self.complete(ec, code); } }; BOOST_PROCESS_V2_ASIO_NAMESPACE::post(descriptor.get_executor(), completer{ec, exit_code, std::move(self)}); } template void operator()(Self &&self, error_code ec, int = 0) { native_exit_code_type exit_code{}; if (!ec) if (::waitpid(pid_, &exit_code, 0) == -1) ec = get_last_error(); std::move(self).complete(ec, exit_code); } }; }; } BOOST_PROCESS_V2_END_NAMESPACE #endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP