blob: 19eef037139f361c7918c755c9a423870167118a [file] [log] [blame]
// Copyright 2015 The Chromium OS 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 "chromeos/process_reaper.h"
#include <sys/signalfd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <base/bind.h>
#include <base/posix/eintr_wrapper.h>
#include <chromeos/asynchronous_signal_handler.h>
#include <chromeos/daemons/daemon.h>
#include <chromeos/location_logging.h>
namespace chromeos {
ProcessReaper::~ProcessReaper() {
if (!registered_)
Unregister();
}
void ProcessReaper::RegisterWithAsynchronousSignalHandler(
AsynchronousSignalHandler* async_signal_handler) {
CHECK(!registered_);
async_signal_handler_ = async_signal_handler;
async_signal_handler->RegisterHandler(
SIGCHLD,
base::Bind(&ProcessReaper::HandleSIGCHLD, base::Unretained(this)));
registered_ = true;
}
void ProcessReaper::RegisterWithDaemon(Daemon* daemon) {
CHECK(!registered_);
daemon_ = daemon;
daemon->RegisterHandler(SIGCHLD, base::Bind(&ProcessReaper::HandleSIGCHLD,
base::Unretained(this)));
registered_ = true;
}
void ProcessReaper::Unregister() {
if (daemon_) {
daemon_->UnregisterHandler(SIGCHLD);
daemon_ = nullptr;
}
if (async_signal_handler_) {
async_signal_handler_->UnregisterHandler(SIGCHLD);
async_signal_handler_ = nullptr;
}
registered_ = false;
}
bool ProcessReaper::WatchForChild(const tracked_objects::Location& from_here,
pid_t pid,
const ChildCallback& callback) {
if (watched_processes_.find(pid) != watched_processes_.end())
return false;
watched_processes_.emplace(pid, WatchedProcess{from_here, callback});
return true;
}
bool ProcessReaper::HandleSIGCHLD(const struct signalfd_siginfo& sigfd_info) {
// One SIGCHLD may correspond to multiple terminated children, so ignore
// sigfd_info and reap any available children.
while (true) {
siginfo_t info;
info.si_pid = 0;
int rc = HANDLE_EINTR(waitid(P_ALL, 0, &info, WNOHANG | WEXITED));
if (rc == -1) {
if (errno != ECHILD) {
PLOG(ERROR) << "waitid failed";
}
break;
}
if (info.si_pid == 0) {
break;
}
auto proc = watched_processes_.find(info.si_pid);
if (proc == watched_processes_.end()) {
LOG(INFO) << "Uninterested process " << info.si_pid
<< " terminated with status " << info.si_status
<< " (code = " << info.si_code << ")";
} else {
DVLOG_LOC(proc->second.location, 1)
<< "Process " << info.si_pid << " terminated with status "
<< info.si_status << " (code = " << info.si_code << ")";
ChildCallback callback = std::move(proc->second.callback);
watched_processes_.erase(proc);
callback.Run(info);
}
}
// Return false to indicate that our handler should not be uninstalled.
return false;
}
} // namespace chromeos