vm/adminvm: add run_service* functions

Calling qrexec service dom0->dom0 can be useful when handling things
that can run in dom0 or other domain. This makes the interface uniform.
Example use cases include GUI VM and Audio VM.
This commit is contained in:
Marek Marczykowski-Górecki 2019-04-04 05:16:28 +02:00
parent b6c4f8456f
commit 50adc60882
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
2 changed files with 78 additions and 1 deletions

View File

@ -33,6 +33,7 @@ system_path = {
'qubes_guid_path': '/usr/bin/qubes-guid', 'qubes_guid_path': '/usr/bin/qubes-guid',
'qrexec_daemon_path': '/usr/sbin/qrexec-daemon', 'qrexec_daemon_path': '/usr/sbin/qrexec-daemon',
'qrexec_client_path': '/usr/bin/qrexec-client', 'qrexec_client_path': '/usr/bin/qrexec-client',
'qrexec_rpc_multiplexer': '/usr/lib/qubes/qubes-rpc-multiplexer',
'qubesdb_daemon_path': '/usr/sbin/qubesdb-daemon', 'qubesdb_daemon_path': '/usr/sbin/qubesdb-daemon',
# Relative to qubes_base_dir # Relative to qubes_base_dir

View File

@ -21,7 +21,8 @@
# #
''' This module contains the AdminVM implementation ''' ''' This module contains the AdminVM implementation '''
import asyncio
import subprocess
import libvirt import libvirt
import qubes import qubes
@ -212,6 +213,81 @@ class AdminVM(qubes.vm.BaseVM):
self._qdb_connection = qubesdb.QubesDB(self.name) self._qdb_connection = qubesdb.QubesDB(self.name)
return self._qdb_connection return self._qdb_connection
@asyncio.coroutine
def run_service(self, service, source=None, user=None,
filter_esc=False, autostart=False, gui=False, **kwargs):
'''Run service on this VM
:param str service: service name
:param qubes.vm.qubesvm.QubesVM source: source domain as presented to
this VM
:param str user: username to run service as
:param bool filter_esc: filter escape sequences to protect terminal \
emulator
:param bool autostart: if :py:obj:`True`, machine will be started if \
it is not running
:param bool gui: when autostarting, also start gui daemon
:rtype: asyncio.subprocess.Process
.. note::
User ``root`` is redefined to ``SYSTEM`` in the Windows agent code
'''
# pylint: disable=unused-argument
source = 'dom0' if source is None else self.app.domains[source].name
if filter_esc:
raise NotImplementedError(
'filter_esc=True not supported on calls to dom0')
if user is None:
user = 'root'
yield from self.fire_event_async('domain-cmd-pre-run', pre_event=True,
start_guid=gui)
if user != 'root':
cmd = ['runuser', '-u', user, '--']
else:
cmd = []
cmd.extend([
qubes.config.system_path['qrexec_rpc_multiplexer'],
service,
source,
'name',
self.name,
])
return (yield from asyncio.create_subprocess_exec(
*cmd,
**kwargs))
@asyncio.coroutine
def run_service_for_stdio(self, *args, 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
kwargs.setdefault('stdin', subprocess.PIPE)
kwargs.setdefault('stdout', subprocess.PIPE)
kwargs.setdefault('stderr', subprocess.PIPE)
p = yield from self.run_service(*args, **kwargs)
# this one is actually a tuple, but there is no need to unpack it
stdouterr = yield from p.communicate(input=input)
if p.returncode:
raise subprocess.CalledProcessError(p.returncode,
args[0], *stdouterr)
return stdouterr
# def __init__(self, **kwargs): # def __init__(self, **kwargs):
# super(QubesAdminVm, self).__init__(qid=0, name="dom0", # super(QubesAdminVm, self).__init__(qid=0, name="dom0",