1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * vim: set ts=8 et sw=2 tw=80:
3 : */
4 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla IPC.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Mozilla Foundation
21 : * Portions created by the Initial Developer are Copyright (C) 2009
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Chris Jones <jones.chris.g@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : //-----------------------------------------------------------------------------
42 : // XXXXXXXXXXXXXXXX
43 : //
44 : // How is this code supposed to be licensed? I don't /think/ that
45 : // this code is doing anything different than, say,
46 : // GeckoChildProcess.h/cpp, so I /think/ this gets a MoFo copyright
47 : // and license. Yes?
48 : //
49 : // XXXXXXXXXXXXXXXX
50 : //-----------------------------------------------------------------------------
51 :
52 : #include <errno.h>
53 : #include <signal.h>
54 : #include <sys/types.h>
55 : #include <sys/wait.h>
56 :
57 : #include "base/eintr_wrapper.h"
58 : #include "base/message_loop.h"
59 : #include "base/process_util.h"
60 :
61 : #include "chrome/common/process_watcher.h"
62 :
63 : // Maximum amount of time (in milliseconds) to wait for the process to exit.
64 : // XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc
65 : static const int kMaxWaitMs = 2000;
66 :
67 : namespace {
68 :
69 : bool
70 0 : IsProcessDead(pid_t process)
71 : {
72 0 : bool exited = false;
73 : // don't care if the process crashed, just if it exited
74 0 : base::DidProcessCrash(&exited, process);
75 0 : return exited;
76 : }
77 :
78 :
79 : class ChildReaper : public base::MessagePumpLibevent::SignalEvent,
80 : public base::MessagePumpLibevent::SignalWatcher
81 : {
82 : public:
83 0 : explicit ChildReaper(pid_t process) : process_(process)
84 : {
85 0 : }
86 :
87 0 : virtual ~ChildReaper()
88 0 : {
89 : // subclasses should have cleaned up |process_| already
90 0 : DCHECK(!process_);
91 :
92 : // StopCatching() is implicit
93 0 : }
94 :
95 : // @override
96 0 : virtual void OnSignal(int sig)
97 : {
98 0 : DCHECK(SIGCHLD == sig);
99 0 : DCHECK(process_);
100 :
101 : // this may be the SIGCHLD for a process other than |process_|
102 0 : if (IsProcessDead(process_)) {
103 0 : process_ = 0;
104 0 : StopCatching();
105 : }
106 0 : }
107 :
108 : protected:
109 0 : void WaitForChildExit()
110 : {
111 0 : DCHECK(process_);
112 0 : HANDLE_EINTR(waitpid(process_, NULL, 0));
113 0 : }
114 :
115 : pid_t process_;
116 :
117 : private:
118 : DISALLOW_EVIL_CONSTRUCTORS(ChildReaper);
119 : };
120 :
121 :
122 : // Fear the reaper
123 : class ChildGrimReaper : public ChildReaper,
124 : public Task
125 : {
126 : public:
127 0 : explicit ChildGrimReaper(pid_t process) : ChildReaper(process)
128 : {
129 0 : }
130 :
131 0 : virtual ~ChildGrimReaper()
132 0 : {
133 0 : if (process_)
134 0 : KillProcess();
135 0 : }
136 :
137 : // @override
138 0 : virtual void Run()
139 : {
140 : // we may have already been signaled by the time this runs
141 0 : if (process_)
142 0 : KillProcess();
143 0 : }
144 :
145 : private:
146 0 : void KillProcess()
147 : {
148 0 : DCHECK(process_);
149 :
150 0 : if (IsProcessDead(process_)) {
151 0 : process_ = 0;
152 0 : return;
153 : }
154 :
155 0 : if (0 == kill(process_, SIGKILL)) {
156 : // XXX this will block for whatever amount of time it takes the
157 : // XXX OS to tear down the process's resources. might need to
158 : // XXX rethink this if it proves expensive
159 0 : WaitForChildExit();
160 : }
161 : else {
162 0 : LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!"
163 0 : << "("<< errno << ").";
164 : }
165 0 : process_ = 0;
166 : }
167 :
168 : DISALLOW_EVIL_CONSTRUCTORS(ChildGrimReaper);
169 : };
170 :
171 :
172 : class ChildLaxReaper : public ChildReaper,
173 : public MessageLoop::DestructionObserver
174 : {
175 : public:
176 0 : explicit ChildLaxReaper(pid_t process) : ChildReaper(process)
177 : {
178 0 : }
179 :
180 0 : virtual ~ChildLaxReaper()
181 0 : {
182 : // WillDestroyCurrentMessageLoop() should have reaped process_ already
183 0 : DCHECK(!process_);
184 0 : }
185 :
186 : // @override
187 0 : virtual void OnSignal(int sig)
188 : {
189 0 : ChildReaper::OnSignal(sig);
190 :
191 0 : if (!process_) {
192 0 : MessageLoop::current()->RemoveDestructionObserver(this);
193 0 : delete this;
194 : }
195 0 : }
196 :
197 : // @override
198 0 : virtual void WillDestroyCurrentMessageLoop()
199 : {
200 0 : DCHECK(process_);
201 :
202 0 : WaitForChildExit();
203 0 : process_ = 0;
204 :
205 : // XXX don't think this is necessary, since destruction can only
206 : // be observed once, but can't hurt
207 0 : MessageLoop::current()->RemoveDestructionObserver(this);
208 0 : delete this;
209 0 : }
210 :
211 : private:
212 : DISALLOW_EVIL_CONSTRUCTORS(ChildLaxReaper);
213 : };
214 :
215 : } // namespace <anon>
216 :
217 :
218 : /**
219 : * Do everything possible to ensure that |process| has been reaped
220 : * before this process exits.
221 : *
222 : * |grim| decides how strict to be with the child's shutdown.
223 : *
224 : * | child exit timeout | upon parent shutdown:
225 : * +--------------------+----------------------------------
226 : * force=true | 2 seconds | kill(child, SIGKILL)
227 : * force=false | infinite | waitpid(child)
228 : *
229 : * If a child process doesn't shut down properly, and |grim=false|
230 : * used, then the parent will wait on the child forever. So,
231 : * |force=false| is expected to be used when an external entity can be
232 : * responsible for terminating hung processes, e.g. automated test
233 : * harnesses.
234 : */
235 : void
236 0 : ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process,
237 : bool force)
238 : {
239 0 : DCHECK(process != base::GetCurrentProcId());
240 0 : DCHECK(process > 0);
241 :
242 0 : if (IsProcessDead(process))
243 0 : return;
244 :
245 0 : MessageLoopForIO* loop = MessageLoopForIO::current();
246 0 : if (force) {
247 0 : ChildGrimReaper* reaper = new ChildGrimReaper(process);
248 :
249 0 : loop->CatchSignal(SIGCHLD, reaper, reaper);
250 : // |loop| takes ownership of |reaper|
251 0 : loop->PostDelayedTask(FROM_HERE, reaper, kMaxWaitMs);
252 : } else {
253 0 : ChildLaxReaper* reaper = new ChildLaxReaper(process);
254 :
255 0 : loop->CatchSignal(SIGCHLD, reaper, reaper);
256 : // |reaper| destroys itself after destruction notification
257 0 : loop->AddDestructionObserver(reaper);
258 : }
259 : }
|