OLD | NEW |
| (Empty) |
1 // Copyright (c) 2009 The Chromium 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 "chrome/browser/child_process_launcher.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/logging.h" | |
9 #include "base/scoped_ptr.h" | |
10 #include "base/thread.h" | |
11 #include "chrome/browser/chrome_thread.h" | |
12 #include "chrome/common/chrome_descriptors.h" | |
13 #include "chrome/common/chrome_switches.h" | |
14 #include "chrome/common/process_watcher.h" | |
15 #include "chrome/common/result_codes.h" | |
16 | |
17 #if defined(OS_WIN) | |
18 #include "chrome/common/sandbox_policy.h" | |
19 #elif defined(OS_LINUX) | |
20 #include "base/singleton.h" | |
21 #include "chrome/browser/crash_handler_host_linux.h" | |
22 #include "chrome/browser/zygote_host_linux.h" | |
23 #include "chrome/browser/renderer_host/render_sandbox_host_linux.h" | |
24 #endif | |
25 | |
26 #if defined(OS_MACOSX) | |
27 #include "chrome/browser/mach_broker_mac.h" | |
28 #endif | |
29 | |
30 #if defined(OS_POSIX) | |
31 #include "base/global_descriptors_posix.h" | |
32 #endif | |
33 | |
34 // TODO(eroman): Debugging helper to make strings show up in mini-dumps. | |
35 // Remove after done investigating 40447. | |
36 class StackString { | |
37 public: | |
38 explicit StackString(const std::wstring& str) { | |
39 length_ = str.size(); | |
40 memcpy(&buffer_[0], str.data(), | |
41 std::min(sizeof(wchar_t) * str.length(), | |
42 sizeof(buffer_))); | |
43 } | |
44 | |
45 std::wstring ToString() { | |
46 return std::wstring(buffer_, length_); | |
47 } | |
48 | |
49 ~StackString() { | |
50 // Hack to make sure compiler doesn't optimize us away. | |
51 if (ToString() != ToString()) | |
52 LOG(INFO) << ToString(); | |
53 } | |
54 | |
55 private: | |
56 wchar_t buffer_[128]; | |
57 size_t length_; | |
58 }; | |
59 | |
60 // Having the functionality of ChildProcessLauncher be in an internal | |
61 // ref counted object allows us to automatically terminate the process when the | |
62 // parent class destructs, while still holding on to state that we need. | |
63 class ChildProcessLauncher::Context | |
64 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> { | |
65 public: | |
66 Context() | |
67 : starting_(true) | |
68 #if defined(OS_LINUX) | |
69 , zygote_(false) | |
70 #endif | |
71 { | |
72 } | |
73 | |
74 void Launch( | |
75 #if defined(OS_WIN) | |
76 const FilePath& exposed_dir, | |
77 #elif defined(OS_POSIX) | |
78 bool use_zygote, | |
79 const base::environment_vector& environ, | |
80 int ipcfd, | |
81 #endif | |
82 CommandLine* cmd_line, | |
83 Client* client) { | |
84 client_ = client; | |
85 | |
86 CHECK(ChromeThread::GetCurrentThreadIdentifier(&client_thread_id_)); | |
87 | |
88 ChromeThread::PostTask( | |
89 ChromeThread::PROCESS_LAUNCHER, FROM_HERE, | |
90 NewRunnableMethod( | |
91 this, | |
92 &Context::LaunchInternal, | |
93 #if defined(OS_WIN) | |
94 exposed_dir, | |
95 #elif defined(POSIX) | |
96 use_zygote, | |
97 environ, | |
98 ipcfd, | |
99 #endif | |
100 cmd_line)); | |
101 } | |
102 | |
103 void ResetClient() { | |
104 // No need for locking as this function gets called on the same thread that | |
105 // client_ would be used. | |
106 CHECK(ChromeThread::CurrentlyOn(client_thread_id_)); | |
107 client_ = NULL; | |
108 } | |
109 | |
110 private: | |
111 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; | |
112 friend class ChildProcessLauncher; | |
113 | |
114 ~Context() { | |
115 Terminate(); | |
116 } | |
117 | |
118 void LaunchInternal( | |
119 #if defined(OS_WIN) | |
120 const FilePath& exposed_dir, | |
121 #elif defined(OS_POSIX) | |
122 bool use_zygote, | |
123 const base::environment_vector& env, | |
124 int ipcfd, | |
125 #endif | |
126 CommandLine* cmd_line) { | |
127 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); | |
128 base::ProcessHandle handle = base::kNullProcessHandle; | |
129 #if defined(OS_WIN) | |
130 // TODO(eroman): Remove after done investigating 40447. | |
131 StackString stack_command_line(cmd_line->command_line_string()); | |
132 // This line might crash, since it calls the string copy-constructor: | |
133 StackString stack_program(cmd_line->program()); | |
134 | |
135 handle = sandbox::StartProcessWithAccess(cmd_line, exposed_dir); | |
136 #elif defined(OS_POSIX) | |
137 | |
138 #if defined(OS_LINUX) | |
139 if (use_zygote) { | |
140 base::GlobalDescriptors::Mapping mapping; | |
141 mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd)); | |
142 const int crash_signal_fd = | |
143 Singleton<RendererCrashHandlerHostLinux>()->GetDeathSignalSocket(); | |
144 if (crash_signal_fd >= 0) { | |
145 mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal, | |
146 crash_signal_fd)); | |
147 } | |
148 handle = Singleton<ZygoteHost>()->ForkRenderer(cmd_line->argv(), mapping); | |
149 } else | |
150 // Fall through to the normal posix case below when we're not zygoting. | |
151 #endif | |
152 { | |
153 base::file_handle_mapping_vector fds_to_map; | |
154 fds_to_map.push_back(std::make_pair( | |
155 ipcfd, | |
156 kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); | |
157 | |
158 #if defined(OS_LINUX) | |
159 // On Linux, we need to add some extra file descriptors for crash handling | |
160 // and the sandbox. | |
161 bool is_renderer = | |
162 cmd_line->GetSwitchValueASCII(switches::kProcessType) == | |
163 switches::kRendererProcess; | |
164 bool is_plugin = | |
165 cmd_line->GetSwitchValueASCII(switches::kProcessType) == | |
166 switches::kPluginProcess; | |
167 | |
168 if (is_renderer || is_plugin) { | |
169 int crash_signal_fd; | |
170 if (is_renderer) { | |
171 crash_signal_fd = Singleton<RendererCrashHandlerHostLinux>()-> | |
172 GetDeathSignalSocket(); | |
173 } else { | |
174 crash_signal_fd = Singleton<PluginCrashHandlerHostLinux>()-> | |
175 GetDeathSignalSocket(); | |
176 } | |
177 if (crash_signal_fd >= 0) { | |
178 fds_to_map.push_back(std::make_pair( | |
179 crash_signal_fd, | |
180 kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor)); | |
181 } | |
182 } | |
183 if (is_renderer) { | |
184 const int sandbox_fd = | |
185 Singleton<RenderSandboxHostLinux>()->GetRendererSocket(); | |
186 fds_to_map.push_back(std::make_pair( | |
187 sandbox_fd, | |
188 kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); | |
189 } | |
190 #endif // defined(OS_LINUX) | |
191 | |
192 // Actually launch the app. | |
193 bool launched; | |
194 #if defined(OS_MACOSX) | |
195 task_t child_task; | |
196 launched = base::LaunchAppAndGetTask( | |
197 cmd_line->argv(), env, fds_to_map, false, &child_task, &handle); | |
198 if (launched && child_task != MACH_PORT_NULL) { | |
199 MachBroker::instance()->RegisterPid( | |
200 handle, | |
201 MachBroker::MachInfo().SetTask(child_task)); | |
202 } | |
203 #else | |
204 launched = base::LaunchApp(cmd_line->argv(), env, fds_to_map, | |
205 /* wait= */false, &handle); | |
206 #endif | |
207 if (!launched) | |
208 handle = base::kNullProcessHandle; | |
209 } | |
210 #endif // else defined(OS_POSIX) | |
211 | |
212 ChromeThread::PostTask( | |
213 client_thread_id_, FROM_HERE, | |
214 NewRunnableMethod( | |
215 this, | |
216 &ChildProcessLauncher::Context::Notify, | |
217 #if defined(OS_LINUX) | |
218 use_zygote, | |
219 #endif | |
220 handle)); | |
221 } | |
222 | |
223 void Notify( | |
224 #if defined(OS_LINUX) | |
225 bool zygote, | |
226 #endif | |
227 base::ProcessHandle handle) { | |
228 starting_ = false; | |
229 process_.set_handle(handle); | |
230 #if defined(OS_LINUX) | |
231 zygote_ = zygote; | |
232 #endif | |
233 if (client_) { | |
234 client_->OnProcessLaunched(); | |
235 } else { | |
236 Terminate(); | |
237 } | |
238 } | |
239 | |
240 void Terminate() { | |
241 if (!process_.handle()) | |
242 return; | |
243 | |
244 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So | |
245 // don't this on the UI/IO threads. | |
246 ChromeThread::PostTask( | |
247 ChromeThread::PROCESS_LAUNCHER, FROM_HERE, | |
248 NewRunnableFunction( | |
249 &ChildProcessLauncher::Context::TerminateInternal, | |
250 #if defined(OS_LINUX) | |
251 zygote_, | |
252 #endif | |
253 process_.handle())); | |
254 process_.set_handle(base::kNullProcessHandle); | |
255 } | |
256 | |
257 static void TerminateInternal( | |
258 #if defined(OS_LINUX) | |
259 bool zygote, | |
260 #endif | |
261 base::ProcessHandle handle) { | |
262 base::Process process(handle); | |
263 // Client has gone away, so just kill the process. Using exit code 0 | |
264 // means that UMA won't treat this as a crash. | |
265 process.Terminate(ResultCodes::NORMAL_EXIT); | |
266 // On POSIX, we must additionally reap the child. | |
267 #if defined(OS_POSIX) | |
268 #if defined(OS_LINUX) | |
269 if (zygote) { | |
270 // If the renderer was created via a zygote, we have to proxy the reaping | |
271 // through the zygote process. | |
272 Singleton<ZygoteHost>()->EnsureProcessTerminated(handle); | |
273 } else | |
274 #endif // OS_LINUX | |
275 { | |
276 ProcessWatcher::EnsureProcessTerminated(handle); | |
277 } | |
278 #endif // OS_POSIX | |
279 process.Close(); | |
280 } | |
281 | |
282 Client* client_; | |
283 ChromeThread::ID client_thread_id_; | |
284 base::Process process_; | |
285 bool starting_; | |
286 | |
287 #if defined(OS_LINUX) | |
288 bool zygote_; | |
289 #endif | |
290 }; | |
291 | |
292 | |
293 ChildProcessLauncher::ChildProcessLauncher( | |
294 #if defined(OS_WIN) | |
295 const FilePath& exposed_dir, | |
296 #elif defined(OS_POSIX) | |
297 bool use_zygote, | |
298 const base::environment_vector& environ, | |
299 int ipcfd, | |
300 #endif | |
301 CommandLine* cmd_line, | |
302 Client* client) { | |
303 context_ = new Context(); | |
304 context_->Launch( | |
305 #if defined(OS_WIN) | |
306 exposed_dir, | |
307 #elif defined(OS_POSIX) | |
308 use_zygote, | |
309 environ, | |
310 ipcfd, | |
311 #endif | |
312 cmd_line, | |
313 client); | |
314 } | |
315 | |
316 ChildProcessLauncher::~ChildProcessLauncher() { | |
317 context_->ResetClient(); | |
318 } | |
319 | |
320 bool ChildProcessLauncher::IsStarting() { | |
321 return context_->starting_; | |
322 } | |
323 | |
324 base::ProcessHandle ChildProcessLauncher::GetHandle() { | |
325 DCHECK(!context_->starting_); | |
326 return context_->process_.handle(); | |
327 } | |
328 | |
329 bool ChildProcessLauncher::DidProcessCrash() { | |
330 bool did_crash, child_exited; | |
331 base::ProcessHandle handle = context_->process_.handle(); | |
332 #if defined(OS_LINUX) | |
333 if (context_->zygote_) { | |
334 did_crash = Singleton<ZygoteHost>()->DidProcessCrash(handle, &child_exited); | |
335 } else | |
336 #endif | |
337 { | |
338 did_crash = base::DidProcessCrash(&child_exited, handle); | |
339 } | |
340 | |
341 // POSIX: If the process crashed, then the kernel closed the socket for it | |
342 // and so the child has already died by the time we get here. Since | |
343 // DidProcessCrash called waitpid with WNOHANG, it'll reap the process. | |
344 // However, if DidProcessCrash didn't reap the child, we'll need to in | |
345 // Terminate via ProcessWatcher. So we can't close the handle here. | |
346 if (child_exited) | |
347 context_->process_.Close(); | |
348 | |
349 return did_crash; | |
350 } | |
351 | |
352 void ChildProcessLauncher::SetProcessBackgrounded(bool background) { | |
353 DCHECK(!context_->starting_); | |
354 context_->process_.SetProcessBackgrounded(background); | |
355 } | |
OLD | NEW |