Home | History | Annotate | Download | only in brillo
      1 // Copyright 2015 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "brillo/process_reaper.h"
      6 
      7 #include <sys/signalfd.h>
      8 #include <sys/types.h>
      9 #include <sys/wait.h>
     10 
     11 #include <base/bind.h>
     12 #include <base/posix/eintr_wrapper.h>
     13 #include <brillo/asynchronous_signal_handler.h>
     14 #include <brillo/location_logging.h>
     15 
     16 namespace brillo {
     17 
     18 ProcessReaper::~ProcessReaper() {
     19   Unregister();
     20 }
     21 
     22 void ProcessReaper::Register(
     23     AsynchronousSignalHandlerInterface* async_signal_handler) {
     24   CHECK(!async_signal_handler_);
     25   async_signal_handler_ = async_signal_handler;
     26   async_signal_handler->RegisterHandler(
     27       SIGCHLD,
     28       base::Bind(&ProcessReaper::HandleSIGCHLD, base::Unretained(this)));
     29 }
     30 
     31 void ProcessReaper::Unregister() {
     32   if (!async_signal_handler_)
     33     return;
     34   async_signal_handler_->UnregisterHandler(SIGCHLD);
     35   async_signal_handler_ = nullptr;
     36 }
     37 
     38 bool ProcessReaper::WatchForChild(const base::Location& from_here,
     39                                   pid_t pid,
     40                                   const ChildCallback& callback) {
     41   if (watched_processes_.find(pid) != watched_processes_.end())
     42     return false;
     43   watched_processes_.emplace(pid, WatchedProcess{from_here, callback});
     44   return true;
     45 }
     46 
     47 bool ProcessReaper::ForgetChild(pid_t pid) {
     48   return watched_processes_.erase(pid) != 0;
     49 }
     50 
     51 bool ProcessReaper::HandleSIGCHLD(
     52     const struct signalfd_siginfo& /* sigfd_info */) {
     53   // One SIGCHLD may correspond to multiple terminated children, so ignore
     54   // sigfd_info and reap any available children.
     55   while (true) {
     56     siginfo_t info;
     57     info.si_pid = 0;
     58     int rc = HANDLE_EINTR(waitid(P_ALL, 0, &info, WNOHANG | WEXITED));
     59 
     60     if (rc == -1) {
     61       if (errno != ECHILD) {
     62         PLOG(ERROR) << "waitid failed";
     63       }
     64       break;
     65     }
     66 
     67     if (info.si_pid == 0) {
     68       break;
     69     }
     70 
     71     auto proc = watched_processes_.find(info.si_pid);
     72     if (proc == watched_processes_.end()) {
     73       LOG(INFO) << "Untracked process " << info.si_pid
     74                 << " terminated with status " << info.si_status
     75                 << " (code = " << info.si_code << ")";
     76     } else {
     77       DVLOG_LOC(proc->second.location, 1)
     78           << "Process " << info.si_pid << " terminated with status "
     79           << info.si_status << " (code = " << info.si_code << ")";
     80       ChildCallback callback = std::move(proc->second.callback);
     81       watched_processes_.erase(proc);
     82       callback.Run(info);
     83     }
     84   }
     85 
     86   // Return false to indicate that our handler should not be uninstalled.
     87   return false;
     88 }
     89 
     90 }  // namespace brillo
     91