python获取交互式ssh shell的方法
作者:zlucifer 发布时间:2021-11-25 13:15:21
更新,最近在学unix环境编程,了解一下进程的创建过程,用最原始的方式实现了一个ssh命令的执行。
#coding=utf8
'''
用python实现了一个简单的shell,了解进程创建
类unix 环境下 fork和exec 两个系统调用完成进程的创建
'''
import sys, os
def myspawn(cmdline):
argv = cmdline.split()
if len(argv) == 0:
return
program_file = argv[0]
pid = os.fork()
if pid < 0:
sys.stderr.write("fork error")
elif pid == 0:
# child
os.execvp(program_file, argv)
sys.stderr.write("cannot exec: "+ cmdline)
sys.exit(127)
# parent
pid, status = os.waitpid(pid, 0)
ret = status >> 8 # 返回值是一个16位的二进制数字,高8位为退出状态码,低8位为程序结束系统信号的编号
signal_num = status & 0x0F
sys.stdout.write("ret: %s, signal: %s\n" % (ret, signal_num))
return ret
def ssh(host, user, port=22, password=None):
if password:
sys.stdout.write("password is: '%s' , plz paste it into ssh\n" % (password))
cmdline = "ssh %s@%s -p %s " % (user, host, port)
ret = myspawn(cmdline)
if __name__ == "__main__":
host = ''
user = ''
password = ''
ssh(host, user, password=password)
最近在做一个项目,需要在客户端集成一个交互式ssh功能,大概就是客户端跟服务器申请个可用的机器,服务端返回个ip,端口,密码, 然后客户端就可以直接登录到机器上操做了。该程序基于paramiko模块。
经查找,从paramiko的源码包demos目录下,可以看到交互式shell的实现,就是那个demo.py。但是用起来有些bug,于是我给修改了一下interactive.py(我把windows的代码删掉了,剩下的只能在linux下用)。代码如下:
#coding=utf-8
import socket
import sys
import os
import termios
import tty
import fcntl
import signal
import struct
import select
now_channel = None
def interactive_shell(chan):
posix_shell(chan)
def ioctl_GWINSZ(fd):
try:
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'aaaa'))
except:
return
return cr
def getTerminalSize():
cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
return int(cr[1]), int(cr[0])
def resize_pty(signum=0, frame=0):
width, height = getTerminalSize()
if now_channel is not None:
now_channel.resize_pty(width=width, height=height)
def posix_shell(chan):
global now_channel
now_channel = chan
resize_pty()
signal.signal(signal.SIGWINCH, resize_pty) # 终端大小改变时,修改pty终端大小
stdin = os.fdopen(sys.stdin.fileno(), 'r', 0) # stdin buff置为空,否则粘贴多字节或者按方向键的时候显示不正确
fd = stdin.fileno()
oldtty = termios.tcgetattr(fd)
newtty = termios.tcgetattr(fd)
newtty[3] = newtty[3] | termios.ICANON
try:
termios.tcsetattr(fd, termios.TCSANOW, newtty)
tty.setraw(fd)
tty.setcbreak(fd)
chan.settimeout(0.0)
while True:
try:
r, w, e = select.select([chan, stdin], [], [])
except:
# 解决SIGWINCH信号将休眠的select系统调用唤醒引发的系统中断,忽略中断重新调用解决。
continue
if chan in r:
try:
x = chan.recv(1024)
if len(x) == 0:
print 'rn*** EOFrn',
break
sys.stdout.write(x)
sys.stdout.flush()
except socket.timeout:
pass
if stdin in r:
x = stdin.read(1)
if len(x) == 0:
break
chan.send(x)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
使用示例:
#coding=utf8
import paramiko
import interactive
#记录日志
paramiko.util.log_to_file('/tmp/aaa')
#建立ssh连接
ssh=paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('192.168.1.11',port=22,username='hahaha',password='********',compress=True)
#建立交互式shell连接
channel=ssh.invoke_shell()
#建立交互式管道
interactive.interactive_shell(channel)
#关闭连接
channel.close()
ssh.close()
interactive.py代码中主要修复了几个问题:
1、当读取键盘输入时,方向键会有问题,因为按一次方向键会产生3个字节数据,我的理解是按键一次会被select捕捉一次标准输入有变化,但是我每次只处理1个字节的数据,其他的数据会存放在输入缓冲区中,等待下次按键的时候一起发过去。这就导致了本来3个字节才能完整定义一个方向键的行为,但是我只发过去一个字节,所以终端并不知道我要干什么。所以没有变化,当下次触发按键,才会把上一次的信息完整发过去,看起来就是按一下方向键有延迟。多字节的粘贴也是一个原理。解决办法是将输入缓冲区置为0,这样就没有缓冲,有多少发过去多少,这样就不会有那种显示的延迟问题了。
2、终端大小适应。paramiko.channel会创建一个pty(伪终端),有个默认的大小(width=80, height=24),所以登录过去会发现能显示的区域很小,并且是固定的。编辑vim的时候尤其痛苦。channel中有resize_pty方法,但是需要获取到当前终端的大小。经查找,当终端窗口发生变化时,系统会给前台进程组发送SIGWINCH信号,也就是当进程收到该信号时,获取一下当前size,然后再同步到pty中,那pty中的进程等于也感受到了窗口变化,也会收到SIGWINCH信号。
3、读写‘慢'设备(包括pipe,终端设备,网络连接等)。读时,数据不存在,需要等待;写时,缓冲区满或其他原因,需要等待。ssh通道属于这一类的。本来进程因为网络没有通信,select调用为阻塞中的状态,但是当终端窗口大小变化,接收到SIGWINCH信号被唤醒。此时select会出现异常,触发系统中断(4, 'Interrupted system call'),但是这种情况只会出现一次,当重新调用select方法又会恢复正常。所以捕获到select异常后重新进行select可以解决该问题。
来源:https://blog.csdn.net/zlucifer/article/details/70858491


