Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / src / trusted / plugin / service_runtime.cc
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::DoPostMessage(nacl::string message) {
103   PostMessageResource* continuation = new PostMessageResource(message);
104   CHECK(continuation != NULL);
105   NaClLog(4, "PluginReverseInterface::DoPostMessage(%s)\n", message.c_str());
106   plugin::WeakRefCallOnMainThread(
107       anchor_,
108       0,  /* delay in ms */
109       this,
110       &plugin::PluginReverseInterface::PostMessage_MainThreadContinuation,
111       continuation);
112 }
113
114 void PluginReverseInterface::StartupInitializationComplete() {
115   NaClLog(4, "PluginReverseInterface::StartupInitializationComplete\n");
116   if (init_done_cb_.pp_completion_callback().func != NULL) {
117     NaClLog(4,
118             "PluginReverseInterface::StartupInitializationComplete:"
119             " invoking CB\n");
120     pp::Module::Get()->core()->CallOnMainThread(0, init_done_cb_, PP_OK);
121   } else {
122     NaClLog(1,
123             "PluginReverseInterface::StartupInitializationComplete:"
124             " init_done_cb_ not valid, skipping.\n");
125   }
126 }
127
128 void PluginReverseInterface::PostMessage_MainThreadContinuation(
129     PostMessageResource* p,
130     int32_t err) {
131   UNREFERENCED_PARAMETER(err);
132   NaClLog(4,
133           "PluginReverseInterface::PostMessage_MainThreadContinuation(%s)\n",
134           p->message.c_str());
135   plugin_->PostMessage(std::string("DEBUG_POSTMESSAGE:") + p->message);
136 }
137
138 bool PluginReverseInterface::EnumerateManifestKeys(
139     std::set<nacl::string>* out_keys) {
140   Manifest const* mp = manifest_;
141
142   if (!mp->GetFileKeys(out_keys)) {
143     return false;
144   }
145
146   return true;
147 }
148
149 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey
150 // and invoke StreamAsFile with a completion callback that invokes
151 // GetPOSIXFileDesc.
152 bool PluginReverseInterface::OpenManifestEntry(nacl::string url_key,
153                                                struct NaClFileInfo* info) {
154   ErrorInfo error_info;
155   bool op_complete = false;  // NB: mu_ and cv_ also controls access to this!
156   // The to_open object is owned by the weak ref callback. Because this function
157   // waits for the callback to finish, the to_open object will be deallocated on
158   // the main thread before this function can return. The pointers it contains
159   // to stack variables will not leak.
160   OpenManifestEntryResource* to_open =
161       new OpenManifestEntryResource(url_key, info,
162                                     &error_info, &op_complete);
163   CHECK(to_open != NULL);
164   NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n",
165           url_key.c_str());
166   // This assumes we are not on the main thread.  If false, we deadlock.
167   plugin::WeakRefCallOnMainThread(
168       anchor_,
169       0,
170       this,
171       &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation,
172       to_open);
173   NaClLog(4,
174           "PluginReverseInterface::OpenManifestEntry:"
175           " waiting on main thread\n");
176   bool shutting_down;
177   do {
178     nacl::MutexLocker take(&mu_);
179     for (;;) {
180       NaClLog(4,
181               "PluginReverseInterface::OpenManifestEntry:"
182               " got lock, checking shutdown and completion: (%s, %s)\n",
183               shutting_down_ ? "yes" : "no",
184               op_complete ? "yes" : "no");
185       shutting_down = shutting_down_;
186       if (op_complete || shutting_down) {
187         NaClLog(4,
188                 "PluginReverseInterface::OpenManifestEntry:"
189                 " done!\n");
190         break;
191       }
192       NaClXCondVarWait(&cv_, &mu_);
193     }
194   } while (0);
195   if (shutting_down) {
196     NaClLog(4,
197             "PluginReverseInterface::OpenManifestEntry:"
198             " plugin is shutting down\n");
199     return false;
200   }
201   // out_desc has the returned descriptor if successful, else -1.
202
203   // The caller is responsible for not closing *out_desc.  If it is
204   // closed prematurely, then another open could re-use the OS
205   // descriptor, confusing the opened_ map.  If the caller is going to
206   // want to make a NaClDesc object and transfer it etc., then the
207   // caller should DUP the descriptor (but remember the original
208   // value) for use by the NaClDesc object, which closes when the
209   // object is destroyed.
210   NaClLog(4,
211           "PluginReverseInterface::OpenManifestEntry:"
212           " *out_desc = %d\n",
213           info->desc);
214   if (info->desc == -1) {
215     // TODO(bsy,ncbray): what else should we do with the error?  This
216     // is a runtime error that may simply be a programming error in
217     // the untrusted code, or it may be something else wrong w/ the
218     // manifest.
219     NaClLog(4,
220             "OpenManifestEntry: failed for key %s, code %d (%s)\n",
221             url_key.c_str(),
222             error_info.error_code(),
223             error_info.message().c_str());
224   }
225   return true;
226 }
227
228 // Transfer point from OpenManifestEntry() which runs on the main thread
229 // (Some PPAPI actions -- like StreamAsFile -- can only run on the main thread).
230 // OpenManifestEntry() is waiting on a condvar for this continuation to
231 // complete.  We Broadcast and awaken OpenManifestEntry() whenever we are done
232 // either here, or in a later MainThreadContinuation step, if there are
233 // multiple steps.
234 void PluginReverseInterface::OpenManifestEntry_MainThreadContinuation(
235     OpenManifestEntryResource* p,
236     int32_t err) {
237   OpenManifestEntryResource *open_cont;
238   UNREFERENCED_PARAMETER(err);
239   // CallOnMainThread continuations always called with err == PP_OK.
240
241   NaClLog(4, "Entered OpenManifestEntry_MainThreadContinuation\n");
242
243   std::string mapped_url;
244   PnaclOptions pnacl_options;
245   if (!manifest_->ResolveKey(p->url, &mapped_url,
246                              &pnacl_options, p->error_info)) {
247     NaClLog(4, "OpenManifestEntry_MainThreadContinuation: ResolveKey failed\n");
248     // Failed, and error_info has the details on what happened.  Wake
249     // up requesting thread -- we are done.
250     nacl::MutexLocker take(&mu_);
251     *p->op_complete_ptr = true;  // done...
252     p->file_info->desc = -1;  // but failed.
253     NaClXCondVarBroadcast(&cv_);
254     return;
255   }
256   NaClLog(4,
257           "OpenManifestEntry_MainThreadContinuation: "
258           "ResolveKey: %s -> %s (pnacl_translate(%d))\n",
259           p->url.c_str(), mapped_url.c_str(), pnacl_options.translate());
260
261   open_cont = new OpenManifestEntryResource(*p);  // copy ctor!
262   CHECK(open_cont != NULL);
263   open_cont->url = mapped_url;
264   if (!pnacl_options.translate()) {
265     pp::CompletionCallback stream_cc = WeakRefNewCallback(
266         anchor_,
267         this,
268         &PluginReverseInterface::StreamAsFile_MainThreadContinuation,
269         open_cont);
270     // Normal files.
271     if (!PnaclUrls::IsPnaclComponent(mapped_url)) {
272       if (!plugin_->StreamAsFile(mapped_url,
273                                  stream_cc.pp_completion_callback())) {
274         NaClLog(4,
275                 "OpenManifestEntry_MainThreadContinuation: "
276                 "StreamAsFile failed\n");
277         nacl::MutexLocker take(&mu_);
278         *p->op_complete_ptr = true;  // done...
279         p->file_info->desc = -1;       // but failed.
280         p->error_info->SetReport(ERROR_MANIFEST_OPEN,
281                                  "ServiceRuntime: StreamAsFile failed");
282         NaClXCondVarBroadcast(&cv_);
283         return;
284       }
285       NaClLog(4,
286               "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n");
287     } else {
288       // Special PNaCl support files, that are installed on the
289       // user machine.
290       int32_t fd = PnaclResources::GetPnaclFD(
291           plugin_,
292           PnaclUrls::PnaclComponentURLToFilename(mapped_url).c_str());
293       if (fd < 0) {
294         // We checked earlier if the pnacl component wasn't installed
295         // yet, so this shouldn't happen. At this point, we can't do much
296         // anymore, so just continue with an invalid fd.
297         NaClLog(4,
298                 "OpenManifestEntry_MainThreadContinuation: "
299                 "GetReadonlyPnaclFd failed\n");
300         // TODO(jvoung): Separate the error codes?
301         p->error_info->SetReport(ERROR_MANIFEST_OPEN,
302                                  "ServiceRuntime: GetPnaclFd failed");
303       }
304       nacl::MutexLocker take(&mu_);
305       *p->op_complete_ptr = true;  // done!
306       // TODO(ncbray): enable the fast loading and validation paths for this
307       // type of file.
308       p->file_info->desc = fd;
309       NaClXCondVarBroadcast(&cv_);
310       NaClLog(4,
311               "OpenManifestEntry_MainThreadContinuation: GetPnaclFd okay\n");
312     }
313   } else {
314     // Requires PNaCl translation, but that's not supported.
315     NaClLog(4,
316             "OpenManifestEntry_MainThreadContinuation: "
317             "Requires PNaCl translation -- not supported\n");
318     nacl::MutexLocker take(&mu_);
319     *p->op_complete_ptr = true;  // done...
320     p->file_info->desc = -1;  // but failed.
321     p->error_info->SetReport(
322         ERROR_MANIFEST_OPEN,
323         "ServiceRuntime: Translating OpenManifestEntry files not supported");
324     NaClXCondVarBroadcast(&cv_);
325     return;
326   }
327   // p is deleted automatically
328 }
329
330 void PluginReverseInterface::StreamAsFile_MainThreadContinuation(
331     OpenManifestEntryResource* p,
332     int32_t result) {
333   NaClLog(4,
334           "Entered StreamAsFile_MainThreadContinuation\n");
335
336   nacl::MutexLocker take(&mu_);
337   if (result == PP_OK) {
338     NaClLog(4, "StreamAsFile_MainThreadContinuation: GetFileInfo(%s)\n",
339             p->url.c_str());
340     *p->file_info = plugin_->GetFileInfo(p->url);
341
342     NaClLog(4,
343             "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n",
344             p->file_info->desc);
345   } else {
346     NaClLog(4,
347             "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n");
348     p->file_info->desc = -1;
349     p->error_info->SetReport(ERROR_MANIFEST_OPEN,
350                              "Plugin StreamAsFile failed at callback");
351   }
352   *p->op_complete_ptr = true;
353   NaClXCondVarBroadcast(&cv_);
354 }
355
356 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) {
357   bool op_complete = false;
358   bool op_result;
359   CloseManifestEntryResource* to_close =
360       new CloseManifestEntryResource(desc, &op_complete, &op_result);
361
362   bool shutting_down;
363   plugin::WeakRefCallOnMainThread(
364       anchor_,
365       0,
366       this,
367       &plugin::PluginReverseInterface::
368         CloseManifestEntry_MainThreadContinuation,
369       to_close);
370   // wait for completion or surf-away.
371   do {
372     nacl::MutexLocker take(&mu_);
373     for (;;) {
374       shutting_down = shutting_down_;
375       if (op_complete || shutting_down) {
376         break;
377       }
378       NaClXCondVarWait(&cv_, &mu_);
379     }
380   } while (0);
381
382   if (shutting_down) return false;
383   // op_result true if close was successful; false otherwise (e.g., bad desc).
384   return op_result;
385 }
386
387 void PluginReverseInterface::CloseManifestEntry_MainThreadContinuation(
388     CloseManifestEntryResource* cls,
389     int32_t err) {
390   UNREFERENCED_PARAMETER(err);
391
392   nacl::MutexLocker take(&mu_);
393   // TODO(bsy): once the plugin has a reliable way to report that the
394   // file usage is done -- and sel_ldr uses this RPC call -- we should
395   // tell the plugin that the associated resources can be freed.
396   *cls->op_result_ptr = true;
397   *cls->op_complete_ptr = true;
398   NaClXCondVarBroadcast(&cv_);
399   // cls automatically deleted
400 }
401
402 void PluginReverseInterface::ReportCrash() {
403   NaClLog(4, "PluginReverseInterface::ReportCrash\n");
404
405   if (crash_cb_.pp_completion_callback().func != NULL) {
406     NaClLog(4, "PluginReverseInterface::ReportCrash: invoking CB\n");
407     pp::Module::Get()->core()->CallOnMainThread(0, crash_cb_, PP_OK);
408   } else {
409     NaClLog(1,
410             "PluginReverseInterface::ReportCrash:"
411             " crash_cb_ not valid, skipping\n");
412   }
413 }
414
415 void PluginReverseInterface::ReportExitStatus(int exit_status) {
416   service_runtime_->set_exit_status(exit_status);
417 }
418
419 int64_t PluginReverseInterface::RequestQuotaForWrite(
420     nacl::string file_id, int64_t offset, int64_t bytes_to_write) {
421   NaClLog(4,
422           "PluginReverseInterface::RequestQuotaForWrite:"
423           " (file_id='%s', offset=%" NACL_PRId64 ", bytes_to_write=%"
424           NACL_PRId64 ")\n", file_id.c_str(), offset, bytes_to_write);
425   uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10);
426   nacl::MutexLocker take(&mu_);
427   if (quota_files_.count(file_key) == 0) {
428     // Look up failed to find the requested quota managed resource.
429     NaClLog(4, "PluginReverseInterface::RequestQuotaForWrite: failed...\n");
430     return 0;
431   }
432
433   // Because we now only support this interface for tempfiles which are not
434   // pepper objects, we can just do some crude quota enforcement here rather
435   // than calling out to pepper from the main thread.
436   if (offset + bytes_to_write >= kMaxTempQuota)
437     return 0;
438
439   return bytes_to_write;
440 }
441
442 void PluginReverseInterface::AddTempQuotaManagedFile(
443     const nacl::string& file_id) {
444   NaClLog(4, "PluginReverseInterface::AddTempQuotaManagedFile: "
445           "(file_id='%s')\n", file_id.c_str());
446   uint64_t file_key = STRTOULL(file_id.c_str(), NULL, 10);
447   nacl::MutexLocker take(&mu_);
448   quota_files_.insert(file_key);
449 }
450
451 ServiceRuntime::ServiceRuntime(Plugin* plugin,
452                                const Manifest* manifest,
453                                bool main_service_runtime,
454                                pp::CompletionCallback init_done_cb,
455                                pp::CompletionCallback crash_cb)
456     : plugin_(plugin),
457       main_service_runtime_(main_service_runtime),
458       reverse_service_(NULL),
459       anchor_(new nacl::WeakRefAnchor()),
460       rev_interface_(new PluginReverseInterface(anchor_, plugin,
461                                                 manifest,
462                                                 this,
463                                                 init_done_cb, crash_cb)),
464       exit_status_(-1),
465       start_sel_ldr_done_(false),
466       callback_factory_(this) {
467   NaClSrpcChannelInitialize(&command_channel_);
468   NaClXMutexCtor(&mu_);
469   NaClXCondVarCtor(&cond_);
470 }
471
472 bool ServiceRuntime::LoadModule(nacl::DescWrapper* nacl_desc,
473                                 ErrorInfo* error_info) {
474   NaClLog(4, "ServiceRuntime::LoadModule"
475           " (this=%p, subprocess=%p)\n",
476           static_cast<void*>(this),
477           static_cast<void*>(subprocess_.get()));
478   CHECK(nacl_desc);
479   // Create the command channel to the sel_ldr and load the nexe from nacl_desc.
480   if (!subprocess_->SetupCommand(&command_channel_)) {
481     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL,
482                           "ServiceRuntime: command channel creation failed");
483     return false;
484   }
485
486   if (!subprocess_->LoadModule(&command_channel_, nacl_desc)) {
487     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_CMD_CHANNEL,
488                           "ServiceRuntime: load module failed");
489     return false;
490   }
491   return true;
492 }
493
494 bool ServiceRuntime::InitReverseService(ErrorInfo* error_info) {
495   // Hook up the reverse service channel.  We are the IMC client, but
496   // provide SRPC service.
497   NaClDesc* out_conn_cap;
498   NaClSrpcResultCodes rpc_result =
499       NaClSrpcInvokeBySignature(&command_channel_,
500                                 "reverse_setup::h",
501                                 &out_conn_cap);
502
503   if (NACL_SRPC_RESULT_OK != rpc_result) {
504     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SETUP,
505                           "ServiceRuntime: reverse setup rpc failed");
506     return false;
507   }
508   //  Get connection capability to service runtime where the IMC
509   //  server/SRPC client is waiting for a rendezvous.
510   NaClLog(4, "ServiceRuntime: got 0x%" NACL_PRIxPTR "\n",
511           (uintptr_t) out_conn_cap);
512   nacl::DescWrapper* conn_cap = plugin_->wrapper_factory()->MakeGenericCleanup(
513       out_conn_cap);
514   if (conn_cap == NULL) {
515     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_WRAPPER,
516                           "ServiceRuntime: wrapper allocation failure");
517     return false;
518   }
519   out_conn_cap = NULL;  // ownership passed
520   NaClLog(4, "ServiceRuntime::InitReverseService: starting reverse service\n");
521   reverse_service_ = new nacl::ReverseService(conn_cap, rev_interface_->Ref());
522   if (!reverse_service_->Start()) {
523     error_info->SetReport(ERROR_SEL_LDR_COMMUNICATION_REV_SERVICE,
524                           "ServiceRuntime: starting reverse services failed");
525     return false;
526   }
527   return true;
528 }
529
530 bool ServiceRuntime::StartModule(ErrorInfo* error_info) {
531   // start the module.  otherwise we cannot connect for multimedia
532   // subsystem since that is handled by user-level code (not secure!)
533   // in libsrpc.
534   int load_status = -1;
535   NaClSrpcResultCodes rpc_result =
536       NaClSrpcInvokeBySignature(&command_channel_,
537                                 "start_module::i",
538                                 &load_status);
539
540   if (NACL_SRPC_RESULT_OK != rpc_result) {
541     error_info->SetReport(ERROR_SEL_LDR_START_MODULE,
542                           "ServiceRuntime: could not start nacl module");
543     return false;
544   }
545   NaClLog(4, "ServiceRuntime::StartModule (load_status=%d)\n",
546           load_status);
547   if (main_service_runtime_) {
548     plugin_->ReportSelLdrLoadStatus(load_status);
549   }
550   if (LOAD_OK != load_status) {
551     error_info->SetReport(
552         ERROR_SEL_LDR_START_STATUS,
553         NaClErrorString(static_cast<NaClErrorCode>(load_status)));
554     return false;
555   }
556   return true;
557 }
558
559 void ServiceRuntime::StartSelLdr(const SelLdrStartParams& params,
560                                  pp::CompletionCallback callback) {
561   NaClLog(4, "ServiceRuntime::Start\n");
562
563   nacl::scoped_ptr<SelLdrLauncherChrome>
564       tmp_subprocess(new SelLdrLauncherChrome());
565   if (NULL == tmp_subprocess.get()) {
566     NaClLog(LOG_ERROR, "ServiceRuntime::Start (subprocess create failed)\n");
567     if (main_service_runtime_) {
568       ErrorInfo error_info;
569       error_info.SetReport(
570           ERROR_SEL_LDR_CREATE_LAUNCHER,
571           "ServiceRuntime: failed to create sel_ldr launcher");
572       plugin_->ReportLoadError(error_info);
573     }
574     pp::Module::Get()->core()->CallOnMainThread(0, callback, PP_ERROR_FAILED);
575     return;
576   }
577   pp::CompletionCallback internal_callback =
578       callback_factory_.NewCallback(&ServiceRuntime::StartSelLdrContinuation,
579                                     callback);
580
581   tmp_subprocess->Start(plugin_->pp_instance(),
582                         params.url.c_str(),
583                         params.uses_irt,
584                         params.uses_ppapi,
585                         params.enable_dev_interfaces,
586                         params.enable_dyncode_syscalls,
587                         params.enable_exception_handling,
588                         params.enable_crash_throttling,
589                         &start_sel_ldr_error_message_,
590                         internal_callback);
591   subprocess_.reset(tmp_subprocess.release());
592 }
593
594 void ServiceRuntime::StartSelLdrContinuation(int32_t pp_error,
595                                              pp::CompletionCallback callback) {
596   if (pp_error != PP_OK) {
597     NaClLog(LOG_ERROR, "ServiceRuntime::StartSelLdrContinuation "
598                        " (start failed)\n");
599     if (main_service_runtime_) {
600       std::string error_message;
601       pp::Var var_error_message_cpp(pp::PASS_REF, start_sel_ldr_error_message_);
602       if (var_error_message_cpp.is_string()) {
603         error_message = var_error_message_cpp.AsString();
604       }
605       ErrorInfo error_info;
606       error_info.SetReportWithConsoleOnlyError(
607           ERROR_SEL_LDR_LAUNCH,
608           "ServiceRuntime: failed to start",
609           error_message);
610       plugin_->ReportLoadError(error_info);
611     }
612   }
613   pp::Module::Get()->core()->CallOnMainThread(0, callback, pp_error);
614 }
615
616 void ServiceRuntime::WaitForSelLdrStart() {
617   nacl::MutexLocker take(&mu_);
618   while(!start_sel_ldr_done_) {
619     NaClXCondVarWait(&cond_, &mu_);
620   }
621 }
622
623 void ServiceRuntime::SignalStartSelLdrDone() {
624   nacl::MutexLocker take(&mu_);
625   start_sel_ldr_done_ = true;
626   NaClXCondVarSignal(&cond_);
627 }
628
629 bool ServiceRuntime::LoadNexeAndStart(nacl::DescWrapper* nacl_desc,
630                                       const pp::CompletionCallback& crash_cb) {
631   NaClLog(4, "ServiceRuntime::LoadNexeAndStart (nacl_desc=%p)\n",
632           reinterpret_cast<void*>(nacl_desc));
633   ErrorInfo error_info;
634   bool ok = LoadModule(nacl_desc, &error_info) &&
635             InitReverseService(&error_info) &&
636             StartModule(&error_info);
637   if (!ok) {
638     if (main_service_runtime_) {
639       plugin_->ReportLoadError(error_info);
640     }
641     // On a load failure the service runtime does not crash itself to
642     // avoid a race where the no-more-senders error on the reverse
643     // channel esrvice thread might cause the crash-detection logic to
644     // kick in before the start_module RPC reply has been received. So
645     // we induce a service runtime crash here. We do not release
646     // subprocess_ since it's needed to collect crash log output after
647     // the error is reported.
648     Log(LOG_FATAL, "reap logs");
649     if (NULL == reverse_service_) {
650       // No crash detector thread.
651       NaClLog(LOG_ERROR, "scheduling to get crash log\n");
652       pp::Module::Get()->core()->CallOnMainThread(0, crash_cb, PP_OK);
653       NaClLog(LOG_ERROR, "should fire soon\n");
654     } else {
655       NaClLog(LOG_ERROR, "Reverse service thread will pick up crash log\n");
656     }
657     return false;
658   }
659
660   NaClLog(4, "ServiceRuntime::LoadNexeAndStart (return 1)\n");
661   return true;
662 }
663
664 SrpcClient* ServiceRuntime::SetupAppChannel() {
665   NaClLog(4, "ServiceRuntime::SetupAppChannel (subprocess_=%p)\n",
666           reinterpret_cast<void*>(subprocess_.get()));
667   nacl::DescWrapper* connect_desc = subprocess_->socket_addr()->Connect();
668   if (NULL == connect_desc) {
669     NaClLog(LOG_ERROR, "ServiceRuntime::SetupAppChannel (connect failed)\n");
670     return NULL;
671   } else {
672     NaClLog(4, "ServiceRuntime::SetupAppChannel (conect_desc=%p)\n",
673             static_cast<void*>(connect_desc));
674     SrpcClient* srpc_client = SrpcClient::New(connect_desc);
675     NaClLog(4, "ServiceRuntime::SetupAppChannel (srpc_client=%p)\n",
676             static_cast<void*>(srpc_client));
677     delete connect_desc;
678     return srpc_client;
679   }
680 }
681
682 bool ServiceRuntime::Log(int severity, const nacl::string& msg) {
683   NaClSrpcResultCodes rpc_result =
684       NaClSrpcInvokeBySignature(&command_channel_,
685                                 "log:is:",
686                                 severity,
687                                 strdup(msg.c_str()));
688   return (NACL_SRPC_RESULT_OK == rpc_result);
689 }
690
691 void ServiceRuntime::Shutdown() {
692   rev_interface_->ShutDown();
693   anchor_->Abandon();
694   // Abandon callbacks, tell service threads to quit if they were
695   // blocked waiting for main thread operations to finish.  Note that
696   // some callbacks must still await their completion event, e.g.,
697   // CallOnMainThread must still wait for the time out, or I/O events
698   // must finish, so resources associated with pending events cannot
699   // be deallocated.
700
701   // Note that this does waitpid() to get rid of any zombie subprocess.
702   subprocess_.reset(NULL);
703
704   NaClSrpcDtor(&command_channel_);
705
706   // subprocess_ has been shut down, but threads waiting on messages
707   // from the service runtime may not have noticed yet.  The low-level
708   // NaClSimpleRevService code takes care to refcount the data objects
709   // that it needs, and reverse_service_ is also refcounted.  We wait
710   // for the service threads to get their EOF indications.
711   if (reverse_service_ != NULL) {
712     reverse_service_->WaitForServiceThreadsToExit();
713     reverse_service_->Unref();
714     reverse_service_ = NULL;
715   }
716 }
717
718 ServiceRuntime::~ServiceRuntime() {
719   NaClLog(4, "ServiceRuntime::~ServiceRuntime (this=%p)\n",
720           static_cast<void*>(this));
721   // We do this just in case Shutdown() was not called.
722   subprocess_.reset(NULL);
723   if (reverse_service_ != NULL) {
724     reverse_service_->Unref();
725   }
726
727   rev_interface_->Unref();
728
729   anchor_->Unref();
730   NaClCondVarDtor(&cond_);
731   NaClMutexDtor(&mu_);
732 }
733
734 void ServiceRuntime::set_exit_status(int exit_status) {
735   nacl::MutexLocker take(&mu_);
736   if (main_service_runtime_)
737     plugin_->set_exit_status(exit_status & 0xff);
738 }
739
740 nacl::string ServiceRuntime::GetCrashLogOutput() {
741   if (NULL != subprocess_.get()) {
742     return subprocess_->GetCrashLogOutput();
743   } else {
744     return std::string();
745   }
746 }
747
748 }  // namespace plugin