21 #include <boost/iostreams/device/file_descriptor.hpp> 22 #include <boost/iostreams/stream.hpp> 27 #include <unordered_map> 33 #include <sys/eventfd.h> 34 #include <sys/signalfd.h> 36 namespace io = boost::iostreams;
43 DeathObserverImpl(
const std::shared_ptr<core::posix::SignalTrap>& trap)
44 : on_sig_child_connection
60 throw std::logic_error(
61 "DeathObserver::DeathObserverImpl: Given SignalTrap" 62 " instance does not trap Signal::sig_chld.");
67 if (process.
pid() == -1)
70 std::lock_guard<std::mutex> lg(guard);
73 auto new_process = std::make_pair(process.
pid(), process);
74 std::tie(std::ignore, added) = children.insert(new_process);
81 if (::waitpid(process.
pid(), &status, WNOHANG) != 0)
85 signals.child_died(new_process.second);
86 children.erase(new_process.first);
96 std::lock_guard<std::mutex> lg(guard);
97 return children.count(process.
pid()) > 0;
100 const core::Signal<core::posix::ChildProcess>&
child_died()
const override 102 return signals.child_died;
107 pid_t pid{-1};
int status{-1};
110 pid = ::waitpid(0, &status, WNOHANG);
126 std::lock_guard<std::mutex> lg(guard);
127 auto it = children.find(pid);
129 if (it != children.end())
131 if (WIFSIGNALED(status) || WIFEXITED(status))
133 signals.child_died(it->second);
141 mutable std::mutex guard;
142 std::unordered_map<pid_t, core::posix::ChildProcess> children;
143 core::ScopedConnection on_sig_child_connection;
146 core::Signal<core::posix::ChildProcess>
child_died;
151 std::unique_ptr<core::posix::ChildProcess::DeathObserver>
153 std::shared_ptr<core::posix::SignalTrap> trap)
155 static std::atomic<bool> has_been_created_once{
false};
157 if (has_been_created_once.exchange(
true))
158 throw std::runtime_error
160 "DeathObserver::create_once_with_signal_trap: " 161 "Cannot create more than one instance." 166 std::unique_ptr<core::posix::ChildProcess::DeathObserver> result
168 new DeathObserverImpl{trap}
176 has_been_created_once.store(
false);
178 std::rethrow_exception(std::current_exception());
181 assert(
false &&
"We should never reach here.");
184 return std::unique_ptr<core::posix::ChildProcess::DeathObserver>{};
191 ChildProcess::Pipe ChildProcess::Pipe::invalid()
194 static std::once_flag flag;
196 std::call_once(flag, [&]() { p.close_read_fd(); p.close_write_fd(); });
201 ChildProcess::Pipe::Pipe()
203 int rc = ::pipe(fds);
206 throw std::system_error(errno, std::system_category());
209 ChildProcess::Pipe::Pipe(
const ChildProcess::Pipe& rhs) : fds{-1, -1}
211 if (rhs.fds[0] != -1)
212 fds[0] = ::dup(rhs.fds[0]);
214 if (rhs.fds[1] != -1)
215 fds[1] = ::dup(rhs.fds[1]);
218 ChildProcess::Pipe::~Pipe()
226 int ChildProcess::Pipe::read_fd()
const 231 void ChildProcess::Pipe::close_read_fd()
240 int ChildProcess::Pipe::write_fd()
const 245 void ChildProcess::Pipe::close_write_fd()
254 ChildProcess::Pipe& ChildProcess::Pipe::operator=(
const ChildProcess::Pipe& rhs)
261 if (rhs.fds[0] != -1)
262 fds[0] = ::dup(rhs.fds[0]);
265 if (rhs.fds[1] != -1)
266 fds[1] = ::dup(rhs.fds[1]);
278 const ChildProcess::Pipe& stderr,
279 const ChildProcess::Pipe& stdin,
280 const ChildProcess::Pipe& stdout)
281 : pipes{stderr, stdin, stdout},
282 serr(pipes.stderr.read_fd(), io::never_close_handle),
283 sin(pipes.stdin.write_fd(), io::never_close_handle),
284 sout(pipes.stdout.read_fd(), io::never_close_handle),
288 original_parent_pid(::getpid()),
289 original_child_pid(pid)
296 if (original_parent_pid == getpid())
300 if (original_child_pid != -1)
301 ::kill(original_child_pid, SIGKILL);
311 io::stream_buffer<io::file_descriptor_source>
serr;
312 io::stream_buffer<io::file_descriptor_sink>
sin;
313 io::stream_buffer<io::file_descriptor_source>
sout;
328 static const pid_t invalid_pid = 1;
329 return ChildProcess(invalid_pid, Pipe::invalid(), Pipe::invalid(), Pipe::invalid());
332 ChildProcess::ChildProcess(pid_t pid,
333 const ChildProcess::Pipe& stdin_pipe,
334 const ChildProcess::Pipe& stdout_pipe,
335 const ChildProcess::Pipe& stderr_pipe)
337 d(
new Private{pid, stdin_pipe, stdout_pipe, stderr_pipe})
341 ChildProcess::~ChildProcess()
348 pid_t result_pid = ::waitpid(pid(), std::addressof(status), static_cast<int>(flags));
350 if (result_pid == -1)
351 throw std::system_error(errno, std::system_category());
357 result.
status = wait::Result::Status::no_state_change;
361 if (WIFEXITED(status))
363 result.
status = wait::Result::Status::exited;
365 }
else if (WIFSIGNALED(status))
367 result.
status = wait::Result::Status::signaled;
370 }
else if (WIFSTOPPED(status))
372 result.
status = wait::Result::Status::stopped;
374 }
else if (WIFCONTINUED(status))
376 result.
status = wait::Result::Status::continued;
io::stream_buffer< io::file_descriptor_sink > sin
virtual bool add(const ChildProcess &child)=0
add adds the specified child to the list of observed child processes.
Status
The Status enum wrap's the posix exit status.
Flags
Flags enumerates different behavior when waiting for a child process to change state.
struct core::posix::wait::Result::@4::@7 if_stopped
The Process class models a process and possible operations on it.
ChildProcess::Pipe stdout
union core::posix::wait::Result::@4 detail
Union of result-specific details.
Private(pid_t pid, const ChildProcess::Pipe &stderr, const ChildProcess::Pipe &stdin, const ChildProcess::Pipe &stdout)
virtual void on_sig_child()=0
Checks and reaps all child processes registered with the observer instance.
struct core::posix::wait::Result::@4::@6 if_signaled
static std::unique_ptr< DeathObserver > create_once_with_signal_trap(std::shared_ptr< SignalTrap > trap)
Creates the unique instance of class DeathObserver.
ChildProcess::Pipe stderr
enum core::posix::wait::Result::Status status
CORE_POSIX_DLL_PUBLIC std::istream & cin() noexcept(true)
Access this process's stdin.
pid_t original_parent_pid
The Result struct encapsulates the result of waiting for a process state change.
Signal
The Signal enum collects the most common POSIX signals.
The DeathObserver class observes child process' states and emits a signal when a monitored child has ...
CORE_POSIX_DLL_PUBLIC std::ostream & cerr() noexcept(true)
Access this process's stderr.
io::stream_buffer< io::file_descriptor_source > serr
virtual pid_t pid() const
Query the pid of the process.
struct core::posix::wait::Result::@4::@5 if_exited
The Process class models a child process of this process.
virtual bool has(const ChildProcess &child) const =0
has checks whether the specified child is observed.
CORE_POSIX_DLL_PUBLIC std::ostream & cout() noexcept(true)
Access this process's stdout.
io::stream_buffer< io::file_descriptor_source > sout
virtual const core::Signal< ChildProcess > & child_died() const =0
child_died is emitted whenever an observed child ceases to exist.