Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / mojo / runner / child_process.cc
blobdc9b59f2a77cbfe5ac52fe6c376847fef41178f5
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
3 // found in the LICENSE file.
5 #include "mojo/runner/child_process.h"
7 #include "base/base_switches.h"
8 #include "base/bind.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/synchronization/waitable_event.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "base/threading/thread.h"
22 #include "base/threading/thread_checker.h"
23 #include "mojo/edk/embedder/embedder.h"
24 #include "mojo/edk/embedder/platform_channel_pair.h"
25 #include "mojo/edk/embedder/process_delegate.h"
26 #include "mojo/edk/embedder/scoped_platform_handle.h"
27 #include "mojo/edk/embedder/simple_platform_support.h"
28 #include "mojo/message_pump/message_pump_mojo.h"
29 #include "mojo/public/cpp/bindings/binding.h"
30 #include "mojo/public/cpp/system/core.h"
31 #include "mojo/runner/child_process.mojom.h"
32 #include "mojo/runner/native_application_support.h"
33 #include "mojo/runner/switches.h"
35 #if defined(OS_LINUX) && !defined(OS_ANDROID)
36 #include "base/rand_util.h"
37 #include "base/sys_info.h"
38 #include "mojo/runner/linux_sandbox.h"
39 #endif
41 namespace mojo {
42 namespace runner {
44 namespace {
46 // Blocker ---------------------------------------------------------------------
48 // Blocks a thread until another thread unblocks it, at which point it unblocks
49 // and runs a closure provided by that thread.
50 class Blocker {
51 public:
52 class Unblocker {
53 public:
54 explicit Unblocker(Blocker* blocker = nullptr) : blocker_(blocker) {}
55 ~Unblocker() {}
57 void Unblock(base::Closure run_after) {
58 DCHECK(blocker_);
59 DCHECK(blocker_->run_after_.is_null());
60 blocker_->run_after_ = run_after;
61 blocker_->event_.Signal();
62 blocker_ = nullptr;
65 private:
66 Blocker* blocker_;
68 // Copy and assign allowed.
71 Blocker() : event_(true, false) {}
72 ~Blocker() {}
74 void Block() {
75 DCHECK(run_after_.is_null());
76 event_.Wait();
77 if (!run_after_.is_null())
78 run_after_.Run();
81 Unblocker GetUnblocker() { return Unblocker(this); }
83 private:
84 base::WaitableEvent event_;
85 base::Closure run_after_;
87 DISALLOW_COPY_AND_ASSIGN(Blocker);
90 // AppContext ------------------------------------------------------------------
92 class ChildControllerImpl;
94 // Should be created and initialized on the main thread.
95 class AppContext : public embedder::ProcessDelegate {
96 public:
97 AppContext()
98 : io_thread_("io_thread"), controller_thread_("controller_thread") {}
99 ~AppContext() override {}
101 void Init() {
102 // Initialize Mojo before starting any threads.
103 embedder::Init(make_scoped_ptr(new embedder::SimplePlatformSupport()));
105 // Create and start our I/O thread.
106 base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
107 CHECK(io_thread_.StartWithOptions(io_thread_options));
108 io_runner_ = io_thread_.task_runner().get();
109 CHECK(io_runner_.get());
111 // Create and start our controller thread.
112 base::Thread::Options controller_thread_options;
113 controller_thread_options.message_loop_type =
114 base::MessageLoop::TYPE_CUSTOM;
115 controller_thread_options.message_pump_factory =
116 base::Bind(&common::MessagePumpMojo::Create);
117 CHECK(controller_thread_.StartWithOptions(controller_thread_options));
118 controller_runner_ = controller_thread_.task_runner().get();
119 CHECK(controller_runner_.get());
121 // TODO(vtl): This should be SLAVE, not NONE.
122 embedder::InitIPCSupport(embedder::ProcessType::NONE, controller_runner_,
123 this, io_runner_,
124 embedder::ScopedPlatformHandle());
127 void Shutdown() {
128 Blocker blocker;
129 shutdown_unblocker_ = blocker.GetUnblocker();
130 controller_runner_->PostTask(
131 FROM_HERE, base::Bind(&AppContext::ShutdownOnControllerThread,
132 base::Unretained(this)));
133 blocker.Block();
136 base::SingleThreadTaskRunner* io_runner() const { return io_runner_.get(); }
138 base::SingleThreadTaskRunner* controller_runner() const {
139 return controller_runner_.get();
142 ChildControllerImpl* controller() const { return controller_.get(); }
144 void set_controller(scoped_ptr<ChildControllerImpl> controller) {
145 controller_ = controller.Pass();
148 private:
149 void ShutdownOnControllerThread() {
150 // First, destroy the controller.
151 controller_.reset();
153 // Next shutdown IPC. We'll unblock the main thread in OnShutdownComplete().
154 embedder::ShutdownIPCSupport();
157 // ProcessDelegate implementation.
158 void OnShutdownComplete() override {
159 shutdown_unblocker_.Unblock(base::Closure());
162 base::Thread io_thread_;
163 scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
165 base::Thread controller_thread_;
166 scoped_refptr<base::SingleThreadTaskRunner> controller_runner_;
168 // Accessed only on the controller thread.
169 scoped_ptr<ChildControllerImpl> controller_;
171 // Used to unblock the main thread on shutdown.
172 Blocker::Unblocker shutdown_unblocker_;
174 DISALLOW_COPY_AND_ASSIGN(AppContext);
177 // ChildControllerImpl ------------------------------------------------------
179 class ChildControllerImpl : public ChildController {
180 public:
181 ~ChildControllerImpl() override {
182 DCHECK(thread_checker_.CalledOnValidThread());
184 // TODO(vtl): Pass in the result from |MainMain()|.
185 on_app_complete_.Run(MOJO_RESULT_UNIMPLEMENTED);
188 // To be executed on the controller thread. Creates the |ChildController|,
189 // etc.
190 static void Init(AppContext* app_context,
191 base::NativeLibrary app_library,
192 embedder::ScopedPlatformHandle platform_channel,
193 const Blocker::Unblocker& unblocker) {
194 DCHECK(app_context);
195 DCHECK(platform_channel.is_valid());
197 DCHECK(!app_context->controller());
199 scoped_ptr<ChildControllerImpl> impl(
200 new ChildControllerImpl(app_context, app_library, unblocker));
202 ScopedMessagePipeHandle host_message_pipe(embedder::CreateChannel(
203 platform_channel.Pass(),
204 base::Bind(&ChildControllerImpl::DidCreateChannel,
205 base::Unretained(impl.get())),
206 base::ThreadTaskRunnerHandle::Get()));
208 impl->Bind(host_message_pipe.Pass());
210 app_context->set_controller(impl.Pass());
213 void Bind(ScopedMessagePipeHandle handle) { binding_.Bind(handle.Pass()); }
215 void OnConnectionError() {
216 // A connection error means the connection to the shell is lost. This is not
217 // recoverable.
218 LOG(ERROR) << "Connection error to the shell.";
219 _exit(1);
222 // |ChildController| methods:
223 void StartApp(InterfaceRequest<Application> application_request,
224 const StartAppCallback& on_app_complete) override {
225 DCHECK(thread_checker_.CalledOnValidThread());
227 on_app_complete_ = on_app_complete;
228 unblocker_.Unblock(base::Bind(&ChildControllerImpl::StartAppOnMainThread,
229 base::Unretained(app_library_),
230 base::Passed(&application_request)));
233 void ExitNow(int32_t exit_code) override {
234 DVLOG(2) << "ChildControllerImpl::ExitNow(" << exit_code << ")";
235 _exit(exit_code);
238 private:
239 ChildControllerImpl(AppContext* app_context,
240 base::NativeLibrary app_library,
241 const Blocker::Unblocker& unblocker)
242 : app_context_(app_context),
243 app_library_(app_library),
244 unblocker_(unblocker),
245 channel_info_(nullptr),
246 binding_(this) {
247 binding_.set_connection_error_handler([this]() { OnConnectionError(); });
250 // Callback for |embedder::CreateChannel()|.
251 void DidCreateChannel(embedder::ChannelInfo* channel_info) {
252 DVLOG(2) << "ChildControllerImpl::DidCreateChannel()";
253 DCHECK(thread_checker_.CalledOnValidThread());
254 channel_info_ = channel_info;
257 static void StartAppOnMainThread(
258 base::NativeLibrary app_library,
259 InterfaceRequest<Application> application_request) {
260 if (!RunNativeApplication(app_library, application_request.Pass())) {
261 LOG(ERROR) << "Failure to RunNativeApplication()";
265 base::ThreadChecker thread_checker_;
266 AppContext* const app_context_;
267 base::NativeLibrary app_library_;
268 Blocker::Unblocker unblocker_;
269 StartAppCallback on_app_complete_;
271 embedder::ChannelInfo* channel_info_;
272 Binding<ChildController> binding_;
274 DISALLOW_COPY_AND_ASSIGN(ChildControllerImpl);
277 } // namespace
279 int ChildProcessMain() {
280 DVLOG(2) << "ChildProcessMain()";
281 const base::CommandLine& command_line =
282 *base::CommandLine::ForCurrentProcess();
284 #if defined(OS_LINUX) && !defined(OS_ANDROID)
285 using sandbox::syscall_broker::BrokerFilePermission;
286 scoped_ptr<mandoline::LinuxSandbox> sandbox;
287 #endif
288 base::NativeLibrary app_library = 0;
289 if (command_line.HasSwitch(switches::kChildProcess)) {
290 // Load the application library before we engage the sandbox.
291 app_library = mojo::runner::LoadNativeApplication(
292 command_line.GetSwitchValuePath(switches::kChildProcess));
294 #if defined(OS_LINUX) && !defined(OS_ANDROID)
295 if (command_line.HasSwitch(switches::kEnableSandbox)) {
296 // Warm parts of base.
297 base::RandUint64();
298 base::SysInfo::AmountOfPhysicalMemory();
299 base::SysInfo::MaxSharedMemorySize();
300 base::SysInfo::NumberOfProcessors();
302 // Do whatever warming that the mojo application wants.
303 typedef void (*SandboxWarmFunction)();
304 SandboxWarmFunction sandbox_warm = reinterpret_cast<SandboxWarmFunction>(
305 base::GetFunctionPointerFromNativeLibrary(app_library,
306 "MojoSandboxWarm"));
307 if (sandbox_warm)
308 sandbox_warm();
310 // TODO(erg,jln): Allowing access to all of /dev/shm/ makes it easy to
311 // spy on other shared memory using processes. This is a temporary hack
312 // so that we have some sandbox until we have proper shared memory
313 // support integrated into mojo.
314 std::vector<BrokerFilePermission> permissions;
315 permissions.push_back(
316 BrokerFilePermission::ReadWriteCreateUnlinkRecursive("/dev/shm/"));
317 sandbox.reset(new mandoline::LinuxSandbox(permissions));
318 sandbox->Warmup();
319 sandbox->EngageNamespaceSandbox();
320 sandbox->EngageSeccompSandbox();
321 sandbox->Seal();
323 #endif
326 embedder::ScopedPlatformHandle platform_channel =
327 embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
328 command_line);
329 CHECK(platform_channel.is_valid());
331 DCHECK(!base::MessageLoop::current());
333 AppContext app_context;
334 app_context.Init();
336 Blocker blocker;
337 app_context.controller_runner()->PostTask(
338 FROM_HERE,
339 base::Bind(&ChildControllerImpl::Init, base::Unretained(&app_context),
340 base::Unretained(app_library), base::Passed(&platform_channel),
341 blocker.GetUnblocker()));
342 // This will block, then run whatever the controller wants.
343 blocker.Block();
345 app_context.Shutdown();
347 return 0;
350 } // namespace runner
351 } // namespace mojo