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