¿Obtener respuesta ssh usando Python?
Recientemente, estaba trabajando en un proyecto que requería integrar una función ssh interactiva en el lado del cliente. Básicamente, el cliente solicita al servidor una máquina disponible, el servidor devuelve la IP, el puerto y la contraseña, y el cliente puede iniciar sesión en la máquina y realizar el trabajo directamente. El programa está basado en el módulo paramiko.
Después de buscar en el directorio demos del paquete de código fuente de paramiko, vi la implementación del shell interactivo, demo.py. Sin embargo, hubo algunos errores durante el uso, por lo que modifiqué ligeramente Interactive.py (eliminé el código de Windows y el código restante solo se puede usar en Linux).
El código es el siguiente:
[python]?view Plain?copy
#coding=utf-8
¿importar?
¿importar?termios
importar?tty?
importar?fcntl? p>
¿importar?seleccionar?
¿ahora_canal?Ninguno?
def?interactive_shell(chan):?
posix_shell(chan)?
def?ioctl_GWINSZ(fd):?
intenta :?
cr?=?struct.unpack('hh', ?fcntl.ioctl(fd, ?termios.TIOCGWINSZ, 'aaaa'))?
excepto: ?
¿regresar?
¿regresar?
¿regresar?
def?resize_pty(signum=0,?frame=0):? p>
ancho, alto =?getTerminalSize()?
si now_channel no es Ninguno:?
now_channel.resize_pty(ancho=ancho, alto=alto)? p>
def?posix_shell(chan):?
global?now_channel?
now_channel?=?chan?
resize_pty()? p>
signal.signal(signal.SIGWINCH,?resize_pty)?#?¿Modificar el tamaño del terminal pty cuando cambia?
stdin?=?os.fdopen(sys.stdin.fileno(), ?' r', ?0)?#Establezca stdin.buff en nulo; de lo contrario, pegue varios bytes o presione las teclas de flecha para mostrar incorrectamente ?
fd?=?stdin.fileno()?
oldtty?=?termios.tcgetattr(fd)?
newtty?=?termios.tcgetattr( fd)?
newtty[3]?|?termios.ICANON?
prueba:?
termios.tcsetattr(fd,?termios.TCSANOW,? ¿newtty)?
tty.setraw(fd)?
tty.setcbreak(fd)?
chan.settimeout(0.0)?
mientras?
intenta: ?
r, ?w, ?e? =?select.select([chan, ?stdin], ? [], ? []) ?
excepto: ?
#?Resuelva la interrupción del sistema provocada por la señal SIGWINCH ignorando la interrupción y llamándola nuevamente, despertando así la llamada del sistema de selección inactiva.
¿continuar?
intentar:?
x = chan.recv(1024)?
si?
¿imprimir?
¿romper?
sys.stdout.write(x)?
sys.stdout.flush()?
¿excepto?
¿pasar?
x = stdin.read(1)?
¿si?
¿romper?
Finalmente:?
termios.tcsetattr(sys.stdin,?termios.TCSADRAIN,?oldtty)?
Ejemplo de uso:
[python]?view Plain? p>
p>
paramiko.util.log_too_file('/tmp/aaa')?
#¿Establecer una conexión ssh?
ssh=paramiko.SSHClient()?
ssh.load_system_host_keys()?
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())?
ssh.connect('192.168.1.11', puerto=22, nombre de usuario='jajaja', contraseña='********', compress=True)?
#Create ¿Conexión de shell interactiva?
channel=ssh.invoke_shell()?
#¿Crear una canalización interactiva?
interactive.interactive_shell(canal)?
#¿Cerrar la conexión?
channel.close()?
ssh.close()?
Hay varias correcciones importantes en el código interactivo.py:
1. Hay un problema con las teclas de flecha al leer la entrada del teclado, porque presionar las teclas de flecha una vez generará 3 bytes de datos. Tengo entendido que una vez que cambia la entrada estándar, SELECT captura una pulsación de tecla, pero solo estoy procesando 1 byte a la vez y el resto se almacena en el búfer de entrada. Los datos restantes se almacenan en el búfer de entrada y se envían la siguiente vez que se presiona una tecla. De esta manera, se necesitan 3 bytes para definir completamente el comportamiento de una tecla de flecha, pero solo envío un byte, por lo que el terminal no sabe lo que estoy intentando hacer. Entonces nada cambia, solo cuando se activa la siguiente pulsación de tecla la información anterior se envía completa, parece que hay un retraso al presionar el d-pad. El mismo principio se aplica al pegado multibyte. La solución es establecer el búfer de entrada en 0 para que no haya almacenamiento en búfer y simplemente enviar todo lo que pueda para no tener problemas de retraso en la visualización.
2. Adaptación del tamaño del terminal. paramiko.channel creará un pty (pseudo terminal) con un tamaño predeterminado (ancho = 80, alto = 24), por lo que cuando inicie sesión, encontrará que el área visualizable es muy pequeña y fija. El canal tiene un método resize_pty, pero necesita obtener el tamaño actual del terminal. Después de la búsqueda, cuando la ventana del terminal cambia, el sistema enviará una señal SIGWINCH al grupo de procesos de primer plano, es decir, cuando el proceso recibe la señal, obtiene el tamaño actual y luego lo sincroniza con pty, el proceso pty. Si también siente que la ventana cambia, también se recibirá la señal SIGWINCH.
3. Leer y escribir dispositivos "lentos" (incluidas tuberías, dispositivos terminales, conexiones de red, etc.). Al leer, los datos no existen y es necesario esperar; al escribir, el búfer está lleno o por otras razones, es necesario esperar. El canal ssh entra en esta categoría. Originalmente, debido a que no había comunicación de red, se bloqueó la llamada de selección del proceso, pero cuando el tamaño de la ventana del terminal cambió y se recibió la señal SIGWINCH, se despertó. En este momento, seleccionar provocará una excepción y provocará una interrupción del sistema (4, "Interrupción de llamada del sistema"), pero esto solo sucederá una vez. Cuando se vuelva a llamar al método de selección, volverá a la normalidad. Entonces, detectar la excepción de selección y luego rehacer la selección debería resolver el problema.