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 <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/mount.h> 13 #include <pthread.h> 14 15 #include "ppapi/c/pp_errors.h" 16 #include "ppapi/c/pp_module.h" 17 #include "ppapi/c/ppb.h" 18 #include "ppapi/c/ppb_instance.h" 19 #include "ppapi/c/ppb_messaging.h" 20 #include "ppapi/c/ppb_var.h" 21 #include "ppapi/c/ppp.h" 22 #include "ppapi/c/ppp_instance.h" 23 #include "ppapi/c/ppp_messaging.h" 24 #include "nacl_io/nacl_io.h" 25 26 #include "handlers.h" 27 #include "queue.h" 28 29 #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 30 31 #if defined(WIN32) 32 #define va_copy(d, s) ((d) = (s)) 33 #endif 34 35 typedef struct { 36 const char* name; 37 HandleFunc function; 38 } FuncNameMapping; 39 40 static PP_Instance g_instance = 0; 41 static PPB_GetInterface get_browser_interface = NULL; 42 static PPB_Messaging* ppb_messaging_interface = NULL; 43 static PPB_Var* ppb_var_interface = NULL; 44 45 static FuncNameMapping g_function_map[] = { 46 {"fopen", HandleFopen}, 47 {"fwrite", HandleFwrite}, 48 {"fread", HandleFread}, 49 {"fseek", HandleFseek}, 50 {"fclose", HandleFclose}, 51 {"stat", HandleStat}, 52 {"opendir", HandleOpendir}, 53 {"readdir", HandleReaddir}, 54 {"closedir", HandleClosedir}, 55 {"mkdir", HandleMkdir}, 56 {"gethostbyname", HandleGethostbyname}, 57 {NULL, NULL}, 58 }; 59 60 /** A handle to the thread the handles messages. */ 61 static pthread_t g_handle_message_thread; 62 63 /** 64 * Create a new PP_Var from a C string. 65 * @param[in] str The string to convert. 66 * @return A new PP_Var with the contents of |str|. */ 67 struct PP_Var CStrToVar(const char* str) { 68 if (ppb_var_interface != NULL) { 69 return ppb_var_interface->VarFromUtf8(str, strlen(str)); 70 } 71 return PP_MakeUndefined(); 72 } 73 74 /** 75 * Printf to a newly allocated C string. 76 * @param[in] format A printf format string. 77 * @param[in] args The printf arguments. 78 * @return The newly constructed string. Caller takes ownership. */ 79 char* VprintfToNewString(const char* format, va_list args) { 80 va_list args_copy; 81 int length; 82 char* buffer; 83 int result; 84 85 va_copy(args_copy, args); 86 length = vsnprintf(NULL, 0, format, args); 87 buffer = (char*)malloc(length + 1); /* +1 for NULL-terminator. */ 88 result = vsnprintf(&buffer[0], length + 1, format, args_copy); 89 assert(result == length); 90 return buffer; 91 } 92 93 /** 94 * Printf to a newly allocated C string. 95 * @param[in] format A print format string. 96 * @param[in] ... The printf arguments. 97 * @return The newly constructed string. Caller takes ownership. */ 98 char* PrintfToNewString(const char* format, ...) { 99 va_list args; 100 char* result; 101 va_start(args, format); 102 result = VprintfToNewString(format, args); 103 va_end(args); 104 return result; 105 } 106 107 /** 108 * Printf to a new PP_Var. 109 * @param[in] format A print format string. 110 * @param[in] ... The printf arguments. 111 * @return A new PP_Var. */ 112 struct PP_Var PrintfToVar(const char* format, ...) { 113 if (ppb_var_interface != NULL) { 114 char* string; 115 va_list args; 116 struct PP_Var var; 117 118 va_start(args, format); 119 string = VprintfToNewString(format, args); 120 va_end(args); 121 122 var = ppb_var_interface->VarFromUtf8(string, strlen(string)); 123 free(string); 124 125 return var; 126 } 127 128 return PP_MakeUndefined(); 129 } 130 131 /** 132 * Convert a PP_Var to a C string, given a buffer. 133 * @param[in] var The PP_Var to convert. 134 * @param[out] buffer The buffer to write to. 135 * @param[in] length The length of |buffer|. 136 * @return The number of characters written. */ 137 uint32_t VarToCStr(struct PP_Var var, char* buffer, uint32_t length) { 138 if (ppb_var_interface != NULL) { 139 uint32_t var_length; 140 const char* str = ppb_var_interface->VarToUtf8(var, &var_length); 141 /* str is NOT NULL-terminated. Copy using memcpy. */ 142 uint32_t min_length = MIN(var_length, length - 1); 143 memcpy(buffer, str, min_length); 144 buffer[min_length] = 0; 145 146 return min_length; 147 } 148 149 return 0; 150 } 151 152 /** 153 * Given a message from JavaScript, parse it for functions and parameters. 154 * 155 * The format of the message is: 156 * function, param1, param2, param3, etc. 157 * where each element is separated by the \1 character. 158 * 159 * e.g. 160 * "function\1first parameter\1second parameter" 161 * 162 * How to use: 163 * char* function; 164 * char* params[4]; 165 * int num_params = ParseMessage(msg, &function, ¶ms, 4); 166 * 167 * @param[in, out] message The message to parse. This string is modified 168 * in-place. 169 * @param[out] out_function The function name. 170 * @param[out] out_params An array of strings, one for each parameter parsed. 171 * @param[in] max_params The maximum number of parameters to parse. 172 * @return The number of parameters parsed. */ 173 static size_t ParseMessage(char* message, 174 char** out_function, 175 char** out_params, 176 size_t max_params) { 177 char* separator; 178 char* param_start; 179 size_t num_params = 0; 180 181 /* Parse the message: function\1param1\1param2\1param3,... */ 182 *out_function = &message[0]; 183 184 separator = strchr(message, 1); 185 if (!separator) { 186 return num_params; 187 } 188 189 *separator = 0; /* NULL-terminate function. */ 190 191 while (separator && num_params < max_params) { 192 param_start = separator + 1; 193 separator = strchr(param_start, 1); 194 if (separator) { 195 *separator = 0; 196 out_params[num_params++] = param_start; 197 } 198 } 199 200 out_params[num_params++] = param_start; 201 202 return num_params; 203 } 204 205 /** 206 * Given a function name, look up its handler function. 207 * @param[in] function_name The function name to look up. 208 * @return The handler function mapped to |function_name|. */ 209 static HandleFunc GetFunctionByName(const char* function_name) { 210 FuncNameMapping* map_iter = g_function_map; 211 for (; map_iter->name; ++map_iter) { 212 if (strcmp(map_iter->name, function_name) == 0) { 213 return map_iter->function; 214 } 215 } 216 217 return NULL; 218 } 219 220 /** Handle as message from JavaScript on the worker thread. 221 * 222 * @param[in] message The message to parse and handle. */ 223 static void HandleMessage(char* message) { 224 char* function_name; 225 char* params[MAX_PARAMS]; 226 size_t num_params; 227 char* output = NULL; 228 int result; 229 HandleFunc function; 230 231 num_params = ParseMessage(message, &function_name, ¶ms[0], MAX_PARAMS); 232 233 function = GetFunctionByName(function_name); 234 if (!function) { 235 /* Function name wasn't found. Error. */ 236 ppb_messaging_interface->PostMessage( 237 g_instance, 238 PrintfToVar("Error: Unknown function \"%s\"", function_name)); 239 return; 240 } 241 242 /* Function name was found, call it. */ 243 result = (*function)(num_params, ¶ms[0], &output); 244 if (result != 0) { 245 /* Error. */ 246 struct PP_Var var; 247 if (output != NULL) { 248 var = PrintfToVar("Error: Function \"%s\" returned error %d. " 249 "Additional output: %s", 250 function_name, 251 result, 252 output); 253 free(output); 254 } else { 255 var = PrintfToVar( 256 "Error: Function \"%s\" returned error %d.", function_name, result); 257 } 258 259 /* Post the error to JavaScript, so the user can see it. */ 260 ppb_messaging_interface->PostMessage(g_instance, var); 261 return; 262 } 263 264 if (output != NULL) { 265 /* Function returned an output string. Send it to JavaScript. */ 266 ppb_messaging_interface->PostMessage(g_instance, CStrToVar(output)); 267 free(output); 268 } 269 } 270 271 /** A worker thread that handles messages from JavaScript. 272 * @param[in] user_data Unused. 273 * @return unused. */ 274 void* HandleMessageThread(void* user_data) { 275 while (1) { 276 char* message = DequeueMessage(); 277 HandleMessage(message); 278 free(message); 279 } 280 } 281 282 static PP_Bool Instance_DidCreate(PP_Instance instance, 283 uint32_t argc, 284 const char* argn[], 285 const char* argv[]) { 286 g_instance = instance; 287 nacl_io_init_ppapi(instance, get_browser_interface); 288 289 // By default, nacl_io mounts / to pass through to the original NaCl 290 // filesystem (which doesn't do much). Let's remount it to a memfs 291 // filesystem. 292 umount("/"); 293 mount("", "/", "memfs", 0, ""); 294 295 mount("", /* source */ 296 "/persistent", /* target */ 297 "html5fs", /* filesystemtype */ 298 0, /* mountflags */ 299 "type=PERSISTENT,expected_size=1048576"); /* data */ 300 301 mount("", /* source. Use relative URL */ 302 "/http", /* target */ 303 "httpfs", /* filesystemtype */ 304 0, /* mountflags */ 305 ""); /* data */ 306 307 pthread_create(&g_handle_message_thread, NULL, &HandleMessageThread, NULL); 308 InitializeMessageQueue(); 309 310 return PP_TRUE; 311 } 312 313 static void Instance_DidDestroy(PP_Instance instance) {} 314 315 static void Instance_DidChangeView(PP_Instance instance, 316 PP_Resource view_resource) {} 317 318 static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {} 319 320 static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance, 321 PP_Resource url_loader) { 322 /* NaCl modules do not need to handle the document load function. */ 323 return PP_FALSE; 324 } 325 326 static void Messaging_HandleMessage(PP_Instance instance, 327 struct PP_Var message) { 328 char buffer[1024]; 329 VarToCStr(message, &buffer[0], 1024); 330 if (!EnqueueMessage(strdup(buffer))) { 331 struct PP_Var var; 332 var = PrintfToVar( 333 "Warning: dropped message \"%s\" because the queue was full.", message); 334 ppb_messaging_interface->PostMessage(g_instance, var); 335 } 336 } 337 338 PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id, 339 PPB_GetInterface get_browser) { 340 get_browser_interface = get_browser; 341 ppb_messaging_interface = 342 (PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE)); 343 ppb_var_interface = (PPB_Var*)(get_browser(PPB_VAR_INTERFACE)); 344 return PP_OK; 345 } 346 347 PP_EXPORT const void* PPP_GetInterface(const char* interface_name) { 348 if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) { 349 static PPP_Instance instance_interface = { 350 &Instance_DidCreate, 351 &Instance_DidDestroy, 352 &Instance_DidChangeView, 353 &Instance_DidChangeFocus, 354 &Instance_HandleDocumentLoad, 355 }; 356 return &instance_interface; 357 } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) { 358 static PPP_Messaging messaging_interface = { 359 &Messaging_HandleMessage, 360 }; 361 return &messaging_interface; 362 } 363 return NULL; 364 } 365 366 PP_EXPORT void PPP_ShutdownModule() {} 367