通过使用paramiko python ssh库远程执行xterm运行的程序

5
程序的流程如下:
  1. 使用 Paramiko 库连接 Linux 机器上的 OpenSSH 服务器
  2. 打开 X11 会话
  3. 运行 xterm 可执行文件
  4. 在终端中输入可执行文件名称并运行一些其他程序(例如 Firefox)。
如果有人能够解释如何在使用以下代码打开的终端中运行某个可执行文件并提供示例源代码(源码),我将不胜感激:
import select
import sys
import paramiko
import Xlib.support.connect as xlib_connect
import os
import socket
import subprocess



# run xming
XmingProc = subprocess.Popen("C:/Program Files (x86)/Xming/Xming.exe :0 -clipboard -multiwindow")
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(SSHServerIP, SSHServerPort, username=user, password=pwd)
transport = ssh_client.get_transport()
channelOppositeEdges = {}

local_x11_display = xlib_connect.get_display(os.environ['DISPLAY'])
inputSockets = []

def x11_handler(channel, (src_addr, src_port)):
    local_x11_socket = xlib_connect.get_socket(*local_x11_display[:3])
    inputSockets.append(local_x11_socket)
    inputSockets.append(channel)
    channelOppositeEdges[local_x11_socket.fileno()] = channel
    channelOppositeEdges[channel.fileno()] = local_x11_socket
    transport._queue_incoming_channel(channel)

session = transport.open_session()
inputSockets.append(session)
session.request_x11(handler = x11_handler)
session.exec_command('xterm')
transport.accept()

while not session.exit_status_ready():
    readable, writable, exceptional = select.select(inputSockets,[],[])
    if len(transport.server_accepts) > 0:
        transport.accept()
    for sock in readable:
        if sock is session:
            while session.recv_ready():
                sys.stdout.write(session.recv(4096))
            while session.recv_stderr_ready():
                sys.stderr.write(session.recv_stderr(4096))   
        else: 
            try:
                data = sock.recv(4096)
                counterPartSocket  = channelOppositeEdges[sock.fileno()]
                counterPartSocket.sendall(data)
            except socket.error:
                inputSockets.remove(sock)
                inputSockets.remove(counterPartSocket)
                del channelOppositeEdges[sock.fileno()]
                del channelOppositeEdges[counterPartSocket.fileno()]
                sock.close()
                counterPartSocket.close()

print 'Exit status:', session.recv_exit_status()
while session.recv_ready():
    sys.stdout.write(session.recv(4096))
while session.recv_stderr_ready():
    sys.stdout.write(session.recv_stderr(4096))
session.close()
XmingProc.terminate()
XmingProc.wait()

我在思考将程序运行在子线程中,同时运行xterm的线程正在等待子线程终止。


1
“在终端中输入”是什么意思?如果是通过X11发送模拟按键,则此练习(a)很奇怪(b)很难(c)相当无意义,且(d)与您发布的代码完全无关。如果您只需要以某种方式使Firefox在xterm中运行,则可以使用类似于session.exec_command('xterm -e firefox')的东西。 - n. m.
@n.m. 谢谢。你的命令对我很有帮助。然而,我的最终目标是以某种方式模拟X11用户的行为。这意味着运行程序、打开文件和执行普通用户会做的事情。除了运行程序之外,还需要在一定程度上控制程序。 - rok
为什么要从xterm运行Firefox?可能是因为X会话没有从xterm传播到Firefox,后者找不到要运行的DISPLAY。xterm应该用于具有stdout / stderr文本输出的命令行应用程序。 - LMC
似乎你正在尝试开发一个终端模拟器。 - LMC
1个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
2

这有点像黑客行为,但是嘿,仍然可行。

在远程端,你可以这样做:在xterm中运行netcat,监听来自某个端口的任何数据,并将收到的任何内容导入bash。虽然不完全等同于直接在xterm中输入它,但几乎和直接在bash中输入一样好,我希望这能让你更接近你的目标。如果你真的想直接与xterm进行交互,你可能需要阅读此文

例如:

终端1:

% nc -l 3333 | bash
终端2(在此处键入echo hi):
% nc localhost 3333
echo hi
现在你应该看到第一个终端中弹出了“hi”。现在使用xterm&尝试一下。这对我来说起作用了。 以下是如何在Python中自动化此过程的方法。您可能需要添加一些代码,使服务器能够在准备就绪时告诉客户端,而不是使用愚蠢的time.sleep
import select
import sys
import paramiko
import Xlib.support.connect as xlib_connect
import os
import socket
import subprocess

# for connecting to netcat running remotely
from multiprocessing import Process
import time

# data
import getpass
SSHServerPort=22
SSHServerIP = "localhost"
# get username/password interactively, or use some other method..
user = getpass.getuser()
pwd = getpass.getpass("enter pw for '" + user + "': ")
NETCAT_PORT = 3333
FIREFOX_CMD="/path/to/firefox &"
#FIREFOX_CMD="xclock&"#or this :)

def run_stuff_in_xterm():
    time.sleep(5)
    s = socket.socket(socket.AF_INET6 if ":" in SSHServerIP else socket.AF_INET, socket.SOCK_STREAM)
    s.connect((SSHServerIP, NETCAT_PORT))
    s.send("echo \"Hello there! Are you watching?\"\n")
    s.send(FIREFOX_CMD + "\n")
    time.sleep(30)
    s.send("echo bye bye\n")
    time.sleep(2)
    s.close()

