SSH escape key ("~") only works when connection is stuck?

10

3

When I have an unresponsive SSH connection, I can kill it with <enter>~.. However, when the connection is responding, the ~ escape doesn't work. It just prints a tilda at the console.

So if I want to modify SSH port forwarding and press <enter>~C<enter>, all I get is:

~C: command not found

(From bash, not from ssh.)

What do I need to do so that the SSH escape key works properly?

EDIT: I have found a big clue: the remote shell was actually ash, not bash. When I run bash on the remote machine, the SSH escape key works! When I run ash inside bash inside ash, again, it doesn't work!

But this is very strange. The escape key should be caught by the SSH client and not even forwarded to the remote shell. So why should it matter exactly which remote shell is receiving input from SSH?

Alex D

Posted 2015-10-12T08:19:24.903

Reputation: 291

1~. and ~C work for me in Bash too (when using SSH on a Mac). – Arjan – 2015-10-12T08:28:05.777

In case it makes a difference, ssh -V prints OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.3, OpenSSL 1.0.1f 6 Jan 2014. – Alex D – 2015-10-12T08:33:44.930

On my up-to-date Mac it's much older, OpenSSH_6.2p2, OSSLShim 0.9.8r 8 Dec 2011... – Arjan – 2015-10-12T08:55:24.753

Just in case it might be related to the remote SSH server: I get the same (expected) behaviour when using ssh localhost. The remote host I tested with earlier shows OpenSSH_4.3p2, OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008. Wow... ;-) – Arjan – 2015-10-12T09:06:18.320

Answers

6

Simple workaround: run the cat command, then enter the escape sequence.

The cat command will by default print what is passed in stdin, so while it's running there will be no escape characters sent and you can use the ssh escape key as normal. When done just ctrl-c out of cat back to the shell.

Example If needed, open up a prompt and execute cat by typing cat and pressing enter.

$ 
$ cat 

Now type ~?

~?
Supported escape sequences:
 ~.   - terminate connection (and any multiplexed sessions)
 ~B   - send a BREAK to the remote system
 ~C   - open a command line
 ~R   - request rekey
 ~V/v - decrease/increase verbosity (LogLevel)
 ~^Z  - suspend ssh
 ~#   - list forwarded connections
 ~&   - background ssh (when waiting for connections to terminate)
 ~?   - this message
 ~~   - send the escape character by typing it twice

It works! Now just enter any command. Then to return to prompt press control-C.

^C
$

busybox user

Posted 2015-10-12T08:19:24.903

Reputation: 61

1This is so simple that I have no idea what you are suggesting to do. Can you expand on your answer a bit? – Stephen Rauch – 2017-03-27T18:55:25.670

1Kindly Edit your answer and write more details in order to help users understand – yass – 2017-03-27T19:04:47.220

Wow awesome! That works. It should be marked as the answer. I added an example to hopefully make it more comprehensible for others. – Nebula – 2017-05-30T12:26:34.830

4

I've figured out the secret!

As I posted in the "edit" above, the remote shell was BusyBox ash, not bash.

From libbb/lineedit.c:2336-2338, in the BusyBox sources:

/* Print out the command prompt, optionally ask where cursor is */
parse_and_put_prompt(prompt);
ask_terminal();

