OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "mojo/shell/runner/host/child_process_host.h" | 5 #include "mojo/shell/runner/host/child_process_host.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
13 #include "base/location.h" | 13 #include "base/location.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/macros.h" | 15 #include "base/macros.h" |
16 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
17 #include "base/process/kill.h" | 17 #include "base/process/kill.h" |
18 #include "base/process/launch.h" | 18 #include "base/process/launch.h" |
19 #include "base/task_runner.h" | 19 #include "base/task_runner.h" |
20 #include "base/thread_task_runner_handle.h" | 20 #include "base/thread_task_runner_handle.h" |
| 21 #include "mojo/edk/embedder/embedder.h" |
21 #include "mojo/public/cpp/bindings/interface_ptr_info.h" | 22 #include "mojo/public/cpp/bindings/interface_ptr_info.h" |
22 #include "mojo/public/cpp/system/core.h" | 23 #include "mojo/public/cpp/system/core.h" |
23 #include "mojo/shell/runner/host/switches.h" | 24 #include "mojo/shell/runner/host/switches.h" |
24 #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" | 25 #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" |
25 | 26 |
26 #if defined(OS_LINUX) && !defined(OS_ANDROID) | 27 #if defined(OS_LINUX) && !defined(OS_ANDROID) |
27 #include "sandbox/linux/services/namespace_sandbox.h" | 28 #include "sandbox/linux/services/namespace_sandbox.h" |
28 #endif | 29 #endif |
29 | 30 |
30 #if defined(OS_WIN) | 31 #if defined(OS_WIN) |
31 #include "base/win/windows_version.h" | 32 #include "base/win/windows_version.h" |
32 #endif | 33 #endif |
33 | 34 |
34 namespace mojo { | 35 namespace mojo { |
35 namespace shell { | 36 namespace shell { |
36 | 37 |
| 38 ChildProcessHost::PipeHolder::PipeHolder() {} |
| 39 |
| 40 void ChildProcessHost::PipeHolder::Reject() { |
| 41 base::AutoLock lock(lock_); |
| 42 reject_pipe_ = true; |
| 43 pipe_.reset(); |
| 44 } |
| 45 |
| 46 void ChildProcessHost::PipeHolder::SetPipe(ScopedMessagePipeHandle pipe) { |
| 47 base::AutoLock lock(lock_); |
| 48 DCHECK(!pipe_.is_valid()); |
| 49 if (!reject_pipe_) |
| 50 pipe_ = std::move(pipe); |
| 51 } |
| 52 |
| 53 ScopedMessagePipeHandle ChildProcessHost::PipeHolder::PassPipe() { |
| 54 base::AutoLock lock(lock_); |
| 55 DCHECK(pipe_.is_valid()); |
| 56 return std::move(pipe_); |
| 57 } |
| 58 |
| 59 ChildProcessHost::PipeHolder::~PipeHolder() {} |
| 60 |
37 ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, | 61 ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, |
38 bool start_sandboxed, | 62 bool start_sandboxed, |
39 const base::FilePath& app_path) | 63 const base::FilePath& app_path) |
40 : launch_process_runner_(launch_process_runner), | 64 : launch_process_runner_(launch_process_runner), |
41 start_sandboxed_(start_sandboxed), | 65 start_sandboxed_(start_sandboxed), |
42 app_path_(app_path), | 66 app_path_(app_path), |
43 channel_info_(nullptr), | 67 channel_info_(nullptr), |
44 start_child_process_event_(false, false), | 68 start_child_process_event_(false, false), |
45 weak_factory_(this) { | 69 weak_factory_(this) { |
46 if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) | 70 pipe_holder_ = new PipeHolder(); |
47 serializer_platform_channel_pair_.reset(new edk::PlatformChannelPair(true)); | 71 if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { |
48 | 72 node_channel_.reset(new edk::PlatformChannelPair); |
49 child_message_pipe_ = embedder::CreateChannel( | 73 primordial_pipe_token_ = edk::GenerateRandomToken(); |
50 platform_channel_pair_.PassServerHandle(), | 74 } else { |
51 base::Bind(&ChildProcessHost::DidCreateChannel, base::Unretained(this)), | 75 pipe_holder_->SetPipe(embedder::CreateChannel( |
52 base::ThreadTaskRunnerHandle::Get()); | 76 platform_channel_pair_.PassServerHandle(), |
| 77 base::Bind(&ChildProcessHost::DidCreateChannel, base::Unretained(this)), |
| 78 base::ThreadTaskRunnerHandle::Get())); |
| 79 OnMessagePipeCreated(); |
| 80 } |
53 } | 81 } |
54 | 82 |
55 ChildProcessHost::ChildProcessHost(ScopedHandle channel) | 83 ChildProcessHost::ChildProcessHost(ScopedHandle channel) |
56 : launch_process_runner_(nullptr), | 84 : launch_process_runner_(nullptr), |
57 start_sandboxed_(false), | 85 start_sandboxed_(false), |
58 channel_info_(nullptr), | 86 channel_info_(nullptr), |
59 start_child_process_event_(false, false), | 87 start_child_process_event_(false, false), |
60 weak_factory_(this) { | 88 weak_factory_(this) { |
61 CHECK(channel.is_valid()); | 89 CHECK(channel.is_valid()); |
62 ScopedMessagePipeHandle handle(MessagePipeHandle(channel.release().value())); | 90 ScopedMessagePipeHandle handle(MessagePipeHandle(channel.release().value())); |
63 controller_.Bind(InterfacePtrInfo<ChildController>(std::move(handle), 0u)); | 91 controller_.Bind(InterfacePtrInfo<ChildController>(std::move(handle), 0u)); |
64 } | 92 } |
65 | 93 |
66 ChildProcessHost::~ChildProcessHost() { | 94 ChildProcessHost::~ChildProcessHost() { |
67 if (!app_path_.empty()) | 95 if (!app_path_.empty()) |
68 CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; | 96 CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; |
69 } | 97 } |
70 | 98 |
71 void ChildProcessHost::Start( | 99 void ChildProcessHost::Start(const ProcessReadyCallback& callback) { |
72 const base::Callback<void(base::ProcessId)>& pid_available_callback) { | |
73 DCHECK(!child_process_.IsValid()); | 100 DCHECK(!child_process_.IsValid()); |
74 DCHECK(child_message_pipe_.is_valid()); | 101 DCHECK(process_ready_callback_.is_null()); |
75 | 102 |
| 103 process_ready_callback_ = callback; |
76 if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { | 104 if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { |
77 std::string client_handle_as_string = | 105 // With the new EDK, bootstrap message pipes are created asynchronously. |
78 serializer_platform_channel_pair_ | 106 // We recieve the bound pipe (if successful) on an arbitrary thread, |
79 ->PrepareToPassClientHandleToChildProcessAsString( | 107 // stash it in the thread-safe |pipe_holder_|, and then try to call |
80 &handle_passing_info_); | 108 // OnMessagePipeCreated() on the host's main thread. |
81 // We can't send the MP for the token serializer implementation as a | 109 // |
82 // platform handle, because that would require the other side to use the | 110 // Because of the way the launcher process shuts down, it's possible for |
83 // token initializer itself! So instead we send it as a string. | 111 // the main thread's MessageLoop to stop running (but not yet be destroyed!) |
84 MojoResult rv = MojoWriteMessage( | 112 // while this boostrap is pending, resulting in OnMessagePipeCreated() never |
85 child_message_pipe_.get().value(), client_handle_as_string.c_str(), | 113 // being called. |
86 static_cast<uint32_t>(client_handle_as_string.size()), nullptr, 0, | 114 // |
87 MOJO_WRITE_MESSAGE_FLAG_NONE); | 115 // A typical child process (i.e. one using ApplicationImpl to bind the other |
88 DCHECK_EQ(rv, MOJO_RESULT_OK); | 116 // end of this pipe) may hang forever waiting for an Initialize() message |
| 117 // unless the pipe is closed. This in turn means that Join() could hang |
| 118 // waiting for the process to exit. Deadlock! |
| 119 // |
| 120 // |pipe_holder_| exists for this reason. If it's still holding onto the |
| 121 // pipe when Join() is called, the pipe will be closed. |
| 122 DCHECK(!primordial_pipe_token_.empty()); |
| 123 edk::CreateParentMessagePipe( |
| 124 primordial_pipe_token_, |
| 125 base::Bind(&OnParentMessagePipeCreated, pipe_holder_, |
| 126 base::ThreadTaskRunnerHandle::Get(), |
| 127 base::Bind(&ChildProcessHost::OnMessagePipeCreated, |
| 128 weak_factory_.GetWeakPtr()))); |
89 } | 129 } |
90 | 130 |
91 controller_.Bind( | |
92 InterfacePtrInfo<ChildController>(std::move(child_message_pipe_), 0u)); | |
93 | |
94 launch_process_runner_->PostTaskAndReply( | 131 launch_process_runner_->PostTaskAndReply( |
95 FROM_HERE, | 132 FROM_HERE, |
96 base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)), | 133 base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)), |
97 base::Bind(&ChildProcessHost::DidStart, weak_factory_.GetWeakPtr(), | 134 base::Bind(&ChildProcessHost::DidStart, weak_factory_.GetWeakPtr())); |
98 pid_available_callback)); | |
99 } | 135 } |
100 | 136 |
101 int ChildProcessHost::Join() { | 137 int ChildProcessHost::Join() { |
102 if (controller_) // We use this as a signal that Start was called. | 138 if (controller_) // We use this as a signal that Start was called. |
103 start_child_process_event_.Wait(); | 139 start_child_process_event_.Wait(); |
| 140 |
104 controller_ = ChildControllerPtr(); | 141 controller_ = ChildControllerPtr(); |
105 DCHECK(child_process_.IsValid()); | 142 DCHECK(child_process_.IsValid()); |
| 143 |
| 144 // Ensure the child pipe is closed even if it wasn't yet connected to the |
| 145 // controller. |
| 146 pipe_holder_->Reject(); |
| 147 |
106 int rv = -1; | 148 int rv = -1; |
107 LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) | 149 LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) |
108 << "Failed to wait for child process"; | 150 << "Failed to wait for child process"; |
| 151 |
109 child_process_.Close(); | 152 child_process_.Close(); |
| 153 |
110 return rv; | 154 return rv; |
111 } | 155 } |
112 | 156 |
113 void ChildProcessHost::StartApp( | 157 void ChildProcessHost::StartApp( |
114 InterfaceRequest<Application> application_request, | 158 InterfaceRequest<Application> application_request, |
115 const ChildController::StartAppCallback& on_app_complete) { | 159 const ChildController::StartAppCallback& on_app_complete) { |
116 DCHECK(controller_); | 160 DCHECK(controller_); |
117 | 161 |
118 on_app_complete_ = on_app_complete; | 162 on_app_complete_ = on_app_complete; |
119 controller_->StartApp( | 163 controller_->StartApp( |
120 std::move(application_request), | 164 std::move(application_request), |
121 base::Bind(&ChildProcessHost::AppCompleted, weak_factory_.GetWeakPtr())); | 165 base::Bind(&ChildProcessHost::AppCompleted, weak_factory_.GetWeakPtr())); |
122 } | 166 } |
123 | 167 |
124 void ChildProcessHost::ExitNow(int32_t exit_code) { | 168 void ChildProcessHost::ExitNow(int32_t exit_code) { |
125 DCHECK(controller_); | 169 DCHECK(controller_); |
126 | 170 |
127 controller_->ExitNow(exit_code); | 171 controller_->ExitNow(exit_code); |
128 } | 172 } |
129 | 173 |
130 void ChildProcessHost::DidStart( | 174 void ChildProcessHost::DidStart() { |
131 const base::Callback<void(base::ProcessId)>& pid_available_callback) { | |
132 DVLOG(2) << "ChildProcessHost::DidStart()"; | 175 DVLOG(2) << "ChildProcessHost::DidStart()"; |
133 | 176 |
134 if (child_process_.IsValid()) { | 177 if (child_process_.IsValid()) { |
135 pid_available_callback.Run(child_process_.Pid()); | 178 MaybeNotifyProcessReady(); |
136 } else { | 179 } else { |
137 LOG(ERROR) << "Failed to start child process"; | 180 LOG(ERROR) << "Failed to start child process"; |
138 AppCompleted(MOJO_RESULT_UNKNOWN); | 181 AppCompleted(MOJO_RESULT_UNKNOWN); |
139 } | 182 } |
140 } | 183 } |
141 | 184 |
142 void ChildProcessHost::DoLaunch() { | 185 void ChildProcessHost::DoLaunch() { |
143 const base::CommandLine* parent_command_line = | 186 const base::CommandLine* parent_command_line = |
144 base::CommandLine::ForCurrentProcess(); | 187 base::CommandLine::ForCurrentProcess(); |
145 base::FilePath target_path = parent_command_line->GetProgram(); | 188 base::FilePath target_path = parent_command_line->GetProgram(); |
146 // |app_path_| can be empty in tests. | 189 // |app_path_| can be empty in tests. |
147 if (!app_path_.MatchesExtension(FILE_PATH_LITERAL(".mojo")) && | 190 if (!app_path_.MatchesExtension(FILE_PATH_LITERAL(".mojo")) && |
148 !app_path_.empty()) { | 191 !app_path_.empty()) { |
149 target_path = app_path_; | 192 target_path = app_path_; |
150 } | 193 } |
151 | 194 |
152 base::CommandLine child_command_line(target_path); | 195 base::CommandLine child_command_line(target_path); |
153 child_command_line.AppendArguments(*parent_command_line, false); | 196 child_command_line.AppendArguments(*parent_command_line, false); |
154 | 197 |
155 if (target_path != app_path_) | 198 if (target_path != app_path_) |
156 child_command_line.AppendSwitchPath(switches::kChildProcess, app_path_); | 199 child_command_line.AppendSwitchPath(switches::kChildProcess, app_path_); |
157 | 200 |
158 if (start_sandboxed_) | 201 if (start_sandboxed_) |
159 child_command_line.AppendSwitch(switches::kEnableSandbox); | 202 child_command_line.AppendSwitch(switches::kEnableSandbox); |
160 | 203 |
161 platform_channel_pair_.PrepareToPassClientHandleToChildProcess( | 204 if (node_channel_.get()) { |
162 &child_command_line, &handle_passing_info_); | 205 node_channel_->PrepareToPassClientHandleToChildProcess( |
| 206 &child_command_line, &handle_passing_info_); |
| 207 } |
| 208 |
| 209 child_command_line.AppendSwitchASCII(switches::kPrimordialPipeToken, |
| 210 primordial_pipe_token_); |
163 | 211 |
164 base::LaunchOptions options; | 212 base::LaunchOptions options; |
165 #if defined(OS_WIN) | 213 #if defined(OS_WIN) |
166 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | 214 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
167 options.handles_to_inherit = &handle_passing_info_; | 215 options.handles_to_inherit = &handle_passing_info_; |
168 } else { | 216 } else { |
169 #if defined(OFFICIAL_BUILD) | 217 #if defined(OFFICIAL_BUILD) |
170 CHECK(false) << "Launching mojo process with inherit_handles is insecure!"; | 218 CHECK(false) << "Launching mojo process with inherit_handles is insecure!"; |
171 #endif | 219 #endif |
172 options.inherit_handles = true; | 220 options.inherit_handles = true; |
(...skipping 30 matching lines...) Expand all Loading... |
203 if (!child_process_.IsValid()) { | 251 if (!child_process_.IsValid()) { |
204 LOG(ERROR) << "Starting the process with a sandbox failed. Missing kernel" | 252 LOG(ERROR) << "Starting the process with a sandbox failed. Missing kernel" |
205 << " support."; | 253 << " support."; |
206 } | 254 } |
207 } else | 255 } else |
208 #endif | 256 #endif |
209 child_process_ = base::LaunchProcess(child_command_line, options); | 257 child_process_ = base::LaunchProcess(child_command_line, options); |
210 | 258 |
211 if (child_process_.IsValid()) { | 259 if (child_process_.IsValid()) { |
212 platform_channel_pair_.ChildProcessLaunched(); | 260 platform_channel_pair_.ChildProcessLaunched(); |
213 if (serializer_platform_channel_pair_.get()) { | 261 if (node_channel_.get()) { |
214 serializer_platform_channel_pair_->ChildProcessLaunched(); | 262 node_channel_->ChildProcessLaunched(); |
215 mojo::embedder::ChildProcessLaunched( | 263 mojo::embedder::ChildProcessLaunched( |
216 child_process_.Handle(), | 264 child_process_.Handle(), |
217 mojo::embedder::ScopedPlatformHandle(mojo::embedder::PlatformHandle( | 265 mojo::embedder::ScopedPlatformHandle(mojo::embedder::PlatformHandle( |
218 serializer_platform_channel_pair_->PassServerHandle().release(). | 266 node_channel_->PassServerHandle().release().handle))); |
219 handle))); | |
220 } | 267 } |
221 } | 268 } |
222 start_child_process_event_.Signal(); | 269 start_child_process_event_.Signal(); |
223 } | 270 } |
224 | 271 |
225 void ChildProcessHost::AppCompleted(int32_t result) { | 272 void ChildProcessHost::AppCompleted(int32_t result) { |
226 if (!on_app_complete_.is_null()) { | 273 if (!on_app_complete_.is_null()) { |
227 auto on_app_complete = on_app_complete_; | 274 auto on_app_complete = on_app_complete_; |
228 on_app_complete_.reset(); | 275 on_app_complete_.reset(); |
229 on_app_complete.Run(result); | 276 on_app_complete.Run(result); |
230 } | 277 } |
231 } | 278 } |
232 | 279 |
233 void ChildProcessHost::DidCreateChannel(embedder::ChannelInfo* channel_info) { | 280 void ChildProcessHost::DidCreateChannel(embedder::ChannelInfo* channel_info) { |
234 DVLOG(2) << "AppChildProcessHost::DidCreateChannel()"; | 281 DVLOG(2) << "AppChildProcessHost::DidCreateChannel()"; |
235 | 282 |
236 DCHECK(channel_info || | 283 DCHECK(channel_info || |
237 base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")); | 284 base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")); |
238 channel_info_ = channel_info; | 285 channel_info_ = channel_info; |
239 } | 286 } |
240 | 287 |
| 288 void ChildProcessHost::OnMessagePipeCreated() { |
| 289 controller_.Bind( |
| 290 InterfacePtrInfo<ChildController>(pipe_holder_->PassPipe(), 0u)); |
| 291 MaybeNotifyProcessReady(); |
| 292 } |
| 293 |
| 294 void ChildProcessHost::MaybeNotifyProcessReady() { |
| 295 if (controller_.is_bound() && child_process_.IsValid()) |
| 296 process_ready_callback_.Run(child_process_.Pid()); |
| 297 } |
| 298 |
| 299 // static |
| 300 void ChildProcessHost::OnParentMessagePipeCreated( |
| 301 scoped_refptr<PipeHolder> holder, |
| 302 scoped_refptr<base::TaskRunner> callback_task_runner, |
| 303 const base::Closure& callback, |
| 304 ScopedMessagePipeHandle pipe) { |
| 305 holder->SetPipe(std::move(pipe)); |
| 306 callback_task_runner->PostTask(FROM_HERE, callback); |
| 307 } |
| 308 |
241 } // namespace shell | 309 } // namespace shell |
242 } // namespace mojo | 310 } // namespace mojo |
OLD | NEW |