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 
~ProcessReaper()18 ProcessReaper::~ProcessReaper() {
19   Unregister();
20 }
21 
Register(AsynchronousSignalHandlerInterface * async_signal_handler)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 
Unregister()31 void ProcessReaper::Unregister() {
32   if (!async_signal_handler_)
33     return;
34   async_signal_handler_->UnregisterHandler(SIGCHLD);
35   async_signal_handler_ = nullptr;
36 }
37 
WatchForChild(const base::Location & from_here,pid_t pid,const ChildCallback & callback)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 
ForgetChild(pid_t pid)47 bool ProcessReaper::ForgetChild(pid_t pid) {
48   return watched_processes_.erase(pid) != 0;
49 }
50 
HandleSIGCHLD(const struct signalfd_siginfo &)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