vm: add run* methods
The base method is run_service, but also implement convenient wrappers.
This commit is contained in:
parent
21c65d7ea8
commit
6dd7c69b3f
@ -22,7 +22,7 @@
|
|||||||
'''
|
'''
|
||||||
Main Qubes() class and related classes.
|
Main Qubes() class and related classes.
|
||||||
'''
|
'''
|
||||||
|
import shlex
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
@ -179,6 +179,23 @@ class QubesBase(qubesmgmt.base.PropertyHolder):
|
|||||||
''' Remove a storage pool '''
|
''' Remove a storage pool '''
|
||||||
self.qubesd_call('dom0', 'mgmt.pool.Remove', name, None)
|
self.qubesd_call('dom0', 'mgmt.pool.Remove', name, None)
|
||||||
|
|
||||||
|
def run_service(self, dest, service, filter_esc=False, user=None,
|
||||||
|
localcmd=None, **kwargs):
|
||||||
|
'''Run qrexec service in a given destination
|
||||||
|
|
||||||
|
*kwargs* are passed verbatim to :py:meth:`subprocess.Popen`.
|
||||||
|
|
||||||
|
:param str dest: Destination - may be a VM name or empty
|
||||||
|
string for default (for a given service)
|
||||||
|
:param str service: service name
|
||||||
|
:param bool filter_esc: filter escape sequences to protect terminal \
|
||||||
|
emulator
|
||||||
|
:param str user: username to run service as
|
||||||
|
:param str localcmd: Command to connect stdin/stdout to
|
||||||
|
:rtype: subprocess.Popen
|
||||||
|
'''
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class QubesLocal(QubesBase):
|
class QubesLocal(QubesBase):
|
||||||
'''Application object communicating through local socket.
|
'''Application object communicating through local socket.
|
||||||
@ -209,6 +226,41 @@ class QubesLocal(QubesBase):
|
|||||||
return_data = client_socket.makefile('rb').read()
|
return_data = client_socket.makefile('rb').read()
|
||||||
return self._parse_qubesd_response(return_data)
|
return self._parse_qubesd_response(return_data)
|
||||||
|
|
||||||
|
def run_service(self, dest, service, filter_esc=False, user=None,
|
||||||
|
localcmd=None, **kwargs):
|
||||||
|
'''Run qrexec service in a given destination
|
||||||
|
|
||||||
|
:param str dest: Destination - may be a VM name or empty
|
||||||
|
string for default (for a given service)
|
||||||
|
:param str service: service name
|
||||||
|
:param bool filter_esc: filter escape sequences to protect terminal \
|
||||||
|
emulator
|
||||||
|
:param str user: username to run service as
|
||||||
|
:param str localcmd: Command to connect stdin/stdout to
|
||||||
|
:rtype: subprocess.Popen
|
||||||
|
'''
|
||||||
|
|
||||||
|
if not dest:
|
||||||
|
raise ValueError('Empty destination name allowed only from a VM')
|
||||||
|
try:
|
||||||
|
self.qubesd_call(dest, 'mgmt.vm.Start')
|
||||||
|
except qubesmgmt.exc.QubesVMNotHaltedError:
|
||||||
|
pass
|
||||||
|
qrexec_opts = ['-d', dest]
|
||||||
|
if filter_esc:
|
||||||
|
qrexec_opts.extend(['-t', '-T'])
|
||||||
|
if localcmd:
|
||||||
|
qrexec_opts.extend(['-l', localcmd])
|
||||||
|
if user is None:
|
||||||
|
user = 'DEFAULT'
|
||||||
|
kwargs.setdefault('stdin', subprocess.PIPE)
|
||||||
|
kwargs.setdefault('stdout', subprocess.PIPE)
|
||||||
|
kwargs.setdefault('stderr', subprocess.PIPE)
|
||||||
|
proc = subprocess.Popen([qubesmgmt.config.QREXEC_CLIENT] +
|
||||||
|
qrexec_opts + ['{}:QUBESRPC {} dom0'.format(user, service)],
|
||||||
|
**kwargs)
|
||||||
|
return proc
|
||||||
|
|
||||||
|
|
||||||
class QubesRemote(QubesBase):
|
class QubesRemote(QubesBase):
|
||||||
'''Application object communicating through qrexec services.
|
'''Application object communicating through qrexec services.
|
||||||
@ -232,3 +284,30 @@ class QubesRemote(QubesBase):
|
|||||||
stderr.decode())
|
stderr.decode())
|
||||||
|
|
||||||
return self._parse_qubesd_response(stdout)
|
return self._parse_qubesd_response(stdout)
|
||||||
|
|
||||||
|
def run_service(self, dest, service, filter_esc=False, user=None,
|
||||||
|
localcmd=None, **kwargs):
|
||||||
|
'''Run qrexec service in a given destination
|
||||||
|
|
||||||
|
:param str dest: Destination - may be a VM name or empty
|
||||||
|
string for default (for a given service)
|
||||||
|
:param str service: service name
|
||||||
|
:param bool filter_esc: filter escape sequences to protect terminal \
|
||||||
|
emulator
|
||||||
|
:param str user: username to run service as
|
||||||
|
:param str localcmd: Command to connect stdin/stdout to
|
||||||
|
:rtype: subprocess.Popen
|
||||||
|
'''
|
||||||
|
if filter_esc:
|
||||||
|
raise NotImplementedError(
|
||||||
|
'filter_esc not implemented for calls from VM')
|
||||||
|
if user:
|
||||||
|
raise ValueError(
|
||||||
|
'non-default user not possible for calls from VM')
|
||||||
|
kwargs.setdefault('stdin', subprocess.PIPE)
|
||||||
|
kwargs.setdefault('stdout', subprocess.PIPE)
|
||||||
|
kwargs.setdefault('stderr', subprocess.PIPE)
|
||||||
|
proc = subprocess.Popen([qubesmgmt.config.QREXEC_CLIENT_VM,
|
||||||
|
dest, service] + shlex.split(localcmd) if localcmd else [],
|
||||||
|
**kwargs)
|
||||||
|
return proc
|
||||||
|
@ -22,3 +22,5 @@
|
|||||||
|
|
||||||
#: path to qubesd socket
|
#: path to qubesd socket
|
||||||
QUBESD_SOCKET = '/var/run/qubesd.sock'
|
QUBESD_SOCKET = '/var/run/qubesd.sock'
|
||||||
|
QREXEC_CLIENT = '/usr/lib/qubes/qrexec-client'
|
||||||
|
QREXEC_CLIENT_VM = '/usr/bin/qrexec-client-vm'
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import qubesmgmt.base
|
import qubesmgmt.base
|
||||||
|
import qubesmgmt.exc
|
||||||
import qubesmgmt.storage
|
import qubesmgmt.storage
|
||||||
import qubesmgmt.features
|
import qubesmgmt.features
|
||||||
|
|
||||||
@ -213,6 +214,54 @@ class QubesVM(qubesmgmt.base.PropertyHolder):
|
|||||||
vm=self.name, vm_name=volname)
|
vm=self.name, vm_name=volname)
|
||||||
return self._volumes
|
return self._volumes
|
||||||
|
|
||||||
|
def run_service(self, service, **kwargs):
|
||||||
|
'''Run service on this VM
|
||||||
|
|
||||||
|
:param str service: service name
|
||||||
|
:rtype: subprocess.Popen
|
||||||
|
'''
|
||||||
|
return self.app.run_service(self._method_dest, service, **kwargs)
|
||||||
|
|
||||||
|
def run_service_for_stdio(self, service, input=None, **kwargs):
|
||||||
|
'''Run a service, pass an optional input and return (stdout, stderr).
|
||||||
|
|
||||||
|
Raises an exception if return code != 0.
|
||||||
|
|
||||||
|
*args* and *kwargs* are passed verbatim to :py:meth:`run_service`.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
There are some combinations if stdio-related *kwargs*, which are
|
||||||
|
not filtered for problems originating between the keyboard and the
|
||||||
|
chair.
|
||||||
|
''' # pylint: disable=redefined-builtin
|
||||||
|
p = self.run_service(service, **kwargs)
|
||||||
|
|
||||||
|
# this one is actually a tuple, but there is no need to unpack it
|
||||||
|
stdouterr = p.communicate(input=input)
|
||||||
|
|
||||||
|
if p.returncode:
|
||||||
|
raise qubesmgmt.exc.QubesVMError(self,
|
||||||
|
'service {!r} failed with retcode {!r}; '
|
||||||
|
'stdout={!r} stderr={!r}'.format(
|
||||||
|
service, p.returncode, *stdouterr))
|
||||||
|
|
||||||
|
return stdouterr
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def prepare_input_for_vmshell(command, input=None):
|
||||||
|
'''Prepare shell input for the given command and optional (real) input
|
||||||
|
''' # pylint: disable=redefined-builtin
|
||||||
|
if input is None:
|
||||||
|
input = b''
|
||||||
|
return b''.join((command.rstrip('\n').encode('utf-8'), b'\n', input))
|
||||||
|
|
||||||
|
def run(self, command, input=None, **kwargs):
|
||||||
|
'''Run a shell command inside the domain using qubes.VMShell qrexec.
|
||||||
|
|
||||||
|
''' # pylint: disable=redefined-builtin
|
||||||
|
return self.run_service_for_stdio('qubes.VMShell',
|
||||||
|
input=self.prepare_input_for_vmshell(command, input), **kwargs)
|
||||||
|
|
||||||
# pylint: disable=abstract-method
|
# pylint: disable=abstract-method
|
||||||
class AdminVM(QubesVM):
|
class AdminVM(QubesVM):
|
||||||
'''Dom0'''
|
'''Dom0'''
|
||||||
|
Loading…
Reference in New Issue
Block a user