Home | History | Annotate | Download | only in plugin
      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/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"
     51 
     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"
     60 
     61 namespace {
     62 
     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;
     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 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");
    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.
    337     NaClLog(4,
    338             "OpenManifestEntry_MainThreadContinuation: "
    339             "pulling down and translating.\n");
    340     if (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));
    353     } else {
    354       nacl::MutexLocker take(&mu_);
    355       *p->op_complete_ptr = true;  // done...
    356       p->file_info->desc = -1;  // but failed.
    357       p->error_info->SetReport(ERROR_PNACL_NOT_ENABLED,
    358                                "ServiceRuntime: GetPnaclFd failed -- pnacl not "
    359                                "enabled with --enable-pnacl.");
    360       NaClXCondVarBroadcast(&cv_);
    361       return;
    362     }
    363   }
    364   // p is deleted automatically
    365 }
    366 
    367 void PluginReverseInterface::StreamAsFile_MainThreadContinuation(
    368     OpenManifestEntryResource* p,
    369     int32_t result) {
    370   NaClLog(4,
    371           "Entered StreamAsFile_MainThreadContinuation\n");
    372 
    373   nacl::MutexLocker take(&mu_);
    374   if (result == PP_OK) {
    375     NaClLog(4, "StreamAsFile_MainThreadContinuation: GetFileInfo(%s)\n",
    376             p->url.c_str());
    377     *p->file_info = plugin_->GetFileInfo(p->url);
    378 
    379     NaClLog(4,
    380             "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n",
    381             p->file_info->desc);
    382   } else {
    383     NaClLog(4,
    384             "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n");
    385     p->file_info->desc = -1;
    386     p->error_info->SetReport(ERROR_MANIFEST_OPEN,
    387                              "Plugin StreamAsFile failed at callback");
    388   }
    389   *p->op_complete_ptr = true;
    390   NaClXCondVarBroadcast(&cv_);
    391 }
    392 
    393 
    394 void PluginReverseInterface::BitcodeTranslate_MainThreadContinuation(
    395     OpenManifestEntryResource* p,
    396     int32_t result) {
    397   NaClLog(4,
    398           "Entered BitcodeTranslate_MainThreadContinuation\n");
    399 
    400   nacl::MutexLocker take(&mu_);
    401   if (result == PP_OK) {
    402     // TODO(jvoung): clean this up. We are assuming that the NaClDesc is
    403     // a host IO desc and doing a downcast. Once the ReverseInterface
    404     // accepts NaClDescs we can avoid this downcast.
    405     NaClDesc* desc = pnacl_coordinator_->ReleaseTranslatedFD()->desc();
    406     struct NaClDescIoDesc* ndiodp = (struct NaClDescIoDesc*)desc;
    407     p->file_info->desc = ndiodp->hd->d;
    408     pnacl_coordinator_.reset(NULL);
    409     NaClLog(4,
    410             "BitcodeTranslate_MainThreadContinuation: PP_OK, desc %d\n",
    411             p->file_info->desc);
    412   } else {
    413     NaClLog(4,
    414             "BitcodeTranslate_MainThreadContinuation: !PP_OK, "
    415             "setting desc -1\n");
    416     p->file_info->desc = -1;
    417     // Error should have been reported by pnacl coordinator.
    418     NaClLog(LOG_ERROR, "PluginReverseInterface::BitcodeTranslate error.\n");
    419   }
    420   *p->op_complete_ptr = true;
    421   NaClXCondVarBroadcast(&cv_);
    422 }
    423 
    424 
    425 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) {
    426   bool op_complete = false;
    427   bool op_result;
    428   CloseManifestEntryResource* to_close =
    429       new CloseManifestEntryResource(desc, &op_complete, &op_result);
    430 
    431   bool shutting_down;
    432   plugin::WeakRefCallOnMainThread(
    433       anchor_,
    434       0,
    435       this,
    436       &plugin::PluginReverseInterface::
    437         CloseManifestEntry_MainThreadContinuation,
    438       to_close);
    439   // wait for completion or surf-away.
    440   do {
    441     nacl::MutexLocker take(&mu_);
    442     for (;;) {
    443       shutting_down = shutting_down_;
    444       if (op_complete || shutting_down) {
    445         break;
    446       }
    447       NaClXCondVarWait(&cv_, &mu_);
    448     }
    449   } while (0);
    450 
    451   if (shutting_down) return false;
    452   // op_result true if close was successful; false otherwise (e.g., bad desc).
    453   return op_result;
    454 }
    455 
    456 void PluginReverseInterface::CloseManifestEntry_MainThreadContinuation(
    457     CloseManifestEntryResource* cls,
    458     int32_t err) {
    459   UNREFERENCED_PARAMETER(err);
    460 
    461   nacl::MutexLocker take(&mu_);
    462   // TODO(bsy): once the plugin has a reliable way to report that the
    463   // file usage is done -- and sel_ldr uses this RPC call -- we should
    464   // tell the plugin that the associated resources can be freed.
    465   *cls->op_result_ptr = true;
    466   *cls->op_complete_ptr = true;
    467   NaClXCondVarBroadcast(&cv_);
    468   // cls automatically deleted
    469 }
    470 
    471 void PluginReverseInterface::ReportCrash() {
    472   NaClLog(4, "PluginReverseInterface::ReportCrash\n");
    473 
    474   if (crash_cb_.pp_completion_callback().func != NULL) {
    475     NaClLog(4, "PluginReverseInterface::ReportCrash: invoking CB\n");
    476     pp::Module::Get()->core()->CallOnMainThread(0, crash_cb_, PP_OK);
    477   } else {
    478     NaClLog(1,
    479             "PluginReverseInterface::ReportCrash:"
    480             " crash_cb_ not valid, skipping\n");
    481   }
    482 }
    483 
    484 void PluginReverseInterface::ReportExitStatus(int exit_status) {
    485   service_runtime_->set_exit_status(exit_status);
    486 }
    487 
    488 void PluginReverseInterface::QuotaRequest_MainThreadContinuation(
    489     QuotaRequest* request,
    490     int32_t err) {
    491   if (err != PP_OK) {
    492     return;
    493   }
    494 
    495   switch (request->data.type) {
    496     case plugin::PepperQuotaType: {
    497       const PPB_FileIOTrusted* file_io_trusted =
    498           static_cast<const PPB_FileIOTrusted*>(
    499               pp::Module::Get()->GetBrowserInterface(
    500                   PPB_FILEIOTRUSTED_INTERFACE));
    501       // Copy the request object because this one will be deleted on return.
    502       // copy ctor!
    503       QuotaRequest* cont_for_response = new QuotaRequest(*request);
    504       pp::CompletionCallback quota_cc = WeakRefNewCallback(
    505           anchor_,
    506           this,
    507           &PluginReverseInterface::QuotaRequest_MainThreadResponse,
    508           cont_for_response);
    509       file_io_trusted->WillWrite(request->data.resource,
    510                                  request->offset,
    511                                  // TODO(sehr): remove need for cast.
    512                                  // Unify WillWrite interface vs Quota request.
    513                                  nacl::assert_cast<int32_t>(
    514                                      request->bytes_requested),
    515                                  quota_cc.pp_completion_callback());
    516       break;
    517     }
    518     case plugin::TempQuotaType: {
    519       uint64_t len = request->offset + request->bytes_requested;
    520       nacl::MutexLocker take(&mu_);
    521       // Do some crude quota enforcement.
    522       if (len > kMaxTempQuota) {
    523         *request->bytes_granted = 0;
    524       } else {
    525         *request->bytes_granted = request->bytes_requested;
    526       }
    527       *request->op_complete_ptr = true;
    528       NaClXCondVarBroadcast(&cv_);
    529       break;
    530     }
    531   }
    532   // request automatically deleted
    533 }
    534 
    535 void PluginReverseInterface::QuotaRequest_MainThreadResponse(
    536     QuotaRequest* request,
    537     int32_t err) {
    538   NaClLog(4,
    539           "PluginReverseInterface::QuotaRequest_MainThreadResponse:"
    540           " (resource=%" NACL_PRIx32 ", offset=%" NACL_PRId64 ", requested=%"
    541           NACL_PRId64 ", err=%" NACL_PRId32 ")\n",
    542           request->data.resource,
    543           request->offset, request->bytes_requested, err);
    544   nacl::MutexLocker take(&mu_);
    545   if (err >= PP_OK) {
    546     *request->bytes_granted = err;
    547   } else {
    548     *request->bytes_granted = 0;
    549   }
    550   *request->op_complete_ptr = true;
    551   NaClXCondVarBroadcast(&cv_);
    552   // request automatically deleted
    553 }
    554 
    555 int64_t PluginReverseInterface::RequestQuotaForWrite(
    556     nacl::string file_id, int64_t offset, int64_t bytes_to_write) {
    557   NaClLog(4,
    558           "PluginReverseInterface::RequestQuotaForWrite:"
    559           " (file_id='%s', offset=%" NACL_PRId64 ", bytes_to_write=%"
    560           NACL_PRId64 ")\n", file_id.c_str(), offset, bytes_to_write);
    561   QuotaData quota_data;
    562   {
    563     nacl::MutexLocker take(&mu_);
    564     uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10);
    565     if (quota_map_.find(file_key) == quota_map_.end()) {
    566       // Look up failed to find the requested quota managed resource.
    567       NaClLog(4, "PluginReverseInterface::RequestQuotaForWrite: failed...\n");
    568       return 0;
    569     }
    570     quota_data = quota_map_[file_key];
    571   }
    572   // Variables set by requesting quota.
    573   int64_t quota_granted = 0;
    574   bool op_complete = false;
    575   QuotaRequest* continuation =
    576       new QuotaRequest(quota_data, offset, bytes_to_write, &quota_granted,
    577                        &op_complete);
    578   // The reverse service is running on a background thread and the PPAPI quota
    579   // methods must be invoked only from the main thread.
    580   plugin::WeakRefCallOnMainThread(
    581       anchor_,
    582       0,  /* delay in ms */
    583       this,
    584       &plugin::PluginReverseInterface::QuotaRequest_MainThreadContinuation,
    585       continuation);
    586   // Wait for the main thread to request quota and signal completion.
    587   // It is also possible that the main thread will signal shut down.
    588   bool shutting_down;
    589   do {
    590     nacl::MutexLocker take(&mu_);
    591     for (;;) {
    592       shutting_down = shutting_down_;
    593       if (op_complete || shutting_down) {
    594         break;
    595       }
    596       NaClXCondVarWait(&cv_, &mu_);
    597     }
    598   } while (0);
    599   if (shutting_down) return 0;
    600   return quota_granted;
    601 }
    602 
    603 void PluginReverseInterface::AddQuotaManagedFile(const nacl::string& file_id,
    604                                                  const pp::FileIO& file_io) {
    605   PP_Resource resource = file_io.pp_resource();
    606   NaClLog(4,
    607           "PluginReverseInterface::AddQuotaManagedFile: "
    608           "(file_id='%s', file_io_ref=%" NACL_PRIx32 ")\n",
    609           file_id.c_str(), resource);
    610   nacl::MutexLocker take(&mu_);
    611   uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10);
    612   QuotaData data(plugin::PepperQuotaType, resource);
    613   quota_map_[file_key] = data;
    614 }
    615 
    616 void PluginReverseInterface::AddTempQuotaManagedFile(
    617     const nacl::string& file_id) {
    618   NaClLog(4, "PluginReverseInterface::AddTempQuotaManagedFile: "
    619           "(file_id='%s')\n", file_id.c_str());
    620   nacl::MutexLocker take(&mu_);
    621   uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10);
    622   QuotaData data(plugin::TempQuotaType, 0);
    623   quota_map_[file_key] = data;
    624 }
    625 
    626 ServiceRuntime::ServiceRuntime(Plugin* plugin,
    627                                const Manifest* manifest,
    628                                bool should_report_uma,
    629                                pp::CompletionCallback init_done_cb,
    630                                pp::CompletionCallback crash_cb)
    631     : plugin_(plugin),
    632       should_report_uma_(should_report_uma),
    633       reverse_service_(NULL),
    634       anchor_(new nacl::WeakRefAnchor()),
    635       rev_interface_(new PluginReverseInterface(anchor_, plugin,
    636                                                 manifest,
    637                                                 this,
    638                                                 init_done_cb, crash_cb)),
    639       exit_status_(-1),
    640       start_sel_ldr_done_(false) {
    641   NaClSrpcChannelInitialize(&command_channel_);
    642   NaClXMutexCtor(&mu_);
    643   NaClXCondVarCtor(&cond_);
    644 }
    645 
    646 bool ServiceRuntime::InitCommunication(nacl::DescWrapper* nacl_desc,
    647                                        ErrorInfo* error_info) {
    648   NaClLog(4, "ServiceRuntime::InitCommunication"
    649           " (this=%p, subprocess=%p)\n",
    650           static_cast<void*>(this),
    651           static_cast<void*>(subprocess_.get()));
    652   // Create the command channel to the sel_ldr and load the nexe from nacl_desc.
    653   if (!subprocess_->SetupCommandAndLoad(&command_channel_, nacl_desc)) {
    654     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL,
    655                           "ServiceRuntime: command channel creation failed");
    656     return false;
    657   }
    658   // Hook up the reverse service channel.  We are the IMC client, but
    659   // provide SRPC service.
    660   NaClDesc* out_conn_cap;
    661   NaClSrpcResultCodes rpc_result =
    662       NaClSrpcInvokeBySignature(&command_channel_,
    663                                 "reverse_setup::h",
    664                                 &out_conn_cap);
    665 
    666   if (NACL_SRPC_RESULT_OK != rpc_result) {
    667     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SETUP,
    668                           "ServiceRuntime: reverse setup rpc failed");
    669     return false;
    670   }
    671   //  Get connection capability to service runtime where the IMC
    672   //  server/SRPC client is waiting for a rendezvous.
    673   NaClLog(4, "ServiceRuntime: got 0x%" NACL_PRIxPTR "\n",
    674           (uintptr_t) out_conn_cap);
    675   nacl::DescWrapper* conn_cap = plugin_->wrapper_factory()->MakeGenericCleanup(
    676       out_conn_cap);
    677   if (conn_cap == NULL) {
    678     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_WRAPPER,
    679                           "ServiceRuntime: wrapper allocation failure");
    680     return false;
    681   }
    682   out_conn_cap = NULL;  // ownership passed
    683   NaClLog(4, "ServiceRuntime::InitCommunication: starting reverse service\n");
    684   reverse_service_ = new nacl::ReverseService(conn_cap, rev_interface_->Ref());
    685   if (!reverse_service_->Start()) {
    686     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SERVICE,
    687                           "ServiceRuntime: starting reverse services failed");
    688     return false;
    689   }
    690 
    691   // start the module.  otherwise we cannot connect for multimedia
    692   // subsystem since that is handled by user-level code (not secure!)
    693   // in libsrpc.
    694   int load_status = -1;
    695   rpc_result =
    696       NaClSrpcInvokeBySignature(&command_channel_,
    697                                 "start_module::i",
    698                                 &load_status);
    699 
    700   if (NACL_SRPC_RESULT_OK != rpc_result) {
    701     error_info->SetReport(ERROR_SEL_LDR_START_MODULE,
    702                           "ServiceRuntime: could not start nacl module");
    703     return false;
    704   }
    705   NaClLog(4, "ServiceRuntime::InitCommunication (load_status=%d)\n",
    706           load_status);
    707   if (should_report_uma_) {
    708     plugin_->ReportSelLdrLoadStatus(load_status);
    709   }
    710   if (LOAD_OK != load_status) {
    711     error_info->SetReport(
    712         ERROR_SEL_LDR_START_STATUS,
    713         NaClErrorString(static_cast<NaClErrorCode>(load_status)));
    714     return false;
    715   }
    716   return true;
    717 }
    718 
    719 bool ServiceRuntime::StartSelLdr(const SelLdrStartParams& params) {
    720   NaClLog(4, "ServiceRuntime::Start\n");
    721 
    722   nacl::scoped_ptr<SelLdrLauncherChrome>
    723       tmp_subprocess(new SelLdrLauncherChrome());
    724   if (NULL == tmp_subprocess.get()) {
    725     NaClLog(LOG_ERROR, "ServiceRuntime::Start (subprocess create failed)\n");
    726     params.error_info->SetReport(
    727         ERROR_SEL_LDR_CREATE_LAUNCHER,
    728         "ServiceRuntime: failed to create sel_ldr launcher");
    729     return false;
    730   }
    731   nacl::string error_message;
    732   bool started = tmp_subprocess->Start(plugin_->pp_instance(),
    733                                        params.url.c_str(),
    734                                        params.uses_irt,
    735                                        params.uses_ppapi,
    736                                        params.enable_dev_interfaces,
    737                                        params.enable_dyncode_syscalls,
    738                                        params.enable_exception_handling,
    739                                        &error_message);
    740   if (!started) {
    741     NaClLog(LOG_ERROR, "ServiceRuntime::Start (start failed)\n");
    742     params.error_info->SetReportWithConsoleOnlyError(
    743         ERROR_SEL_LDR_LAUNCH,
    744         "ServiceRuntime: failed to start",
    745         error_message);
    746     return false;
    747   }
    748 
    749   subprocess_.reset(tmp_subprocess.release());
    750   NaClLog(4, "ServiceRuntime::StartSelLdr (return 1)\n");
    751   return true;
    752 }
    753 
    754 void ServiceRuntime::WaitForSelLdrStart() {
    755   nacl::MutexLocker take(&mu_);
    756   while(!start_sel_ldr_done_) {
    757     NaClXCondVarWait(&cond_, &mu_);
    758   }
    759 }
    760 
    761 void ServiceRuntime::SignalStartSelLdrDone() {
    762   nacl::MutexLocker take(&mu_);
    763   start_sel_ldr_done_ = true;
    764   NaClXCondVarSignal(&cond_);
    765 }
    766 
    767 bool ServiceRuntime::LoadNexeAndStart(nacl::DescWrapper* nacl_desc,
    768                                       ErrorInfo* error_info,
    769                                       const pp::CompletionCallback& crash_cb) {
    770   NaClLog(4, "ServiceRuntime::LoadNexeAndStart (nacl_desc=%p)\n",
    771           reinterpret_cast<void*>(nacl_desc));
    772   if (!InitCommunication(nacl_desc, error_info)) {
    773     // On a load failure the service runtime does not crash itself to
    774     // avoid a race where the no-more-senders error on the reverse
    775     // channel esrvice thread might cause the crash-detection logic to
    776     // kick in before the start_module RPC reply has been received. So
    777     // we induce a service runtime crash here. We do not release
    778     // subprocess_ since it's needed to collect crash log output after
    779     // the error is reported.
    780     Log(LOG_FATAL, "reap logs");
    781     if (NULL == reverse_service_) {
    782       // No crash detector thread.
    783       NaClLog(LOG_ERROR, "scheduling to get crash log\n");
    784       pp::Module::Get()->core()->CallOnMainThread(0, crash_cb, PP_OK);
    785       NaClLog(LOG_ERROR, "should fire soon\n");
    786     } else {
    787       NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n");
    788     }
    789     return false;
    790   }
    791 
    792   NaClLog(4, "ServiceRuntime::LoadNexeAndStart (return 1)\n");
    793   return true;
    794 }
    795 
    796 SrpcClient* ServiceRuntime::SetupAppChannel() {
    797   NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n",
    798           reinterpret_cast<void*>(subprocess_.get()));
    799   nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect();
    800   if (NULL == connect_desc) {
    801     NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n");
    802     return NULL;
    803   } else {
    804     NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n",
    805             static_cast<void*>(connect_desc));
    806     SrpcClient* srpc_client = SrpcClient::New(connect_desc);
    807     NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n",
    808             static_cast<void*>(srpc_client));
    809     delete connect_desc;
    810     return srpc_client;
    811   }
    812 }
    813 
    814 bool ServiceRuntime::Log(int severity, const nacl::string& msg) {
    815   NaClSrpcResultCodes rpc_result =
    816       NaClSrpcInvokeBySignature(&command_channel_,
    817                                 "log:is:",
    818                                 severity,
    819                                 strdup(msg.c_str()));
    820   return (NACL_SRPC_RESULT_OK == rpc_result);
    821 }
    822 
    823 void ServiceRuntime::Shutdown() {
    824   rev_interface_->ShutDown();
    825   anchor_->Abandon();
    826   // Abandon callbacks, tell service threads to quit if they were
    827   // blocked waiting for main thread operations to finish.  Note that
    828   // some callbacks must still await their completion event, e.g.,
    829   // CallOnMainThread must still wait for the time out, or I/O events
    830   // must finish, so resources associated with pending events cannot
    831   // be deallocated.
    832 
    833   // Note that this does waitpid() to get rid of any zombie subprocess.
    834   subprocess_.reset(NULL);
    835 
    836   NaClSrpcDtor(&command_channel_);
    837 
    838   // subprocess_ has been shut down, but threads waiting on messages
    839   // from the service runtime may not have noticed yet.  The low-level
    840   // NaClSimpleRevService code takes care to refcount the data objects
    841   // that it needs, and reverse_service_ is also refcounted.  We wait
    842   // for the service threads to get their EOF indications.
    843   if (reverse_service_ != NULL) {
    844     reverse_service_->WaitForServiceThreadsToExit();
    845     reverse_service_->Unref();
    846     reverse_service_ = NULL;
    847   }
    848 }
    849 
    850 ServiceRuntime::~ServiceRuntime() {
    851   NaClLog(4, "ServiceRuntime::~ServiceRuntime (this=%p)\n",
    852           static_cast<void*>(this));
    853   // We do this just in case Shutdown() was not called.
    854   subprocess_.reset(NULL);
    855   if (reverse_service_ != NULL) {
    856     reverse_service_->Unref();
    857   }
    858 
    859   rev_interface_->Unref();
    860 
    861   anchor_->Unref();
    862   NaClCondVarDtor(&cond_);
    863   NaClMutexDtor(&mu_);
    864 }
    865 
    866 int ServiceRuntime::exit_status() {
    867   nacl::MutexLocker take(&mu_);
    868   return exit_status_;
    869 }
    870 
    871 void ServiceRuntime::set_exit_status(int exit_status) {
    872   nacl::MutexLocker take(&mu_);
    873   exit_status_ = exit_status & 0xff;
    874 }
    875 
    876 nacl::string ServiceRuntime::GetCrashLogOutput() {
    877   if (NULL != subprocess_.get()) {
    878     return subprocess_->GetCrashLogOutput();
    879   } else {
    880     return std::string();
    881   }
    882 }
    883 
    884 }  // namespace plugin
    885