# run xming
XmingProc = subprocess.Popen("C:/Program Files (x86)/Xming/Xming.exe :0 -clipboard -multiwindow")
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(SSHServerIP, SSHServerPort, username=user, password=pwd)
transport = ssh_client.get_transport()
channelOppositeEdges = {}

local_x11_display = xlib_connect.get_display(os.environ['DISPLAY'])
inputSockets = []

def x11_handler(channel, (src_addr, src_port)):
    local_x11_socket = xlib_connect.get_socket(*local_x11_display[:3])
    inputSockets.append(local_x11_socket)
    inputSockets.append(channel)
    channelOppositeEdges[local_x11_socket.fileno()] = channel
    channelOppositeEdges[channel.fileno()] = local_x11_socket
    transport._queue_incoming_channel(channel)

session = transport.open_session()
inputSockets.append(session)
session.request_x11(handler = x11_handler)
session.exec_command("xterm -e \"nc -l 0.0.0.0 %d | /bin/bash\"" % NETCAT_PORT)
p = Process(target=run_stuff_in_xterm)
transport.accept()
p.start()

while not session.exit_status_ready():
    readable, writable, exceptional = select.select(inputSockets,[],[])
    if len(transport.server_accepts) > 0:
        transport.accept()
    for sock in readable:
        if sock is session:
            while session.recv_ready():
                sys.stdout.write(session.recv(4096))
            while session.recv_stderr_ready():
                sys.stderr.write(session.recv_stderr(4096))   
        else: 
            try:
                data = sock.recv(4096)
                counterPartSocket  = channelOppositeEdges[sock.fileno()]
                counterPartSocket.sendall(data)
            except socket.error:
                inputSockets.remove(sock)
                inputSockets.remove(counterPartSocket)
                del channelOppositeEdges[sock.fileno()]
                del channelOppositeEdges[counterPartSocket.fileno()]
                sock.close()
                counterPartSocket.close()

p.join()
print 'Exit status:', session.recv_exit_status()
while session.recv_ready():
    sys.stdout.write(session.recv(4096))
while session.recv_stderr_ready():
    sys.stdout.write(session.recv_stderr(4096))
session.close()
XmingProc.terminate()
XmingProc.wait()

我在Mac上进行了测试,因此注释掉了XmingProc部分并使用 /Applications/Firefox.app/Contents/MacOS/firefox 作为 FIREFOX_CMD(以及 xclock)。

以上设置并不是完全安全的,因为任何在正确时间连接到端口的人都可以在您的远程服务器上运行任意代码,但似乎您打算将其用于测试目的。如果想要提高安全性,可以让netcat绑定到127.0.0.1而不是0.0.0.0,设置一个ssh隧道(运行ssh -L3333:localhost:3333 username@remote-host.com将本地3333端口接收到的所有流量隧道到remote-host.com:3333),然后让Python连接到("localhost", 3333)

现在,您可以结合selenium进行浏览器自动化:

按照此页面的说明操作,即下载selenium独立服务器jar文件,将其放入服务器上的/path/to/some/place目录中,并在服务器上运行pip install -U selenium命令进行安装。

接下来,将以下代码放入/path/to/some/place中的selenium-example.py文件中:

#!/usr/bin/env python
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.keys import Keys
import time

browser = webdriver.Firefox() # Get local session of firefox
browser.get("http://www.yahoo.com") # Load page
assert "Yahoo" in browser.title
elem = browser.find_element_by_name("p") # Find the query box
elem.send_keys("seleniumhq" + Keys.RETURN)
time.sleep(0.2) # Let the page load, will be added to the API
try:
    browser.find_element_by_xpath("//a[contains(@href,'http://docs.seleniumhq.org')]")
except NoSuchElementException:
    assert 0, "can't find seleniumhq"
browser.close()

并更改Firefox命令:

FIREFOX_CMD="cd /path/to/some/place && python selenium-example.py"

请观察Firefox执行Yahoo搜索。您可能还想增加time.sleep的时间。

如果您想运行更多程序,您可以在运行Firefox之前或之后执行以下操作:

# start up xclock, wait for some time to pass, kill it.
s.send("xclock&\n")
time.sleep(1)
s.send("XCLOCK_PID=$!\n")  # stash away the process id (into a bash variable)
time.sleep(30)
s.send("echo \"killing $XCLOCK_PID\"\n")
s.send("kill $XCLOCK_PID\n\n")
time.sleep(5)
如果您想进行常规的X11应用程序控制,我认为您可能需要编写类似的“驱动程序应用程序”,尽管使用不同的库。您可能需要搜索“x11 send {mouse|keyboard} events”以找到更通用的方法。这会带出这些问题,但我相信还有很多其他的。 如果远程端没有立即响应,您可能需要在Wireshark中嗅探网络流量,并检查TCP是否正在将数据批处理,而不是逐行发送它(\n似乎在这里有所帮助,但我想没有保证)。如果是这种情况,您可能会没有办法,但没有什么是不可能的。我希望您不需要走得那么远;-) 另外需要注意的一点是,如果您需要与CLI程序的STDIN / STDOUT通信,则可能需要查看expect脚本(例如使用pexpect,或对于简单情况,您可以使用subprocess.Popen.communicate](http://docs.python.org/2/library/subprocess.html#subprocess.Popen.communicate))。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,