/* * Copyright (c) 2012 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "ppapi/native_client/src/trusted/plugin/srpc_client.h" #include #include "native_client/src/shared/platform/nacl_log.h" #include "ppapi/native_client/src/trusted/plugin/plugin.h" #include "ppapi/native_client/src/trusted/plugin/srpc_params.h" #include "ppapi/native_client/src/trusted/plugin/utility.h" namespace plugin { typedef bool (*RpcFunction)(void* obj, SrpcParams* params); // MethodInfo records the method names and type signatures of an SRPC server. class MethodInfo { public: // statically defined method - called through a pointer MethodInfo(const RpcFunction function_ptr, const char* name, const char* ins, const char* outs, // index is set to UINT_MAX for methods implemented by the plugin, // All methods implemented by nacl modules have indexes // that are lower than UINT_MAX. const uint32_t index = UINT_MAX) : function_ptr_(function_ptr), name_(STRDUP(name)), ins_(STRDUP(ins)), outs_(STRDUP(outs)), index_(index) { } ~MethodInfo() { free(reinterpret_cast(name_)); free(reinterpret_cast(ins_)); free(reinterpret_cast(outs_)); } RpcFunction function_ptr() const { return function_ptr_; } char* name() const { return name_; } char* ins() const { return ins_; } char* outs() const { return outs_; } uint32_t index() const { return index_; } private: NACL_DISALLOW_COPY_AND_ASSIGN(MethodInfo); RpcFunction function_ptr_; char* name_; char* ins_; char* outs_; uint32_t index_; }; SrpcClient::SrpcClient() : srpc_channel_initialised_(false) { PLUGIN_PRINTF(("SrpcClient::SrpcClient (this=%p)\n", static_cast(this))); NaClSrpcChannelInitialize(&srpc_channel_); } SrpcClient* SrpcClient::New(nacl::DescWrapper* wrapper) { nacl::scoped_ptr srpc_client(new SrpcClient()); if (!srpc_client->Init(wrapper)) { PLUGIN_PRINTF(("SrpcClient::New (SrpcClient::Init failed)\n")); return NULL; } return srpc_client.release(); } bool SrpcClient::Init(nacl::DescWrapper* wrapper) { PLUGIN_PRINTF(("SrpcClient::Init (this=%p, wrapper=%p)\n", static_cast(this), static_cast(wrapper))); // Open the channel to pass RPC information back and forth if (!NaClSrpcClientCtor(&srpc_channel_, wrapper->desc())) { return false; } srpc_channel_initialised_ = true; PLUGIN_PRINTF(("SrpcClient::Init (Ctor worked)\n")); // Record the method names in a convenient way for later dispatches. GetMethods(); PLUGIN_PRINTF(("SrpcClient::Init (GetMethods worked)\n")); return true; } SrpcClient::~SrpcClient() { PLUGIN_PRINTF(("SrpcClient::~SrpcClient (this=%p, has_srpc_channel=%d)\n", static_cast(this), srpc_channel_initialised_)); // And delete the connection. if (srpc_channel_initialised_) { PLUGIN_PRINTF(("SrpcClient::~SrpcClient (destroying srpc_channel)\n")); NaClSrpcDtor(&srpc_channel_); } for (Methods::iterator iter = methods_.begin(); iter != methods_.end(); ++iter) { delete iter->second; } PLUGIN_PRINTF(("SrpcClient::~SrpcClient (return)\n")); } void SrpcClient::GetMethods() { PLUGIN_PRINTF(("SrpcClient::GetMethods (this=%p)\n", static_cast(this))); if (NULL == srpc_channel_.client) { return; } uint32_t method_count = NaClSrpcServiceMethodCount(srpc_channel_.client); // Intern the methods into a mapping from identifiers to MethodInfo. for (uint32_t i = 0; i < method_count; ++i) { int retval; const char* method_name; const char* input_types; const char* output_types; retval = NaClSrpcServiceMethodNameAndTypes(srpc_channel_.client, i, &method_name, &input_types, &output_types); if (!retval) { return; } if (!IsValidIdentifierString(method_name, NULL)) { // If name is not an ECMAScript identifier, do not enter it into the // methods_ table. continue; } MethodInfo* method_info = new MethodInfo(NULL, method_name, input_types, output_types, i); if (NULL == method_info) { return; } // Install in the map only if successfully read. methods_[method_name] = method_info; } } bool SrpcClient::HasMethod(const nacl::string& method_name) { bool has_method = (NULL != methods_[method_name]); PLUGIN_PRINTF(( "SrpcClient::HasMethod (this=%p, method_name='%s', return %d)\n", static_cast(this), method_name.c_str(), has_method)); return has_method; } bool SrpcClient::InitParams(const nacl::string& method_name, SrpcParams* params) { MethodInfo* method_info = methods_[method_name]; if (method_info) { return params->Init(method_info->ins(), method_info->outs()); } return false; } bool SrpcClient::Invoke(const nacl::string& method_name, SrpcParams* params) { // It would be better if we could set the exception on each detailed failure // case. However, there are calls to Invoke from within the plugin itself, // and these could leave residual exceptions pending. This seems to be // happening specifically with hard_shutdowns. PLUGIN_PRINTF(("SrpcClient::Invoke (this=%p, method_name='%s', params=%p)\n", static_cast(this), method_name.c_str(), static_cast(params))); // Ensure Invoke was called with a method name that has a binding. if (NULL == methods_[method_name]) { PLUGIN_PRINTF(("SrpcClient::Invoke (ident not in methods_)\n")); return false; } PLUGIN_PRINTF(("SrpcClient::Invoke (sending the rpc)\n")); // Call the method last_error_ = NaClSrpcInvokeV(&srpc_channel_, methods_[method_name]->index(), params->ins(), params->outs()); PLUGIN_PRINTF(("SrpcClient::Invoke (response=%d)\n", last_error_)); if (NACL_SRPC_RESULT_OK != last_error_) { PLUGIN_PRINTF(("SrpcClient::Invoke (err='%s', return 0)\n", NaClSrpcErrorString(last_error_))); return false; } PLUGIN_PRINTF(("SrpcClient::Invoke (return 1)\n")); return true; } void SrpcClient::AttachService(NaClSrpcService* service, void* instance_data) { srpc_channel_.server = service; srpc_channel_.server_instance_data = instance_data; } } // namespace plugin