| 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 |