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.
7 #define NACL_LOG_MODULE_NAME "Plugin::ServiceRuntime"
9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
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.
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"
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;
71 PluginReverseInterface::PluginReverseInterface(
72 nacl::WeakRefAnchor
* anchor
,
74 const Manifest
* manifest
,
75 ServiceRuntime
* service_runtime
,
76 pp::CompletionCallback init_done_cb
,
77 pp::CompletionCallback crash_cb
)
81 service_runtime_(service_runtime
),
82 shutting_down_(false),
83 init_done_cb_(init_done_cb
),
86 NaClXCondVarCtor(&cv_
);
89 PluginReverseInterface::~PluginReverseInterface() {
90 NaClCondVarDtor(&cv_
);
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(
111 &plugin::PluginReverseInterface::Log_MainThreadContinuation
,
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(
123 &plugin::PluginReverseInterface::PostMessage_MainThreadContinuation
,
127 void PluginReverseInterface::StartupInitializationComplete() {
128 NaClLog(4, "PluginReverseInterface::StartupInitializationComplete\n");
129 if (init_done_cb_
.pp_completion_callback().func
!= NULL
) {
131 "PluginReverseInterface::StartupInitializationComplete:"
133 pp::Module::Get()->core()->CallOnMainThread(0, init_done_cb_
, PP_OK
);
136 "PluginReverseInterface::StartupInitializationComplete:"
137 " init_done_cb_ not valid, skipping.\n");
141 void PluginReverseInterface::Log_MainThreadContinuation(
142 LogToJavaScriptConsoleResource
* p
,
144 UNREFERENCED_PARAMETER(err
);
146 "PluginReverseInterface::Log_MainThreadContinuation(%s)\n",
148 plugin_
->AddToConsole(p
->message
);
150 void PluginReverseInterface::PostMessage_MainThreadContinuation(
151 PostMessageResource
* p
,
153 UNREFERENCED_PARAMETER(err
);
155 "PluginReverseInterface::PostMessage_MainThreadContinuation(%s)\n",
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
)) {
171 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey
172 // and invoke StreamAsFile with a completion callback that invokes
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",
188 // This assumes we are not on the main thread. If false, we deadlock.
189 plugin::WeakRefCallOnMainThread(
193 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation
,
196 "PluginReverseInterface::OpenManifestEntry:"
197 " waiting on main thread\n");
200 nacl::MutexLocker
take(&mu_
);
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
) {
210 "PluginReverseInterface::OpenManifestEntry:"
214 NaClXCondVarWait(&cv_
, &mu_
);
219 "PluginReverseInterface::OpenManifestEntry:"
220 " plugin is shutting down\n");
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.
233 "PluginReverseInterface::OpenManifestEntry:"
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
242 "OpenManifestEntry: failed for key %s, code %d (%s)\n",
244 error_info
.error_code(),
245 error_info
.message().c_str());
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
256 void PluginReverseInterface::OpenManifestEntry_MainThreadContinuation(
257 OpenManifestEntryResource
* p
,
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_
);
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(
290 &PluginReverseInterface::StreamAsFile_MainThreadContinuation
,
293 if (!PnaclUrls::IsPnaclComponent(mapped_url
)) {
294 if (!plugin_
->StreamAsFile(mapped_url
,
295 stream_cc
.pp_completion_callback())) {
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_
);
308 "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n");
310 // Special PNaCl support files, that are installed on the
312 int32_t fd
= PnaclResources::GetPnaclFD(
314 PnaclUrls::PnaclComponentURLToFilename(mapped_url
).c_str());
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.
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
330 p
->file_info
->desc
= fd
;
331 NaClXCondVarBroadcast(&cv_
);
333 "OpenManifestEntry_MainThreadContinuation: GetPnaclFd okay\n");
336 // Requires PNaCl translation.
338 "OpenManifestEntry_MainThreadContinuation: "
339 "pulling down and translating.\n");
340 CHECK(plugin_
->nacl_interface()->IsPnaclEnabled());
341 pp::CompletionCallback translate_callback
=
345 &PluginReverseInterface::BitcodeTranslate_MainThreadContinuation
,
347 // Will always call the callback on success or failure.
348 pnacl_coordinator_
.reset(
349 PnaclCoordinator::BitcodeToNative(plugin_
,
352 translate_callback
));
354 // p is deleted automatically
357 void PluginReverseInterface::StreamAsFile_MainThreadContinuation(
358 OpenManifestEntryResource
* p
,
361 "Entered StreamAsFile_MainThreadContinuation\n");
363 nacl::MutexLocker
take(&mu_
);
364 if (result
== PP_OK
) {
365 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetFileInfo(%s)\n",
367 *p
->file_info
= plugin_
->GetFileInfo(p
->url
);
370 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n",
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
,
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
);
400 "BitcodeTranslate_MainThreadContinuation: PP_OK, desc %d\n",
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;
418 CloseManifestEntryResource
* to_close
=
419 new CloseManifestEntryResource(desc
, &op_complete
, &op_result
);
422 plugin::WeakRefCallOnMainThread(
426 &plugin::PluginReverseInterface::
427 CloseManifestEntry_MainThreadContinuation
,
429 // wait for completion or surf-away.
431 nacl::MutexLocker
take(&mu_
);
433 shutting_down
= shutting_down_
;
434 if (op_complete
|| shutting_down
) {
437 NaClXCondVarWait(&cv_
, &mu_
);
441 if (shutting_down
) return false;
442 // op_result true if close was successful; false otherwise (e.g., bad desc).
446 void PluginReverseInterface::CloseManifestEntry_MainThreadContinuation(
447 CloseManifestEntryResource
* cls
,
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
);
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
,
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.
493 QuotaRequest
* cont_for_response
= new QuotaRequest(*request
);
494 pp::CompletionCallback quota_cc
= WeakRefNewCallback(
497 &PluginReverseInterface::QuotaRequest_MainThreadResponse
,
499 file_io_trusted
->WillWrite(request
->data
.resource
,
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());
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;
515 *request
->bytes_granted
= request
->bytes_requested
;
517 *request
->op_complete_ptr
= true;
518 NaClXCondVarBroadcast(&cv_
);
522 // request automatically deleted
525 void PluginReverseInterface::QuotaRequest_MainThreadResponse(
526 QuotaRequest
* request
,
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_
);
536 *request
->bytes_granted
= err
;
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
) {
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");
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
, "a_granted
,
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(
574 &plugin::PluginReverseInterface::QuotaRequest_MainThreadContinuation
,
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.
580 nacl::MutexLocker
take(&mu_
);
582 shutting_down
= shutting_down_
;
583 if (op_complete
|| shutting_down
) {
586 NaClXCondVarWait(&cv_
, &mu_
);
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();
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
)
622 should_report_uma_(should_report_uma
),
623 reverse_service_(NULL
),
624 anchor_(new nacl::WeakRefAnchor()),
625 rev_interface_(new PluginReverseInterface(anchor_
, plugin
,
628 init_done_cb
, crash_cb
)),
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");
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_
,
656 if (NACL_SRPC_RESULT_OK
!= rpc_result
) {
657 error_info
->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SETUP
,
658 "ServiceRuntime: reverse setup rpc failed");
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(
667 if (conn_cap
== NULL
) {
668 error_info
->SetReport(ERROR_SEL_LDR_COMMUNICATION_WRAPPER
,
669 "ServiceRuntime: wrapper allocation failure");
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");
681 // start the module. otherwise we cannot connect for multimedia
682 // subsystem since that is handled by user-level code (not secure!)
684 int load_status
= -1;
686 NaClSrpcInvokeBySignature(&command_channel_
,
690 if (NACL_SRPC_RESULT_OK
!= rpc_result
) {
691 error_info
->SetReport(ERROR_SEL_LDR_START_MODULE
,
692 "ServiceRuntime: could not start nacl module");
695 NaClLog(4, "ServiceRuntime::InitCommunication (load_status=%d)\n",
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
)));
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");
721 nacl::string error_message
;
722 bool started
= tmp_subprocess
->Start(plugin_
->pp_instance(),
726 params
.enable_dev_interfaces
,
727 params
.enable_dyncode_syscalls
,
728 params
.enable_exception_handling
,
729 params
.enable_crash_throttling
,
732 NaClLog(LOG_ERROR
, "ServiceRuntime::Start (start failed)\n");
733 params
.error_info
->SetReportWithConsoleOnlyError(
734 ERROR_SEL_LDR_LAUNCH
,
735 "ServiceRuntime: failed to start",
740 subprocess_
.reset(tmp_subprocess
.release());
741 NaClLog(4, "ServiceRuntime::StartSelLdr (return 1)\n");
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");
778 NaClLog(LOG_ERROR
, "Reverse service thread will pick up crash log\n");
783 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (return 1)\n");
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");
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
));
805 bool ServiceRuntime::Log(int severity
, const nacl::string
& msg
) {
806 NaClSrpcResultCodes rpc_result
=
807 NaClSrpcInvokeBySignature(&command_channel_
,
810 strdup(msg
.c_str()));
811 return (NACL_SRPC_RESULT_OK
== rpc_result
);
814 void ServiceRuntime::Shutdown() {
815 rev_interface_
->ShutDown();
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
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();
853 NaClCondVarDtor(&cond_
);
857 int ServiceRuntime::exit_status() {
858 nacl::MutexLocker
take(&mu_
);
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();
871 return std::string();
875 } // namespace plugin