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 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, &params)) {
    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 }
    483