Search code examples
google-nativeclient

How to do redirection of stdin and stdout in NACL?


The below change updates the nethack nacl port from using nacl-mounts to nacl_io. In the nacl-mounts version a JSPipeMount is used to funnel messages from the browser into the nacl program's stdin. In the nacl_io version how is that accomplished?

https://chromium.googlesource.com/external/naclports/+/8ac229a6e3fddff3e061017a8eb85105874570b3%5E!/


Solution

  • As described in the commit description: "Communication between the browser and the nexe is handled by a TtyNode using Pepper Simple."

    I answered the same question with more detail on the native-client-discuss Google group here: https://groups.google.com/d/msg/native-client-discuss/nOKHWBhBPfs/i_gUYIFuBJAJ

    Here is the contents of that post:

    In nacl_io, this is just a node (MountNodeTty), not a new mount. You can access it by opening the file "/dev/tty".

    The simplest way to use this is by using the "ppapi_simple" library. It does the following:

    • Set up pp::Instance::HandleMessage to forward data to the node, which can then be read by reading from "/dev/tty"
    • Writes to "/dev/tty" are turned into calls to pp::Instance::PostMessage.
    • If you want to map stdin and stdout to /dev/tty, you can set the PS_STDIN and PS_STDOUT arguments to "/dev/tty". You can do this by adding additional attributes to your embed tag:

      <embed src="..." type="application/x-pnacl" PS_STDIN="/dev/tty" PS_STDOUT=/dev/tty"...>

    To do it yourself, you'll have to do what the ppapi_simple library does. Basically, to feed data into the node, you use a call to ioctl:

    Here is the code from pepper_30 (src/ppapi_simple/ps_instance.cc:328). The code from pepper_32 is similar.

    struct tioc_nacl_input_string ioctl_message;
    ioctl_message.length = message_len;
    ioctl_message.buffer = message_str.data();
    int ret =
      ioctl(fd_tty_, TIOCNACLINPUT, reinterpret_cast<char*>(&ioctl_message));
    

    To have the node call pp::Instance::PostMessage when you write to it, you use a different call to ioctl:

    In pepper_30, this is done automatically. All messages are prefixed so you can differentiate them, though. This can be set with an ioctl (src/ppapi_simple/ps_instance.cc:210):

    const char* tty_prefix = getenv("PS_TTY_PREFIX");
    if (tty_prefix) {
      fd_tty_ = open("/dev/tty", O_WRONLY);
      if (fd_tty_ >= 0) {
        ioctl(fd_tty_, TIOCNACLPREFIX, const_cast<char*>(tty_prefix));
      } else {
        Error("Failed to open /dev/tty.\n");
      }
    }
    

    In pepper_31 and later, you must explicitly set up a callback function for the tty node. You can do that with a third ioctl (src/ppapi_simple/ps_instance.cc:221):

      tioc_nacl_output handler;
      handler.handler = TtyOutputHandlerStatic;
      handler.user_data = this;
      ioctl(tty_fd_, TIOCNACLOUTPUT, reinterpret_cast<char*>(&handler));
    

    Finally, if you want to remap stdin/stdout to /dev/tty, you'll have to do that manually as well. This is how ppapi_simple does it (src/ppapi_simple/ps_instance:201):

    int fd0 = open(getenv("PS_STDIN"), O_RDONLY);
    dup2(fd0, 0);
    
    int fd1 = open(getenv("PS_STDOUT"), O_WRONLY);
    dup2(fd1, 1);
    

    Again, the simplest way to implement this is to use ppapi_simple, or to copy the behavior from that library.