That is used to print out the command prompt in ash. But notice, as soon as it prints the prompt, another function called ask_terminal is called. What does ask_terminal do? It prints out the following characters: <ESCAPE>[6n.

You never see those characters in your terminal. Actually, they are an ANSI terminal control escape code. <ESC>[6n is a "Query Cursor Position" command -- it tells the terminal emulator to send back another ANSI escape code, which tells the shell where the cursor (text insertion point) is located in the terminal window.

So as soon as you press Enter, ash prints out <ESC>[6n, and sshd passes that back to ssh and from there to the terminal emulator. Immediately, before you can press ~, your terminal emulator sends something like <ESC>[47;13R to standard input, and ssh passes that over the connection to sshd and from there to ash, telling ash where your cursor is.

Now, the SSH client doesn't actually know what those ANSI escape codes mean. To SSH, they are all just characters read from standard input. Rather than seeing <ENTER>~C, the SSH client sees <ENTER><ESC>[47;13R~C, and since it doesn't see the ~ right after Enter, it doesn't think that it is an escape code.

The question is what to do about this. It would be nice if OpenSSH understood those ANSI escapes sent by the terminal and would still accept the ~ escape character after an ANSI terminal control command. I may send the OpenSSH guys a patch and see if they are interested in fixing this...

Alex D

Posted 2015-10-12T08:19:24.903

Reputation: 291

Nice find; I'd somehow expect the SSH client to only take the local actual keypresses into account, but your explanation proves me wrong there. – Arjan – 2015-10-12T16:40:55.760

Unfortunately, ssh is a console application and only knows what is sent to its standard input. That is why you can cut-and-paste text into a terminal window where ssh is running, and it responds just as if you had typed that text. – Alex D – 2015-10-12T17:57:20.130

1BTW, I sent a patch fixing this to the OpenSSL devs... but it is hanging in limbo, not rejected, but not merged either. – Alex D – 2016-11-08T19:18:31.673

2

You asked Surely, the SSH client escape key is not supposed to work like this??

Yes it should work so:

If you press Enter then ~ and it will not appear the character ~ on the prompt that escape will be act in the ssh.

If you press again ~ it will appear on your prompt and it will act on the working shell (the bash in your example), that will try to expand and use as it knows.

Note that to work the ~ have to be the first of your line buffer, so if you enter any character and after you erase all the line is not anymore new and you need to press Enter again.

When I press ~? I obtain

Supported escape sequences:
~. - terminate connection (and any multiplexed sessions)
~B - send a BREAK to the remote system
~C - open a command line
~R - Request rekey (SSH protocol 2 only)
~^Z - suspend ssh
~# - list forwarded connections
~& - background ssh (when waiting for connections to terminate)
~? - this message
~~ - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

Hastur

Posted 2015-10-12T08:19:24.903

Reputation: 15 043

1Thanks, but this doesn't help. When I press Enter, a newline is sent through SSH to my shell, and the shell responds by displaying a new prompt. Then, when I press ~, a tilda is sent through SSH to the shell, and the shell displays it after the prompt. – Alex D – 2015-10-12T08:41:08.573

2You are describing how the escape key works normally. The whole point of this question is that the escape key is not working normally for me. – Alex D – 2015-10-12T08:41:47.033

This does indeed not answer the question; please delete it so others don't think Alex got an answer. – Arjan – 2015-10-12T09:07:35.740

@AlexD Does it works after a manual Ctrl-J or Ctrl-M? – Hastur – 2015-10-12T09:08:34.783

Haha, no of course "Surely, the SSH client escape key is not supposed to work like this??" is not the question. The question shows Alex knows what to expect. Again, please delete this. – Arjan – 2015-10-12T09:10:41.017

@Hastur, Ctrl-J and Ctrl-M just behave the same as Enter. The shell prints a new prompt, and then when I press ~, it appears at the prompt, as if I am typing a command. – Alex D – 2015-10-12T09:18:27.827

@Arjan "Haha" is never a proper comment and contradicts in terms the please: so, for now, I will ignore both parts. BTW he is an experienced user on another site (more than 17.9k of experience) he should know how to do questions. For me it is a proper answer, it will not exclude the possibility for others user to answer. Users that will come on this page should even find it useful... – Hastur – 2015-10-12T09:18:57.030

(I'd love to remove the "haha" part, but it's too late.) – Arjan – 2015-10-12T09:24:00.133

1I have clarified the question. Even if the question was "should the SSH escape key work like this", this is not a correct answer, because you answer "yes", but then proceed to describe behavior different from what I am seeing (as described in the question). – Alex D – 2015-10-12T09:25:00.183

1Dear @AlexD you are an experienced user.You should put more care when you write a question, even more because you sold you time to solve problems of other people. Here I present to you some of my time. If you dislike the answer and you think it was made without thinking about what you wrote, you can downvote it. Remember that you start with "SSH escape key (“~”) only works...?" and "Surely, the SSH client escape key is not supposed to work like this??". I answered to the 2nd thinking you need confirmations. Now you ask a new one: "...So why should it matter exactly which remote shell is...?" – Hastur – 2015-10-12T09:47:38.703

@AlexD I think that if you find that the problem was only the ash and not bash shell on the second system the proper way to proceed should be to post it as an answer, accept it, then open a new question with the new issue. Nothing personal of course. – Hastur – 2015-10-12T09:51:45.637