1 // Copyright (c) 2017 Klemens D. Morgenstern
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5
6
7 #ifndef BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
8 #define BOOST_PROCESS_DETAIL_POSIX_SIGCHLD_SERVICE_HPP_
9
10 #include <boost/asio/dispatch.hpp>
11 #include <boost/asio/post.hpp>
12 #include <boost/asio/signal_set.hpp>
13 #include <boost/asio/strand.hpp>
14 #include <boost/optional.hpp>
15 #include <signal.h>
16 #include <functional>
17 #include <sys/wait.h>
18
19 namespace boost { namespace process { namespace detail { namespace posix {
20
21 class sigchld_service : public boost::asio::detail::service_base<sigchld_service>
22 {
23 boost::asio::strand<boost::asio::io_context::executor_type> _strand{get_io_context().get_executor()};
24 boost::asio::signal_set _signal_set{get_io_context(), SIGCHLD};
25
26 std::vector<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;
27 inline void _handle_signal(const boost::system::error_code & ec);
28 public:
sigchld_service(boost::asio::io_context & io_context)29 sigchld_service(boost::asio::io_context & io_context)
30 : boost::asio::detail::service_base<sigchld_service>(io_context)
31 {
32 }
33
34 template <typename SignalHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(SignalHandler,void (int,std::error_code))35 BOOST_ASIO_INITFN_RESULT_TYPE(SignalHandler,
36 void (int, std::error_code))
37 async_wait(::pid_t pid, SignalHandler && handler)
38 {
39 boost::asio::async_completion<
40 SignalHandler, void(boost::system::error_code)> init{handler};
41
42 auto & h = init.completion_handler;
43 boost::asio::dispatch(
44 _strand,
45 [this, pid, h]
46 {
47 //check if the child actually is running first
48 int status;
49 auto pid_res = ::waitpid(pid, &status, WNOHANG);
50 if (pid_res < 0)
51 {
52 auto ec = get_last_error();
53 boost::asio::post(
54 _strand,
55 [pid_res, ec, h]
56 {
57 h(pid_res, ec);
58 });
59 }
60 else if ((pid_res == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
61 boost::asio::post(
62 _strand,
63 [status, h]
64 {
65 h(status, {}); //successfully exited already
66 });
67 else //still running
68 {
69 if (_receivers.empty())
70 _signal_set.async_wait(
71 [this](const boost::system::error_code &ec, int)
72 {
73 boost::asio::dispatch(_strand, [this, ec]{this->_handle_signal(ec);});
74 });
75 _receivers.emplace_back(pid, h);
76 }
77 });
78
79 return init.result.get();
80 }
shutdown()81 void shutdown() override
82 {
83 _receivers.clear();
84 }
85
cancel()86 void cancel()
87 {
88 _signal_set.cancel();
89 }
cancel(boost::system::error_code & ec)90 void cancel(boost::system::error_code & ec)
91 {
92 _signal_set.cancel(ec);
93 }
94 };
95
96
_handle_signal(const boost::system::error_code & ec)97 void sigchld_service::_handle_signal(const boost::system::error_code & ec)
98 {
99 std::error_code ec_{ec.value(), std::system_category()};
100
101 if (ec_)
102 {
103 for (auto & r : _receivers)
104 r.second(-1, ec_);
105 return;
106 }
107
108 for (auto & r : _receivers) {
109 int status;
110 int pid = ::waitpid(r.first, &status, WNOHANG);
111 if (pid < 0) {
112 // error (eg: the process no longer exists)
113 r.second(-1, get_last_error());
114 r.first = 0; // mark for deletion
115 } else if (pid == r.first) {
116 r.second(status, ec_);
117 r.first = 0; // mark for deletion
118 }
119 // otherwise the process is still around
120 }
121
122 _receivers.erase(std::remove_if(_receivers.begin(), _receivers.end(),
123 [](const std::pair<::pid_t, std::function<void(int, std::error_code)>> & p)
124 {
125 return p.first == 0;
126 }),
127 _receivers.end());
128
129 if (!_receivers.empty())
130 {
131 _signal_set.async_wait(
132 [this](const boost::system::error_code & ec, int)
133 {
134 boost::asio::post(_strand, [this, ec]{this->_handle_signal(ec);});
135 });
136 }
137 }
138
139
140 }
141 }
142 }
143 }
144
145
146
147
148 #endif
149