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 |