With a little help from an answer found here I was able to use below script to send keystrokes to the VGA console /dev/tty1


use warnings;
use strict;

open( my $TTY1 , '>' , '/dev/tty2' ) or die "Cannot open terminal for output: $!";

my $line = <STDIN>;

for my $chr ( split( // , $line ) ) {
  ioctl $TTY1 , 0x5412 , $chr or die "ioctl returned an error: $!";

# Wait a bit to allow for inspection of the result using setterm.
sleep 3;
# Then send an Enter.
my $chr="\n";
ioctl $TTY1 , 0x5412 , $chr or die "ioctl returned an error: $!";

close( $TTY1 );

It can be tested by:

echo -e 'date' | sudo /tmp/stdin2keyboard-buffer

And I check the current state of VGA console by:

watch sudo setterm -dump 2 -file /dev/stdout

where 2 represents the number that tty returns on the vga console and should also be set as the right tty device in the open statement above. I can even send a Cursor Up followed by an enter and it runs the last command in shell history.

echo -e '\e[A' | sudo /tmp/stdin2keyboard-buffer

Now what I really would like to do, is to send Shift+PgUp to the console so I can see the scroll back of the VGA console too, but I can't seem to figure out the escape sequence for Shift PgUp.

Another approach would be to figure out how to dump the full buffer of /dev/vcsa, but either the device driver doesn't support that or I haven't figured out how to. In any case, the setterm binary doesn't support it.


You are not sending keystrokes.

The disfavourably-regarded TIOCSTI I/O control inserts characters into a terminal device's input stream. This stream is what results after keystrokes have been turned into character sequences. It's the (raw) stream of characters seen by applications programs that read input from the terminal.

The keystrokes that scroll up and down the scroll buffer are stripped out of the input before being turned into character sequences to be put into this stream. There is no way to inject them using TIOCSTI. They are, as far as the POSIX terminal I/O model is concerned, handled entirely locally within the terminal, and are never seen by the host. (The fact that the terminal is not a real terminal but is actually an emulator program running in the kernel on the host, does not change this.)

To inject keystrokes into a KVT, use a uinput device to generate the necessary evdev input events. When it comes to KVTs, evdev input events are conceptually internal to the (emulated) terminal itself, and subject to any local processing within the terminal before being sent to the host.

Further reading

  • I ended up reading about evemu-tools which is an interesting read. It is certainly nice tool for my troubleshooting toolbox. Just have to figure out if I can make it run from the correct terminal. – jippie Jun 9 at 8:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.