| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
| 5 */ | 5 */ |
| 6 | 6 |
| 7 #define NACL_LOG_MODULE_NAME "Plugin_ServiceRuntime" | 7 #define NACL_LOG_MODULE_NAME "Plugin_ServiceRuntime" |
| 8 | 8 |
| 9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" | 9 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" |
| 10 | 10 |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 | 339 |
| 340 void PluginReverseInterface::ReportExitStatus(int exit_status) { | 340 void PluginReverseInterface::ReportExitStatus(int exit_status) { |
| 341 service_runtime_->set_exit_status(exit_status); | 341 service_runtime_->set_exit_status(exit_status); |
| 342 } | 342 } |
| 343 | 343 |
| 344 int64_t PluginReverseInterface::RequestQuotaForWrite( | 344 int64_t PluginReverseInterface::RequestQuotaForWrite( |
| 345 nacl::string file_id, int64_t offset, int64_t bytes_to_write) { | 345 nacl::string file_id, int64_t offset, int64_t bytes_to_write) { |
| 346 return bytes_to_write; | 346 return bytes_to_write; |
| 347 } | 347 } |
| 348 | 348 |
| 349 // Thin wrapper for the arguments of LoadNexeAndStart(), as WeakRefNewCallback | |
| 350 // can take only one argument. Also, this dtor has the responsibility to invoke | |
| 351 // callbacks on destruction. | |
| 352 struct ServiceRuntime::LoadNexeAndStartData { | |
| 353 explicit LoadNexeAndStartData(const pp::CompletionCallback& callback) | |
| 354 : callback(callback) { | |
| 355 } | |
| 356 | |
| 357 ~LoadNexeAndStartData() { | |
| 358 // We must call the callbacks here if they are not yet called, otherwise | |
| 359 // the resource would be leaked. | |
| 360 if (callback.pp_completion_callback().func) | |
| 361 callback.RunAndClear(PP_ERROR_ABORTED); | |
| 362 } | |
| 363 | |
| 364 // On success path, this must be invoked manually. Otherwise the dtor would | |
| 365 // invoke callbacks with error code unexpectedly. | |
| 366 void Clear() { | |
| 367 callback = pp::CompletionCallback(); | |
| 368 } | |
| 369 | |
| 370 pp::CompletionCallback callback; | |
| 371 }; | |
| 372 | |
| 373 ServiceRuntime::ServiceRuntime(Plugin* plugin, | 349 ServiceRuntime::ServiceRuntime(Plugin* plugin, |
| 374 bool main_service_runtime, | 350 bool main_service_runtime, |
| 375 bool uses_nonsfi_mode, | 351 bool uses_nonsfi_mode, |
| 376 pp::CompletionCallback init_done_cb, | 352 pp::CompletionCallback init_done_cb, |
| 377 pp::CompletionCallback crash_cb) | 353 pp::CompletionCallback crash_cb) |
| 378 : plugin_(plugin), | 354 : plugin_(plugin), |
| 379 main_service_runtime_(main_service_runtime), | 355 main_service_runtime_(main_service_runtime), |
| 380 uses_nonsfi_mode_(uses_nonsfi_mode), | 356 uses_nonsfi_mode_(uses_nonsfi_mode), |
| 381 reverse_service_(NULL), | 357 reverse_service_(NULL), |
| 382 anchor_(new nacl::WeakRefAnchor()), | 358 anchor_(new nacl::WeakRefAnchor()), |
| 383 rev_interface_(new PluginReverseInterface(anchor_, plugin, this, | 359 rev_interface_(new PluginReverseInterface(anchor_, plugin, this, |
| 384 init_done_cb, crash_cb)), | 360 init_done_cb, crash_cb)), |
| 385 start_sel_ldr_done_(false), | 361 start_sel_ldr_done_(false), |
| 386 nexe_started_(false) { | 362 start_nexe_done_(false), |
| 363 nexe_started_ok_(false) { |
| 387 NaClSrpcChannelInitialize(&command_channel_); | 364 NaClSrpcChannelInitialize(&command_channel_); |
| 388 NaClXMutexCtor(&mu_); | 365 NaClXMutexCtor(&mu_); |
| 389 NaClXCondVarCtor(&cond_); | 366 NaClXCondVarCtor(&cond_); |
| 390 } | 367 } |
| 391 | 368 |
| 392 void ServiceRuntime::LoadNexeAndStartAfterLoadModule( | |
| 393 LoadNexeAndStartData* data, int32_t pp_error) { | |
| 394 if (pp_error != PP_OK) { | |
| 395 DidLoadNexeAndStart(data, pp_error); | |
| 396 return; | |
| 397 } | |
| 398 | |
| 399 // Here, LoadModule is successfully done. So the remaining task is just | |
| 400 // calling StartModule(), here. | |
| 401 DidLoadNexeAndStart(data, StartModule() ? PP_OK : PP_ERROR_FAILED); | |
| 402 } | |
| 403 | |
| 404 void ServiceRuntime::DidLoadNexeAndStart( | |
| 405 LoadNexeAndStartData* data, int32_t pp_error) { | |
| 406 if (pp_error == PP_OK) { | |
| 407 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (success)\n"); | |
| 408 } else { | |
| 409 // On a load failure the service runtime does not crash itself to | |
| 410 // avoid a race where the no-more-senders error on the reverse | |
| 411 // channel esrvice thread might cause the crash-detection logic to | |
| 412 // kick in before the start_module RPC reply has been received. So | |
| 413 // we induce a service runtime crash here. We do not release | |
| 414 // subprocess_ since it's needed to collect crash log output after | |
| 415 // the error is reported. | |
| 416 Log(LOG_FATAL, "reap logs"); | |
| 417 if (NULL == reverse_service_) { | |
| 418 // No crash detector thread. | |
| 419 NaClLog(LOG_ERROR, "scheduling to get crash log\n"); | |
| 420 // Invoking rev_interface's method is workaround to avoid crash_cb | |
| 421 // gets called twice or more. We should clean this up later. | |
| 422 rev_interface_->ReportCrash(); | |
| 423 NaClLog(LOG_ERROR, "should fire soon\n"); | |
| 424 } else { | |
| 425 NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n"); | |
| 426 } | |
| 427 } | |
| 428 | |
| 429 pp::Module::Get()->core()->CallOnMainThread(0, data->callback, pp_error); | |
| 430 | |
| 431 // Because the ownership of data is taken by caller, we must clear it | |
| 432 // manually here. Otherwise, its dtor invokes callbacks again. | |
| 433 data->Clear(); | |
| 434 } | |
| 435 | |
| 436 bool ServiceRuntime::SetupCommandChannel() { | 369 bool ServiceRuntime::SetupCommandChannel() { |
| 437 NaClLog(4, "ServiceRuntime::SetupCommand (this=%p, subprocess=%p)\n", | 370 NaClLog(4, "ServiceRuntime::SetupCommand (this=%p, subprocess=%p)\n", |
| 438 static_cast<void*>(this), | 371 static_cast<void*>(this), |
| 439 static_cast<void*>(subprocess_.get())); | 372 static_cast<void*>(subprocess_.get())); |
| 440 if (uses_nonsfi_mode_) { | 373 if (uses_nonsfi_mode_) { |
| 441 // In non-SFI mode, no SRPC is used. Just skips and returns success. | 374 // In non-SFI mode, no SRPC is used. Just skips and returns success. |
| 442 return true; | 375 return true; |
| 443 } | 376 } |
| 444 | 377 |
| 445 if (!subprocess_->SetupCommand(&command_channel_)) { | 378 if (!subprocess_->SetupCommand(&command_channel_)) { |
| 446 if (main_service_runtime_) { | 379 if (main_service_runtime_) { |
| 447 ErrorInfo error_info; | 380 ErrorInfo error_info; |
| 448 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, | 381 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, |
| 449 "ServiceRuntime: command channel creation failed"); | 382 "ServiceRuntime: command channel creation failed"); |
| 450 plugin_->ReportLoadError(error_info); | 383 plugin_->ReportLoadError(error_info); |
| 451 } | 384 } |
| 452 return false; | 385 return false; |
| 453 } | 386 } |
| 454 return true; | 387 return true; |
| 455 } | 388 } |
| 456 | 389 |
| 457 void ServiceRuntime::LoadModule(PP_NaClFileInfo file_info, | |
| 458 pp::CompletionCallback callback) { | |
| 459 if (uses_nonsfi_mode_) { | |
| 460 // In non-SFI mode, loading is done a part of LaunchSelLdr. | |
| 461 DidLoadModule(callback, PP_OK); | |
| 462 return; | |
| 463 } | |
| 464 | |
| 465 NaClFileInfo nacl_file_info; | |
| 466 nacl_file_info.desc = ConvertFileDescriptor(file_info.handle, true); | |
| 467 nacl_file_info.file_token.lo = file_info.token_lo; | |
| 468 nacl_file_info.file_token.hi = file_info.token_hi; | |
| 469 NaClDesc* desc = NaClDescIoFromFileInfo(nacl_file_info, O_RDONLY); | |
| 470 if (desc == NULL) { | |
| 471 DidLoadModule(callback, PP_ERROR_FAILED); | |
| 472 return; | |
| 473 } | |
| 474 | |
| 475 // We don't use a scoped_ptr here since we would immediately release the | |
| 476 // DescWrapper to LoadModule(). | |
| 477 nacl::DescWrapper* wrapper = | |
| 478 plugin_->wrapper_factory()->MakeGenericCleanup(desc); | |
| 479 | |
| 480 // TODO(teravest, hidehiko): Replace this by Chrome IPC. | |
| 481 bool result = subprocess_->LoadModule(&command_channel_, wrapper); | |
| 482 DidLoadModule(callback, result ? PP_OK : PP_ERROR_FAILED); | |
| 483 } | |
| 484 | |
| 485 void ServiceRuntime::DidLoadModule(pp::CompletionCallback callback, | |
| 486 int32_t pp_error) { | |
| 487 if (pp_error != PP_OK) { | |
| 488 ErrorInfo error_info; | |
| 489 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, | |
| 490 "ServiceRuntime: load module failed"); | |
| 491 plugin_->ReportLoadError(error_info); | |
| 492 } | |
| 493 callback.Run(pp_error); | |
| 494 } | |
| 495 | |
| 496 bool ServiceRuntime::InitReverseService() { | 390 bool ServiceRuntime::InitReverseService() { |
| 497 if (uses_nonsfi_mode_) { | 391 if (uses_nonsfi_mode_) { |
| 498 // In non-SFI mode, no reverse service is set up. Just returns success. | 392 // In non-SFI mode, no reverse service is set up. Just returns success. |
| 499 return true; | 393 return true; |
| 500 } | 394 } |
| 501 | 395 |
| 502 // Hook up the reverse service channel. We are the IMC client, but | 396 // Hook up the reverse service channel. We are the IMC client, but |
| 503 // provide SRPC service. | 397 // provide SRPC service. |
| 504 NaClDesc* out_conn_cap; | 398 NaClDesc* out_conn_cap; |
| 505 NaClSrpcResultCodes rpc_result = | 399 NaClSrpcResultCodes rpc_result = |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 660 } | 554 } |
| 661 return start_sel_ldr_done_; | 555 return start_sel_ldr_done_; |
| 662 } | 556 } |
| 663 | 557 |
| 664 void ServiceRuntime::SignalStartSelLdrDone() { | 558 void ServiceRuntime::SignalStartSelLdrDone() { |
| 665 nacl::MutexLocker take(&mu_); | 559 nacl::MutexLocker take(&mu_); |
| 666 start_sel_ldr_done_ = true; | 560 start_sel_ldr_done_ = true; |
| 667 NaClXCondVarSignal(&cond_); | 561 NaClXCondVarSignal(&cond_); |
| 668 } | 562 } |
| 669 | 563 |
| 670 void ServiceRuntime::WaitForNexeStart() { | 564 bool ServiceRuntime::WaitForNexeStart() { |
| 671 nacl::MutexLocker take(&mu_); | 565 nacl::MutexLocker take(&mu_); |
| 672 while (!nexe_started_) | 566 while (!start_nexe_done_) |
| 673 NaClXCondVarWait(&cond_, &mu_); | 567 NaClXCondVarWait(&cond_, &mu_); |
| 674 // Reset nexe_started_ here in case we run again. | 568 return nexe_started_ok_; |
| 675 nexe_started_ = false; | |
| 676 } | 569 } |
| 677 | 570 |
| 678 void ServiceRuntime::SignalNexeStarted() { | 571 void ServiceRuntime::SignalNexeStarted(bool ok) { |
| 679 nacl::MutexLocker take(&mu_); | 572 nacl::MutexLocker take(&mu_); |
| 680 nexe_started_ = true; | 573 start_nexe_done_ = true; |
| 574 nexe_started_ok_ = ok; |
| 681 NaClXCondVarSignal(&cond_); | 575 NaClXCondVarSignal(&cond_); |
| 682 } | 576 } |
| 683 | 577 |
| 684 void ServiceRuntime::LoadNexeAndStart(PP_NaClFileInfo file_info, | 578 void ServiceRuntime::LoadNexeAndStart(PP_NaClFileInfo file_info) { |
| 685 const pp::CompletionCallback& callback) { | |
| 686 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (handle_valid=%d " | 579 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (handle_valid=%d " |
| 687 "token_lo=%" NACL_PRIu64 " token_hi=%" NACL_PRIu64 ")\n", | 580 "token_lo=%" NACL_PRIu64 " token_hi=%" NACL_PRIu64 ")\n", |
| 688 file_info.handle != PP_kInvalidFileHandle, | 581 file_info.handle != PP_kInvalidFileHandle, |
| 689 file_info.token_lo, | 582 file_info.token_lo, |
| 690 file_info.token_hi); | 583 file_info.token_hi); |
| 691 | 584 |
| 692 nacl::scoped_ptr<LoadNexeAndStartData> data( | 585 bool ok = LoadNexeAndStartInternal(file_info); |
| 693 new LoadNexeAndStartData(callback)); | 586 if (ok) { |
| 694 if (!SetupCommandChannel() || !InitReverseService()) { | 587 NaClLog(4, "ServiceRuntime::LoadNexeAndStart (success)\n"); |
| 695 DidLoadNexeAndStart(data.get(), PP_ERROR_FAILED); | 588 } else { |
| 696 return; | 589 ReapLogs(); |
| 590 } |
| 591 // This only matters if a background thread is waiting, but we signal in all |
| 592 // cases to simplify the code. |
| 593 SignalNexeStarted(ok); |
| 594 } |
| 595 |
| 596 bool ServiceRuntime::LoadNexeAndStartInternal( |
| 597 const PP_NaClFileInfo& file_info) { |
| 598 if(!SetupCommandChannel()) { |
| 599 return false; |
| 600 } |
| 601 if (!InitReverseService()) { |
| 602 return false; |
| 603 } |
| 604 if (!LoadModule(file_info)) { |
| 605 ErrorInfo error_info; |
| 606 error_info.SetReport(PP_NACL_ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL, |
| 607 "ServiceRuntime: load module failed"); |
| 608 plugin_->ReportLoadError(error_info); |
| 609 return false; |
| 610 } |
| 611 if (!StartModule()) { |
| 612 return false; |
| 613 } |
| 614 return true; |
| 615 } |
| 616 |
| 617 bool ServiceRuntime::LoadModule(const PP_NaClFileInfo& file_info) { |
| 618 if (uses_nonsfi_mode_) { |
| 619 // In non-SFI mode, loading is done a part of LaunchSelLdr. |
| 620 return true; |
| 697 } | 621 } |
| 698 | 622 |
| 699 LoadModule( | 623 NaClFileInfo nacl_file_info; |
| 700 file_info, | 624 nacl_file_info.desc = ConvertFileDescriptor(file_info.handle, true); |
| 701 WeakRefNewCallback(anchor_, | 625 nacl_file_info.file_token.lo = file_info.token_lo; |
| 702 this, | 626 nacl_file_info.file_token.hi = file_info.token_hi; |
| 703 &ServiceRuntime::LoadNexeAndStartAfterLoadModule, | 627 NaClDesc* desc = NaClDescIoFromFileInfo(nacl_file_info, O_RDONLY); |
| 704 data.release())); // Delegate the ownership. | 628 if (desc == NULL) { |
| 629 return false; |
| 630 } |
| 631 // We don't use a scoped_ptr here since we would immediately release the |
| 632 // DescWrapper to LoadModule(). |
| 633 nacl::DescWrapper* wrapper = |
| 634 plugin_->wrapper_factory()->MakeGenericCleanup(desc); |
| 635 // TODO(teravest, hidehiko): Replace this by Chrome IPC. |
| 636 return subprocess_->LoadModule(&command_channel_, wrapper); |
| 637 } |
| 638 |
| 639 void ServiceRuntime::ReapLogs() { |
| 640 // On a load failure the service runtime does not crash itself to |
| 641 // avoid a race where the no-more-senders error on the reverse |
| 642 // channel service thread might cause the crash-detection logic to |
| 643 // kick in before the start_module RPC reply has been received. So |
| 644 // we induce a service runtime crash here. We do not release |
| 645 // subprocess_ since it's needed to collect crash log output after |
| 646 // the error is reported. |
| 647 RemoteLog(LOG_FATAL, "reap logs\n"); |
| 648 if (NULL == reverse_service_) { |
| 649 // No crash detector thread. |
| 650 NaClLog(LOG_ERROR, "scheduling to get crash log\n"); |
| 651 // Invoking rev_interface's method is workaround to avoid crash_cb |
| 652 // gets called twice or more. We should clean this up later. |
| 653 rev_interface_->ReportCrash(); |
| 654 NaClLog(LOG_ERROR, "should fire soon\n"); |
| 655 } else { |
| 656 NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n"); |
| 657 } |
| 705 } | 658 } |
| 706 | 659 |
| 707 SrpcClient* ServiceRuntime::SetupAppChannel() { | 660 SrpcClient* ServiceRuntime::SetupAppChannel() { |
| 708 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", | 661 NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n", |
| 709 reinterpret_cast<void*>(subprocess_.get())); | 662 reinterpret_cast<void*>(subprocess_.get())); |
| 710 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); | 663 nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect(); |
| 711 if (NULL == connect_desc) { | 664 if (NULL == connect_desc) { |
| 712 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); | 665 NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n"); |
| 713 return NULL; | 666 return NULL; |
| 714 } else { | 667 } else { |
| 715 NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n", | 668 NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n", |
| 716 static_cast<void*>(connect_desc)); | 669 static_cast<void*>(connect_desc)); |
| 717 SrpcClient* srpc_client = SrpcClient::New(connect_desc); | 670 SrpcClient* srpc_client = SrpcClient::New(connect_desc); |
| 718 NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n", | 671 NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n", |
| 719 static_cast<void*>(srpc_client)); | 672 static_cast<void*>(srpc_client)); |
| 720 delete connect_desc; | 673 delete connect_desc; |
| 721 return srpc_client; | 674 return srpc_client; |
| 722 } | 675 } |
| 723 } | 676 } |
| 724 | 677 |
| 725 bool ServiceRuntime::Log(int severity, const nacl::string& msg) { | 678 bool ServiceRuntime::RemoteLog(int severity, const nacl::string& msg) { |
| 726 NaClSrpcResultCodes rpc_result = | 679 NaClSrpcResultCodes rpc_result = |
| 727 NaClSrpcInvokeBySignature(&command_channel_, | 680 NaClSrpcInvokeBySignature(&command_channel_, |
| 728 "log:is:", | 681 "log:is:", |
| 729 severity, | 682 severity, |
| 730 strdup(msg.c_str())); | 683 strdup(msg.c_str())); |
| 731 return (NACL_SRPC_RESULT_OK == rpc_result); | 684 return (NACL_SRPC_RESULT_OK == rpc_result); |
| 732 } | 685 } |
| 733 | 686 |
| 734 void ServiceRuntime::Shutdown() { | 687 void ServiceRuntime::Shutdown() { |
| 735 rev_interface_->ShutDown(); | 688 rev_interface_->ShutDown(); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 781 | 734 |
| 782 nacl::string ServiceRuntime::GetCrashLogOutput() { | 735 nacl::string ServiceRuntime::GetCrashLogOutput() { |
| 783 if (NULL != subprocess_.get()) { | 736 if (NULL != subprocess_.get()) { |
| 784 return subprocess_->GetCrashLogOutput(); | 737 return subprocess_->GetCrashLogOutput(); |
| 785 } else { | 738 } else { |
| 786 return std::string(); | 739 return std::string(); |
| 787 } | 740 } |
| 788 } | 741 } |
| 789 | 742 |
| 790 } // namespace plugin | 743 } // namespace plugin |
| OLD | NEW |