Python send control + Q then control + A (special keys)
Pressing Ctrl + key is actually a "user-friendly" way to enter ASCII control characters. This is done by subtracting 64 from the ASCII code of the entered key (taking the capital letter where applicable). The combination Ctrl + H, for example, is equivalent to entering a backspace (H has code 72, 72-64=8
, the backspace character). This Wikipedia page lists the ASCII control characters associated with their key combinations, so Ctrl+A, Ctrl+Q is equivalent to sending the string "\x01\x11"
through the paramiko Channel:
channel = client.invoke_shell()
channel.send('\x01\x11')
Update
To check what is actually transmitted when I press Ctrl+A Ctrl+Q I have devised a small test program:
# decode.py
import sys
while True:
inp = sys.stdin.read(1)
if len(inp) == 0:
break
print ord(inp[0])
If I now call this via ssh localhost python decode.py
and enter Ctrl+A Ctrl+V Ctrl+Q (i have to do the Ctrl+V because Ctrl+Q is interpreted as XON by my local shell and not passed on to the other side), then Enter Ctrl+D to close the connection, i get 1
, 17
and 10
as ordinals, or '\x01\x11\n'
, as expected.
I basically get the same by executing printf '\x01\x11\n' | ssh localhost python decode.py
. If, however, I allocate a pty on the remote end via printf '\x01\x11\n' | ssh -tt localhost python decode.py
the \x11
is intercepted by the remote pty and not passed on through to the running script (i get 1
,10
as output). In this case it helps to send a Ctrl+V (\x16
) before the Ctrl+Q that instructs the pty to pass the next character Verbatim.
As expected printf '\x01\x16\x11\n' | ssh -tt localhost python decode.py
outputs 1
, 17
and 10
.
This works perfectly for me, returning exactly what I would expect. There are obviously some pieces missing from your code above, so this required a little winging.
import sys
import time
import getpass
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('127.0.0.1',
username='apsuser',
password=getpass.getpass('Password: '))
shell = ssh.invoke_shell()
shell.settimeout(0.25)
shell.send('picocom /dev/ttyS0\n')
time.sleep(2)
sys.stdout.buffer.write(shell.recv(10000))
sys.stdout.buffer.flush()
shell.send('\x01')
shell.send('\x11')
time.sleep(2)
sys.stdout.buffer.write(shell.recv(10000))
sys.stdout.buffer.flush()
print()
time.sleep(2)
And the results are:
Password:
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Apr 14 19:55:57 2016 from 127.0.0.1
picocom /dev/ttyS0
apsuser@Steve-Laptop:~$ picocom /dev/ttyS0
picocom v1.7
port is : /dev/ttyS0
flowcontrol : none
baudrate is : 9600
parity is : none
databits are : 8
escape is : C-a
local echo is : no
noinit is : no
noreset is : no
nolock is : no
send_cmd is : sz -vv
receive_cmd is : rz -vv
imap is :
omap is :
emap is : crcrlf,delbs,
Terminal ready
Thanks for using picocom
apsuser@Steve-Laptop:~$
So what did I do that your code does not do?
Just as assumption: maybe pseudoterminal would help
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(...)
channel = сlient.get_transport().open_session()
channel.get_pty()
channel.settimeout(5)
channel.exec_command('\x11\x01')