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     ERROR("Got NULL interface(s): %s%s",
    117           var_iface_ ? "" : "Var ",
    118           buffer_iface_ ? "" : "ArrayBuffer");
    119     return EIO;
    120   }
    121 
    122   // Copy payload data in a new ArrayBuffer
    123   PP_Var buffer = buffer_iface_->Create(count);
    124   memcpy(buffer_iface_->Map(buffer), buf, count);
    125   buffer_iface_->Unmap(buffer);
    126 
    127   Error rtn = SendMessageToJS(write_var_, buffer);
    128   var_iface_->Release(buffer);
    129   return rtn;
    130 }
    131 
    132 Error JSPipeEventEmitter::SetName(const char* name) {
    133   if (var_iface_ == NULL) {
    134     // No error here: many of the tests trigger this message.
    135     LOG_TRACE("Got NULL interface: Var");
    136     return EIO;
    137   }
    138 
    139   // name can only be set once
    140   if (!name_.empty()) {
    141     LOG_ERROR("Attempting to set name more than once.");
    142     return EIO;
    143   }
    144 
    145   // new name must not be empty
    146   if (!name || strlen(name) == 0) {
    147     LOG_ERROR("Empty name is invalid.");
    148     return EIO;
    149   }
    150 
    151   TRACE("set name: %s", name);
    152   name_ = name;
    153   pipe_name_var_ = VarFromCStr(name);
    154   return 0;
    155 }
    156 
    157 Error JSPipeEventEmitter::SendMessageToJS(PP_Var operation, PP_Var payload) {
    158   if (!ppapi_) {
    159     LOG_ERROR("ppapi_ is NULL.");
    160     return EIO;
    161   }
    162 
    163   if (!messaging_iface_ || !var_iface_ || !dict_iface_) {
    164     LOG_ERROR("Got NULL interface(s): %s%s%s",
    165               messaging_iface_ ? "" : "Messaging ",
    166               dict_iface_ ? "" : "Dictionary ",
    167               var_iface_ ? "" : "Var");
    168     return EIO;
    169   }
    170 
    171   // Create dict object which will be sent to JavaScript.
    172   PP_Var dict = dict_iface_->Create();
    173 
    174   // Set three keys in the dictionary: 'pipe', 'operation', and 'payload'
    175   dict_iface_->Set(dict, pipe_key_, pipe_name_var_);
    176   dict_iface_->Set(dict, operation_key_, operation);
    177   dict_iface_->Set(dict, payload_key_, payload);
    178 
    179   // Send the dict via PostMessage
    180   messaging_iface_->PostMessage(ppapi_->GetInstance(), dict);
    181 
    182   // Release the dict
    183   var_iface_->Release(dict);
    184   return 0;
    185 }
    186 
    187 Error JSPipeEventEmitter::SendAckMessage(size_t byte_count) {
    188   TRACE("SendAckMessage %" PRIuS, byte_count);
    189   PP_Var payload;
    190   payload.type = PP_VARTYPE_INT32;
    191   payload.value.as_int = (int32_t)byte_count;
    192 
    193   return SendMessageToJS(ack_var_, payload);
    194 }
    195 
    196 size_t JSPipeEventEmitter::HandleJSWrite(const char* data, size_t len) {
    197   size_t out_len = input_fifo_.Write(data, len);
    198   UpdateStatus_Locked();
    199   return out_len;
    200 }
    201 
    202 void JSPipeEventEmitter::HandleJSAck(size_t byte_count) {
    203   if (byte_count > bytes_sent_) {
    204     ERROR("Unexpected byte count: %" PRIuS, byte_count);
    205     return;
    206   }
    207 
    208   bytes_acked_ = byte_count;
    209   TRACE("HandleAck: %" SCNuS "/%" PRIuS, bytes_acked_, bytes_sent_);
    210   UpdateStatus_Locked();
    211 }
    212 
    213 Error JSPipeEventEmitter::HandleJSWrite(struct PP_Var message) {
    214   TRACE("HandleJSWrite");
    215   if (message.type != PP_VARTYPE_ARRAY_BUFFER) {
    216     ERROR("Expected ArrayBuffer but got %d.", message.type);
    217     return EINVAL;
    218   }
    219   uint32_t length;
    220   if (buffer_iface_->ByteLength(message, &length) != PP_TRUE) {
    221     ERROR("ArrayBuffer.ByteLength returned PP_FALSE");
    222     return EINVAL;
    223   }
    224 
    225   char* buffer = (char*)buffer_iface_->Map(message);
    226 
    227   // Write data to the input fifo
    228   size_t wrote = HandleJSWrite(buffer, length);
    229   buffer_iface_->Unmap(message);
    230   if (wrote != length) {
    231     ERROR("Only wrote %d of %d bytes to pipe", (int)wrote, (int)length);
    232     return EIO;
    233   }
    234   TRACE("done HandleWrite: %d", length);
    235   return 0;
    236 }
    237 
    238 Error JSPipeEventEmitter::HandleJSAck(PP_Var message) {
    239   if (message.type != PP_VARTYPE_INT32) {
    240     ERROR("Integer object expected but got %d.", message.type);
    241     return EINVAL;
    242   }
    243   HandleJSAck(message.value.as_int);
    244   return 0;
    245 }
    246 
    247 int JSPipeEventEmitter::VarStrcmp(PP_Var a, PP_Var b) {
    248   uint32_t length_a = 0;
    249   uint32_t length_b = 0;
    250   const char* cstring_a = var_iface_->VarToUtf8(a, &length_a);
    251   const char* cstring_b = var_iface_->VarToUtf8(a, &length_b);
    252   std::string string_a(cstring_a, length_a);
    253   std::string string_b(cstring_b, length_a);
    254   return strcmp(string_a.c_str(), string_b.c_str());
    255 }
    256 
    257 Error JSPipeEventEmitter::HandleJSMessage(struct PP_Var message) {
    258   Error err = 0;
    259   if (!messaging_iface_ || !var_iface_ || !dict_iface_ || !buffer_iface_) {
    260     ERROR("Got NULL interface(s): %s%s%s%s",
    261           messaging_iface_ ? "" : "Messaging ",
    262           var_iface_ ? "" : "Var ",
    263           dict_iface_ ? "" : "Dictionary ",
    264           buffer_iface_ ? "" : "ArrayBuffer");
    265     return ENOSYS;
    266   }
    267 
    268   // Verify that we have an array with size two.
    269   if (message.type != PP_VARTYPE_DICTIONARY) {
    270     ERROR("Expected Dictionary but got %d.", message.type);
    271     return EINVAL;
    272   }
    273 
    274 #ifndef NDEBUG
    275   PP_Var pipe_name_var = dict_iface_->Get(message, pipe_key_);
    276   if (VarStrcmp(pipe_name_var, pipe_name_var_)) {
    277     ERROR("Wrong pipe name.");
    278     return EINVAL;
    279   }
    280   var_iface_->Release(pipe_name_var);
    281 #endif
    282 
    283   PP_Var operation_var = dict_iface_->Get(message, operation_key_);
    284   if (operation_var.type != PP_VARTYPE_STRING) {
    285     ERROR("Expected String but got %d.", operation_var.type);
    286     err = EINVAL;
    287   } else {
    288     uint32_t length;
    289     const char* operation_string;
    290     operation_string = var_iface_->VarToUtf8(operation_var, &length);
    291     std::string message_type(operation_string, length);
    292 
    293     TRACE("HandleJSMessage %s", message_type.c_str());
    294     PP_Var payload = dict_iface_->Get(message, payload_key_);
    295     if (message_type == kOperationNameWrite) {
    296       err = HandleJSWrite(payload);
    297     } else if (message_type == kOperationNameAck) {
    298       err = HandleJSAck(payload);
    299     } else {
    300       ERROR("Unknown message type: %s", message_type.c_str());
    301       err = EINVAL;
    302     }
    303     var_iface_->Release(payload);
    304   }
    305 
    306   var_iface_->Release(operation_var);
    307   return err;
    308 }
    309 
    310 Error JSPipeEventEmitter::Write_Locked(const char* data,
    311                                        size_t len,
    312                                        int* out_bytes) {
    313   if (GetOSpace() == 0) {
    314     *out_bytes = 0;
    315     return 0;
    316   }
    317 
    318   if (len > GetOSpace())
    319     len = GetOSpace();
    320 
    321   // Limit the size of the data we send with PostMessage to kMaxPostMessageSize
    322   if (len > kMaxPostMessageSize)
    323     len = kMaxPostMessageSize;
    324 
    325   Error err = SendWriteMessage(data, len);
    326   if (err != 0)
    327     return err;
    328   *out_bytes = len;
    329   bytes_sent_ += len;
    330 
    331   UpdateStatus_Locked();
    332   return 0;
    333 }
    334 
    335 }  // namespace nacl_io
    336