Home | History | Annotate | Download | only in nacl_io
      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, &params, 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, &params[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, &params[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