| Index: native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c
|
| diff --git a/native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c b/native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c
|
| index 6a08cd22d8e7af3fd6c015d2c823e69214b7a335..226d50f662ea4546ea45f62b2342dca5fb4840fc 100644
|
| --- a/native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c
|
| +++ b/native_client_sdk/src/examples/demo/nacl_io/nacl_io_demo.c
|
| @@ -6,11 +6,19 @@
|
| #include "nacl_io_demo.h"
|
|
|
| #include <assert.h>
|
| +#include <errno.h>
|
| +#include <fcntl.h>
|
| +#include <limits.h>
|
| #include <stdio.h>
|
| #include <stdlib.h>
|
| #include <string.h>
|
| +#include <sys/ioctl.h>
|
| #include <sys/mount.h>
|
| +#include <sys/param.h>
|
| +#include <sys/select.h>
|
| +#include <sys/stat.h>
|
| #include <pthread.h>
|
| +#include <unistd.h>
|
|
|
| #include "ppapi/c/pp_errors.h"
|
| #include "ppapi/c/pp_module.h"
|
| @@ -23,6 +31,7 @@
|
| #include "ppapi/c/ppp.h"
|
| #include "ppapi/c/ppp_instance.h"
|
| #include "ppapi/c/ppp_messaging.h"
|
| +#include "nacl_io/ioctl.h"
|
| #include "nacl_io/nacl_io.h"
|
|
|
| #include "handlers.h"
|
| @@ -37,9 +46,9 @@ typedef struct {
|
| HandleFunc function;
|
| } FuncNameMapping;
|
|
|
| -PP_Instance g_instance = 0;
|
| -PPB_GetInterface g_get_browser_interface = NULL;
|
| -PPB_Messaging* g_ppb_messaging = NULL;
|
| +static PP_Instance g_instance = 0;
|
| +static PPB_GetInterface g_get_browser_interface = NULL;
|
| +static PPB_Messaging* g_ppb_messaging = NULL;
|
| PPB_Var* g_ppb_var = NULL;
|
| PPB_VarArray* g_ppb_var_array = NULL;
|
| PPB_VarDictionary* g_ppb_var_dictionary = NULL;
|
| @@ -70,6 +79,7 @@ static FuncNameMapping g_function_map[] = {
|
|
|
| /** A handle to the thread the handles messages. */
|
| static pthread_t g_handle_message_thread;
|
| +static pthread_t g_echo_thread;
|
|
|
| /**
|
| * Create a new PP_Var from a C string.
|
| @@ -118,34 +128,25 @@ char* PrintfToNewString(const char* format, ...) {
|
| }
|
|
|
| /**
|
| - * Printf to a new PP_Var.
|
| + * Vprintf to a new PP_Var.
|
| * @param[in] format A print format string.
|
| - * @param[in] ... The printf arguments.
|
| + * @param[in] va_list The printf arguments.
|
| * @return A new PP_Var.
|
| */
|
| -struct PP_Var PrintfToVar(const char* format, ...) {
|
| - char* string;
|
| - va_list args;
|
| +static struct PP_Var VprintfToVar(const char* format, va_list args) {
|
| struct PP_Var var;
|
| -
|
| - va_start(args, format);
|
| - string = VprintfToNewString(format, args);
|
| - va_end(args);
|
| -
|
| + char* string = VprintfToNewString(format, args);
|
| var = g_ppb_var->VarFromUtf8(string, strlen(string));
|
| free(string);
|
| -
|
| return var;
|
| }
|
|
|
| /**
|
| - * Convert a PP_Var to a C string, given a buffer.
|
| + * Convert a PP_Var to a C string.
|
| * @param[in] var The PP_Var to convert.
|
| - * @param[out] buffer The buffer to write to.
|
| - * @param[in] length The length of |buffer|.
|
| - * @return The number of characters written.
|
| + * @return A newly allocated, NULL-terminated string.
|
| */
|
| -const char* VarToCStr(struct PP_Var var) {
|
| +static const char* VarToCStr(struct PP_Var var) {
|
| uint32_t length;
|
| const char* str = g_ppb_var->VarToUtf8(var, &length);
|
| if (str == NULL) {
|
| @@ -174,10 +175,18 @@ struct PP_Var GetDictVar(struct PP_Var dict, const char* key) {
|
| }
|
|
|
| /**
|
| - * Send a newly-created PP_Var to JavaScript, then release it.
|
| - * @param[in] var The PP_Var to send.
|
| + * Post a message to JavaScript.
|
| + * @param[in] format A printf format string.
|
| + * @param[in] ... The printf arguments.
|
| */
|
| -static void PostMessageVar(struct PP_Var var) {
|
| +static void PostMessage(const char* format, ...) {
|
| + struct PP_Var var;
|
| + va_list args;
|
| +
|
| + va_start(args, format);
|
| + var = VprintfToVar(format, args);
|
| + va_end(args);
|
| +
|
| g_ppb_messaging->PostMessage(g_instance, var);
|
| g_ppb_var->Release(var);
|
| }
|
| @@ -243,15 +252,14 @@ static void HandleMessage(struct PP_Var message) {
|
| const char* function_name;
|
| struct PP_Var params;
|
| if (ParseMessage(message, &function_name, ¶ms)) {
|
| - PostMessageVar(CStrToVar("Error: Unable to parse message"));
|
| + PostMessage("Error: Unable to parse message");
|
| return;
|
| }
|
|
|
| HandleFunc function = GetFunctionByName(function_name);
|
| if (!function) {
|
| /* Function name wasn't found. Error. */
|
| - PostMessageVar(
|
| - PrintfToVar("Error: Unknown function \"%s\"", function_name));
|
| + PostMessage("Error: Unknown function \"%s\"", function_name);
|
| return;
|
| }
|
|
|
| @@ -261,24 +269,78 @@ static void HandleMessage(struct PP_Var message) {
|
| int result = (*function)(params, &result_var, &error);
|
| if (result != 0) {
|
| /* Error. */
|
| - struct PP_Var var;
|
| if (error != NULL) {
|
| - var = PrintfToVar("Error: \"%s\" failed: %s.", function_name, error);
|
| + PostMessage("Error: \"%s\" failed: %s.", function_name, error);
|
| free((void*)error);
|
| } else {
|
| - var = PrintfToVar("Error: \"%s\" failed.", function_name);
|
| + PostMessage("Error: \"%s\" failed.", function_name);
|
| }
|
| -
|
| - /* Post the error to JavaScript, so the user can see it. */
|
| - PostMessageVar(var);
|
| return;
|
| }
|
|
|
| /* Function returned an output dictionary. Send it to JavaScript. */
|
| - PostMessageVar(result_var);
|
| + g_ppb_messaging->PostMessage(g_instance, result_var);
|
| g_ppb_var->Release(result_var);
|
| }
|
|
|
| +
|
| +/**
|
| + * Helper function used by EchoThread which reads from a file descriptor
|
| + * and writes all the data that it reads back to the same descriptor.
|
| + */
|
| +static void EchoInput(int fd) {
|
| + char buffer[512];
|
| + while (1) {
|
| + int rtn = read(fd, buffer, 512);
|
| + if (rtn > 0) {
|
| + int wrote = write(fd, buffer, rtn);
|
| + if (wrote < rtn)
|
| + PostMessage("only wrote %d/%d bytes\n", wrote, rtn);
|
| + } else {
|
| + if (rtn < 0 && errno != EAGAIN)
|
| + PostMessage("read failed: %d (%s)\n", errno, strerror(errno));
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Worker thread that listens for input on JS pipe nodes and echos all input
|
| + * back to the same pipe.
|
| + */
|
| +static void* EchoThread(void* user_data) {
|
| + int fd1 = open("/dev/jspipe1", O_RDWR | O_NONBLOCK);
|
| + int fd2 = open("/dev/jspipe2", O_RDWR | O_NONBLOCK);
|
| + int fd3 = open("/dev/jspipe3", O_RDWR | O_NONBLOCK);
|
| + int nfds = MAX(fd1, fd2);
|
| + nfds = MAX(nfds, fd3);
|
| + while (1) {
|
| + fd_set readfds;
|
| + FD_ZERO(&readfds);
|
| + FD_SET(fd1, &readfds);
|
| + FD_SET(fd2, &readfds);
|
| + FD_SET(fd3, &readfds);
|
| + int rtn = select(nfds + 1, &readfds, NULL, NULL, NULL);
|
| + if (rtn < 0 && errno != EAGAIN) {
|
| + PostMessage("select failed: %s\n", strerror(errno));
|
| + break;
|
| + }
|
| + if (rtn > 0) {
|
| + if (FD_ISSET(fd1, &readfds))
|
| + EchoInput(fd1);
|
| + if (FD_ISSET(fd2, &readfds))
|
| + EchoInput(fd2);
|
| + if (FD_ISSET(fd3, &readfds))
|
| + EchoInput(fd3);
|
| + }
|
| +
|
| + }
|
| + close(fd1);
|
| + close(fd2);
|
| + close(fd3);
|
| + return 0;
|
| +}
|
| +
|
| /**
|
| * A worker thread that handles messages from JavaScript.
|
| * @param[in] user_data Unused.
|
| @@ -318,6 +380,7 @@ static PP_Bool Instance_DidCreate(PP_Instance instance,
|
| ""); /* data */
|
|
|
| pthread_create(&g_handle_message_thread, NULL, &HandleMessageThread, NULL);
|
| + pthread_create(&g_echo_thread, NULL, &EchoThread, NULL);
|
| InitializeMessageQueue();
|
|
|
| return PP_TRUE;
|
| @@ -341,12 +404,41 @@ static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
|
|
|
| static void Messaging_HandleMessage(PP_Instance instance,
|
| struct PP_Var message) {
|
| + /* Special case for jspipe input handling */
|
| + if (message.type != PP_VARTYPE_DICTIONARY) {
|
| + PostMessage("Got unexpected message type: %d\n", message.type);
|
| + return;
|
| + }
|
| +
|
| + struct PP_Var pipe_var = CStrToVar("pipe");
|
| + struct PP_Var pipe_name = g_ppb_var_dictionary->Get(message, pipe_var);
|
| + g_ppb_var->Release(pipe_var);
|
| +
|
| + /* Special case for jspipe input handling */
|
| + if (pipe_name.type == PP_VARTYPE_STRING) {
|
| + char file_name[PATH_MAX];
|
| + snprintf(file_name, PATH_MAX, "/dev/%s", VarToCStr(pipe_name));
|
| + int fd = open(file_name, O_RDONLY);
|
| + g_ppb_var->Release(pipe_name);
|
| + if (fd < 0) {
|
| + PostMessage("Warning: opening %s failed.", file_name);
|
| + goto done;
|
| + }
|
| + if (ioctl(fd, NACL_IOC_HANDLEMESSAGE, &message) != 0) {
|
| + PostMessage("Error: ioctl on %s failed: %s", file_name, strerror(errno));
|
| + }
|
| + close(fd);
|
| + goto done;
|
| + }
|
| +
|
| g_ppb_var->AddRef(message);
|
| if (!EnqueueMessage(message)) {
|
| g_ppb_var->Release(message);
|
| - PostMessageVar(
|
| - PrintfToVar("Warning: dropped message because the queue was full."));
|
| + PostMessage("Warning: dropped message because the queue was full.");
|
| }
|
| +
|
| +done:
|
| + g_ppb_var->Release(pipe_name);
|
| }
|
|
|
| #define GET_INTERFACE(var, type, name) \
|
|
|