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