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 */
48static int larger_int_of(int a, int b) {
49  if (a > b)
50    return a;
51  return b;
52}
53
54typedef struct {
55  const char* name;
56  HandleFunc function;
57} FuncNameMapping;
58
59static PP_Instance g_instance = 0;
60static PPB_GetInterface g_get_browser_interface = NULL;
61static PPB_Messaging* g_ppb_messaging = NULL;
62PPB_Var* g_ppb_var = NULL;
63PPB_VarArray* g_ppb_var_array = NULL;
64PPB_VarDictionary* g_ppb_var_dictionary = NULL;
65
66static 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. */
91static pthread_t g_handle_message_thread;
92static 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 */
99struct 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. */
108char* 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 */
131char* 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 */
146static 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 */
159static 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 */
180struct 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 */
192static 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 */
218static 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 */
245static 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 */
261static 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 */
301static 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 */
321static 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 */
359void* 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
367static 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
399static void Instance_DidDestroy(PP_Instance instance) {
400}
401
402static void Instance_DidChangeView(PP_Instance instance,
403                                   PP_Resource view_resource) {
404}
405
406static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {
407}
408
409static 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
415static 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
450done:
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
461PP_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
472PP_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
491PP_EXPORT void PPP_ShutdownModule() {
492}
493