Я искал способ запуска приложений Windows (хост) с виртуальной машины Ubuntu (под VMWare Player). Я немного увлекся и написал сценарии клиента и сервера, перечисленные ниже. Гостевая ОС - это не Windows, поэтому для работы с гостем Windows потребуются некоторые изменения. Я использовал эту настройку, чтобы Git (работающий на гостевой системе Ubuntu) вызывал KDiff3 на хосте при слиянии.
Следующий скрипт Python (host_run_server.py) действует как сервер, принимающий команды от гостя. Он ожидает, что гость предоставит общий ресурс Samba с именем GUEST_ROOT_SHARE
(задайте его в верхней части скрипта), которое раскрывает корень файловой системы. Эта доля отображается на диск GUEST_DRIVE
. Это необходимо для того, чтобы хост и гость могли получить доступ к одним и тем же файлам. В моем случае я уже смонтировал «Мои документы» в папке гостя, чтобы иметь возможность использовать git для файлов моего хоста.
import asyncore, asynchat import os import socket import shlex, subprocess import threading # make the root / of the guest accessible as a samba share and map # this share in the host to GUEST_DRIVE HOST_IP = '192.168.126.1' GUEST_IP = '192.168.126.129' GUEST_ROOT_SHARE = 'root' GUEST_DRIVE = 'K:' TCP_PORT = 5005 BUFFER_SIZE = 1024 ENCODING = 'utf-8' # map network drive try: import win32wnet import pywintypes from win32netcon import RESOURCETYPE_DISK network_path = r'\\{}\{}'.format(GUEST_IP, GUEST_ROOT_SHARE) try: win32wnet.WNetAddConnection2(RESOURCETYPE_DISK, GUEST_DRIVE, network_path) except pywintypes.error as e: if (e.args[0] != 85 or win32wnet.WNetGetUniversalName(GUEST_DRIVE) != network_path): raise except ImportError: pass # allow GUI applications to pop to front on Windows try: import win32gui from win32con import SPI_SETFOREGROUNDLOCKTIMEOUT result = win32gui.SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0) if result is not None: print("Failed:", result) except ImportError: pass class Handler(asynchat.async_chat): def __init__(self, sock, map=None): asynchat.async_chat.__init__(self, sock, map=map) self.remote_ip, self.remote_port = self.socket.getpeername() self.log('connected') self.set_terminator(b'\x00') self.data = b'' self.state = 'cwd' def handle_close(self): remote_ip, remote_port = self.socket.getpeername() self.log('disconnected') self.close() def collect_incoming_data(self, data): self.data += data def found_terminator(self): if self.state == 'cwd': self.cwd = self.data.decode(ENCODING) self.state = 'cmd' self.data = b'' elif self.state == 'cmd': self.cmd = self.data.decode(ENCODING) self.reply() self.state = 'end' def prepare(self): cwd = GUEST_DRIVE + self.cwd.replace('/', '\\') self.log('in {}'.format(cwd)) os.chdir(cwd) cmd_args = [] for arg in shlex.split(self.cmd): if arg.startswith('[FILE]'): arg = arg[6:].replace('/', '\\') if arg.startswith('\\'): arg = GUEST_DRIVE + arg cmd_args.append(arg) return cwd, cmd_args def run(self, cwd, cmd_args): self.log('executing: {}'.format(' '.join(cmd_args))) try: p = subprocess.Popen(cmd_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) out, err = p.communicate() rcode = p.returncode except WindowsError as e: out = b'' err = '{}: {}\n'.format(e.__class__.__name__, e.args[1]).encode(ENCODING) rcode = -1 return rcode, out, err def reply(self): cwd, cmd_args = self.prepare() rc, out, err = self.run(cwd, cmd_args) self.push(str(len(out)).encode(ENCODING) + b'\x00') if len(out): self.push(out) self.push(str(len(err)).encode(ENCODING) + b'\x00') if len(err): self.push(err) self.push(str(rc).encode(ENCODING) + b'\x00') def log(self, msg): print("[{}:{}]\t{}".format(self.remote_ip, self.remote_port, msg)) class HandlerThread(threading.Thread): def __init__(self, sock): super().__init__() self.sock = sock def run(self): handler = Handler(self.sock) asyncore.loop(map=handler._map) class Server(asyncore.dispatcher): def __init__(self, host, port, guest_ip): asyncore.dispatcher.__init__(self, map={}) self.guest_ip = guest_ip self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.bind((host, port)) self.listen(5) print("Service started. Listening on {} port {}." .format(host, port)) def handle_accepted(self, sock, addr): (guest_ip, guest_port) = addr if guest_ip == self.guest_ip: ht = HandlerThread(sock) ht.start() else: print("Ignoring request from {}".format(guest_ip)) server = Server(HOST_IP, TCP_PORT, GUEST_IP) asyncore.loop(map=server._map)
Ниже приведен скрипт для вызова на гостевой стороне (host_run.py).
#!/usr/bin/env python3 import asyncore, asynchat import os import socket import sys from optparse import OptionParser HOST_IP = "192.168.126.1" GUEST_IP = "192.168.126.129" HOST_IS_WINDOWS = True TCP_PORT = 5005 BUFFER_SIZE = 1024 ENCODING = 'utf-8' STD_ENCODING = 'cp1252' if HOST_IS_WINDOWS else ENCODING class HostRun(asynchat.async_chat): def __init__(self, host, port): asynchat.async_chat.__init__(self) self.set_terminator(b'\x00') self.data = b'' self.state = 'stdout1' self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((host, port)) def handle_connect(self): self.push(os.getcwd().encode(ENCODING) + b'\x00') self.push(command.encode(ENCODING) + b'\x00') def collect_incoming_data(self, data): self.data += data def found_terminator(self): if self.state == 'stdout1': stdout_len = int(self.data.decode(ENCODING)) if stdout_len: self.set_terminator(stdout_len) self.state = 'stdout2' else: self.state = 'stderr1' elif self.state == 'stdout2': stdout = self.data.decode(STD_ENCODING) sys.stdout.write(stdout) self.set_terminator(b'\x00') self.state = 'stderr1' elif self.state == 'stderr1': stderr_len = int(self.data.decode(ENCODING)) if stderr_len: self.set_terminator(stderr_len) self.state = 'stderr2' else: self.state = 'rc' elif self.state == 'stderr2': stderr = self.data.decode(STD_ENCODING) sys.stderr.write(stderr) self.set_terminator(b'\x00') self.state = 'rc' elif self.state == 'rc': rc = int(self.data.decode(ENCODING)) sys.exit(rc) self.close_when_done() self.data = b'' def handle_close(self): remote_ip, remote_port = self.socket.getpeername() print("%s:%s disconnected" %(remote_ip, remote_port)) self.close() parser = OptionParser() (options, args) = parser.parse_args() command = ' '.join(args) HostRun(HOST_IP, TCP_PORT) asyncore.loop()
Сценарии заботятся о переводе путей к файлам. Чтобы это работало, вам нужно добавить в клиентские скрипты пути, переданные в качестве аргументов[FILE]
Сначала запустите серверный скрипт на хосте. Теперь вы можете передавать команды клиентскому скрипту:
brecht@krubuntu ~ $ ./host_run.py dir [FILE]/home
Это приведет /home
к K:\home
и, таким образом, выполнить dir K:\home
на хосте. Сервер отправляет вывод stdout / stderr и код возврата обратно клиенту, который выводит его обратно в приглашение оболочки:
Volume in drive K is root Volume Serial Number is 64C2-522A Directory of K:\home 07/22/2012 22:13 <DIR> . 12/04/2012 06:53 <DIR> .. 02/28/2013 21:56 <DIR> brecht 0 File(s) 0 bytes 3 Dir(s) 12,723,302,400 bytes free