Home | History | Annotate | Download | only in devfs
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "nacl_io/devfs/jspipe_event_emitter.h"
      6 
      7 #include <assert.h>
      8 #include <errno.h>
      9 #include <string.h>
     10 
     11 #include <algorithm>
     12 
     13 #define TRACE(format, ...) \
     14   LOG_TRACE("jspipe[%s]: " format, name_.c_str(), ##__VA_ARGS__)
     15 #define ERROR(format, ...) \
     16   LOG_ERROR("jspipe[%s]: " format, name_.c_str(), ##__VA_ARGS__)
     17 
     18 #include "nacl_io/log.h"
     19 #include "nacl_io/osinttypes.h"
     20 #include "nacl_io/pepper_interface.h"
     21 
     22 namespace {
     23 const size_t kMaxPostMessageSize = 64 * 1024;
     24 const char* kDictKeyPipe = "pipe";
     25 const char* kDictKeyOperation = "operation";
     26 const char* kDictKeyPayload = "payload";
     27 const char* kOperationNameAck = "ack";
     28 const char* kOperationNameWrite = "write";
     29 }
     30 
     31 namespace nacl_io {
     32 
     33 JSPipeEventEmitter::JSPipeEventEmitter(PepperInterface* ppapi, size_t size)
     34     : input_fifo_(size),
     35       post_message_buffer_size_(size),
     36       bytes_sent_(0),
     37       bytes_acked_(0),
     38       bytes_read_(0),
     39       ppapi_(ppapi),
     40       messaging_iface_(NULL),
     41       var_iface_(NULL),
     42       array_iface_(NULL),
     43       buffer_iface_(NULL),
     44       dict_iface_(NULL),
     45       pipe_name_var_(PP_MakeUndefined()),
     46       pipe_key_(PP_MakeUndefined()),
     47       operation_key_(PP_MakeUndefined()),
     48       payload_key_(PP_MakeUndefined()),
     49       write_var_(PP_MakeUndefined()),
     50       ack_var_(PP_MakeUndefined()) {
     51   UpdateStatus_Locked();
     52   if (ppapi == NULL) {
     53     TRACE("missing PPAPI provider");
     54     return;
     55   }
     56   messaging_iface_ = ppapi->GetMessagingInterface();
     57   var_iface_ = ppapi->GetVarInterface();
     58   array_iface_ = ppapi->GetVarArrayInterface();
     59   buffer_iface_ = ppapi->GetVarArrayBufferInterface();
     60   dict_iface_ = ppapi->GetVarDictionaryInterface();
     61 
     62   if (var_iface_ == NULL)
     63     return;
     64 
     65   pipe_key_ = VarFromCStr(kDictKeyPipe);
     66   operation_key_ = VarFromCStr(kDictKeyOperation);
     67   payload_key_ = VarFromCStr(kDictKeyPayload);
     68   write_var_ = VarFromCStr(kOperationNameWrite);
     69   ack_var_ = VarFromCStr(kOperationNameAck);
     70 }
     71 
     72 void JSPipeEventEmitter::Destroy() {
     73   if (var_iface_ == NULL)
     74     return;
     75   var_iface_->Release(pipe_name_var_);
     76   var_iface_->Release(pipe_key_);
     77   var_iface_->Release(operation_key_);
     78   var_iface_->Release(payload_key_);
     79   var_iface_->Release(write_var_);
     80   var_iface_->Release(ack_var_);
     81 }
     82 
     83 PP_Var JSPipeEventEmitter::VarFromCStr(const char* string) {
     84   assert(var_iface_);
     85   return var_iface_->VarFromUtf8(string, strlen(string));
     86 }
     87 
     88 void JSPipeEventEmitter::UpdateStatus_Locked() {
     89   uint32_t status = 0;
     90   if (!input_fifo_.IsEmpty())
     91     status |= POLLIN;
     92 
     93   if (GetOSpace() > 0)
     94     status |= POLLOUT;
     95 
     96   ClearEvents_Locked(~status);
     97   RaiseEvents_Locked(status);
     98 }
     99 
    100 Error JSPipeEventEmitter::Read_Locked(char* data, size_t len, int* out_bytes) {
    101   *out_bytes = input_fifo_.Read(data, len);
    102   if (*out_bytes > 0) {
    103     bytes_read_ += *out_bytes;
    104     Error err = SendAckMessage(bytes_read_);
    105     if (err != 0)
    106       ERROR("Sending ACK failed: %d\n", err.error);
    107   }
    108 
    109   UpdateStatus_Locked();
    110   return 0;
    111 }
    112 
    113 Error JSPipeEventEmitter::SendWriteMessage(const void* buf, size_t count) {
    114   TRACE("SendWriteMessage [%" PRIuS "] total=%" PRIuS, count, bytes_sent_);
    115   if (!var_iface_ || !buffer_iface_)
    116     return EIO;
    117 
    118   // Copy payload data in a new ArrayBuffer
    119   PP_Var buffer = buffer_iface_->Create(count);
    120   memcpy(buffer_iface_->Map(buffer), buf, count);
    121   buffer_iface_->Unmap(buffer);
    122 
    123   Error rtn = SendMessageToJS(write_var_, buffer);
    124   var_iface_->Release(buffer);
    125   return rtn;
    126 }
    127 
    128 Error JSPipeEventEmitter::SetName(const char* name) {
    129   if (var_iface_ == NULL)
    130     return EIO;
    131 
    132   // name can only be set once
    133   if (!name_.empty())
    134     return EIO;
    135 
    136   // new name must not be empty
    137   if (!name || strlen(name) == 0)
    138     return EIO;
    139 
    140   TRACE("set name: %s", name);
    141   name_ = name;
    142   pipe_name_var_ = VarFromCStr(name);
    143   return 0;
    144 }
    145 
    146 Error JSPipeEventEmitter::SendMessageToJS(PP_Var operation, PP_Var payload) {
    147   if (!ppapi_ || !messaging_iface_ || !var_iface_ || !dict_iface_)
    148     return EIO;
    149 
    150   // Create dict object which will be sent to JavaScript.
    151   PP_Var dict = dict_iface_->Create();
    152 
    153   // Set three keys in the dictionary: 'pipe', 'operation', and 'payload'
    154   dict_iface_->Set(dict, pipe_key_, pipe_name_var_);
    155   dict_iface_->Set(dict, operation_key_, operation);
    156   dict_iface_->Set(dict, payload_key_, payload);
    157 
    158   // Send the dict via PostMessage
    159   messaging_iface_->PostMessage(ppapi_->GetInstance(), dict);
    160 
    161   // Release the dict
    162   var_iface_->Release(dict);
    163   return 0;
    164 }
    165 
    166 Error JSPipeEventEmitter::SendAckMessage(size_t byte_count) {
    167   TRACE("SendAckMessage %" PRIuS, byte_count);
    168   PP_Var payload;
    169   payload.type = PP_VARTYPE_INT32;
    170   payload.value.as_int = (int32_t)byte_count;
    171 
    172   return SendMessageToJS(ack_var_, payload);
    173 }
    174 
    175 size_t JSPipeEventEmitter::HandleJSWrite(const char* data, size_t len) {
    176   size_t out_len = input_fifo_.Write(data, len);
    177   UpdateStatus_Locked();
    178   return out_len;
    179 }
    180 
    181 void JSPipeEventEmitter::HandleJSAck(size_t byte_count) {
    182   if (byte_count > bytes_sent_) {
    183     ERROR("HandleAck unexpected byte count: %" PRIuS, byte_count);
    184     return;
    185   }
    186 
    187   bytes_acked_ = byte_count;
    188   TRACE("HandleAck: %" SCNuS "/%" PRIuS, bytes_acked_, bytes_sent_);
    189   UpdateStatus_Locked();
    190 }
    191 
    192 Error JSPipeEventEmitter::HandleJSWrite(struct PP_Var message) {
    193   TRACE("HandleJSWrite");
    194   if (message.type != PP_VARTYPE_ARRAY_BUFFER) {
    195     TRACE("HandleJSWrite expected ArrayBuffer but got %d.", message.type);
    196     return EINVAL;
    197   }
    198   uint32_t length;
    199   if (buffer_iface_->ByteLength(message, &length) != PP_TRUE)
    200     return EINVAL;
    201 
    202   char* buffer = (char*)buffer_iface_->Map(message);
    203 
    204   // Write data to the input fifo
    205   size_t wrote = HandleJSWrite(buffer, length);
    206   buffer_iface_->Unmap(message);
    207   if (wrote != length) {
    208     LOG_ERROR("Only wrote %d of %d bytes to pipe", (int)wrote, (int)length);
    209     return EIO;
    210   }
    211   TRACE("done HandleWrite: %d", length);
    212   return 0;
    213 }
    214 
    215 Error JSPipeEventEmitter::HandleJSAck(PP_Var message) {
    216   if (message.type != PP_VARTYPE_INT32) {
    217     TRACE("HandleAck integer object expected but got %d.", message.type);
    218     return EINVAL;
    219   }
    220   HandleJSAck(message.value.as_int);
    221   return 0;
    222 }
    223 
    224 int JSPipeEventEmitter::VarStrcmp(PP_Var a, PP_Var b) {
    225   uint32_t length_a = 0;
    226   uint32_t length_b = 0;
    227   const char* cstring_a = var_iface_->VarToUtf8(a, &length_a);
    228   const char* cstring_b = var_iface_->VarToUtf8(a, &length_b);
    229   std::string string_a(cstring_a, length_a);
    230   std::string string_b(cstring_b, length_a);
    231   return strcmp(string_a.c_str(), string_b.c_str());
    232 }
    233 
    234 Error JSPipeEventEmitter::HandleJSMessage(struct PP_Var message) {
    235   Error err = 0;
    236   if (!messaging_iface_ || !var_iface_ || !dict_iface_ || !buffer_iface_) {
    237     TRACE("HandleJSMessage: missing PPAPI interfaces");
    238     return ENOSYS;
    239   }
    240 
    241   // Verify that we have an array with size two.
    242   if (message.type != PP_VARTYPE_DICTIONARY) {
    243     TRACE("HandleJSMessage passed non-dictionary var");
    244     return EINVAL;
    245   }
    246 
    247 #ifndef NDEBUG
    248   PP_Var pipe_name_var = dict_iface_->Get(message, pipe_key_);
    249   if (VarStrcmp(pipe_name_var, pipe_name_var_)) {
    250     TRACE("HandleJSMessage wrong pipe name");
    251     return EINVAL;
    252   }
    253   var_iface_->Release(pipe_name_var);
    254 #endif
    255 
    256   PP_Var operation_var = dict_iface_->Get(message, operation_key_);
    257   if (operation_var.type != PP_VARTYPE_STRING) {
    258     TRACE("HandleJSMessage invalid operation");
    259     err = EINVAL;
    260   } else {
    261     uint32_t length;
    262     const char* operation_string;
    263     operation_string = var_iface_->VarToUtf8(operation_var, &length);
    264     std::string message_type(operation_string, length);
    265 
    266     TRACE("HandleJSMessage %s", message_type.c_str());
    267     PP_Var payload = dict_iface_->Get(message, payload_key_);
    268     if (message_type == kOperationNameWrite) {
    269       err = HandleJSWrite(payload);
    270     } else if (message_type == kOperationNameAck) {
    271       err = HandleJSAck(payload);
    272     } else {
    273       TRACE("Unknown message type: %s", message_type.c_str());
    274       err = EINVAL;
    275     }
    276     var_iface_->Release(payload);
    277   }
    278 
    279   var_iface_->Release(operation_var);
    280   return err;
    281 }
    282 
    283 Error JSPipeEventEmitter::Write_Locked(const char* data,
    284                                        size_t len,
    285                                        int* out_bytes) {
    286   if (GetOSpace() == 0) {
    287     *out_bytes = 0;
    288     return 0;
    289   }
    290 
    291   if (len > GetOSpace())
    292     len = GetOSpace();
    293 
    294   // Limit the size of the data we send with PostMessage to kMaxPostMessageSize
    295   if (len > kMaxPostMessageSize)
    296     len = kMaxPostMessageSize;
    297 
    298   Error err = SendWriteMessage(data, len);
    299   if (err != 0)
    300     return err;
    301   *out_bytes = len;
    302   bytes_sent_ += len;
    303 
    304   UpdateStatus_Locked();
    305   return 0;
    306 }
    307 
    308 }  // namespace nacl_io
    309