I have a program, mpg123, which has an interactive mode that allows keyboard commands from stdin to do useful things such as control volume. I am trying to start up mpg123 so that it reads commands from a named pipe; that way I can have other programs interact with it.
In one terminal I do the following:
mkfifo pipe
tail -n1 -f pipe | mpg123 -vC /some/song.mp3
And in another terminal I do the following:
cat > pipe
-
Now, I was expecting that the -
would be sent to the mpg123 program exactly the same as if I was sitting in that terminal smacking the - key, but that is not what happens. Can anyone please tell me what I am doing incorrectly?
Best Answer
It seems that
-C
causes mpg123 to read from the terminal, not from stdin. I see this, however, in my version of mpg123's man page:This may be what you are looking for; try
mpg123 -vR <pipe
. The interaction in your example would become something like the following (this sets the volume to 30%):But then, what does
-C
do that-R
doesn't that results in the former mode failing to read from stdin when a named pipe, rather than a terminal, is connected?A quick look at the mpg123 source code indicates that it uses the termios facilities to read keypresses from the terminal, using
tcsetattr
to put it in the so-called "non-canonical mode", where keypresses are transmitted to the reader without further processing (in particular, without waiting for a complete line to have been typed):(This is the same as the GNU libc code sample.)
Then, in a loop, a function
get_key
is called, which usesselect
to tell whether file descriptor 0 (stdin) has data available and, if so, reads one byte from it (read(0,val,1)
). But this still doesn't explain why a terminal works but a pipe doesn't! The answer lies in the terminal initialization code:Note that if either
tcgetattr
orterm_setup
fails, thenterm_enable
is set to 0. (The function to read keys from the terminal starts withif(!term_enable) return 0;
.) And, indeed, when stdin isn't a terminal,tcgetattr
fails, the corresponding error message is printed, and the keypress-handling code is skipped:This explains why attempting to send commands by piping into
mpg123 -C
fails. That's a debatable choice by the implementors; presumably by simply allowingtcgetattr
/tcsetattr
to fail (perhaps by using a switch for that purpose), instead of disabling the keypress-handling code handling, your attempt would have worked.