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/daemons/daemon.h>
     15 #include <brillo/location_logging.h>
     16 
     17 namespace brillo {
     18 
     19 ProcessReaper::~ProcessReaper() {
     20   Unregister();
     21 }
     22 
     23 void ProcessReaper::Register(
     24     AsynchronousSignalHandlerInterface* async_signal_handler) {
     25   CHECK(!async_signal_handler_);
     26   async_signal_handler_ = async_signal_handler;
     27   async_signal_handler->RegisterHandler(
     28       SIGCHLD,
     29       base::Bind(&ProcessReaper::HandleSIGCHLD, base::Unretained(this)));
     30 }
     31 
     32 void ProcessReaper::Unregister() {
     33   if (!async_signal_handler_)
     34     return;
     35   async_signal_handler_->UnregisterHandler(SIGCHLD);
     36   async_signal_handler_ = nullptr;
     37 }
     38 
     39 bool ProcessReaper::WatchForChild(const tracked_objects::Location& from_here,
     40                                   pid_t pid,
     41                                   const ChildCallback& callback) {
     42   if (watched_processes_.find(pid) != watched_processes_.end())
     43     return false;
     44   watched_processes_.emplace(pid, WatchedProcess{from_here, callback});
     45   return true;
     46 }
     47 
     48 bool ProcessReaper::ForgetChild(pid_t pid) {
     49   return watched_processes_.erase(pid) != 0;
     50 }
     51 
     52 bool ProcessReaper::HandleSIGCHLD(
     53     const struct signalfd_siginfo& /* sigfd_info */) {
     54   // One SIGCHLD may correspond to multiple terminated children, so ignore
     55   // sigfd_info and reap any available children.
     56   while (true) {
     57     siginfo_t info;
     58     info.si_pid = 0;
     59     int rc = HANDLE_EINTR(waitid(P_ALL, 0, &info, WNOHANG | WEXITED));
     60 
     61     if (rc == -1) {
     62       if (errno != ECHILD) {
     63         PLOG(ERROR) << "waitid failed";
     64       }
     65       break;
     66     }
     67 
     68     if (info.si_pid == 0) {
     69       break;
     70     }
     71 
     72     auto proc = watched_processes_.find(info.si_pid);
     73     if (proc == watched_processes_.end()) {
     74       LOG(INFO) << "Untracked process " << info.si_pid
     75                 << " terminated with status " << info.si_status
     76                 << " (code = " << info.si_code << ")";
     77     } else {
     78       DVLOG_LOC(proc->second.location, 1)
     79           << "Process " << info.si_pid << " terminated with status "
     80           << info.si_status << " (code = " << info.si_code << ")";
     81       ChildCallback callback = std::move(proc->second.callback);
     82       watched_processes_.erase(proc);
     83       callback.Run(info);
     84     }
     85   }
     86 
     87   // Return false to indicate that our handler should not be uninstalled.
     88   return false;
     89 }
     90 
     91 }  // namespace brillo
     92