python获取交互式ssh shell的方法

更新,最近在学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可以解决该问题。

以上这篇python获取交互式ssh shell的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Python中整数的缓存机制讲解

    在python中,如下代码结果一定不会让你吃惊: Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:06:53) [MSC v.1600 64 bit (AMD64)] on win32 Type "copyright", "credits" or "license()" for more information. >>> a=345 >>> b=a >

  • Python3使用requests模块实现显示下载进度的方法详解

    本文实例讲述了Python3使用requests模块实现显示下载进度的方法.分享给大家供大家参考,具体如下: 一.配置request 1. 相关资料 请求关键参数:stream=True.默认情况下,当你进行网络请求后,响应体会立即被下载.你可以通过 stream 参数覆盖这个行为,推迟下载响应体直到访问 Response.content 属性. tarball_url = 'https://github.com/kennethreitz/requests/tarball/master' r =

  • 使用Python自动化破解自定义字体混淆信息的方法实例

    注意:本示例仅供学习参考- 混淆原理 出于某种原因,明文信息通过自定义字体进行渲染,达到混淆目的. 举个例子: 网页源码 <p>123</p> 在正常字体的渲染下,浏览者看到的是 123 这 3 个数字. 如果创建一种自定义字体,把 1 渲染成 5,那么浏览者看到的便是 523 这 3 个数字. 这样便达到混淆信息的效果,常见于对付爬虫之类的自动化工具. 破解方法 下载自定义字体文件(通常在 css @font-face 中找到),保存成 a.ttf 文件. 安装以下依赖项目 te

  • Python从文件中读取数据的方法讲解

    编写了一个名为learning_python.txt的文件,内容如下: [root@centos7 tmp]# cat learning_python.txt In Python you can code; In Python you can learn object; In Python you can learn class. 要求:编写一个程序,它读取这个文件并打印三次. 1.第一次打印时读取整个文件: 2.第二次打印时遍历文件对象: 3.第三次打印时将各行存储在一个列表中,再在with代

  • centos6.5安装python3.7.1之后无法使用pip的解决方案

    编译安装全是坑-- 第一遍装完无法使用pip,报错找不到ssl模块.各种报错: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available. ... configure: error: Invalid --with-openssl value ... 结果各种捣鼓. 1.注意cetos6.5自带的openssl版本是1.0.1,需要升级到1.0.2

  • Python实现的爬取百度文库功能示例

    本文实例讲述了Python实现的爬取百度文库功能.分享给大家供大家参考,具体如下: # -*- coding: utf-8 -*- from selenium import webdriver from bs4 import BeautifulSoup from docx import Document from docx.enum.text import WD_ALIGN_PARAGRAPH# 用来居中显示标题 from time import sleep from selenium.webd

  • Python基础之文件读取的讲解

    with open(filename) as fp: dataMat = [] for line in fp.readlines(): # fp.readlines()返回一个list,list of strs # 也即line类型为`str` curLine = line.strip().split('\t') # 只有`str`类型才有strip()成员函数, # 在经过split()分割,得到list类型 # 也即curLine类型为list # curLine 仍然是由字符串构成的lis

  • Python通过for循环理解迭代器和生成器实例详解

    本文实例讲述了Python通过for循环理解迭代器和生成器.分享给大家供大家参考,具体如下: 迭代器 可迭代对象 通过 for-in- 循环依次拿到数据进行使用的过程称为遍历,也叫迭代.我们把可以通过 for-in- 语句迭代读取数据的对象称之为可迭代对象. - 通过 isinstance()可以判断一个对象是否可以迭代 # 判断列表 print(isinstance([], Iterable) 打印结果为 True 即为可迭代对象. - 自定义一个能容纳数据的类,测试该类的可迭代性 impor

  • Python标准库使用OrderedDict类的实例讲解

    目标:创建一个字典,记录几对python词语,使用OrderedDict类来写,并按顺序输出. 写完报错: [root@centos7 tmp]# python python_terms.py File "python_terms.py", line 9 from name,language in python_terms.items(): ^ SyntaxError: invalid syntax 代码如下: from collections import OrderedDict p

  • Python实现的对本地host127.0.0.1主机进行扫描端口功能示例

    本文实例讲述了Python实现的对本地host127.0.0.1主机进行扫描端口功能.分享给大家供大家参考,具体如下: 前面一篇<Python IP端口扫描工具类>详细介绍了端口扫描功能,这里分析一下针对本地localhost端口扫描的方法. 具体代码如下: #!/usr/local/bin/python #-*- coding: UTF-8 -*- #端口扫描器 import socket import thread import time socket.setdefaulttimeout(

随机推荐