0

A virtual machine (using linux+kvm+qemu) is setup to provided a serial port for a terminal, which is made available via a pseudo-terminal, some random /dev/pts/<number>

I use screen as a way to interact with /dev/pts/<number>, as it has proven better than cat /dev/pts/<number> & cat > /dev/pts/<number> which did not correctly handle escapes like ctrl-c, or echoed input multiple times.

The issue and core of this question is that the settings of the "tty/pts" as inquired via stty --all inside the shell wihtin screen /dev/pts/<number> does not have the correct settings with respect to the dimensinos (cols and rows) which effectively causes headache by incorrect line-wrapping etc inside the shell of the VM.

Since there is more than 1 machine and terminal/tty/pts at play here I am not experienced enought to understand how to setup the correct settings.

How can the screen /dev/pts/<number> shell be made aware of the correct stty settings?

** Update **

The output of stty --all within the shell of the vm is.

root@mail:~# stty --all
speed 115200 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon ixoff
-iuclc -ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon -iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke -flusho -extproc

the output of stty --all in the shell of the hosting system is

speed 38400 baud; rows 39; columns 147; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc
Thomas Dickey
  • 76,765
fraleone
  • 797
  • Could you post the result of stty --all and say what the correct setting should be? – Eduardo Trápani Dec 12 '20 at 17:53
  • @EduardoTrápani I added both stty --all as it output when (a) within the vm (i.e. via the pts interface) and (b) how it is nomally on the host system. – fraleone Dec 12 '20 at 22:07
  • @EduardoTrápani correct would be a setting where at the least the cols and rows are set to the correct values 147 and 39 respectively – fraleone Dec 12 '20 at 22:14
  • That connection via a serial port prevents the shell from seeing the host machine's screensize. I'd use resize. – Thomas Dickey Dec 13 '20 at 00:28
  • @ThomasDickey is resize any different then setting the values via stty cols XX rows YY? If the serial port prevents that, why does it yet work behind a ssh connection, to what I uderstand in both cases the SHELL is run remotely (i.e. not where the terminal emulator is?) – fraleone Dec 14 '20 at 08:13
  • resize does it automatically (asking the terminal what its dimensions are), rather than constants, but uses the same interface as stty running on the local machine. ssh uses a protocol "NAWS" (negotiations about window size) which is not available to the serial interface). – Thomas Dickey Dec 14 '20 at 08:48

2 Answers2

1

If you set your interactive shell initialization to run resize, it will ask the terminal how large it is, and execute the same system calls that stty would.

This chunk at the end of a .bashrc would work:

if [ -t 0 ] && [ -t 1 ] && [[ $TERM == screen* ]] && [ -f /usr/bin/resize ]; then
  resize >/dev/null
  # stty -a
fi

It checks if the standard input/output are terminals (i.e., the session is interactive), if TERM is set to one of the screen flavors, and if resize is installed.

Redirecting the output of resize does not interfere with its sending escape sequences to the terminal and getting a reply because it opens its own stream to the tty for this.

Further reading:

Thomas Dickey
  • 76,765
0

tl;dr; to make the internal terminal behind the serial connection receive the dimension settings of the terminal emulator put this in your ./bashrc

set_terminal_dimension() (
  old="$(stty -g)"
  stty raw -echo min 0 time 1
  printf '\033[18t' >/dev/tty
  IFS=';t' read -r _ rows cols _ </dev/tty
  stty "$old";
  stty cols "$cols" rows "$rows"
)
PROMPT_COMMAND='set_terminal_dimension'

Background details

Being connected to linux system on one of its serial connection (i.e. /dev/ttyS0) provides a byte data stream of input and output. It does however not provide of IPC (inter process communication methods, like SIGNALS). Hence there is no SIGWINCH which would:

The SIGWINCH signal is sent to a process when its controlling terminal changes its size (a window change).

Also of course the different Computer that displays the output received from the serial connection cannot issue the terminal settings (e.g. via stty) since it is not the same machine.

Between the two machines: a) TARGET-Sys providing a shell behind a serial connection and b) CONNECTING-Sys reading and writing to the TARGET system via the serial connection, the trouble is this:

The System "a) TARGET" is running the shell which needs to be set the correct info about connections (i.e. possible via the command line utility stty). It cannot know the dimensions of how many characters can be displayed on the screen (cols and rows, which is known only to the "b) CONNECTION"-system and more precisely its terminal emulator application).

The systems a) and b) being connected by two byte streams i)input and ii)output, needs to exchange the cols, rows information over the same stream one primarily sees in keyboard input being send to and output being received from the "a) TARGET" system. Clearly this exchange of information could conflict with the normal bytes already send, which is why a protocol can be used in which the information is attempted to be specially marked by using escape byte sequences. As laid out in this answer serial communication is in contrast to ssh connection (also connection two systems), not setup with a special protocol like NAWS (see answer to "How are terminal length and width forwarded over SSH and telnet?").

Another answer here suggests the ready made program resize which is often packed by distros. This answer suggests more light-weight options, which may be more in line with a "no-frills-reduce-package-dependencies"-attitude one could expect on many systems that use serial communication over a GUI (e.g Xorg server)

On the Archlinux Wiki on "Working with the serial console" there in the "Troubleshooting section" in addition to mention the resize command which is many distributions include in the packages for xterm. Since using a serial conncetion often coincides with no need for graphical output/gui on the system one connects to via a serial connection, the xterm solution might be rather a oversized solution (give Xorg server.... being package dependencies).

Therefore, regarding the dimensions of the terminal this is told there

  1. If you don't want to install xterm, it is possible to do the same work via a shell function. Put the following function into your zshrc and invoke it without parameters after resizing the terminal emulator's window:

The solution sketched there also sheds more light on how the Terminal is requesting and receives a response form the terminal emulator at the other side. A more light-weight solution could be along the lines of this bash script /bin/sbin/resize.sh:

#!/bin/bash

store tty settings before

old="$(stty -g)"

briefly disable any stty setting, like echo or line discipline

because for a brief moment a escape sequence is send (to be received

by the receiver (a terminal emulator program)

a) raw => no line discipline

b) -echo => disable repeating and display stuff

c) min 0 time 1 => return a read with after 1 tenth of a sec and no minimum bytes

stty raw -echo min 0 time 1

send the request an escape sequence for the outer terminal emulator

printf '\033[18t' >/dev/tty

use read shell builtin to read the return into the variables rows, cols

(IFS is used in combination with a "_" (as "garbage variable") to

get rid of not neede part of response)

IFS=';t' read -r _ rows cols _ </dev/tty

(previous settings are restore, ie. the "raw" and "-echo" is undone)

stty "$old";

finally the received info ( the info provided via the same stream

of bits as bytes as also actually incoming the bytes generated by the

keyboard ) and which was read into the variables was set before

stty cols "$cols" rows "$rows"

fraleone
  • 797