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 */ 6 7 #define NACL_LOG_MODULE_NAME "Plugin::ServiceRuntime" 8 9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" 10 11 #include <string.h> 12 #include <set> 13 #include <string> 14 #include <utility> 15 16 #include "base/compiler_specific.h" 17 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" 35 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" 45 46 #include "ppapi/c/pp_errors.h" 47 #include "ppapi/cpp/core.h" 48 #include "ppapi/cpp/completion_callback.h" 49 50 #include "ppapi/native_client/src/trusted/plugin/manifest.h" 51 #include "ppapi/native_client/src/trusted/plugin/plugin.h" 52 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" 53 #include "ppapi/native_client/src/trusted/plugin/pnacl_options.h" 54 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h" 55 #include "ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h" 56 #include "ppapi/native_client/src/trusted/plugin/srpc_client.h" 57 #include "ppapi/native_client/src/trusted/weak_ref/call_on_main_thread.h" 58 59 namespace { 60 61 // For doing crude quota enforcement on writes to temp files. 62 // We do not allow a temp file bigger than 128 MB for now. 63 // There is currently a limit of 32M for nexe text size, so 128M 64 // should be plenty for static data 65 const int64_t kMaxTempQuota = 0x8000000; 66 67 } // namespace 68 69 namespace plugin { 70 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_); 87 } 88 89 PluginReverseInterface::~PluginReverseInterface() { 90 NaClCondVarDtor(&cv_); 91 NaClMutexDtor(&mu_); 92 } 93 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"); 100 } 101 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); 113 } 114 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); 125 } 126 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"); 138 } 139 } 140 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); 149 } 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); 158 } 159 160 bool PluginReverseInterface::EnumerateManifestKeys( 161 std::set<nacl::string>* out_keys) { 162 Manifest const* mp = manifest_; 163 164 if (!mp->GetFileKeys(out_keys)) { 165 return false; 166 } 167 168 return true; 169 } 170 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_, 191 0, 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; 213 } 214 NaClXCondVarWait(&cv_, &mu_); 215 } 216 } while (0); 217 if (shutting_down) { 218 NaClLog(4, 219 "PluginReverseInterface::OpenManifestEntry:" 220 " plugin is shutting down\n"); 221 return false; 222 } 223 // out_desc has the returned descriptor if successful, else -1. 224 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()); 246 } 247 return true; 248 } 249 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. 262 263 NaClLog(4, "Entered OpenManifestEntry_MainThreadContinuation\n"); 264 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; 277 } 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()); 282 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; 306 } 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 checked earlier if the pnacl component wasn't installed 317 // yet, so this shouldn't happen. At this point, we can't do much 318 // anymore, so just continue 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"); 325 } 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"); 334 } 335 } else { 336 // Requires PNaCl translation, but that's not supported. 337 NaClLog(4, 338 "OpenManifestEntry_MainThreadContinuation: " 339 "Requires PNaCl translation -- not supported\n"); 340 nacl::MutexLocker take(&mu_); 341 *p->op_complete_ptr = true; // done... 342 p->file_info->desc = -1; // but failed. 343 p->error_info->SetReport( 344 ERROR_MANIFEST_OPEN, 345 "ServiceRuntime: Translating OpenManifestEntry files not supported"); 346 NaClXCondVarBroadcast(&cv_); 347 return; 348 } 349 // p is deleted automatically 350 } 351 352 void PluginReverseInterface::StreamAsFile_MainThreadContinuation( 353 OpenManifestEntryResource* p, 354 int32_t result) { 355 NaClLog(4, 356 "Entered StreamAsFile_MainThreadContinuation\n"); 357 358 nacl::MutexLocker take(&mu_); 359 if (result == PP_OK) { 360 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetFileInfo(%s)\n", 361 p->url.c_str()); 362 *p->file_info = plugin_->GetFileInfo(p->url); 363 364 NaClLog(4, 365 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n", 366 p->file_info->desc); 367 } else { 368 NaClLog(4, 369 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n"); 370 p->file_info->desc = -1; 371 p->error_info->SetReport(ERROR_MANIFEST_OPEN, 372 "Plugin StreamAsFile failed at callback"); 373 } 374 *p->op_complete_ptr = true; 375 NaClXCondVarBroadcast(&cv_); 376 } 377 378 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) { 379 bool op_complete = false; 380 bool op_result; 381 CloseManifestEntryResource* to_close = 382 new CloseManifestEntryResource(desc, &op_complete, &op_result); 383 384 bool shutting_down; 385 plugin::WeakRefCallOnMainThread( 386 anchor_, 387 0, 388 this, 389 &plugin::PluginReverseInterface:: 390 CloseManifestEntry_MainThreadContinuation, 391 to_close); 392 // wait for completion or surf-away. 393 do { 394 nacl::MutexLocker take(&mu_); 395 for (;;) { 396 shutting_down = shutting_down_; 397 if (op_complete || shutting_down) { 398 break; 399 } 400 NaClXCondVarWait(&cv_, &mu_); 401 } 402 } while (0); 403 404 if (shutting_down) return false; 405 // op_result true if close was successful; false otherwise (e.g., bad desc). 406 return op_result; 407 } 408 409 void PluginReverseInterface::CloseManifestEntry_MainThreadContinuation( 410 CloseManifestEntryResource* cls, 411 int32_t err) { 412 UNREFERENCED_PARAMETER(err); 413 414 nacl::MutexLocker take(&mu_); 415 // TODO(bsy): once the plugin has a reliable way to report that the 416 // file usage is done -- and sel_ldr uses this RPC call -- we should 417 // tell the plugin that the associated resources can be freed. 418 *cls->op_result_ptr = true; 419 *cls->op_complete_ptr = true; 420 NaClXCondVarBroadcast(&cv_); 421 // cls automatically deleted 422 } 423 424 void PluginReverseInterface::ReportCrash() { 425 NaClLog(4, "PluginReverseInterface::ReportCrash\n"); 426 427 if (crash_cb_.pp_completion_callback().func != NULL) { 428 NaClLog(4, "PluginReverseInterface::ReportCrash: invoking CB\n"); 429 pp::Module::Get()->core()->CallOnMainThread(0, crash_cb_, PP_OK); 430 } else { 431 NaClLog(1, 432 "PluginReverseInterface::ReportCrash:" 433 " crash_cb_ not valid, skipping\n"); 434 } 435 } 436 437 void PluginReverseInterface::ReportExitStatus(int exit_status) { 438 service_runtime_->set_exit_status(exit_status); 439 } 440 441 int64_t PluginReverseInterface::RequestQuotaForWrite( 442 nacl::string file_id, int64_t offset, int64_t bytes_to_write) { 443 NaClLog(4, 444 "PluginReverseInterface::RequestQuotaForWrite:" 445 " (file_id='%s', offset=%" NACL_PRId64 ", bytes_to_write=%" 446 NACL_PRId64 ")\n", file_id.c_str(), offset, bytes_to_write); 447 uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10); 448 nacl::MutexLocker take(&mu_); 449 if (quota_files_.count(file_key) == 0) { 450 // Look up failed to find the requested quota managed resource. 451 NaClLog(4, "PluginReverseInterface::RequestQuotaForWrite: failed...\n"); 452 return 0; 453 } 454 455 // Because we now only support this interface for tempfiles which are not 456 // pepper objects, we can just do some crude quota enforcement here rather 457 // than calling out to pepper from the main thread. 458 if (offset + bytes_to_write >= kMaxTempQuota) 459 return 0; 460 461 return bytes_to_write; 462 } 463 464 void PluginReverseInterface::AddTempQuotaManagedFile( 465 const nacl::string& file_id) { 466 NaClLog(4, "PluginReverseInterface::AddTempQuotaManagedFile: " 467 "(file_id='%s')\n", file_id.c_str()); 468 uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10); 469 nacl::MutexLocker take(&mu_); 470 quota_files_.insert(file_key); 471 } 472 473 ServiceRuntime::ServiceRuntime(Plugin* plugin, 474 const Manifest* manifest, 475 bool main_service_runtime, 476 pp::CompletionCallback init_done_cb, 477 pp::CompletionCallback crash_cb) 478 : plugin_(plugin), 479 main_service_runtime_(main_service_runtime), 480 reverse_service_(NULL), 481 anchor_(new nacl::WeakRefAnchor()), 482 rev_interface_(new PluginReverseInterface(anchor_, plugin, 483 manifest, 484 this, 485 init_done_cb, crash_cb)), 486 exit_status_(-1), 487 start_sel_ldr_done_(false) { 488 NaClSrpcChannelInitialize(&command_channel_); 489 NaClXMutexCtor(&mu_); 490 NaClXCondVarCtor(&cond_); 491 } 492 493 bool ServiceRuntime::InitCommunication(nacl::DescWrapper* nacl_desc, 494 ErrorInfo* error_info) { 495 NaClLog(4, "ServiceRuntime::InitCommunication" 496 " (this=%p, subprocess=%p)\n", 497 static_cast<void*>(this), 498 static_cast<void*>(subprocess_.get())); 499 // Create the command channel to the sel_ldr and load the nexe from nacl_desc. 500 if (!subprocess_->SetupCommandAndLoad(&command_channel_, nacl_desc)) { 501 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, 502 "ServiceRuntime: command channel creation failed"); 503 return false; 504 } 505 // Hook up the reverse service channel. We are the IMC client, but 506 // provide SRPC service. 507 NaClDesc* out_conn_cap; 508 NaClSrpcResultCodes rpc_result = 509 NaClSrpcInvokeBySignature(&command_channel_, 510 "reverse_setup::h", 511 &out_conn_cap); 512 513 if (NACL_SRPC_RESULT_OK != rpc_result) { 514 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SETUP, 515 "ServiceRuntime: reverse setup rpc failed"); 516 return false; 517 } 518 // Get connection capability to service runtime where the IMC 519 // server/SRPC client is waiting for a rendezvous. 520 NaClLog(4, "ServiceRuntime: got 0x%" NACL_PRIxPTR "\n", 521 (uintptr_t) out_conn_cap); 522 nacl::DescWrapper* conn_cap = plugin_->wrapper_factory()->MakeGenericCleanup( 523 out_conn_cap); 524 if (conn_cap == NULL) { 525 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_WRAPPER, 526 "ServiceRuntime: wrapper allocation failure"); 527 return false; 528 } 529 out_conn_cap = NULL; // ownership passed 530 NaClLog(4, "ServiceRuntime::InitCommunication: starting reverse service\n"); 531 reverse_service_ = new nacl::ReverseService(conn_cap, rev_interface_->Ref()); 532 if (!reverse_service_->Start()) { 533 error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SERVICE, 534 "ServiceRuntime: starting reverse services failed"); 535 return false; 536 } 537 538 // start the module. otherwise we cannot connect for multimedia 539 // subsystem since that is handled by user-level code (not secure!) 540 // in libsrpc. 541 int load_status = -1; 542 rpc_result = 543 NaClSrpcInvokeBySignature(&command_channel_, 544 "start_module::i", 545 &load_status); 546 547 if (NACL_SRPC_RESULT_OK != rpc_result) { 548 error_info->SetReport(ERROR_SEL_LDR_START_MODULE, 549 "ServiceRuntime: could not start nacl module"); 550 return false; 551 } 552 NaClLog(4, "ServiceRuntime::InitCommunication (load_status=%d)\n", 553 load_status); 554 if (main_service_runtime_) { 555 plugin_->ReportSelLdrLoadStatus(load_status); 556 } 557 if (LOAD_OK != load_status) { 558 error_info->SetReport( 559 ERROR_SEL_LDR_START_STATUS, 560 NaClErrorString(static_cast<NaClErrorCode>(load_status))); 561 return false; 562 } 563 return true; 564 } 565 566 bool ServiceRuntime::StartSelLdr(const SelLdrStartParams& params) { 567 NaClLog(4, "ServiceRuntime::Start\n"); 568 569 nacl::scoped_ptr<SelLdrLauncherChrome> 570 tmp_subprocess(new SelLdrLauncherChrome()); 571 if (NULL == tmp_subprocess.get()) { 572 NaClLog(LOG_ERROR, "ServiceRuntime::Start (subprocess create failed)\n"); 573 params.error_info->SetReport( 574 ERROR_SEL_LDR_CREATE_LAUNCHER, 575 "ServiceRuntime: failed to create sel_ldr launcher"); 576 return false; 577 } 578 nacl::string error_message; 579 bool started = tmp_subprocess->Start(plugin_->pp_instance(), 580 params.url.c_str(), 581 params.uses_irt, 582 params.uses_ppapi, 583 params.enable_dev_interfaces, 584 params.enable_dyncode_syscalls, 585 params.enable_exception_handling, 586 params.enable_crash_throttling, 587 &error_message); 588 if (!started) { 589 NaClLog(LOG_ERROR, "ServiceRuntime::Start (start failed)\n"); 590 params.error_info->SetReportWithConsoleOnlyError( 591 ERROR_SEL_LDR_LAUNCH, 592 "ServiceRuntime: failed to start", 593 error_message); 594 return false; 595 } 596 597 subprocess_.reset(tmp_subprocess.release()); 598 NaClLog(4, "ServiceRuntime::StartSelLdr (return 1)\n"); 599 return true; 600 } 601 602 void ServiceRuntime::WaitForSelLdrStart() { 603 nacl::MutexLocker take(&mu_); 604 while(!start_sel_ldr_done_) { 605 NaClXCondVarWait(&cond_, &mu_); 606 } 607 } 608 609 void ServiceRuntime::SignalStartSelLdrDone() { 610 nacl::MutexLocker take(&mu_); 611 start_sel_ldr_done_ = true; 612 NaClXCondVarSignal(&cond_); 613 } 614 615 bool ServiceRuntime::LoadNexeAndStart(nacl::DescWrapper* nacl_desc, 616 ErrorInfo* error_info, 617 const pp::CompletionCallback& crash_cb) { 618 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (nacl_desc=%p)\n", 619 reinterpret_cast<void*>(nacl_desc)); 620 if (!InitCommunication(nacl_desc, error_info)) { 621 // On a load failure the service runtime does not crash itself to 622 // avoid a race where the no-more-senders error on the reverse 623 // channel esrvice thread might cause the crash-detection logic to 624 // kick in before the start_module RPC reply has been received. So 625 // we induce a service runtime crash here. We do not release 626 // subprocess_ since it's needed to collect crash log output after 627 // the error is reported. 628 Log(LOG_FATAL, "reap logs"); 629 if (NULL == reverse_service_) { 630 // No crash detector thread. 631 NaClLog(LOG_ERROR, "scheduling to get crash log\n"); 632 pp::Module::Get()->core()->CallOnMainThread(0, crash_cb, PP_OK); 633 NaClLog(LOG_ERROR, "should fire soon\n"); 634 } else { 635 NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n"); 636 } 637 return false; 638 } 639 640 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (return 1)\n"); 641 return true; 642 } 643 644 SrpcClient* ServiceRuntime::SetupAppChannel() { 645 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", 646 reinterpret_cast<void*>(subprocess_.get())); 647 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); 648 if (NULL == connect_desc) { 649 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); 650 return NULL; 651 } else { 652 NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n", 653 static_cast<void*>(connect_desc)); 654 SrpcClient* srpc_client = SrpcClient::New(connect_desc); 655 NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n", 656 static_cast<void*>(srpc_client)); 657 delete connect_desc; 658 return srpc_client; 659 } 660 } 661 662 bool ServiceRuntime::Log(int severity, const nacl::string& msg) { 663 NaClSrpcResultCodes rpc_result = 664 NaClSrpcInvokeBySignature(&command_channel_, 665 "log:is:", 666 severity, 667 strdup(msg.c_str())); 668 return (NACL_SRPC_RESULT_OK == rpc_result); 669 } 670 671 void ServiceRuntime::Shutdown() { 672 rev_interface_->ShutDown(); 673 anchor_->Abandon(); 674 // Abandon callbacks, tell service threads to quit if they were 675 // blocked waiting for main thread operations to finish. Note that 676 // some callbacks must still await their completion event, e.g., 677 // CallOnMainThread must still wait for the time out, or I/O events 678 // must finish, so resources associated with pending events cannot 679 // be deallocated. 680 681 // Note that this does waitpid() to get rid of any zombie subprocess. 682 subprocess_.reset(NULL); 683 684 NaClSrpcDtor(&command_channel_); 685 686 // subprocess_ has been shut down, but threads waiting on messages 687 // from the service runtime may not have noticed yet. The low-level 688 // NaClSimpleRevService code takes care to refcount the data objects 689 // that it needs, and reverse_service_ is also refcounted. We wait 690 // for the service threads to get their EOF indications. 691 if (reverse_service_ != NULL) { 692 reverse_service_->WaitForServiceThreadsToExit(); 693 reverse_service_->Unref(); 694 reverse_service_ = NULL; 695 } 696 } 697 698 ServiceRuntime::~ServiceRuntime() { 699 NaClLog(4, "ServiceRuntime::~ServiceRuntime (this=%p)\n", 700 static_cast<void*>(this)); 701 // We do this just in case Shutdown() was not called. 702 subprocess_.reset(NULL); 703 if (reverse_service_ != NULL) { 704 reverse_service_->Unref(); 705 } 706 707 rev_interface_->Unref(); 708 709 anchor_->Unref(); 710 NaClCondVarDtor(&cond_); 711 NaClMutexDtor(&mu_); 712 } 713 714 void ServiceRuntime::set_exit_status(int exit_status) { 715 nacl::MutexLocker take(&mu_); 716 if (main_service_runtime_) 717 plugin_->set_exit_status(exit_status & 0xff); 718 } 719 720 nacl::string ServiceRuntime::GetCrashLogOutput() { 721 if (NULL != subprocess_.get()) { 722 return subprocess_->GetCrashLogOutput(); 723 } else { 724 return std::string(); 725 } 726 } 727 728 } // namespace plugin 729