2013-10-06 13 views
5

przepływu programu jest:zdalne wykonanie programu przez metę xterm korzystania paramiko Pythona ssh bibliotekę

  1. Połącz z serwerem OpenSSH na komputerze z systemem Linux z wykorzystaniem biblioteki Paramiko sesję
  2. Otwarte x11
  3. Run xterm wykonywalny
  4. Uruchom inny program (np. Firefox), wpisując nazwę pliku wykonywalnego na terminalu i uruchamiając go.

Byłbym wdzięczny, jeśli ktoś może wyjaśnić, w jaki sposób spowodować, że niektóre wykonywalny, aby uruchomić w terminalu, który był otwarty za pomocą następującego kodu i dostarczenie kodu źródłowego próbki (source):

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() 

byłem myślenie o uruchomieniu programu w wątku potomnym, podczas gdy wątek z uruchomionym xterm czeka na zakończenie dziecka.

+1

Co masz na myśli przez "wpisywanie w terminalu"? Jeśli wysyła symulowane naciśnięcia klawiszy przez X11, to ćwiczenie jest (a) nieparzyste (b) trudne (c) raczej bezcelowe i (d) niezwiązane z kodem, który opublikowałeś. Jeśli chcesz, aby Firefox działał w Xterm, możesz użyć czegoś podobnego do 'session.exec_command ('xterm -e firefox')'. –

+0

@ n.m. Dzięki. Twoje polecenie pomaga mi. Jednak moim ostatecznym celem jest w pewien sposób naśladowanie zachowania użytkownika X11. Oznacza to uruchamianie programów, otwieranie plików i robienie rzeczy, które zrobiłby przeciętny użytkownik. Oznacza to, że oprócz uruchamiania programów, istnieje potrzeba kontrolowania programów na pewnym podstawowym poziomie. – user1264304

+0

dlaczego uruchamianie firefox z xterm? Być może sesja X nie jest propagowana z xterm do firefox, a druga nie znajduje DISPLAY do uruchomienia. xterm powinien być użyty do polecenia aplikacji liniowej z wyjściem tekstowym na stdout/stderr –

Odpowiedz

2

Cóż, to trochę hack, ale hej.

To, co można zrobić na zdalnym końcu, jest następujące: Wewnątrz xterma uruchamiamy netcat, słuchamy wszelkich danych przychodzących na niektórych portach i przetwarzamy wszystko, co otrzymamy, na bash. Nie jest to całkiem to samo, co wpisanie go w Xterma, ale jest prawie tak dobre, jak wpisanie go bezpośrednio w bash, więc mam nadzieję, że przybliży cię to trochę do celu. Jeśli naprawdę chcesz bezpośrednio współpracować z xterm, możesz chcieć read this.

Na przykład:

zacisk 1:

% nc -l 3333 | bash 

Terminal 2 (typ echo hi tutaj):

% nc localhost 3333 
echo hi 

Teraz powinieneś zobaczyć hi pop z pierwszego terminala. Teraz spróbuj z xterm&. To zadziałało dla mnie.

Oto, jak zautomatyzować to w Pythonie. Możesz dodać kod, który umożliwi serwerowi powiadomienie klienta, kiedy będzie gotowy, zamiast używania głupich 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() 

I przetestowane na komputerze Macintosh więc komentarzem na XmingProc bitów i używana /Applications/Firefox.app/Contents/MacOS/firefox jak FIREFOX_CMD (i xclock).

Powyższa konfiguracja nie jest bezpieczna, ponieważ każdy, kto łączy się z portem w odpowiednim czasie, może uruchomić dowolny kod na serwerze zdalnym, ale wydaje się, że i tak zamierzasz go użyć do testowania. Jeśli chcesz poprawić bezpieczeństwo, możesz utworzyć powiązanie netcat z 127.0.0.1 zamiast 0.0.0.0, skonfigurować tunel SSH (uruchom ssh -L3333:localhost:3333 [email protected], aby tunelować cały ruch otrzymany lokalnie na porcie 3333 na remote-host.com:3333) i pozwól Pythonowi łączyć się z ("localhost", 3333).

Teraz można połączyć to z selenium automatyzacji przeglądarki:

Postępuj zgodnie z instrukcjami z this page, tjpobierz samodzielny plik jar selenu, umieść go w /path/to/some/place (na serwerze) i pip install -U selenium (ponownie na serwerze).

Następnie umieścić następujący kod do selenium-example.py w /path/to/some/place:

#!/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() 

i zmienić polecenie Firefox:

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

I oglądać firefox zrobić wyszukiwania Yahoo. Możesz także chcieć zwiększyć wartość time.sleep.

Jeśli chcesz uruchomić więcej programów, można robić takie rzeczy przed firefox lub po uruchomieniu:

# 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) 

Jeśli chcesz zrobić wykonać ogólną kontrolę aplikacji X11, myślę, że trzeba napisać podobny "aplikacje sterowników", aczkolwiek przy użyciu różnych bibliotek. Możesz chcieć wyszukać zdarzenia "x11 send {mouse | keyboard}", aby znaleźć bardziej ogólne podejście. To przynosi thesequestions, ale jestem pewien, że jest o wiele więcej.

Jeśli zdalny koniec nie reaguje natychmiastowo, możesz powąchać ruch sieciowy w Wireshark i sprawdzić, czy TCP nie zgłasza danych, zamiast wysyłać je wiersz po linii (\n wydaje się pomagać tutaj, ale myślę, że nie ma żadnej gwarancji). W takim przypadku możesz być out of luck, ale nothing is impossible. Mam nadzieję, że nie musicie posuwać się aż tak daleko ;-)

Jeszcze jedna uwaga: jeśli chcesz komunikować się ze STDIN/STDOUT w programach CLI, możesz chcieć sprawdzić skrypty (np. Używając pexpect lub w prostych przypadkach możesz użyć podprocesu.Popen.communicate] (http://docs.python.org/2/library/subprocess.html#subprocess.Popen.communicate)).