Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do terminal size changes get sent to command line applications though ssh or telnet?

How do terminal size changes get sent to command line applications through ssh or telnet?

  1. A user connects to a remote machine with ssh or telnet.

  2. They start editing a document in VIM.

  3. Then they resize their terminal window.

The terminal must tell ssh/telnet that the window size had changed. How does this happen?

ssh/telnet then uses its own particular method to send that change to sshd/telnetd. What are these methods?

sshd/telnetd then tells the application that the terminal size has changed. How is this done. Is it the same method as from the terminal to ssh/telnet?

like image 881
fadedbee Avatar asked Oct 03 '13 10:10

fadedbee


1 Answers

This is the messy world of pseudo terminals.

Locally, when you resize your terminal your foreground process group gets a SIGWINCH and you can use ioctl to fetch the new size. But what does this have to do with the remote vim process ?

The subject is quite complicated but the gist of it is that the remove server (sshd) does this:

  1. Opens a master psedoterminal device using posix_openpt (or openpty)
  2. Forks a new child (typically this is bound to become a shell)
  3. Severs its terminal connection using setsid()
  4. Opens the terminal device (created in step 1) which becomes its controlling terminal
  5. Replaces the standard descriptors (STDIN_FILENO and friends) with the fd from step 4

At this point anything the server process writes to the master side ends up as input to the slave side BUT with a terminal line discipline so the kernel does a bit of magic - like sending signals - when writing certain combinations, and you can also issue ioctl calls with useful effects.


The best way to think about this is to explore the openssh suite.

  • The client monitors for SIGWINCH - see clientloop.c and sets received_window_change_signal = 1 when it receives it

  • The function client_check_window_change checks that flag and tells the server:

    packet_start(SSH_CMSG_WINDOW_SIZE);
    packet_put_int((u_int)ws.ws_row);
    ...
    

So now the server should receive a packet which specifies a (potentially new) size.

  • The server calls pty_change_window_size with the received sizes which does the real magic:

    struct winsize w;
    w.ws_row = row;
    ...
    (void) ioctl(ptyfd, TIOCSWINSZ, &w); /* This is it! */
    

This sets the new window size of the slave. If the new size differs from the old, the kernel sends a SIGWINCH to the foreground process group associated with that pty. Thus vim also gets that signal and can update its idea of the terminal size.

like image 138
cnicutar Avatar answered Oct 12 '22 13:10

cnicutar