Home | History | Annotate | Download | only in nacl_io_demo
      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, &params)) {
    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