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