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