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, &params, 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, &params[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, &params[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