python获取交互式ssh shell的方法
更新,最近在学unix环境编程,了解一下进程的创建过程,用最原始的方式实现了一个ssh命令的执行。
#coding=utf8
'''
用python实现了一个简单的shell,了解进程创建
类unix环境下fork和exec两个系统调用完成进程的创建
'''
importsys,os
defmyspawn(cmdline):
argv=cmdline.split()
iflen(argv)==0:
return
program_file=argv[0]
pid=os.fork()
ifpid<0:
sys.stderr.write("forkerror")
elifpid==0:
#child
os.execvp(program_file,argv)
sys.stderr.write("cannotexec:"+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))
returnret
defssh(host,user,port=22,password=None):
ifpassword:
sys.stdout.write("passwordis:'%s',plzpasteitintossh\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
importsocket
importsys
importos
importtermios
importtty
importfcntl
importsignal
importstruct
importselect
now_channel=None
definteractive_shell(chan):
posix_shell(chan)
defioctl_GWINSZ(fd):
try:
cr=struct.unpack('hh',fcntl.ioctl(fd,termios.TIOCGWINSZ,'aaaa'))
except:
return
returncr
defgetTerminalSize():
cr=ioctl_GWINSZ(0)orioctl_GWINSZ(1)orioctl_GWINSZ(2)
returnint(cr[1]),int(cr[0])
defresize_pty(signum=0,frame=0):
width,height=getTerminalSize()
ifnow_channelisnotNone:
now_channel.resize_pty(width=width,height=height)
defposix_shell(chan):
globalnow_channel
now_channel=chan
resize_pty()
signal.signal(signal.SIGWINCH,resize_pty)#终端大小改变时,修改pty终端大小
stdin=os.fdopen(sys.stdin.fileno(),'r',0)#stdinbuff置为空,否则粘贴多字节或者按方向键的时候显示不正确
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)
whileTrue:
try:
r,w,e=select.select([chan,stdin],[],[])
except:
#解决SIGWINCH信号将休眠的select系统调用唤醒引发的系统中断,忽略中断重新调用解决。
continue
ifchaninr:
try:
x=chan.recv(1024)
iflen(x)==0:
print'rn***EOFrn',
break
sys.stdout.write(x)
sys.stdout.flush()
exceptsocket.timeout:
pass
ifstdininr:
x=stdin.read(1)
iflen(x)==0:
break
chan.send(x)
finally:
termios.tcsetattr(sys.stdin,termios.TCSADRAIN,oldtty)
使用示例:
#coding=utf8
importparamiko
importinteractive
#记录日志
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,'Interruptedsystemcall'),但是这种情况只会出现一次,当重新调用select方法又会恢复正常。所以捕获到select异常后重新进行select可以解决该问题。
以上这篇python获取交互式sshshell的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。