猜你喜欢
- 周一 至 周日 时间格式化转化(Y --- 年 M --- 月 D--- 天)
- 1.假设已经有mysql-5.5.10.tar.gz以及cmake-2.8.4.tar.gz两个源文件(1)先安装cmake(mysql5.
- Python中的正则表达式要用到re模块,下面先介绍一下正则表达式需要用到的特殊字符和说明常用的RegEx基础语法语法说明\d匹配一个数字字
- 如果要用某个开源框架,需要安装多个依赖包可以如下操作:如依赖文件形式如下(可以不要版本号):txt文件名为requirements.txt,
- Pycharm运行时总是跳出Python Console最近运行程序的时候发现,每次点击运行之后,都是出现的Python Console。最
- Jupyter平台默认开发的字体为宋体,在高分屏下视觉效果差在 C:\User\用户名\.jupyter\custom下面的custom.c
- 0 程序环境与所学函数本章程序运行需要导入下面三个库,并定义了一个显示图像的函数所学函数##放大、缩小cv.resize(img,dsize
- 滚动回归所谓滚动回归,通常用在时间序列上。记当前时刻为 t,回归时长为 s,则一直使用 当作自变量来预测 。使用滚动回归的目的通常是为了避免
- 工具:Jupyter notebook + Anaconda游戏规则: * 一种玩法是买尾号。2元一个数字,中奖是20元。每个数字出现的概率
- 1. timeit.timeit(stmt=‘pass', setup=‘pass', timer=<default
- 使用原生SQL语句进行对数据库操作,可完成数据库表的建立和删除,及数据表内容的增删改查操作等。其可操作性很强,如可以直接使用“show da
- 《色彩解答》系列之一 色彩层次这次我们将深入进去了解一下众多色彩在一起之后所存在的“比例”关系。我们在使用色彩的时候不可能把所有的色彩都做得
- (1)抓取今日头条街拍图片(2)分析今日头条街拍图片结构keyword: 街拍pd: atlasdvpf: pcaid: 4916page_
- 由于一些读者对于960 Grid System CSS Framework的原理和使用方法比较感兴趣,暴风彬彬今天将和大家一同分享这篇关于9
- 图例如下https://github.com/Dongvdong/python_Smartvoice上电后,只要周围声音超过 2000,开始
- 前言在使用vue过程中,总会用到这样哪样的UI框架,在install这些框架时,往往还需要安装其要求的依赖,或是相关的编译环境,下面将记录描
- 我们最终的视图技巧利用了一个高级python技术。 假设你发现自己在各个不同视图里重复了大量代码,就像 这个例子:def my_view1(
- 视频读取视频读取,主要利用VideoCapture类下的方法打开视频并获取视频中的帧,具体示例如下:#include<iostream
- 本文实例为大家分享了python3 pillow模块验证码的具体代码,供大家参考,具体内容如下直接放代码吧,该写的注释基本都写了# -*-
- python字典中,值可任意更改;但键是唯一的,不支持直接修改。若真的需要修改字典中的键,可通过几种间接方式实现。新建空白字典。info =