1 /* Copyright (c) 2012 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 6 #include "nacl_io_demo.h" 7 8 #include <assert.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <limits.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/ioctl.h> 16 #include <sys/mount.h> 17 #include <sys/param.h> 18 #include <sys/select.h> 19 #include <sys/stat.h> 20 #include <pthread.h> 21 #include <unistd.h> 22 23 #include "ppapi/c/pp_errors.h" 24 #include "ppapi/c/pp_module.h" 25 #include "ppapi/c/ppb.h" 26 #include "ppapi/c/ppb_instance.h" 27 #include "ppapi/c/ppb_messaging.h" 28 #include "ppapi/c/ppb_var.h" 29 #include "ppapi/c/ppb_var_array.h" 30 #include "ppapi/c/ppb_var_dictionary.h" 31 #include "ppapi/c/ppp.h" 32 #include "ppapi/c/ppp_instance.h" 33 #include "ppapi/c/ppp_messaging.h" 34 #include "nacl_io/ioctl.h" 35 #include "nacl_io/nacl_io.h" 36 37 #include "handlers.h" 38 #include "queue.h" 39 40 #if defined(WIN32) 41 #define va_copy(d, s) ((d) = (s)) 42 #endif 43 44 typedef struct { 45 const char* name; 46 HandleFunc function; 47 } FuncNameMapping; 48 49 static PP_Instance g_instance = 0; 50 static PPB_GetInterface g_get_browser_interface = NULL; 51 static PPB_Messaging* g_ppb_messaging = NULL; 52 PPB_Var* g_ppb_var = NULL; 53 PPB_VarArray* g_ppb_var_array = NULL; 54 PPB_VarDictionary* g_ppb_var_dictionary = NULL; 55 56 static FuncNameMapping g_function_map[] = { 57 {"fopen", HandleFopen}, 58 {"fwrite", HandleFwrite}, 59 {"fread", HandleFread}, 60 {"fseek", HandleFseek}, 61 {"fclose", HandleFclose}, 62 {"fflush", HandleFflush}, 63 {"stat", HandleStat}, 64 {"opendir", HandleOpendir}, 65 {"readdir", HandleReaddir}, 66 {"closedir", HandleClosedir}, 67 {"mkdir", HandleMkdir}, 68 {"rmdir", HandleRmdir}, 69 {"chdir", HandleChdir}, 70 {"getcwd", HandleGetcwd}, 71 {"getaddrinfo", HandleGetaddrinfo}, 72 {"gethostbyname", HandleGethostbyname}, 73 {"connect", HandleConnect}, 74 {"send", HandleSend}, 75 {"recv", HandleRecv}, 76 {"close", HandleClose}, 77 {NULL, NULL}, 78 }; 79 80 /** A handle to the thread the handles messages. */ 81 static pthread_t g_handle_message_thread; 82 static pthread_t g_echo_thread; 83 84 /** 85 * Create a new PP_Var from a C string. 86 * @param[in] str The string to convert. 87 * @return A new PP_Var with the contents of |str|. 88 */ 89 struct PP_Var CStrToVar(const char* str) { 90 return g_ppb_var->VarFromUtf8(str, strlen(str)); 91 } 92 93 /** 94 * Printf to a newly allocated C string. 95 * @param[in] format A printf format string. 96 * @param[in] args The printf arguments. 97 * @return The newly constructed string. Caller takes ownership. */ 98 char* VprintfToNewString(const char* format, va_list args) { 99 va_list args_copy; 100 int length; 101 char* buffer; 102 int result; 103 104 va_copy(args_copy, args); 105 length = vsnprintf(NULL, 0, format, args); 106 buffer = (char*)malloc(length + 1); /* +1 for NULL-terminator. */ 107 result = vsnprintf(&buffer[0], length + 1, format, args_copy); 108 if (result != length) { 109 assert(0); 110 return NULL; 111 } 112 return buffer; 113 } 114 115 /** 116 * Printf to a newly allocated C string. 117 * @param[in] format A print format string. 118 * @param[in] ... The printf arguments. 119 * @return The newly constructed string. Caller takes ownership. 120 */ 121 char* PrintfToNewString(const char* format, ...) { 122 va_list args; 123 char* result; 124 va_start(args, format); 125 result = VprintfToNewString(format, args); 126 va_end(args); 127 return result; 128 } 129 130 /** 131 * Vprintf to a new PP_Var. 132 * @param[in] format A print format string. 133 * @param[in] va_list The printf arguments. 134 * @return A new PP_Var. 135 */ 136 static struct PP_Var VprintfToVar(const char* format, va_list args) { 137 struct PP_Var var; 138 char* string = VprintfToNewString(format, args); 139 var = g_ppb_var->VarFromUtf8(string, strlen(string)); 140 free(string); 141 return var; 142 } 143 144 /** 145 * Convert a PP_Var to a C string. 146 * @param[in] var The PP_Var to convert. 147 * @return A newly allocated, NULL-terminated string. 148 */ 149 static const char* VarToCStr(struct PP_Var var) { 150 uint32_t length; 151 const char* str = g_ppb_var->VarToUtf8(var, &length); 152 if (str == NULL) { 153 return NULL; 154 } 155 156 /* str is NOT NULL-terminated. Copy using memcpy. */ 157 char* new_str = (char*)malloc(length + 1); 158 memcpy(new_str, str, length); 159 new_str[length] = 0; 160 return new_str; 161 } 162 163 /** 164 * Get a value from a Dictionary, given a string key. 165 * @param[in] dict The dictionary to look in. 166 * @param[in] key The key to look up. 167 * @return PP_Var The value at |key| in the |dict|. If the key doesn't exist, 168 * return a PP_Var with the undefined value. 169 */ 170 struct PP_Var GetDictVar(struct PP_Var dict, const char* key) { 171 struct PP_Var key_var = CStrToVar(key); 172 struct PP_Var value = g_ppb_var_dictionary->Get(dict, key_var); 173 g_ppb_var->Release(key_var); 174 return value; 175 } 176 177 /** 178 * Post a message to JavaScript. 179 * @param[in] format A printf format string. 180 * @param[in] ... The printf arguments. 181 */ 182 static void PostMessage(const char* format, ...) { 183 struct PP_Var var; 184 va_list args; 185 186 va_start(args, format); 187 var = VprintfToVar(format, args); 188 va_end(args); 189 190 g_ppb_messaging->PostMessage(g_instance, var); 191 g_ppb_var->Release(var); 192 } 193 194 /** 195 * Given a message from JavaScript, parse it for functions and parameters. 196 * 197 * The format of the message is: 198 * { 199 * "cmd": <function name>, 200 * "args": [<arg0>, <arg1>, ...] 201 * } 202 * 203 * @param[in] message The message to parse. 204 * @param[out] out_function The function name. 205 * @param[out] out_params A PP_Var array. 206 * @return 0 if successful, otherwise 1. 207 */ 208 static int ParseMessage(struct PP_Var message, 209 const char** out_function, 210 struct PP_Var* out_params) { 211 if (message.type != PP_VARTYPE_DICTIONARY) { 212 return 1; 213 } 214 215 struct PP_Var cmd_value = GetDictVar(message, "cmd"); 216 *out_function = VarToCStr(cmd_value); 217 g_ppb_var->Release(cmd_value); 218 if (cmd_value.type != PP_VARTYPE_STRING) { 219 return 1; 220 } 221 222 *out_params = GetDictVar(message, "args"); 223 if (out_params->type != PP_VARTYPE_ARRAY) { 224 return 1; 225 } 226 227 return 0; 228 } 229 230 /** 231 * Given a function name, look up its handler function. 232 * @param[in] function_name The function name to look up. 233 * @return The handler function mapped to |function_name|. 234 */ 235 static HandleFunc GetFunctionByName(const char* function_name) { 236 FuncNameMapping* map_iter = g_function_map; 237 for (; map_iter->name; ++map_iter) { 238 if (strcmp(map_iter->name, function_name) == 0) { 239 return map_iter->function; 240 } 241 } 242 243 return NULL; 244 } 245 246 /** 247 * Handle as message from JavaScript on the worker thread. 248 * 249 * @param[in] message The message to parse and handle. 250 */ 251 static void HandleMessage(struct PP_Var message) { 252 const char* function_name; 253 struct PP_Var params; 254 if (ParseMessage(message, &function_name, ¶ms)) { 255 PostMessage("Error: Unable to parse message"); 256 return; 257 } 258 259 HandleFunc function = GetFunctionByName(function_name); 260 if (!function) { 261 /* Function name wasn't found. Error. */ 262 PostMessage("Error: Unknown function \"%s\"", function_name); 263 return; 264 } 265 266 /* Function name was found, call it. */ 267 struct PP_Var result_var; 268 const char* error; 269 int result = (*function)(params, &result_var, &error); 270 if (result != 0) { 271 /* Error. */ 272 if (error != NULL) { 273 PostMessage("Error: \"%s\" failed: %s.", function_name, error); 274 free((void*)error); 275 } else { 276 PostMessage("Error: \"%s\" failed.", function_name); 277 } 278 return; 279 } 280 281 /* Function returned an output dictionary. Send it to JavaScript. */ 282 g_ppb_messaging->PostMessage(g_instance, result_var); 283 g_ppb_var->Release(result_var); 284 } 285 286 287 /** 288 * Helper function used by EchoThread which reads from a file descriptor 289 * and writes all the data that it reads back to the same descriptor. 290 */ 291 static void EchoInput(int fd) { 292 char buffer[512]; 293 while (1) { 294 int rtn = read(fd, buffer, 512); 295 if (rtn > 0) { 296 int wrote = write(fd, buffer, rtn); 297 if (wrote < rtn) 298 PostMessage("only wrote %d/%d bytes\n", wrote, rtn); 299 } else { 300 if (rtn < 0 && errno != EAGAIN) 301 PostMessage("read failed: %d (%s)\n", errno, strerror(errno)); 302 break; 303 } 304 } 305 } 306 307 /** 308 * Worker thread that listens for input on JS pipe nodes and echos all input 309 * back to the same pipe. 310 */ 311 static void* EchoThread(void* user_data) { 312 int fd1 = open("/dev/jspipe1", O_RDWR | O_NONBLOCK); 313 int fd2 = open("/dev/jspipe2", O_RDWR | O_NONBLOCK); 314 int fd3 = open("/dev/jspipe3", O_RDWR | O_NONBLOCK); 315 int nfds = MAX(fd1, fd2); 316 nfds = MAX(nfds, fd3); 317 while (1) { 318 fd_set readfds; 319 FD_ZERO(&readfds); 320 FD_SET(fd1, &readfds); 321 FD_SET(fd2, &readfds); 322 FD_SET(fd3, &readfds); 323 int rtn = select(nfds + 1, &readfds, NULL, NULL, NULL); 324 if (rtn < 0 && errno != EAGAIN) { 325 PostMessage("select failed: %s\n", strerror(errno)); 326 break; 327 } 328 if (rtn > 0) { 329 if (FD_ISSET(fd1, &readfds)) 330 EchoInput(fd1); 331 if (FD_ISSET(fd2, &readfds)) 332 EchoInput(fd2); 333 if (FD_ISSET(fd3, &readfds)) 334 EchoInput(fd3); 335 } 336 337 } 338 close(fd1); 339 close(fd2); 340 close(fd3); 341 return 0; 342 } 343 344 /** 345 * A worker thread that handles messages from JavaScript. 346 * @param[in] user_data Unused. 347 * @return unused. 348 */ 349 void* HandleMessageThread(void* user_data) { 350 while (1) { 351 struct PP_Var message = DequeueMessage(); 352 HandleMessage(message); 353 g_ppb_var->Release(message); 354 } 355 } 356 357 static PP_Bool Instance_DidCreate(PP_Instance instance, 358 uint32_t argc, 359 const char* argn[], 360 const char* argv[]) { 361 g_instance = instance; 362 nacl_io_init_ppapi(instance, g_get_browser_interface); 363 364 // By default, nacl_io mounts / to pass through to the original NaCl 365 // filesystem (which doesn't do much). Let's remount it to a memfs 366 // filesystem. 367 umount("/"); 368 mount("", "/", "memfs", 0, ""); 369 370 mount("", /* source */ 371 "/persistent", /* target */ 372 "html5fs", /* filesystemtype */ 373 0, /* mountflags */ 374 "type=PERSISTENT,expected_size=1048576"); /* data */ 375 376 mount("", /* source. Use relative URL */ 377 "/http", /* target */ 378 "httpfs", /* filesystemtype */ 379 0, /* mountflags */ 380 ""); /* data */ 381 382 pthread_create(&g_handle_message_thread, NULL, &HandleMessageThread, NULL); 383 pthread_create(&g_echo_thread, NULL, &EchoThread, NULL); 384 InitializeMessageQueue(); 385 386 return PP_TRUE; 387 } 388 389 static void Instance_DidDestroy(PP_Instance instance) { 390 } 391 392 static void Instance_DidChangeView(PP_Instance instance, 393 PP_Resource view_resource) { 394 } 395 396 static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) { 397 } 398 399 static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance, 400 PP_Resource url_loader) { 401 /* NaCl modules do not need to handle the document load function. */ 402 return PP_FALSE; 403 } 404 405 static void Messaging_HandleMessage(PP_Instance instance, 406 struct PP_Var message) { 407 /* Special case for jspipe input handling */ 408 if (message.type != PP_VARTYPE_DICTIONARY) { 409 PostMessage("Got unexpected message type: %d\n", message.type); 410 return; 411 } 412 413 struct PP_Var pipe_var = CStrToVar("pipe"); 414 struct PP_Var pipe_name = g_ppb_var_dictionary->Get(message, pipe_var); 415 g_ppb_var->Release(pipe_var); 416 417 /* Special case for jspipe input handling */ 418 if (pipe_name.type == PP_VARTYPE_STRING) { 419 char file_name[PATH_MAX]; 420 snprintf(file_name, PATH_MAX, "/dev/%s", VarToCStr(pipe_name)); 421 int fd = open(file_name, O_RDONLY); 422 g_ppb_var->Release(pipe_name); 423 if (fd < 0) { 424 PostMessage("Warning: opening %s failed.", file_name); 425 goto done; 426 } 427 if (ioctl(fd, NACL_IOC_HANDLEMESSAGE, &message) != 0) { 428 PostMessage("Error: ioctl on %s failed: %s", file_name, strerror(errno)); 429 } 430 close(fd); 431 goto done; 432 } 433 434 g_ppb_var->AddRef(message); 435 if (!EnqueueMessage(message)) { 436 g_ppb_var->Release(message); 437 PostMessage("Warning: dropped message because the queue was full."); 438 } 439 440 done: 441 g_ppb_var->Release(pipe_name); 442 } 443 444 #define GET_INTERFACE(var, type, name) \ 445 var = (type*)(get_browser(name)); \ 446 if (!var) { \ 447 printf("Unable to get interface " name "\n"); \ 448 return PP_ERROR_FAILED; \ 449 } 450 451 PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id, 452 PPB_GetInterface get_browser) { 453 g_get_browser_interface = get_browser; 454 GET_INTERFACE(g_ppb_messaging, PPB_Messaging, PPB_MESSAGING_INTERFACE); 455 GET_INTERFACE(g_ppb_var, PPB_Var, PPB_VAR_INTERFACE); 456 GET_INTERFACE(g_ppb_var_array, PPB_VarArray, PPB_VAR_ARRAY_INTERFACE); 457 GET_INTERFACE( 458 g_ppb_var_dictionary, PPB_VarDictionary, PPB_VAR_DICTIONARY_INTERFACE); 459 return PP_OK; 460 } 461 462 PP_EXPORT const void* PPP_GetInterface(const char* interface_name) { 463 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) { 464 static PPP_Instance instance_interface = { 465 &Instance_DidCreate, 466 &Instance_DidDestroy, 467 &Instance_DidChangeView, 468 &Instance_DidChangeFocus, 469 &Instance_HandleDocumentLoad, 470 }; 471 return &instance_interface; 472 } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) { 473 static PPP_Messaging messaging_interface = { 474 &Messaging_HandleMessage, 475 }; 476 return &messaging_interface; 477 } 478 return NULL; 479 } 480 481 PP_EXPORT void PPP_ShutdownModule() { 482 } 483