| OLD | NEW |
| (Empty) |
| 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 } | |
| OLD | NEW |