cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / ppapi / native_client / src / trusted / plugin / service_runtime.cc
blobfd845a50277fad7a569e2625f546405a28c90220
1 /*
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
7 #define NACL_LOG_MODULE_NAME "Plugin::ServiceRuntime"
9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
11 #include <string.h>
12 #include <set>
13 #include <string>
14 #include <utility>
16 #include "base/compiler_specific.h"
18 #include "native_client/src/include/checked_cast.h"
19 #include "native_client/src/include/portability_io.h"
20 #include "native_client/src/include/portability_string.h"
21 #include "native_client/src/include/nacl_macros.h"
22 #include "native_client/src/include/nacl_scoped_ptr.h"
23 #include "native_client/src/include/nacl_string.h"
24 #include "native_client/src/shared/platform/nacl_check.h"
25 #include "native_client/src/shared/platform/nacl_log.h"
26 #include "native_client/src/shared/platform/nacl_sync.h"
27 #include "native_client/src/shared/platform/nacl_sync_checked.h"
28 #include "native_client/src/shared/platform/nacl_sync_raii.h"
29 #include "native_client/src/shared/platform/scoped_ptr_refcount.h"
30 #include "native_client/src/trusted/desc/nacl_desc_imc.h"
31 // remove when we no longer need to cast the DescWrapper below.
32 #include "native_client/src/trusted/desc/nacl_desc_io.h"
33 #include "native_client/src/trusted/desc/nrd_xfer.h"
34 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h"
36 // This is here due to a Windows API collision; plugin.h through
37 // file_downloader.h transitively includes Instance.h which defines a
38 // PostMessage method, so this undef must appear before any of those.
39 #ifdef PostMessage
40 #undef PostMessage
41 #endif
42 #include "native_client/src/public/imc_types.h"
43 #include "native_client/src/trusted/service_runtime/nacl_error_code.h"
44 #include "native_client/src/trusted/validator/nacl_file_info.h"
46 #include "ppapi/c/pp_errors.h"
47 #include "ppapi/c/trusted/ppb_file_io_trusted.h"
48 #include "ppapi/cpp/core.h"
49 #include "ppapi/cpp/completion_callback.h"
50 #include "ppapi/cpp/file_io.h"
52 #include "ppapi/native_client/src/trusted/plugin/manifest.h"
53 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
54 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
55 #include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
56 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h"
57 #include "ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h"
58 #include "ppapi/native_client/src/trusted/plugin/srpc_client.h"
59 #include "ppapi/native_client/src/trusted/weak_ref/call_on_main_thread.h"
61 namespace {
63 // For doing crude quota enforcement on writes to temp files.
64 // We do not allow a temp file bigger than 512 MB for now.
65 const uint64_t kMaxTempQuota = 0x20000000;
67 } // namespace
69 namespace plugin {
71 PluginReverseInterface::PluginReverseInterface(
72 nacl::WeakRefAnchor* anchor,
73 Plugin* plugin,
74 const Manifest* manifest,
75 ServiceRuntime* service_runtime,
76 pp::CompletionCallback init_done_cb,
77 pp::CompletionCallback crash_cb)
78 : anchor_(anchor),
79 plugin_(plugin),
80 manifest_(manifest),
81 service_runtime_(service_runtime),
82 shutting_down_(false),
83 init_done_cb_(init_done_cb),
84 crash_cb_(crash_cb) {
85 NaClXMutexCtor(&mu_);
86 NaClXCondVarCtor(&cv_);
89 PluginReverseInterface::~PluginReverseInterface() {
90 NaClCondVarDtor(&cv_);
91 NaClMutexDtor(&mu_);
94 void PluginReverseInterface::ShutDown() {
95 NaClLog(4, "PluginReverseInterface::Shutdown: entered\n");
96 nacl::MutexLocker take(&mu_);
97 shutting_down_ = true;
98 NaClXCondVarBroadcast(&cv_);
99 NaClLog(4, "PluginReverseInterface::Shutdown: broadcasted, exiting\n");
102 void PluginReverseInterface::Log(nacl::string message) {
103 LogToJavaScriptConsoleResource* continuation =
104 new LogToJavaScriptConsoleResource(message);
105 CHECK(continuation != NULL);
106 NaClLog(4, "PluginReverseInterface::Log(%s)\n", message.c_str());
107 plugin::WeakRefCallOnMainThread(
108 anchor_,
109 0, /* delay in ms */
110 this,
111 &plugin::PluginReverseInterface::Log_MainThreadContinuation,
112 continuation);
115 void PluginReverseInterface::DoPostMessage(nacl::string message) {
116 PostMessageResource* continuation = new PostMessageResource(message);
117 CHECK(continuation != NULL);
118 NaClLog(4, "PluginReverseInterface::DoPostMessage(%s)\n", message.c_str());
119 plugin::WeakRefCallOnMainThread(
120 anchor_,
121 0, /* delay in ms */
122 this,
123 &plugin::PluginReverseInterface::PostMessage_MainThreadContinuation,
124 continuation);
127 void PluginReverseInterface::StartupInitializationComplete() {
128 NaClLog(4, "PluginReverseInterface::StartupInitializationComplete\n");
129 if (init_done_cb_.pp_completion_callback().func != NULL) {
130 NaClLog(4,
131 "PluginReverseInterface::StartupInitializationComplete:"
132 " invoking CB\n");
133 pp::Module::Get()->core()->CallOnMainThread(0, init_done_cb_, PP_OK);
134 } else {
135 NaClLog(1,
136 "PluginReverseInterface::StartupInitializationComplete:"
137 " init_done_cb_ not valid, skipping.\n");
141 void PluginReverseInterface::Log_MainThreadContinuation(
142 LogToJavaScriptConsoleResource* p,
143 int32_t err) {
144 UNREFERENCED_PARAMETER(err);
145 NaClLog(4,
146 "PluginReverseInterface::Log_MainThreadContinuation(%s)\n",
147 p->message.c_str());
148 plugin_->AddToConsole(p->message);
150 void PluginReverseInterface::PostMessage_MainThreadContinuation(
151 PostMessageResource* p,
152 int32_t err) {
153 UNREFERENCED_PARAMETER(err);
154 NaClLog(4,
155 "PluginReverseInterface::PostMessage_MainThreadContinuation(%s)\n",
156 p->message.c_str());
157 plugin_->PostMessage(std::string("DEBUG_POSTMESSAGE:") + p->message);
160 bool PluginReverseInterface::EnumerateManifestKeys(
161 std::set<nacl::string>* out_keys) {
162 Manifest const* mp = manifest_;
164 if (!mp->GetFileKeys(out_keys)) {
165 return false;
168 return true;
171 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey
172 // and invoke StreamAsFile with a completion callback that invokes
173 // GetPOSIXFileDesc.
174 bool PluginReverseInterface::OpenManifestEntry(nacl::string url_key,
175 struct NaClFileInfo* info) {
176 ErrorInfo error_info;
177 bool op_complete = false; // NB: mu_ and cv_ also controls access to this!
178 // The to_open object is owned by the weak ref callback. Because this function
179 // waits for the callback to finish, the to_open object will be deallocated on
180 // the main thread before this function can return. The pointers it contains
181 // to stack variables will not leak.
182 OpenManifestEntryResource* to_open =
183 new OpenManifestEntryResource(url_key, info,
184 &error_info, &op_complete);
185 CHECK(to_open != NULL);
186 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n",
187 url_key.c_str());
188 // This assumes we are not on the main thread. If false, we deadlock.
189 plugin::WeakRefCallOnMainThread(
190 anchor_,
192 this,
193 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation,
194 to_open);
195 NaClLog(4,
196 "PluginReverseInterface::OpenManifestEntry:"
197 " waiting on main thread\n");
198 bool shutting_down;
199 do {
200 nacl::MutexLocker take(&mu_);
201 for (;;) {
202 NaClLog(4,
203 "PluginReverseInterface::OpenManifestEntry:"
204 " got lock, checking shutdown and completion: (%s, %s)\n",
205 shutting_down_ ? "yes" : "no",
206 op_complete ? "yes" : "no");
207 shutting_down = shutting_down_;
208 if (op_complete || shutting_down) {
209 NaClLog(4,
210 "PluginReverseInterface::OpenManifestEntry:"
211 " done!\n");
212 break;
214 NaClXCondVarWait(&cv_, &mu_);
216 } while (0);
217 if (shutting_down) {
218 NaClLog(4,
219 "PluginReverseInterface::OpenManifestEntry:"
220 " plugin is shutting down\n");
221 return false;
223 // out_desc has the returned descriptor if successful, else -1.
225 // The caller is responsible for not closing *out_desc. If it is
226 // closed prematurely, then another open could re-use the OS
227 // descriptor, confusing the opened_ map. If the caller is going to
228 // want to make a NaClDesc object and transfer it etc., then the
229 // caller should DUP the descriptor (but remember the original
230 // value) for use by the NaClDesc object, which closes when the
231 // object is destroyed.
232 NaClLog(4,
233 "PluginReverseInterface::OpenManifestEntry:"
234 " *out_desc = %d\n",
235 info->desc);
236 if (info->desc == -1) {
237 // TODO(bsy,ncbray): what else should we do with the error? This
238 // is a runtime error that may simply be a programming error in
239 // the untrusted code, or it may be something else wrong w/ the
240 // manifest.
241 NaClLog(4,
242 "OpenManifestEntry: failed for key %s, code %d (%s)\n",
243 url_key.c_str(),
244 error_info.error_code(),
245 error_info.message().c_str());
247 return true;
250 // Transfer point from OpenManifestEntry() which runs on the main thread
251 // (Some PPAPI actions -- like StreamAsFile -- can only run on the main thread).
252 // OpenManifestEntry() is waiting on a condvar for this continuation to
253 // complete. We Broadcast and awaken OpenManifestEntry() whenever we are done
254 // either here, or in a later MainThreadContinuation step, if there are
255 // multiple steps.
256 void PluginReverseInterface::OpenManifestEntry_MainThreadContinuation(
257 OpenManifestEntryResource* p,
258 int32_t err) {
259 OpenManifestEntryResource *open_cont;
260 UNREFERENCED_PARAMETER(err);
261 // CallOnMainThread continuations always called with err == PP_OK.
263 NaClLog(4, "Entered OpenManifestEntry_MainThreadContinuation\n");
265 std::string mapped_url;
266 PnaclOptions pnacl_options;
267 if (!manifest_->ResolveKey(p->url, &mapped_url,
268 &pnacl_options, p->error_info)) {
269 NaClLog(4, "OpenManifestEntry_MainThreadContinuation: ResolveKey failed\n");
270 // Failed, and error_info has the details on what happened. Wake
271 // up requesting thread -- we are done.
272 nacl::MutexLocker take(&mu_);
273 *p->op_complete_ptr = true; // done...
274 p->file_info->desc = -1; // but failed.
275 NaClXCondVarBroadcast(&cv_);
276 return;
278 NaClLog(4,
279 "OpenManifestEntry_MainThreadContinuation: "
280 "ResolveKey: %s -> %s (pnacl_translate(%d))\n",
281 p->url.c_str(), mapped_url.c_str(), pnacl_options.translate());
283 open_cont = new OpenManifestEntryResource(*p); // copy ctor!
284 CHECK(open_cont != NULL);
285 open_cont->url = mapped_url;
286 if (!pnacl_options.translate()) {
287 pp::CompletionCallback stream_cc = WeakRefNewCallback(
288 anchor_,
289 this,
290 &PluginReverseInterface::StreamAsFile_MainThreadContinuation,
291 open_cont);
292 // Normal files.
293 if (!PnaclUrls::IsPnaclComponent(mapped_url)) {
294 if (!plugin_->StreamAsFile(mapped_url,
295 stream_cc.pp_completion_callback())) {
296 NaClLog(4,
297 "OpenManifestEntry_MainThreadContinuation: "
298 "StreamAsFile failed\n");
299 nacl::MutexLocker take(&mu_);
300 *p->op_complete_ptr = true; // done...
301 p->file_info->desc = -1; // but failed.
302 p->error_info->SetReport(ERROR_MANIFEST_OPEN,
303 "ServiceRuntime: StreamAsFile failed");
304 NaClXCondVarBroadcast(&cv_);
305 return;
307 NaClLog(4,
308 "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n");
309 } else {
310 // Special PNaCl support files, that are installed on the
311 // user machine.
312 int32_t fd = PnaclResources::GetPnaclFD(
313 plugin_,
314 PnaclUrls::PnaclComponentURLToFilename(mapped_url).c_str());
315 if (fd < 0) {
316 // We should check earlier if the pnacl component wasn't installed
317 // yet. At this point, we can't do much anymore, so just continue
318 // with an invalid fd.
319 NaClLog(4,
320 "OpenManifestEntry_MainThreadContinuation: "
321 "GetReadonlyPnaclFd failed\n");
322 // TODO(jvoung): Separate the error codes?
323 p->error_info->SetReport(ERROR_MANIFEST_OPEN,
324 "ServiceRuntime: GetPnaclFd failed");
326 nacl::MutexLocker take(&mu_);
327 *p->op_complete_ptr = true; // done!
328 // TODO(ncbray): enable the fast loading and validation paths for this
329 // type of file.
330 p->file_info->desc = fd;
331 NaClXCondVarBroadcast(&cv_);
332 NaClLog(4,
333 "OpenManifestEntry_MainThreadContinuation: GetPnaclFd okay\n");
335 } else {
336 // Requires PNaCl translation.
337 NaClLog(4,
338 "OpenManifestEntry_MainThreadContinuation: "
339 "pulling down and translating.\n");
340 CHECK(plugin_->nacl_interface()->IsPnaclEnabled());
341 pp::CompletionCallback translate_callback =
342 WeakRefNewCallback(
343 anchor_,
344 this,
345 &PluginReverseInterface::BitcodeTranslate_MainThreadContinuation,
346 open_cont);
347 // Will always call the callback on success or failure.
348 pnacl_coordinator_.reset(
349 PnaclCoordinator::BitcodeToNative(plugin_,
350 mapped_url,
351 pnacl_options,
352 translate_callback));
354 // p is deleted automatically
357 void PluginReverseInterface::StreamAsFile_MainThreadContinuation(
358 OpenManifestEntryResource* p,
359 int32_t result) {
360 NaClLog(4,
361 "Entered StreamAsFile_MainThreadContinuation\n");
363 nacl::MutexLocker take(&mu_);
364 if (result == PP_OK) {
365 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetFileInfo(%s)\n",
366 p->url.c_str());
367 *p->file_info = plugin_->GetFileInfo(p->url);
369 NaClLog(4,
370 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n",
371 p->file_info->desc);
372 } else {
373 NaClLog(4,
374 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n");
375 p->file_info->desc = -1;
376 p->error_info->SetReport(ERROR_MANIFEST_OPEN,
377 "Plugin StreamAsFile failed at callback");
379 *p->op_complete_ptr = true;
380 NaClXCondVarBroadcast(&cv_);
384 void PluginReverseInterface::BitcodeTranslate_MainThreadContinuation(
385 OpenManifestEntryResource* p,
386 int32_t result) {
387 NaClLog(4,
388 "Entered BitcodeTranslate_MainThreadContinuation\n");
390 nacl::MutexLocker take(&mu_);
391 if (result == PP_OK) {
392 // TODO(jvoung): clean this up. We are assuming that the NaClDesc is
393 // a host IO desc and doing a downcast. Once the ReverseInterface
394 // accepts NaClDescs we can avoid this downcast.
395 NaClDesc* desc = pnacl_coordinator_->ReleaseTranslatedFD()->desc();
396 struct NaClDescIoDesc* ndiodp = (struct NaClDescIoDesc*)desc;
397 p->file_info->desc = ndiodp->hd->d;
398 pnacl_coordinator_.reset(NULL);
399 NaClLog(4,
400 "BitcodeTranslate_MainThreadContinuation: PP_OK, desc %d\n",
401 p->file_info->desc);
402 } else {
403 NaClLog(4,
404 "BitcodeTranslate_MainThreadContinuation: !PP_OK, "
405 "setting desc -1\n");
406 p->file_info->desc = -1;
407 // Error should have been reported by pnacl coordinator.
408 NaClLog(LOG_ERROR, "PluginReverseInterface::BitcodeTranslate error.\n");
410 *p->op_complete_ptr = true;
411 NaClXCondVarBroadcast(&cv_);
415 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) {
416 bool op_complete = false;
417 bool op_result;
418 CloseManifestEntryResource* to_close =
419 new CloseManifestEntryResource(desc, &op_complete, &op_result);
421 bool shutting_down;
422 plugin::WeakRefCallOnMainThread(
423 anchor_,
425 this,
426 &plugin::PluginReverseInterface::
427 CloseManifestEntry_MainThreadContinuation,
428 to_close);
429 // wait for completion or surf-away.
430 do {
431 nacl::MutexLocker take(&mu_);
432 for (;;) {
433 shutting_down = shutting_down_;
434 if (op_complete || shutting_down) {
435 break;
437 NaClXCondVarWait(&cv_, &mu_);
439 } while (0);
441 if (shutting_down) return false;
442 // op_result true if close was successful; false otherwise (e.g., bad desc).
443 return op_result;
446 void PluginReverseInterface::CloseManifestEntry_MainThreadContinuation(
447 CloseManifestEntryResource* cls,
448 int32_t err) {
449 UNREFERENCED_PARAMETER(err);
451 nacl::MutexLocker take(&mu_);
452 // TODO(bsy): once the plugin has a reliable way to report that the
453 // file usage is done -- and sel_ldr uses this RPC call -- we should
454 // tell the plugin that the associated resources can be freed.
455 *cls->op_result_ptr = true;
456 *cls->op_complete_ptr = true;
457 NaClXCondVarBroadcast(&cv_);
458 // cls automatically deleted
461 void PluginReverseInterface::ReportCrash() {
462 NaClLog(4, "PluginReverseInterface::ReportCrash\n");
464 if (crash_cb_.pp_completion_callback().func != NULL) {
465 NaClLog(4, "PluginReverseInterface::ReportCrash: invoking CB\n");
466 pp::Module::Get()->core()->CallOnMainThread(0, crash_cb_, PP_OK);
467 } else {
468 NaClLog(1,
469 "PluginReverseInterface::ReportCrash:"
470 " crash_cb_ not valid, skipping\n");
474 void PluginReverseInterface::ReportExitStatus(int exit_status) {
475 service_runtime_->set_exit_status(exit_status);
478 void PluginReverseInterface::QuotaRequest_MainThreadContinuation(
479 QuotaRequest* request,
480 int32_t err) {
481 if (err != PP_OK) {
482 return;
485 switch (request->data.type) {
486 case plugin::PepperQuotaType: {
487 const PPB_FileIOTrusted* file_io_trusted =
488 static_cast<const PPB_FileIOTrusted*>(
489 pp::Module::Get()->GetBrowserInterface(
490 PPB_FILEIOTRUSTED_INTERFACE));
491 // Copy the request object because this one will be deleted on return.
492 // copy ctor!
493 QuotaRequest* cont_for_response = new QuotaRequest(*request);
494 pp::CompletionCallback quota_cc = WeakRefNewCallback(
495 anchor_,
496 this,
497 &PluginReverseInterface::QuotaRequest_MainThreadResponse,
498 cont_for_response);
499 file_io_trusted->WillWrite(request->data.resource,
500 request->offset,
501 // TODO(sehr): remove need for cast.
502 // Unify WillWrite interface vs Quota request.
503 nacl::assert_cast<int32_t>(
504 request->bytes_requested),
505 quota_cc.pp_completion_callback());
506 break;
508 case plugin::TempQuotaType: {
509 uint64_t len = request->offset + request->bytes_requested;
510 nacl::MutexLocker take(&mu_);
511 // Do some crude quota enforcement.
512 if (len > kMaxTempQuota) {
513 *request->bytes_granted = 0;
514 } else {
515 *request->bytes_granted = request->bytes_requested;
517 *request->op_complete_ptr = true;
518 NaClXCondVarBroadcast(&cv_);
519 break;
522 // request automatically deleted
525 void PluginReverseInterface::QuotaRequest_MainThreadResponse(
526 QuotaRequest* request,
527 int32_t err) {
528 NaClLog(4,
529 "PluginReverseInterface::QuotaRequest_MainThreadResponse:"
530 " (resource=%" NACL_PRIx32 ", offset=%" NACL_PRId64 ", requested=%"
531 NACL_PRId64 ", err=%" NACL_PRId32 ")\n",
532 request->data.resource,
533 request->offset, request->bytes_requested, err);
534 nacl::MutexLocker take(&mu_);
535 if (err >= PP_OK) {
536 *request->bytes_granted = err;
537 } else {
538 *request->bytes_granted = 0;
540 *request->op_complete_ptr = true;
541 NaClXCondVarBroadcast(&cv_);
542 // request automatically deleted
545 int64_t PluginReverseInterface::RequestQuotaForWrite(
546 nacl::string file_id, int64_t offset, int64_t bytes_to_write) {
547 NaClLog(4,
548 "PluginReverseInterface::RequestQuotaForWrite:"
549 " (file_id='%s', offset=%" NACL_PRId64 ", bytes_to_write=%"
550 NACL_PRId64 ")\n", file_id.c_str(), offset, bytes_to_write);
551 QuotaData quota_data;
553 nacl::MutexLocker take(&mu_);
554 uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10);
555 if (quota_map_.find(file_key) == quota_map_.end()) {
556 // Look up failed to find the requested quota managed resource.
557 NaClLog(4, "PluginReverseInterface::RequestQuotaForWrite: failed...\n");
558 return 0;
560 quota_data = quota_map_[file_key];
562 // Variables set by requesting quota.
563 int64_t quota_granted = 0;
564 bool op_complete = false;
565 QuotaRequest* continuation =
566 new QuotaRequest(quota_data, offset, bytes_to_write, &quota_granted,
567 &op_complete);
568 // The reverse service is running on a background thread and the PPAPI quota
569 // methods must be invoked only from the main thread.
570 plugin::WeakRefCallOnMainThread(
571 anchor_,
572 0, /* delay in ms */
573 this,
574 &plugin::PluginReverseInterface::QuotaRequest_MainThreadContinuation,
575 continuation);
576 // Wait for the main thread to request quota and signal completion.
577 // It is also possible that the main thread will signal shut down.
578 bool shutting_down;
579 do {
580 nacl::MutexLocker take(&mu_);
581 for (;;) {
582 shutting_down = shutting_down_;
583 if (op_complete || shutting_down) {
584 break;
586 NaClXCondVarWait(&cv_, &mu_);
588 } while (0);
589 if (shutting_down) return 0;
590 return quota_granted;
593 void PluginReverseInterface::AddQuotaManagedFile(const nacl::string& file_id,
594 const pp::FileIO& file_io) {
595 PP_Resource resource = file_io.pp_resource();
596 NaClLog(4,
597 "PluginReverseInterface::AddQuotaManagedFile: "
598 "(file_id='%s', file_io_ref=%" NACL_PRIx32 ")\n",
599 file_id.c_str(), resource);
600 nacl::MutexLocker take(&mu_);
601 uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10);
602 QuotaData data(plugin::PepperQuotaType, resource);
603 quota_map_[file_key] = data;
606 void PluginReverseInterface::AddTempQuotaManagedFile(
607 const nacl::string& file_id) {
608 NaClLog(4, "PluginReverseInterface::AddTempQuotaManagedFile: "
609 "(file_id='%s')\n", file_id.c_str());
610 nacl::MutexLocker take(&mu_);
611 uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10);
612 QuotaData data(plugin::TempQuotaType, 0);
613 quota_map_[file_key] = data;
616 ServiceRuntime::ServiceRuntime(Plugin* plugin,
617 const Manifest* manifest,
618 bool should_report_uma,
619 pp::CompletionCallback init_done_cb,
620 pp::CompletionCallback crash_cb)
621 : plugin_(plugin),
622 should_report_uma_(should_report_uma),
623 reverse_service_(NULL),
624 anchor_(new nacl::WeakRefAnchor()),
625 rev_interface_(new PluginReverseInterface(anchor_, plugin,
626 manifest,
627 this,
628 init_done_cb, crash_cb)),
629 exit_status_(-1),
630 start_sel_ldr_done_(false) {
631 NaClSrpcChannelInitialize(&command_channel_);
632 NaClXMutexCtor(&mu_);
633 NaClXCondVarCtor(&cond_);
636 bool ServiceRuntime::InitCommunication(nacl::DescWrapper* nacl_desc,
637 ErrorInfo* error_info) {
638 NaClLog(4, "ServiceRuntime::InitCommunication"
639 " (this=%p, subprocess=%p)\n",
640 static_cast<void*>(this),
641 static_cast<void*>(subprocess_.get()));
642 // Create the command channel to the sel_ldr and load the nexe from nacl_desc.
643 if (!subprocess_->SetupCommandAndLoad(&command_channel_, nacl_desc)) {
644 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL,
645 "ServiceRuntime: command channel creation failed");
646 return false;
648 // Hook up the reverse service channel. We are the IMC client, but
649 // provide SRPC service.
650 NaClDesc* out_conn_cap;
651 NaClSrpcResultCodes rpc_result =
652 NaClSrpcInvokeBySignature(&command_channel_,
653 "reverse_setup::h",
654 &out_conn_cap);
656 if (NACL_SRPC_RESULT_OK != rpc_result) {
657 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SETUP,
658 "ServiceRuntime: reverse setup rpc failed");
659 return false;
661 // Get connection capability to service runtime where the IMC
662 // server/SRPC client is waiting for a rendezvous.
663 NaClLog(4, "ServiceRuntime: got 0x%" NACL_PRIxPTR "\n",
664 (uintptr_t) out_conn_cap);
665 nacl::DescWrapper* conn_cap = plugin_->wrapper_factory()->MakeGenericCleanup(
666 out_conn_cap);
667 if (conn_cap == NULL) {
668 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_WRAPPER,
669 "ServiceRuntime: wrapper allocation failure");
670 return false;
672 out_conn_cap = NULL; // ownership passed
673 NaClLog(4, "ServiceRuntime::InitCommunication: starting reverse service\n");
674 reverse_service_ = new nacl::ReverseService(conn_cap, rev_interface_->Ref());
675 if (!reverse_service_->Start()) {
676 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SERVICE,
677 "ServiceRuntime: starting reverse services failed");
678 return false;
681 // start the module. otherwise we cannot connect for multimedia
682 // subsystem since that is handled by user-level code (not secure!)
683 // in libsrpc.
684 int load_status = -1;
685 rpc_result =
686 NaClSrpcInvokeBySignature(&command_channel_,
687 "start_module::i",
688 &load_status);
690 if (NACL_SRPC_RESULT_OK != rpc_result) {
691 error_info->SetReport(ERROR_SEL_LDR_START_MODULE,
692 "ServiceRuntime: could not start nacl module");
693 return false;
695 NaClLog(4, "ServiceRuntime::InitCommunication (load_status=%d)\n",
696 load_status);
697 if (should_report_uma_) {
698 plugin_->ReportSelLdrLoadStatus(load_status);
700 if (LOAD_OK != load_status) {
701 error_info->SetReport(
702 ERROR_SEL_LDR_START_STATUS,
703 NaClErrorString(static_cast<NaClErrorCode>(load_status)));
704 return false;
706 return true;
709 bool ServiceRuntime::StartSelLdr(const SelLdrStartParams& params) {
710 NaClLog(4, "ServiceRuntime::Start\n");
712 nacl::scoped_ptr<SelLdrLauncherChrome>
713 tmp_subprocess(new SelLdrLauncherChrome());
714 if (NULL == tmp_subprocess.get()) {
715 NaClLog(LOG_ERROR, "ServiceRuntime::Start (subprocess create failed)\n");
716 params.error_info->SetReport(
717 ERROR_SEL_LDR_CREATE_LAUNCHER,
718 "ServiceRuntime: failed to create sel_ldr launcher");
719 return false;
721 nacl::string error_message;
722 bool started = tmp_subprocess->Start(plugin_->pp_instance(),
723 params.url.c_str(),
724 params.uses_irt,
725 params.uses_ppapi,
726 params.enable_dev_interfaces,
727 params.enable_dyncode_syscalls,
728 params.enable_exception_handling,
729 params.enable_crash_throttling,
730 &error_message);
731 if (!started) {
732 NaClLog(LOG_ERROR, "ServiceRuntime::Start (start failed)\n");
733 params.error_info->SetReportWithConsoleOnlyError(
734 ERROR_SEL_LDR_LAUNCH,
735 "ServiceRuntime: failed to start",
736 error_message);
737 return false;
740 subprocess_.reset(tmp_subprocess.release());
741 NaClLog(4, "ServiceRuntime::StartSelLdr (return 1)\n");
742 return true;
745 void ServiceRuntime::WaitForSelLdrStart() {
746 nacl::MutexLocker take(&mu_);
747 while(!start_sel_ldr_done_) {
748 NaClXCondVarWait(&cond_, &mu_);
752 void ServiceRuntime::SignalStartSelLdrDone() {
753 nacl::MutexLocker take(&mu_);
754 start_sel_ldr_done_ = true;
755 NaClXCondVarSignal(&cond_);
758 bool ServiceRuntime::LoadNexeAndStart(nacl::DescWrapper* nacl_desc,
759 ErrorInfo* error_info,
760 const pp::CompletionCallback& crash_cb) {
761 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (nacl_desc=%p)\n",
762 reinterpret_cast<void*>(nacl_desc));
763 if (!InitCommunication(nacl_desc, error_info)) {
764 // On a load failure the service runtime does not crash itself to
765 // avoid a race where the no-more-senders error on the reverse
766 // channel esrvice thread might cause the crash-detection logic to
767 // kick in before the start_module RPC reply has been received. So
768 // we induce a service runtime crash here. We do not release
769 // subprocess_ since it's needed to collect crash log output after
770 // the error is reported.
771 Log(LOG_FATAL, "reap logs");
772 if (NULL == reverse_service_) {
773 // No crash detector thread.
774 NaClLog(LOG_ERROR, "scheduling to get crash log\n");
775 pp::Module::Get()->core()->CallOnMainThread(0, crash_cb, PP_OK);
776 NaClLog(LOG_ERROR, "should fire soon\n");
777 } else {
778 NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n");
780 return false;
783 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (return 1)\n");
784 return true;
787 SrpcClient* ServiceRuntime::SetupAppChannel() {
788 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n",
789 reinterpret_cast<void*>(subprocess_.get()));
790 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect();
791 if (NULL == connect_desc) {
792 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n");
793 return NULL;
794 } else {
795 NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n",
796 static_cast<void*>(connect_desc));
797 SrpcClient* srpc_client = SrpcClient::New(connect_desc);
798 NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n",
799 static_cast<void*>(srpc_client));
800 delete connect_desc;
801 return srpc_client;
805 bool ServiceRuntime::Log(int severity, const nacl::string& msg) {
806 NaClSrpcResultCodes rpc_result =
807 NaClSrpcInvokeBySignature(&command_channel_,
808 "log:is:",
809 severity,
810 strdup(msg.c_str()));
811 return (NACL_SRPC_RESULT_OK == rpc_result);
814 void ServiceRuntime::Shutdown() {
815 rev_interface_->ShutDown();
816 anchor_->Abandon();
817 // Abandon callbacks, tell service threads to quit if they were
818 // blocked waiting for main thread operations to finish. Note that
819 // some callbacks must still await their completion event, e.g.,
820 // CallOnMainThread must still wait for the time out, or I/O events
821 // must finish, so resources associated with pending events cannot
822 // be deallocated.
824 // Note that this does waitpid() to get rid of any zombie subprocess.
825 subprocess_.reset(NULL);
827 NaClSrpcDtor(&command_channel_);
829 // subprocess_ has been shut down, but threads waiting on messages
830 // from the service runtime may not have noticed yet. The low-level
831 // NaClSimpleRevService code takes care to refcount the data objects
832 // that it needs, and reverse_service_ is also refcounted. We wait
833 // for the service threads to get their EOF indications.
834 if (reverse_service_ != NULL) {
835 reverse_service_->WaitForServiceThreadsToExit();
836 reverse_service_->Unref();
837 reverse_service_ = NULL;
841 ServiceRuntime::~ServiceRuntime() {
842 NaClLog(4, "ServiceRuntime::~ServiceRuntime (this=%p)\n",
843 static_cast<void*>(this));
844 // We do this just in case Shutdown() was not called.
845 subprocess_.reset(NULL);
846 if (reverse_service_ != NULL) {
847 reverse_service_->Unref();
850 rev_interface_->Unref();
852 anchor_->Unref();
853 NaClCondVarDtor(&cond_);
854 NaClMutexDtor(&mu_);
857 int ServiceRuntime::exit_status() {
858 nacl::MutexLocker take(&mu_);
859 return exit_status_;
862 void ServiceRuntime::set_exit_status(int exit_status) {
863 nacl::MutexLocker take(&mu_);
864 exit_status_ = exit_status & 0xff;
867 nacl::string ServiceRuntime::GetCrashLogOutput() {
868 if (NULL != subprocess_.get()) {
869 return subprocess_->GetCrashLogOutput();
870 } else {
871 return std::string();
875 } // namespace plugin