48

When using the serial console of my system I always end up with $COLUMNS=80 and $LINES=24.

While I can change these variables manually it is somewhat annoying to do this any time when the client side terminal window has been resized.

Usually I'm connecting to the console using screen /dev/mytty baudrate.

Changing the $TERM environment variable to "screen" or "xterm" does not help.

Will I need to call getty with some of those instead of vt100?

Needless to say that all of this works fine, when I connect to the same machine using ssh.

Caleb
  • 70,105

10 Answers10

45

Like the commentators before me mentioned there is no alternative to calling resize after every command, if you don't have this command and you don't want to install a package where it's in (xterm), here are two POSIX shell script that do the same using ANSI terminal escape codes:

res() {

  old=$(stty -g)
  stty raw -echo min 0 time 5

  printf '\0337\033[r\033[999;999H\033[6n\0338' > /dev/tty
  IFS='[;R' read -r _ rows cols _ < /dev/tty

  stty "$old"

  # echo "cols:$cols"
  # echo "rows:$rows"
  stty cols "$cols" rows "$rows"
}

res2() {

  old=$(stty -g)
  stty raw -echo min 0 time 5

  printf '\033[18t' > /dev/tty
  IFS=';t' read -r _ rows cols _ < /dev/tty

  stty "$old"

  # echo "cols:$cols"
  # echo "rows:$rows"
  stty cols "$cols" rows "$rows"
}

