blob: 58efd1a49fb6e315db96db351495422257c34870 [file] [log] [blame]
/*
* 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 "components/nacl/renderer/plugin/srpc_client.h"
#include <string.h>
#include "components/nacl/renderer/plugin/plugin.h"
#include "components/nacl/renderer/plugin/srpc_params.h"
#include "components/nacl/renderer/plugin/utility.h"
#include "native_client/src/shared/platform/nacl_log.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<void*>(name_));
free(reinterpret_cast<void*>(ins_));
free(reinterpret_cast<void*>(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<void*>(this)));
NaClSrpcChannelInitialize(&srpc_channel_);
}
SrpcClient* SrpcClient::New(nacl::DescWrapper* wrapper) {
nacl::scoped_ptr<SrpcClient> 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<void*>(this),
static_cast<void*>(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<void*>(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<void*>(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 std::string& method_name) {
bool has_method = (NULL != methods_[method_name]);
PLUGIN_PRINTF((
"SrpcClient::HasMethod (this=%p, method_name='%s', return %d)\n",
static_cast<void*>(this), method_name.c_str(), has_method));
return has_method;
}
bool SrpcClient::InitParams(const std::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 std::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<void*>(this),
method_name.c_str(),
static_cast<void*>(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