| // Copyright 2021 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/developer/debug/shared/message_loop_linux.h" | |
| #include <lib/syslog/cpp/macros.h> | |
| #include <signal.h> | |
| #include <sys/signalfd.h> | |
| #include <sys/wait.h> | |
| #include "src/lib/files/eintr_wrapper.h" | |
| namespace debug { | |
| struct MessageLoopLinux::SignalWatchInfo { | |
| pid_t pid = -1; | |
| SignalWatcher watcher; | |
| }; | |
| MessageLoopLinux::MessageLoopLinux() { | |
| // Register for signals from child processes. We may need to add to the set of signals in the | |
| // future as requirements grow. | |
| sigset_t mask; | |
| sigemptyset(&mask); | |
| sigaddset(&mask, SIGCHLD); | |
| // Prevents the signals that are being processed via the signal fd from being sent via the normal | |
| // signal channel. | |
| sigprocmask(SIG_BLOCK, &mask, NULL); | |
| signal_fd_.reset(signalfd(-1, &mask, 0)); | |
| } | |
| MessageLoopLinux::~MessageLoopLinux() = default; | |
| MessageLoopLinux* MessageLoopLinux::Current() { | |
| return reinterpret_cast<MessageLoopLinux*>(MessageLoop::Current()); | |
| } | |
| bool MessageLoopLinux::Init(std::string* error_message) { | |
| if (!MessageLoopPoll::Init(error_message)) | |
| return false; | |
| signal_fd_watch_ = | |
| WatchFD(WatchMode::kRead, signal_fd_.get(), [this](int fd, bool readable, bool, bool) { | |
| if (!readable) | |
| return; | |
| FX_DCHECK(fd == signal_fd_.get()); | |
| struct signalfd_siginfo fdsi; | |
| auto nread = HANDLE_EINTR(read(signal_fd_.get(), &fdsi, sizeof(struct signalfd_siginfo))); | |
| FX_DCHECK(nread == sizeof(signalfd_siginfo)); | |
| for (const auto& [index, info] : signal_watches_) { | |
| if (info.pid == static_cast<int>(fdsi.ssi_pid)) { | |
| // The full status is available only from waitpid, the fdsi.status only contains the | |
| // child signal number. | |
| int status = 0; | |
| if (waitpid(info.pid, &status, __WALL | WUNTRACED | WNOHANG) >= 0) | |
| info.watcher(fdsi.ssi_pid, status); | |
| return; | |
| } | |
| } | |
| }); | |
| return true; | |
| } | |
| void MessageLoopLinux::Cleanup() { | |
| // Force unregister our signal watch before cleaning up current MessageLoop. | |
| signal_fd_watch_ = WatchHandle(); | |
| MessageLoopPoll::Cleanup(); | |
| } | |
| MessageLoop::WatchHandle MessageLoopLinux::WatchChildSignals(pid_t pid, SignalWatcher watcher) { | |
| // The dispatch code for watch callbacks requires this be called on the same thread as the message | |
| // loop is. | |
| FX_DCHECK(Current() == static_cast<MessageLoop*>(this)); | |
| SignalWatchInfo info; | |
| info.pid = pid; | |
| info.watcher = std::move(watcher); | |
| // The reason this function must be called on the message loop thread is that otherwise adding a | |
| // new watch would require synchronously breaking out of the existing poll() call to add the new | |
| // handle and then resuming it. | |
| int watch_id = GetNextWatchId(); | |
| signal_watches_[watch_id] = std::move(info); | |
| return WatchHandle(this, watch_id); | |
| } | |
| void MessageLoopLinux::StopWatching(int id) { | |
| // The dispatch code for watch callbacks requires this be called on the | |
| // same thread as the message loop is. | |
| FX_DCHECK(MessageLoopLinux::Current() == this); | |
| { | |
| std::lock_guard<std::mutex> guard(mutex_); | |
| // Check for signal watches. | |
| auto found = signal_watches_.find(id); | |
| if (found != signal_watches_.end()) { | |
| signal_watches_.erase(found); | |
| return; | |
| } | |
| } | |
| // All other watches are removed by the base class. | |
| MessageLoopPoll::StopWatching(id); | |
| } | |
| } // namespace debug |