BTW, in my .profile file you will find the following: [ $(tty) = /dev/ttyS0 ] && res so that the the terminal size is determined on every login over the serial line (the one I use for management), e.g. after you reboot the device.
See also the idea by rsaw in the comments to have the line [ $(tty) = /dev/ttyS0 ] && trap res2 DEBUG there instead so the resizing runs after every command (note that AFAIK it's not or not always possible on busybox though).

phk
  • 5,953
  • 7
  • 42
  • 71
  • 3
    PS: To make more permanent, add [[ $(tty) == /dev/ttyS0 ]] && trap res2 DEBUG to one of the shell profile configs (e.g., /etc/profile,~/.bash_profile). This will make it be run after every single command (which would only be a good thing if you're resizing windows/panes with screen/tmux/terminal-emulator). – rsaw Dec 30 '16 at 20:24
  • 2
    After using it for a few mins I quickly realized that both res & res2 are too slow for anything but use on first-login. On my machines, they're both taking 0.5sec to finish ... making all my commands appear sluggish (when used with DEBUG trap). Whoops! Can't have that. Guess I'll be installing xterm. – rsaw Dec 30 '16 at 20:59
  • 3
    @phk xterm's resize is waaaay faster -- usually 0.002sec. – rsaw Dec 30 '16 at 21:23
  • 1
    @rsaw Oh OK, good to know, I thought it would behave similar and therefore be similarly slow. I remember that the one in some busyboxes seemed to be just as slow to me. – phk Dec 30 '16 at 21:31
  • 1
    Thanks for this standalone solution. I'm using a console-only distro that does not have x11 or xterm installed so resize is not an option. – thom_nic Jan 23 '17 at 17:30
  • 1
    @thom_nic, FWIW, on Fedora, resize is packaged as xterm-resize that doesn't have any X11 dependencies. Thus, it should be fine for console-only distributions. – maxschlepzig Mar 07 '18 at 14:07
  • For anyone using GNU screen to serial console into a Raspberry Pi, I have found that the res function here works, while the res2 function does not. Using iTerm2 on macOS 10.15.5 – Bruno Bronosky Jun 16 '20 at 11:00
  • This is just a perfect answer for me. I'm using this in an anaconda script to create /etc/profile.d/term_rezise.sh which will run res2 on login. Brilliant – Orsiris de Jong Jun 01 '23 at 17:00
20

Just for the record, here is the answer to this Problem (Usenet won):

Console Applications running inside virtual terminal applications (xterm, rxvt and friends) will receive SIGWINCH after a resize operation has taken place. Thus the application will be able to redraw the window etc. in the corresponding signal handler.

Unfortunately when using a serial console, there is no such mechanism.

It is however possible for the application to actively ask for the current console Window size. So the second best thing is to do this every time a command prompt is printed by the shell.

This can be achieved by first compiling a special resize executable and then using the following in bashrc:

if [ $(tty) == '/dev/ttyS0' ]; then
  trap resize DEBUG
fi

Of course this will not change the console size settings in a console application during runtime.

ndemou
  • 2,809
  • 1
    Shouldn’t it be possible to run a protocol over the serial line, that does offer all the features? I mean we do have a client and a server. They could use inband escape sequences to do just about everything, and still work with a plain text serial console! –  Feb 02 '16 at 14:35
  • 1
    Actually, the comment in the code makes it plain that it's not the version of resize which is installed on your system. – Thomas Dickey Feb 16 '16 at 15:23
15

"Resizable" terminals as such are a result of NAWS (Negotiate About Window Size from RFC 1073 Telnet Window Size Option).

If you are connected directly to the computer using a serial port, there is no negotiation involved, and the computer has no direct knowledge of your terminal's screen-size.

If a terminal can negotiate the size, the computer will send SIGWINCH to applications running in the terminal, telling them to update their notion of the screensize.

When the computer does not know the screensize, it typically sets the size shown by stty -a (rows and columns) to zero. For interactive use, this is a little unfriendly, and some systems use environment variables LINES and COLUMNS to help. The values assigned may be derived from the terminal description; more often they are simply hardcoded. The convention for these variables requires that they take effect unless explicitly suppressed, e.g., in curses applications use_env function. On the positive side, those variables can be useful when no reliable information is available. On the negative side, there is no convenient method for altering those variables.

The resize program (a utility provided with xterm) can use the VT100-style cursor position report escape sequence for determining the screen size. This can be run from the command-line; there is (again) no convenient way to do it automatically. As a side-effect, resize updates the information on rows/columns seen by stty. Its use for providing updated environment variables is mainly useful for cases such as this, where LINES and COLUMNS are set, and should be updated.

Thomas Dickey
  • 76,765
3

When running a shell session over a serial line it is sufficient to call the resize command inside that session - after establishing the connection and after each terminal geometry change.

The resize command is part of xterm but doesn't depend on X11. For example, on Fedora it's separately packaged as xterm-resize.

How it works: the resize command measures the height/width via some cursor movements and then sends those values to the terminal via escape sequences.

With a shell like zsh this also automagically updates the LINES and COLUMNS variables (alternatively, one can evaluate the export statements the commands prints to stdout).

Why this is necessary: with a local or ssh session the terminal is able to signal the session about geometry changes (cf. SIGWINCH). This mechanism doesn't work over a serial connection.

maxschlepzig
  • 57,532
3

Here is simple and fast resize function that works only for bash. It is modified from phk's res2, making use of bash's read -d delim to avoid letting the timeout to finish read.

resize() {
  old=$(stty -g)
  stty -echo
  printf '\033[18t'
  IFS=';' read -d t _ rows cols _
  stty "$old"
  stty cols "$cols" rows "$rows"
}
  • As the resize command in Debian (and therefore also in RaspiOS and Ubuntu) is still part of the xterm package many years after someone added a patch to Debians bug tracking system this is now my preferred solution for a headless Raspberry Pi. – Sven Geggus Apr 01 '21 at 10:35
2

Here is another solution that worked great for me on my embedded Linux system (Overo running Angstrom). I just ran it from my .bashrc file. I didn't want to use resize because that requires installing some X packages, and I didn't want that.

Telling your Raspberry Pi that your terminal is bigger than 24 lines | Shallow Thoughts Blog

  • 5
    Please don't just post a link: include the relevant detail so that the information is available here as well... – jasonwryan May 03 '14 at 00:51
  • 1
    Too bad it needs Python. – Craig McQueen Oct 31 '14 at 04:52
  • 2
    Not only is this not an answer, it is also not a link to an answer. It is a link to a page that links to an answer, that is effectively just a python implementation of the res function from the top voted answer https://unix.stackexchange.com/a/283206/9745 – Bruno Bronosky Jun 16 '20 at 10:57
2

While the resize command worked for me (stty manipulations were horribly slow), apparently resize in DEBUG trap messes up shell pipes.

Here is the solution I’ve ended up using. It runs resize after the prompt, but before the first command in pipe, unlike plain DEBUG hook that would run for each subcommand in foo | bar | baz.

Note that we can’t just put resize in PROMPT_COMMAND because it runs when the prompt is printed and it didn’t work well in my Vim "^Z+fg"-based workflow.

  1. Resize terminal window while in Vim.
  2. ^Z (suspend job).
  3. PROMPT_COMMAND executes and enables DEBUG hook (that would be triggered on next command).
  4. Sometimes I adjust window size again at this point. That’s exactly why I can’t use PROMPT_COMMAND alone.
  5. Type fg or ls | cat and hit enter.
  6. DEBUG hook fires once and disables itself until the next prompt.
  7. Shell runs user input.
if [ "$(tty)" = /dev/ttyS0 ]; then  
        # Run resize only after prompt so that we don’t
        # mess up pipes and other things.
        debug_hook_resize() {
                if [ -n "${debug_hook_prompt+x}" ]; then
                        unset debug_hook_prompt
                        resize 2>&1 >/dev/null
                fi
        }
        prompt_hook_resize() {
                debug_hook_prompt=
        }
        PROMPT_COMMAND=prompt_hook_resize
        trap debug_hook_resize DEBUG
fi
tie
  • 121
1

In case you could use FreeBSD instead, there's the resizewin(1) command, which does exactly what you want.

Sparhawk
  • 19,941
0

Improving upon Sven Geggus answer:

This can be achieved by first compiling a special resize executable and then using the following in bashrc

A simple output redirection makes the gnu resize command works. Just add the following to your .bashrc:

alias rsz='resize >/dev/null'
if [ $(tty) == '/dev/ttyS0' ]; then
  trap rsz DEBUG
fi

In other words, you do not need a special resize command.

Beshr
  • 1
0

I use this for Serial Gadget (based on phk's answer).
Calling resize only once per line.

sudo nano .profile

add:

# resize function for connected serial console
resize() {
  old=$(stty -g)
  stty raw -echo min 0 time 5

printf '\0337\033[r\033[999;999H\033[6n\0338' > /dev/tty IFS='[;R' read -r _ rows cols _ < /dev/tty

stty "$old"

#echo "cols:$cols" #echo "rows:$rows" stty cols "$cols" rows "$rows" }

ALL_COMMANDS_FINISHED=1

PreCommands() { if [ "$ALL_COMMANDS_FINISHED" = '1' ]; then ALL_COMMANDS_FINISHED=0 resize #echo "1->0" fi }

PostCommands() { ALL_COMMANDS_FINISHED=1 }

if login thru tty*

if [ $(tty) = /dev/tty* ]; then

PreCommands is called before every single command on a command line

trap PreCommands DEBUG

PostCommands is called after all commands on a command line are finished

PROMPT_COMMAND="PostCommands" fi

A.J.Bauer
  • 101