Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include <errno.h>
8 : #include <signal.h>
9 : #include <sys/types.h>
10 : #include <sys/wait.h>
11 :
12 : #include "base/eintr_wrapper.h"
13 : #include "base/message_loop.h"
14 : #include "base/process_util.h"
15 :
16 : #include "chrome/common/process_watcher.h"
17 :
18 : // Maximum amount of time (in milliseconds) to wait for the process to exit.
19 : // XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc
20 : static const int kMaxWaitMs = 2000;
21 :
22 : namespace {
23 :
24 : bool
25 0 : IsProcessDead(pid_t process)
26 : {
27 0 : bool exited = false;
28 : // don't care if the process crashed, just if it exited
29 0 : base::DidProcessCrash(&exited, process);
30 0 : return exited;
31 : }
32 :
33 :
34 : class ChildReaper : public base::MessagePumpLibevent::SignalEvent,
35 : public base::MessagePumpLibevent::SignalWatcher
36 : {
37 : public:
38 0 : explicit ChildReaper(pid_t process) : process_(process)
39 : {
40 0 : }
41 :
42 0 : virtual ~ChildReaper()
43 0 : {
44 : // subclasses should have cleaned up |process_| already
45 0 : DCHECK(!process_);
46 :
47 : // StopCatching() is implicit
48 0 : }
49 :
50 : // @override
51 0 : virtual void OnSignal(int sig)
52 : {
53 0 : DCHECK(SIGCHLD == sig);
54 0 : DCHECK(process_);
55 :
56 : // this may be the SIGCHLD for a process other than |process_|
57 0 : if (IsProcessDead(process_)) {
58 0 : process_ = 0;
59 0 : StopCatching();
60 : }
61 0 : }
62 :
63 : protected:
64 0 : void WaitForChildExit()
65 : {
66 0 : DCHECK(process_);
67 0 : HANDLE_EINTR(waitpid(process_, NULL, 0));
68 0 : }
69 :
70 : pid_t process_;
71 :
72 : private:
73 : DISALLOW_EVIL_CONSTRUCTORS(ChildReaper);
74 : };
75 :
76 :
77 : // Fear the reaper
78 : class ChildGrimReaper : public ChildReaper,
79 : public mozilla::Runnable
80 : {
81 : public:
82 0 : explicit ChildGrimReaper(pid_t process)
83 0 : : ChildReaper(process)
84 0 : , mozilla::Runnable("ChildGrimReaper")
85 : {
86 0 : }
87 :
88 0 : virtual ~ChildGrimReaper()
89 0 : {
90 0 : if (process_)
91 0 : KillProcess();
92 0 : }
93 :
94 0 : NS_IMETHOD Run() override
95 : {
96 : // we may have already been signaled by the time this runs
97 0 : if (process_)
98 0 : KillProcess();
99 :
100 0 : return NS_OK;
101 : }
102 :
103 : private:
104 0 : void KillProcess()
105 : {
106 0 : DCHECK(process_);
107 :
108 0 : if (IsProcessDead(process_)) {
109 0 : process_ = 0;
110 0 : return;
111 : }
112 :
113 0 : if (0 == kill(process_, SIGKILL)) {
114 : // XXX this will block for whatever amount of time it takes the
115 : // XXX OS to tear down the process's resources. might need to
116 : // XXX rethink this if it proves expensive
117 0 : WaitForChildExit();
118 : }
119 : else {
120 0 : CHROMIUM_LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!"
121 0 : << "("<< errno << ").";
122 : }
123 0 : process_ = 0;
124 : }
125 :
126 : DISALLOW_EVIL_CONSTRUCTORS(ChildGrimReaper);
127 : };
128 :
129 :
130 : class ChildLaxReaper : public ChildReaper,
131 : public MessageLoop::DestructionObserver
132 : {
133 : public:
134 0 : explicit ChildLaxReaper(pid_t process) : ChildReaper(process)
135 : {
136 0 : }
137 :
138 0 : virtual ~ChildLaxReaper()
139 0 : {
140 : // WillDestroyCurrentMessageLoop() should have reaped process_ already
141 0 : DCHECK(!process_);
142 0 : }
143 :
144 : // @override
145 0 : virtual void OnSignal(int sig)
146 : {
147 0 : ChildReaper::OnSignal(sig);
148 :
149 0 : if (!process_) {
150 0 : MessageLoop::current()->RemoveDestructionObserver(this);
151 0 : delete this;
152 : }
153 0 : }
154 :
155 : // @override
156 0 : virtual void WillDestroyCurrentMessageLoop()
157 : {
158 0 : DCHECK(process_);
159 :
160 0 : WaitForChildExit();
161 0 : process_ = 0;
162 :
163 : // XXX don't think this is necessary, since destruction can only
164 : // be observed once, but can't hurt
165 0 : MessageLoop::current()->RemoveDestructionObserver(this);
166 0 : delete this;
167 0 : }
168 :
169 : private:
170 : DISALLOW_EVIL_CONSTRUCTORS(ChildLaxReaper);
171 : };
172 :
173 : } // namespace <anon>
174 :
175 :
176 : /**
177 : * Do everything possible to ensure that |process| has been reaped
178 : * before this process exits.
179 : *
180 : * |grim| decides how strict to be with the child's shutdown.
181 : *
182 : * | child exit timeout | upon parent shutdown:
183 : * +--------------------+----------------------------------
184 : * force=true | 2 seconds | kill(child, SIGKILL)
185 : * force=false | infinite | waitpid(child)
186 : *
187 : * If a child process doesn't shut down properly, and |grim=false|
188 : * used, then the parent will wait on the child forever. So,
189 : * |force=false| is expected to be used when an external entity can be
190 : * responsible for terminating hung processes, e.g. automated test
191 : * harnesses.
192 : */
193 : void
194 0 : ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process,
195 : bool force)
196 : {
197 0 : DCHECK(process != base::GetCurrentProcId());
198 0 : DCHECK(process > 0);
199 :
200 0 : if (IsProcessDead(process))
201 0 : return;
202 :
203 0 : MessageLoopForIO* loop = MessageLoopForIO::current();
204 0 : if (force) {
205 0 : RefPtr<ChildGrimReaper> reaper = new ChildGrimReaper(process);
206 :
207 0 : loop->CatchSignal(SIGCHLD, reaper, reaper);
208 : // |loop| takes ownership of |reaper|
209 0 : loop->PostDelayedTask(reaper.forget(), kMaxWaitMs);
210 : } else {
211 0 : ChildLaxReaper* reaper = new ChildLaxReaper(process);
212 :
213 0 : loop->CatchSignal(SIGCHLD, reaper, reaper);
214 : // |reaper| destroys itself after destruction notification
215 0 : loop->AddDestructionObserver(reaper);
216 : }
217 : }
|