Add ICU message format support
[chromium-blink-merge.git] / mojo / runner / child_process.cc
blobeda7829a2af093b1e4b26ce79e2f84f5a43905f2
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/system/core.h"
30 #include "mojo/runner/child_process.mojom.h"
31 #include "mojo/runner/native_application_support.h"
32 #include "mojo/runner/switches.h"
34 #if defined(OS_LINUX) && !defined(OS_ANDROID)
35 #include "base/rand_util.h"
36 #include "base/sys_info.h"
37 #include "mojo/runner/linux_sandbox.h"
38 #endif
40 namespace mojo {
41 namespace runner {
43 namespace {
45 // Blocker ---------------------------------------------------------------------
47 // Blocks a thread until another thread unblocks it, at which point it unblocks
48 // and runs a closure provided by that thread.
49 class Blocker {
50 public:
51 class Unblocker {
52 public:
53 explicit Unblocker(Blocker* blocker = nullptr) : blocker_(blocker) {}
54 ~Unblocker() {}
56 void Unblock(base::Closure run_after) {
57 DCHECK(blocker_);
58 DCHECK(blocker_->run_after_.is_null());
59 blocker_->run_after_ = run_after;
60 blocker_->event_.Signal();
61 blocker_ = nullptr;
64 private:
65 Blocker* blocker_;
67 // Copy and assign allowed.
70 Blocker() : event_(true, false) {}
71 ~Blocker() {}
73 void Block() {
74 DCHECK(run_after_.is_null());
75 event_.Wait();
76 if (!run_after_.is_null())
77 run_after_.Run();
80 Unblocker GetUnblocker() { return Unblocker(this); }
82 private:
83 base::WaitableEvent event_;
84 base::Closure run_after_;
86 DISALLOW_COPY_AND_ASSIGN(Blocker);
89 // AppContext ------------------------------------------------------------------
91 class ChildControllerImpl;
93 // Should be created and initialized on the main thread.
94 class AppContext : public embedder::ProcessDelegate {
95 public:
96 AppContext()
97 : io_thread_("io_thread"), controller_thread_("controller_thread") {}
98 ~AppContext() override {}
100 void Init() {
101 // Initialize Mojo before starting any threads.
102 embedder::Init(make_scoped_ptr(new embedder::SimplePlatformSupport()));
104 // Create and start our I/O thread.
105 base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
106 CHECK(io_thread_.StartWithOptions(io_thread_options));
107 io_runner_ = io_thread_.task_runner().get();
108 CHECK(io_runner_.get());
110 // Create and start our controller thread.
111 base::Thread::Options controller_thread_options;
112 controller_thread_options.message_loop_type =
113 base::MessageLoop::TYPE_CUSTOM;
114 controller_thread_options.message_pump_factory =
115 base::Bind(&common::MessagePumpMojo::Create);
116 CHECK(controller_thread_.StartWithOptions(controller_thread_options));
117 controller_runner_ = controller_thread_.task_runner().get();
118 CHECK(controller_runner_.get());
120 // TODO(vtl): This should be SLAVE, not NONE.
121 embedder::InitIPCSupport(embedder::ProcessType::NONE, controller_runner_,
122 this, io_runner_,
123 embedder::ScopedPlatformHandle());
126 void Shutdown() {
127 Blocker blocker;
128 shutdown_unblocker_ = blocker.GetUnblocker();
129 controller_runner_->PostTask(
130 FROM_HERE, base::Bind(&AppContext::ShutdownOnControllerThread,
131 base::Unretained(this)));
132 blocker.Block();
135 base::SingleThreadTaskRunner* io_runner() const { return io_runner_.get(); }
137 base::SingleThreadTaskRunner* controller_runner() const {
138 return controller_runner_.get();
141 ChildControllerImpl* controller() const { return controller_.get(); }
143 void set_controller(scoped_ptr<ChildControllerImpl> controller) {
144 controller_ = controller.Pass();
147 private:
148 void ShutdownOnControllerThread() {
149 // First, destroy the controller.
150 controller_.reset();
152 // Next shutdown IPC. We'll unblock the main thread in OnShutdownComplete().
153 embedder::ShutdownIPCSupport();
156 // ProcessDelegate implementation.
157 void OnShutdownComplete() override {
158 shutdown_unblocker_.Unblock(base::Closure());
161 base::Thread io_thread_;
162 scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
164 base::Thread controller_thread_;
165 scoped_refptr<base::SingleThreadTaskRunner> controller_runner_;
167 // Accessed only on the controller thread.
168 scoped_ptr<ChildControllerImpl> controller_;
170 // Used to unblock the main thread on shutdown.
171 Blocker::Unblocker shutdown_unblocker_;
173 DISALLOW_COPY_AND_ASSIGN(AppContext);
176 // ChildControllerImpl ------------------------------------------------------
178 class ChildControllerImpl : public ChildController {
179 public:
180 ~ChildControllerImpl() override {
181 DCHECK(thread_checker_.CalledOnValidThread());
183 // TODO(vtl): Pass in the result from |MainMain()|.
184 on_app_complete_.Run(MOJO_RESULT_UNIMPLEMENTED);
187 // To be executed on the controller thread. Creates the |ChildController|,
188 // etc.
189 static void Init(AppContext* app_context,
190 base::NativeLibrary app_library,
191 embedder::ScopedPlatformHandle platform_channel,
192 const Blocker::Unblocker& unblocker) {
193 DCHECK(app_context);
194 DCHECK(platform_channel.is_valid());
196 DCHECK(!app_context->controller());
198 scoped_ptr<ChildControllerImpl> impl(
199 new ChildControllerImpl(app_context, app_library, unblocker));
201 ScopedMessagePipeHandle host_message_pipe(embedder::CreateChannel(
202 platform_channel.Pass(),
203 base::Bind(&ChildControllerImpl::DidCreateChannel,
204 base::Unretained(impl.get())),
205 base::ThreadTaskRunnerHandle::Get()));
207 impl->Bind(host_message_pipe.Pass());
209 app_context->set_controller(impl.Pass());
212 void Bind(ScopedMessagePipeHandle handle) { binding_.Bind(handle.Pass()); }
214 void OnConnectionError() {
215 // A connection error means the connection to the shell is lost. This is not
216 // recoverable.
217 LOG(ERROR) << "Connection error to the shell.";
218 _exit(1);
221 // |ChildController| methods:
222 void StartApp(InterfaceRequest<Application> application_request,
223 const StartAppCallback& on_app_complete) override {
224 DCHECK(thread_checker_.CalledOnValidThread());
226 on_app_complete_ = on_app_complete;
227 unblocker_.Unblock(base::Bind(&ChildControllerImpl::StartAppOnMainThread,
228 base::Unretained(app_library_),
229 base::Passed(&application_request)));
232 void ExitNow(int32_t exit_code) override {
233 DVLOG(2) << "ChildControllerImpl::ExitNow(" << exit_code << ")";
234 _exit(exit_code);
237 private:
238 ChildControllerImpl(AppContext* app_context,
239 base::NativeLibrary app_library,
240 const Blocker::Unblocker& unblocker)
241 : app_context_(app_context),
242 app_library_(app_library),
243 unblocker_(unblocker),
244 channel_info_(nullptr),
245 binding_(this) {
246 binding_.set_connection_error_handler([this]() { OnConnectionError(); });
249 // Callback for |embedder::CreateChannel()|.
250 void DidCreateChannel(embedder::ChannelInfo* channel_info) {
251 DVLOG(2) << "ChildControllerImpl::DidCreateChannel()";
252 DCHECK(thread_checker_.CalledOnValidThread());
253 channel_info_ = channel_info;
256 static void StartAppOnMainThread(
257 base::NativeLibrary app_library,
258 InterfaceRequest<Application> application_request) {
259 if (!RunNativeApplication(app_library, application_request.Pass())) {
260 LOG(ERROR) << "Failure to RunNativeApplication()";
264 base::ThreadChecker thread_checker_;
265 AppContext* const app_context_;
266 base::NativeLibrary app_library_;
267 Blocker::Unblocker unblocker_;
268 StartAppCallback on_app_complete_;
270 embedder::ChannelInfo* channel_info_;
271 Binding<ChildController> binding_;
273 DISALLOW_COPY_AND_ASSIGN(ChildControllerImpl);
276 } // namespace
278 int ChildProcessMain() {
279 DVLOG(2) << "ChildProcessMain()";
280 const base::CommandLine& command_line =
281 *base::CommandLine::ForCurrentProcess();
283 #if defined(OS_LINUX) && !defined(OS_ANDROID)
284 using sandbox::syscall_broker::BrokerFilePermission;
285 scoped_ptr<mandoline::LinuxSandbox> sandbox;
286 #endif
287 base::NativeLibrary app_library = 0;
288 if (command_line.HasSwitch(switches::kChildProcess)) {
289 // Load the application library before we engage the sandbox.
290 mojo::shell::NativeApplicationCleanup cleanup =
291 command_line.HasSwitch(switches::kDeleteAfterLoad)
292 ? mojo::shell::NativeApplicationCleanup::DELETE
293 : mojo::shell::NativeApplicationCleanup::DONT_DELETE;
294 app_library = mojo::runner::LoadNativeApplication(
295 command_line.GetSwitchValuePath(switches::kChildProcess), cleanup);
297 #if defined(OS_LINUX) && !defined(OS_ANDROID)
298 if (command_line.HasSwitch(switches::kEnableSandbox)) {
299 // Warm parts of base.
300 base::RandUint64();
301 base::SysInfo::AmountOfPhysicalMemory();
302 base::SysInfo::MaxSharedMemorySize();
303 base::SysInfo::NumberOfProcessors();
305 // Do whatever warming that the mojo application wants.
306 typedef void (*SandboxWarmFunction)();
307 SandboxWarmFunction sandbox_warm = reinterpret_cast<SandboxWarmFunction>(
308 base::GetFunctionPointerFromNativeLibrary(app_library,
309 "MojoSandboxWarm"));
310 if (sandbox_warm)
311 sandbox_warm();
313 std::vector<BrokerFilePermission> permissions;
314 sandbox.reset(new mandoline::LinuxSandbox(permissions));
315 sandbox->Warmup();
316 sandbox->EngageNamespaceSandbox();
317 sandbox->EngageSeccompSandbox();
318 sandbox->Seal();
320 #endif
323 embedder::ScopedPlatformHandle platform_channel =
324 embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
325 command_line);
326 CHECK(platform_channel.is_valid());
328 DCHECK(!base::MessageLoop::current());
330 AppContext app_context;
331 app_context.Init();
333 Blocker blocker;
334 app_context.controller_runner()->PostTask(
335 FROM_HERE,
336 base::Bind(&ChildControllerImpl::Init, base::Unretained(&app_context),
337 base::Unretained(app_library), base::Passed(&platform_channel),
338 blocker.GetUnblocker()));
339 // This will block, then run whatever the controller wants.
340 blocker.Block();
342 app_context.Shutdown();
344 return 0;
347 } // namespace runner
348 } // namespace mojo