I am trying to write a program in Rust that writes a string to a named pipe. However, the data only shows up on the receiving end after the program quits.
Here's my code:
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use std::io::Write;
use std::fs::{File, OpenOptions};
fn send_data(file: &mut File, payload: &str) -> Result<(), std::io::Error> {
file.write(payload.as_bytes()).unwrap();
// I tried file.write_all() and file.flush(); same result
Ok(())
}
fn main() {
let mut file = OpenOptions::new()
.write(true)
.open("/tmp/my_named_pipe")
.expect("Failed to open named pipe for writing");
let mut stdout = std::io::stdout().into_raw_mode().unwrap();
stdout.flush().unwrap();
loop {
let key = match std::io::stdin().keys().next() {
Some(Ok(input)) => input,
_ => break,
};
match key { // press x to send data, q to quit
Key::Char('q') => break,
Key::Char('x') => send_data(&mut file, "foobar").unwrap(),
_ => (),
}
}
}
I'm creating a named pipe with mkfifo /tmp/my_named_pipe
and start listening to it with tail -f /tmp/my_named_pipe
. Then, in a different terminal window, I run my program (and press 'x' several times). The 'tail -f' doesn't show anything. When I quit my program (by pressing 'q') all strings show all at once.
I'd expect every key press to call send_data()
and every call to render on the tail -f
side immediately. How can this be accomplished?
As suggested, here is my comment slightly developed in an answer.
The problem does not stand on the Rust side, but on the tail -f
side.
After creating the pipe with mkfifo /tmp/my_named_pipe
, we can start a producer with:
( while true; do echo -n X ; sleep 1 ; done ) >/tmp/my_named_pipe
This writes a single X
, without new-line, into the pipe, every second.
In another terminal, trying to consume the pipe with tail -f /tmp/my_named_pipe
does not show anything till the producer is stopped.
This is the problem reported in the question.
If we replace echo -n X
with echo X
(i.e. we generate a new-line each time), in the producer, this does not change anything in the consumer.
Then, the problem is not related to tail
waiting for a line before outputting anything; it's probably due to the way it watches a pipe (I did not take the time to study the source code).
However, trying to consume the pipe with dd if=/tmp/my_named_pipe bs=1
or simply cat /tmp/my_named_pipe
shows an X
every second, even without new-line.
This confirms that the problem is due to tail
.