blob: d53d17fe2d2a423e275251188094a73a4b96bfa9 [file] [log] [blame]
// 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