How to write to stdin and read from stdout using nacl_io?

447 views
Skip to first unread message

Cary Cherng

unread,
Oct 11, 2013, 7:16:09 AM10/11/13
to native-cli...@googlegroups.com
In the past the nacl-mounts library had a class JSPipeMount in "nacl-mounts/console/JSPipeMount.h" that could be used to write to stdin and also send stdout to the javascript message event listener.

Now nacl_sdk includes part of nacl-mounts under the name nacl_io. But nacl_io as far as I can see doesn't have something analgous to JSPipeMount to give a way to manipulate stdin and stdout. What is the current way to achieve this now?

Ben Smith

unread,
Oct 14, 2013, 2:58:14 AM10/14/13
to native-cli...@googlegroups.com
In nacl_io, this is just a node, 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.

-Ben

Cary Cherng

unread,
Oct 14, 2013, 10:35:05 PM10/14/13
to native-cli...@googlegroups.com
Thanks Ben. I was able to get ps_stdin working but I am having trouble with ps_stdout.

In the below code when the embed tag has PS_STDIN="/dev/tty" but no PS_STDOUT attribute the browser displays
Hello World STDOUT 1. Hello World STDOUT 3.
which seems reasonable.

But when PS_STDOUT="/dev/tty" is added to the embed tag the browser displays just
Hello World STDOUT 1.
suggesting that the getline is blocking and that the postMessage from javascript isn't reaching the getline.

The correct output in the browser should be
Hello World STDOUT 1. Hello World STDOUT 2. Hello World STDOUT 3.
Is there something I am missing or misunderstanding?

hello.js:

    moduleDidLoad = ->
      helloModule = document.getElementById('helloModule')
      helloModule.postMessage('Hello World STDOUT 2\n\n')

    handleMessage = (message_event) ->
      $('#status_field').append(message_event.data)


hello.cpp:

    int example_main(int argc, char* argv[]) {
      printf("Hello World STDOUT 1.\n");

      std::string cmd;
      getline(std::cin, cmd);
      printf(cmd.c_str());

      printf("Hello World STDOUT 3.\n");

      return 0;
    }
    PPAPI_SIMPLE_REGISTER_MAIN(example_main)

On Friday, October 11, 2013 4:16:09 AM UTC-7, Cary Cherng wrote:

Sam Clegg

unread,
Oct 15, 2013, 11:44:05 AM10/15/13
to native-cli...@googlegroups.com
I think you also need to specify PS_TTY_PREFIX otherwise ppapi_simple
doesn't know which messages are tty-related:

https://code.google.com/p/chromium/codesearch#chromium/src/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc&l=213

Also, be aware that when you program exits (or returns from main()),
any messages still in flight (most likely the last calls the printf)
may be lost and never make it back to javascript. We are considering
various way to address this.

Sorry for the current lack of documentation in this area. We hope to
address this soon.

cheers,
sam
> --
> You received this message because you are subscribed to the Google Groups
> "Native-Client-Discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to native-client-di...@googlegroups.com.
> To post to this group, send email to native-cli...@googlegroups.com.
> Visit this group at http://groups.google.com/group/native-client-discuss.
> For more options, visit https://groups.google.com/groups/opt_out.

Cary Cherng

unread,
Oct 15, 2013, 6:20:08 PM10/15/13
to native-cli...@googlegroups.com
Thanks Sam, adding the ps_tty_prefix with the random string 'sf:' worked to unblock the getline

But I am seeing undesired output. It appears that each character of 'sf:Hello World STDOUT 2\n' sent from the browser to the nexe is automatically echoed by the nexe back to the brower. So the nexe echoes back 'sf:Hello World STDOUT 2\n' with each character prefixed with 'sf:' giving the below browser output. How can I stop that?

sf:Hello World STDOUT 1. sf:Hsf:esf:lsf:lsf:osf: sf:Wsf:osf:rsf:lsf:dsf: sf:Ssf:Tsf:Dsf:Osf:Usf:Tsf: sf:2sf: sf:Hello World STDOUT 2Hello World STDOUT 3.


hello.js:

    # Attributes of embed tag
    # ps_stdin='/dev/tty'
    # ps_stdout='/dev/tty' 
    # ps_tty_prefix='sf:'

    moduleDidLoad = ->
      helloModule = document.getElementById('helloModule')
      helloModule.postMessage('sf:Hello World STDOUT 2\n')

    handleMessage = (message_event) ->
      $('#status_field').append(message_event.data)


hello.cpp:

    int example_main(int argc, char* argv[]) {
      printf("Hello World STDOUT 1.\n");

      std::string cmd;
      getline(std::cin, cmd);
      printf(cmd.c_str());

      printf("Hello World STDOUT 3.\n");

      return 0;
    }
    PPAPI_SIMPLE_REGISTER_MAIN(example_main)

Reply all
Reply to author
Forward
0 new messages