2 * Copyright © 2013 Canonical Ltd.
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
19 #include <core/posix/child_process.h>
22 #include <boost/iostreams/device/file_descriptor.hpp>
23 #include <boost/iostreams/stream.hpp>
29 #include <unordered_map>
35 #include <sys/eventfd.h>
36 #include <sys/signalfd.h>
43 namespace io = boost::iostreams;
49 struct DeathObserverImpl : public core::posix::ChildProcess::DeathObserver
51 DeathObserverImpl(const std::shared_ptr<core::posix::SignalTrap>& trap)
52 : on_sig_child_connection
54 trap->signal_raised().connect([this](core::posix::Signal signal)
58 case core::posix::Signal::sig_chld:
67 if (!trap->has(core::posix::Signal::sig_chld))
68 throw std::logic_error(
69 "DeathObserver::DeathObserverImpl: Given SignalTrap"
70 " instance does not trap Signal::sig_chld.");
73 bool add(const core::posix::ChildProcess& process) override
75 if (process.pid() == -1)
78 std::lock_guard<std::mutex> lg(guard);
81 auto new_process = std::make_pair(process.pid(), process);
82 std::tie(std::ignore, added) = children.insert(new_process);
86 // The process may have died between it's instantiation and it
87 // being added to the children map. Check that it's still alive.
89 if (::waitpid(process.pid(), &status, WNOHANG) != 0) // child no longer alive
91 // we missed the SIGCHLD signal so we must now manually
92 // inform our subscribers.
93 signals.child_died(new_process.second);
94 children.erase(new_process.first);
102 bool has(const core::posix::ChildProcess& process) const override
104 std::lock_guard<std::mutex> lg(guard);
105 return children.count(process.pid()) > 0;
108 const core::Signal<core::posix::ChildProcess>& child_died() const override
110 return signals.child_died;
113 void on_sig_child() override
115 pid_t pid{-1}; int status{-1};
118 pid = ::waitpid(0, &status, WNOHANG);
124 break; // No more children
126 continue; // Ignore stray SIGCHLD signals
130 break; // No more children
134 std::lock_guard<std::mutex> lg(guard);
135 auto it = children.find(pid);
137 if (it != children.end())
139 if (WIFSIGNALED(status) || WIFEXITED(status))
141 signals.child_died(it->second);
149 mutable std::mutex guard;
150 std::unordered_map<pid_t, core::posix::ChildProcess> children;
151 core::ScopedConnection on_sig_child_connection;
154 core::Signal<core::posix::ChildProcess> child_died;
159 std::unique_ptr<core::posix::ChildProcess::DeathObserver>
160 core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(
161 std::shared_ptr<core::posix::SignalTrap> trap)
163 static std::atomic<bool> has_been_created_once{false};
165 if (has_been_created_once.exchange(true))
166 throw std::runtime_error
168 "DeathObserver::create_once_with_signal_trap: "
169 "Cannot create more than one instance."
174 std::unique_ptr<core::posix::ChildProcess::DeathObserver> result
176 new DeathObserverImpl{trap}
182 // We make sure that a throwing c'tor does not impact our ability to
183 // retry creation of a DeathObserver instance.
184 has_been_created_once.store(false);
186 std::rethrow_exception(std::current_exception());
189 assert(false && "We should never reach here.");
191 // Silence the compiler.
192 return std::unique_ptr<core::posix::ChildProcess::DeathObserver>{};
199 ChildProcess::Pipe ChildProcess::Pipe::invalid()
202 static std::once_flag flag;
204 std::call_once(flag, [&]() { p.close_read_fd(); p.close_write_fd(); });
209 ChildProcess::Pipe::Pipe()
211 int rc = ::pipe(fds);
214 throw std::system_error(errno, std::system_category());
217 ChildProcess::Pipe::Pipe(const ChildProcess::Pipe& rhs) : fds{-1, -1}
219 if (rhs.fds[0] != -1)
220 fds[0] = ::dup(rhs.fds[0]);
222 if (rhs.fds[1] != -1)
223 fds[1] = ::dup(rhs.fds[1]);
226 ChildProcess::Pipe::~Pipe()
234 int ChildProcess::Pipe::read_fd() const
239 void ChildProcess::Pipe::close_read_fd()
248 int ChildProcess::Pipe::write_fd() const
253 void ChildProcess::Pipe::close_write_fd()
262 ChildProcess::Pipe& ChildProcess::Pipe::operator=(const ChildProcess::Pipe& rhs)
269 if (rhs.fds[0] != -1)
270 fds[0] = ::dup(rhs.fds[0]);
273 if (rhs.fds[1] != -1)
274 fds[1] = ::dup(rhs.fds[1]);
281 struct ChildProcess::Private
283 // stdin and stdout are always "relative" to the childprocess, i.e., we
284 // write to stdin of the child process and read from its stdout.
286 const ChildProcess::Pipe& stderr,
287 const ChildProcess::Pipe& stdin,
288 const ChildProcess::Pipe& stdout)
289 : pipes{stderr, stdin, stdout},
291 serr(pipes.stderr.read_fd(), io::never_close_handle),
292 sin(pipes.stdin.write_fd(), io::never_close_handle),
293 sout(pipes.stdout.read_fd(), io::never_close_handle),
298 original_parent_pid(::getpid()),
299 original_child_pid(pid)
305 // Check if we are in the original parent process.
306 if (original_parent_pid == getpid() && !dont_kill_on_cleanup)
308 // If so, check if we are considering a valid pid here.
309 // If so, we kill the original child.
310 if (original_child_pid != -1)
311 ::kill(original_child_pid, SIGKILL);
317 ChildProcess::Pipe stdin;
318 ChildProcess::Pipe stdout;
319 ChildProcess::Pipe stderr;
323 io::stream_buffer<io::file_descriptor_source> serr;
324 io::stream_buffer<io::file_descriptor_sink> sin;
325 io::stream_buffer<io::file_descriptor_source> sout;
331 // We need to store the original parent pid as we might have been forked
332 // and with our automatic cleanup in place, it might happen that the d'tor
333 // is called from the child process.
334 pid_t original_parent_pid;
335 pid_t original_child_pid;
337 bool dont_kill_on_cleanup = false;
340 ChildProcess ChildProcess::invalid()
342 // We take the init process as child.
343 static const pid_t invalid_pid = 1;
344 return ChildProcess(invalid_pid, Pipe::invalid(), Pipe::invalid(), Pipe::invalid());
347 ChildProcess::ChildProcess(pid_t pid,
348 const ChildProcess::Pipe& stdin_pipe,
349 const ChildProcess::Pipe& stdout_pipe,
350 const ChildProcess::Pipe& stderr_pipe)
352 d(new Private{pid, stdin_pipe, stdout_pipe, stderr_pipe})
356 ChildProcess::~ChildProcess()
360 wait::Result ChildProcess::wait_for(const wait::Flags& flags)
363 pid_t result_pid = ::waitpid(pid(), std::addressof(status), static_cast<int>(flags));
365 if (result_pid == -1)
366 throw std::system_error(errno, std::system_category());
372 result.status = wait::Result::Status::no_state_change;
376 if (WIFEXITED(status))
378 result.status = wait::Result::Status::exited;
379 result.detail.if_exited.status = static_cast<exit::Status>(WEXITSTATUS(status));
380 } else if (WIFSIGNALED(status))
382 result.status = wait::Result::Status::signaled;
383 result.detail.if_signaled.signal = static_cast<Signal>(WTERMSIG(status));
384 result.detail.if_signaled.core_dumped = WCOREDUMP(status);
385 } else if (WIFSTOPPED(status))
387 result.status = wait::Result::Status::stopped;
388 result.detail.if_stopped.signal = static_cast<Signal>(WSTOPSIG(status));
391 else if (WIFCONTINUED(status))
393 result.status = wait::Result::Status::continued;
400 void ChildProcess::dont_kill_on_cleanup()
402 d->dont_kill_on_cleanup = true;
406 std::istream& ChildProcess::cerr()
411 std::ostream& ChildProcess::cin()
416 std::istream& ChildProcess::cout()