srpc_client.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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#include "native_client/src/trusted/plugin/srpc_client.h"
8
9#include <string.h>
10
11#include "native_client/src/shared/platform/nacl_log.h"
12#include "native_client/src/trusted/plugin/plugin.h"
13#include "native_client/src/trusted/plugin/srpc_params.h"
14#include "native_client/src/trusted/plugin/utility.h"
15
16namespace plugin {
17
18typedef bool (*RpcFunction)(void* obj, SrpcParams* params);
19
20// MethodInfo records the method names and type signatures of an SRPC server.
21class MethodInfo {
22 public:
23  // statically defined method - called through a pointer
24  MethodInfo(const RpcFunction function_ptr,
25             const char* name,
26             const char* ins,
27             const char* outs,
28             // index is set to UINT_MAX for methods implemented by the plugin,
29             // All methods implemented by nacl modules have indexes
30             // that are lower than UINT_MAX.
31             const uint32_t index = UINT_MAX) :
32    function_ptr_(function_ptr),
33    name_(STRDUP(name)),
34    ins_(STRDUP(ins)),
35    outs_(STRDUP(outs)),
36    index_(index) { }
37
38  ~MethodInfo() {
39    free(reinterpret_cast<void*>(name_));
40    free(reinterpret_cast<void*>(ins_));
41    free(reinterpret_cast<void*>(outs_));
42  }
43
44  RpcFunction function_ptr() const { return function_ptr_; }
45  char* name() const { return name_; }
46  char* ins() const { return ins_; }
47  char* outs() const { return outs_; }
48  uint32_t index() const { return index_; }
49
50 private:
51  NACL_DISALLOW_COPY_AND_ASSIGN(MethodInfo);
52  RpcFunction function_ptr_;
53  char* name_;
54  char* ins_;
55  char* outs_;
56  uint32_t index_;
57};
58
59SrpcClient::SrpcClient()
60    : srpc_channel_initialised_(false) {
61  PLUGIN_PRINTF(("SrpcClient::SrpcClient (this=%p)\n",
62                 static_cast<void*>(this)));
63  NaClSrpcChannelInitialize(&srpc_channel_);
64}
65
66SrpcClient* SrpcClient::New(nacl::DescWrapper* wrapper) {
67  nacl::scoped_ptr<SrpcClient> srpc_client(new SrpcClient());
68  if (!srpc_client->Init(wrapper)) {
69    PLUGIN_PRINTF(("SrpcClient::New (SrpcClient::Init failed)\n"));
70    return NULL;
71  }
72  return srpc_client.release();
73}
74
75bool SrpcClient::Init(nacl::DescWrapper* wrapper) {
76  PLUGIN_PRINTF(("SrpcClient::Init (this=%p, wrapper=%p)\n",
77                 static_cast<void*>(this),
78                 static_cast<void*>(wrapper)));
79  // Open the channel to pass RPC information back and forth
80  if (!NaClSrpcClientCtor(&srpc_channel_, wrapper->desc())) {
81    return false;
82  }
83  srpc_channel_initialised_ = true;
84  PLUGIN_PRINTF(("SrpcClient::Init (Ctor worked)\n"));
85  // Record the method names in a convenient way for later dispatches.
86  GetMethods();
87  PLUGIN_PRINTF(("SrpcClient::Init (GetMethods worked)\n"));
88  return true;
89}
90
91SrpcClient::~SrpcClient() {
92  PLUGIN_PRINTF(("SrpcClient::~SrpcClient (this=%p, has_srpc_channel=%d)\n",
93                 static_cast<void*>(this), srpc_channel_initialised_));
94  // And delete the connection.
95  if (srpc_channel_initialised_) {
96    PLUGIN_PRINTF(("SrpcClient::~SrpcClient (destroying srpc_channel)\n"));
97    NaClSrpcDtor(&srpc_channel_);
98  }
99  for (Methods::iterator iter = methods_.begin();
100       iter != methods_.end();
101       ++iter) {
102    delete iter->second;
103  }
104  PLUGIN_PRINTF(("SrpcClient::~SrpcClient (return)\n"));
105}
106
107void SrpcClient::GetMethods() {
108  PLUGIN_PRINTF(("SrpcClient::GetMethods (this=%p)\n",
109                 static_cast<void*>(this)));
110  if (NULL == srpc_channel_.client) {
111    return;
112  }
113  uint32_t method_count = NaClSrpcServiceMethodCount(srpc_channel_.client);
114  // Intern the methods into a mapping from identifiers to MethodInfo.
115  for (uint32_t i = 0; i < method_count; ++i) {
116    int retval;
117    const char* method_name;
118    const char* input_types;
119    const char* output_types;
120
121    retval = NaClSrpcServiceMethodNameAndTypes(srpc_channel_.client,
122                                               i,
123                                               &method_name,
124                                               &input_types,
125                                               &output_types);
126    if (!retval) {
127      return;
128    }
129    if (!IsValidIdentifierString(method_name, NULL)) {
130      // If name is not an ECMAScript identifier, do not enter it into the
131      // methods_ table.
132      continue;
133    }
134    MethodInfo* method_info =
135        new MethodInfo(NULL, method_name, input_types, output_types, i);
136    if (NULL == method_info) {
137      return;
138    }
139    // Install in the map only if successfully read.
140    methods_[method_name] = method_info;
141  }
142}
143
144bool SrpcClient::HasMethod(const nacl::string& method_name) {
145  bool has_method = (NULL != methods_[method_name]);
146  PLUGIN_PRINTF((
147      "SrpcClient::HasMethod (this=%p, method_name='%s', return %d)\n",
148      static_cast<void*>(this), method_name.c_str(), has_method));
149  return has_method;
150}
151
152bool SrpcClient::InitParams(const nacl::string& method_name,
153                            SrpcParams* params) {
154  MethodInfo* method_info = methods_[method_name];
155  if (method_info) {
156    return params->Init(method_info->ins(), method_info->outs());
157  }
158  return false;
159}
160
161bool SrpcClient::Invoke(const nacl::string& method_name, SrpcParams* params) {
162  // It would be better if we could set the exception on each detailed failure
163  // case.  However, there are calls to Invoke from within the plugin itself,
164  // and these could leave residual exceptions pending.  This seems to be
165  // happening specifically with hard_shutdowns.
166  PLUGIN_PRINTF(("SrpcClient::Invoke (this=%p, method_name='%s', params=%p)\n",
167                 static_cast<void*>(this),
168                 method_name.c_str(),
169                 static_cast<void*>(params)));
170
171  // Ensure Invoke was called with a method name that has a binding.
172  if (NULL == methods_[method_name]) {
173    PLUGIN_PRINTF(("SrpcClient::Invoke (ident not in methods_)\n"));
174    return false;
175  }
176
177  PLUGIN_PRINTF(("SrpcClient::Invoke (sending the rpc)\n"));
178  // Call the method
179  last_error_ = NaClSrpcInvokeV(&srpc_channel_,
180                                methods_[method_name]->index(),
181                                params->ins(),
182                                params->outs());
183  PLUGIN_PRINTF(("SrpcClient::Invoke (response=%d)\n", last_error_));
184  if (NACL_SRPC_RESULT_OK != last_error_) {
185    PLUGIN_PRINTF(("SrpcClient::Invoke (err='%s', return 0)\n",
186                   NaClSrpcErrorString(last_error_)));
187    return false;
188  }
189
190  PLUGIN_PRINTF(("SrpcClient::Invoke (return 1)\n"));
191  return true;
192}
193
194void SrpcClient::AttachService(NaClSrpcService* service, void* instance_data) {
195  srpc_channel_.server = service;
196  srpc_channel_.server_instance_data = instance_data;
197}
198
199}  // namespace plugin
200