parent
8e3621c4e5
commit
cce809c2cb
@ -201,7 +201,6 @@ class GUI(qubes.ext.Extension):
|
|||||||
'Cannot start qubes-guid for domain {!r}'.format(vm.name))
|
'Cannot start qubes-guid for domain {!r}'.format(vm.name))
|
||||||
|
|
||||||
vm.fire_event('monitor-layout-change')
|
vm.fire_event('monitor-layout-change')
|
||||||
vm.wait_for_session()
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import copy
|
import copy
|
||||||
import base64
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
@ -642,6 +643,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
self._libvirt_domain = None
|
self._libvirt_domain = None
|
||||||
self._qdb_connection = None
|
self._qdb_connection = None
|
||||||
|
|
||||||
|
#: this :py:class:`asyncio.Event` will fire when session is obtained
|
||||||
|
self.have_session = asyncio.Event()
|
||||||
|
|
||||||
if xml is None:
|
if xml is None:
|
||||||
# we are creating new VM and attributes came through kwargs
|
# we are creating new VM and attributes came through kwargs
|
||||||
assert hasattr(self, 'qid')
|
assert hasattr(self, 'qid')
|
||||||
@ -788,7 +793,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
# methods for changing domain state
|
# methods for changing domain state
|
||||||
#
|
#
|
||||||
|
|
||||||
def start(self, preparing_dvm=False, start_guid=True,
|
async def start(self, preparing_dvm=False, start_guid=True,
|
||||||
notify_function=None, mem_required=None):
|
notify_function=None, mem_required=None):
|
||||||
'''Start domain
|
'''Start domain
|
||||||
|
|
||||||
@ -808,19 +813,22 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
self.fire_event_pre('domain-pre-start', preparing_dvm=preparing_dvm,
|
self.fire_event_pre('domain-pre-start', preparing_dvm=preparing_dvm,
|
||||||
start_guid=start_guid, mem_required=mem_required)
|
start_guid=start_guid, mem_required=mem_required)
|
||||||
|
|
||||||
self.storage.verify()
|
await asyncio.get_event_loop().run_in_executor(None,
|
||||||
|
self.storage.verify)
|
||||||
|
|
||||||
if self.netvm is not None:
|
if self.netvm is not None:
|
||||||
# pylint: disable = no-member
|
# pylint: disable = no-member
|
||||||
if self.netvm.qid != 0:
|
if self.netvm.qid != 0:
|
||||||
if not self.netvm.is_running():
|
if not self.netvm.is_running():
|
||||||
self.netvm.start(start_guid=start_guid,
|
await self.netvm.start(start_guid=start_guid,
|
||||||
notify_function=notify_function)
|
notify_function=notify_function)
|
||||||
|
|
||||||
self.storage.start()
|
await asyncio.get_event_loop().run_in_executor(None,
|
||||||
|
self.storage.start)
|
||||||
self._update_libvirt_domain()
|
self._update_libvirt_domain()
|
||||||
|
|
||||||
qmemman_client = self.request_memory(mem_required)
|
qmemman_client = await asyncio.get_event_loop().run_in_executor(None,
|
||||||
|
self.request_memory, mem_required)
|
||||||
try:
|
try:
|
||||||
self.libvirt_domain.createWithFlags(libvirt.VIR_DOMAIN_START_PAUSED)
|
self.libvirt_domain.createWithFlags(libvirt.VIR_DOMAIN_START_PAUSED)
|
||||||
except:
|
except:
|
||||||
@ -833,7 +841,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
preparing_dvm=preparing_dvm, start_guid=start_guid)
|
preparing_dvm=preparing_dvm, start_guid=start_guid)
|
||||||
|
|
||||||
self.log.info('Setting Qubes DB info for the VM')
|
self.log.info('Setting Qubes DB info for the VM')
|
||||||
self.start_qubesdb()
|
await self.start_qubesdb()
|
||||||
self.create_qdb_entries()
|
self.create_qdb_entries()
|
||||||
|
|
||||||
if preparing_dvm:
|
if preparing_dvm:
|
||||||
@ -855,7 +863,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
# self.start_guid()
|
# self.start_guid()
|
||||||
|
|
||||||
if not preparing_dvm:
|
if not preparing_dvm:
|
||||||
self.start_qrexec_daemon()
|
await self.start_qrexec_daemon()
|
||||||
|
|
||||||
self.fire_event('domain-start',
|
self.fire_event('domain-start',
|
||||||
preparing_dvm=preparing_dvm, start_guid=start_guid)
|
preparing_dvm=preparing_dvm, start_guid=start_guid)
|
||||||
@ -864,12 +872,14 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
if self.is_running() or self.is_paused():
|
if self.is_running() or self.is_paused():
|
||||||
# This avoids losing the exception if an exception is raised in
|
# This avoids losing the exception if an exception is raised in
|
||||||
# self.force_shutdown(), because the vm is not running or paused
|
# self.force_shutdown(), because the vm is not running or paused
|
||||||
self.force_shutdown()
|
await self.kill()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
asyncio.ensure_future(self.wait_for_session())
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def shutdown(self, force=False, wait=False):
|
async def shutdown(self, force=False, wait=False):
|
||||||
'''Shutdown domain.
|
'''Shutdown domain.
|
||||||
|
|
||||||
:raises qubes.exc.QubesVMNotStartedError: \
|
:raises qubes.exc.QubesVMNotStartedError: \
|
||||||
@ -882,14 +892,16 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
self.fire_event_pre('domain-pre-shutdown', force=force)
|
self.fire_event_pre('domain-pre-shutdown', force=force)
|
||||||
|
|
||||||
self.libvirt_domain.shutdown()
|
self.libvirt_domain.shutdown()
|
||||||
self.storage.stop()
|
|
||||||
|
await asyncio.get_event_loop().run_in_executor(None,
|
||||||
|
self.storage.stop)
|
||||||
|
|
||||||
while wait and not self.is_halted():
|
while wait and not self.is_halted():
|
||||||
time.sleep(0.25)
|
await asyncio.sleep(0.25)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def kill(self):
|
async def kill(self):
|
||||||
'''Forcefuly shutdown (destroy) domain.
|
'''Forcefuly shutdown (destroy) domain.
|
||||||
|
|
||||||
:raises qubes.exc.QubesVMNotStartedError: \
|
:raises qubes.exc.QubesVMNotStartedError: \
|
||||||
@ -900,7 +912,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
raise qubes.exc.QubesVMNotStartedError(self)
|
raise qubes.exc.QubesVMNotStartedError(self)
|
||||||
|
|
||||||
self.libvirt_domain.destroy()
|
self.libvirt_domain.destroy()
|
||||||
self.storage.stop()
|
await asyncio.get_event_loop().run_in_executor(None,
|
||||||
|
self.storage.stop)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -909,11 +922,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Call to deprecated function force_shutdown(), use kill() instead',
|
'Call to deprecated function force_shutdown(), use kill() instead',
|
||||||
DeprecationWarning, stacklevel=2)
|
DeprecationWarning, stacklevel=2)
|
||||||
self.kill(*args, **kwargs)
|
return self.kill(*args, **kwargs)
|
||||||
|
|
||||||
return self
|
async def suspend(self):
|
||||||
|
|
||||||
def suspend(self):
|
|
||||||
'''Suspend (pause) domain.
|
'''Suspend (pause) domain.
|
||||||
|
|
||||||
:raises qubes.exc.QubesVMNotRunnignError: \
|
:raises qubes.exc.QubesVMNotRunnignError: \
|
||||||
@ -934,7 +945,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def pause(self):
|
async def pause(self):
|
||||||
'''Pause (suspend) domain. This currently delegates to \
|
'''Pause (suspend) domain. This currently delegates to \
|
||||||
:py:meth:`suspend`.'''
|
:py:meth:`suspend`.'''
|
||||||
|
|
||||||
@ -945,7 +956,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def resume(self):
|
async def resume(self):
|
||||||
'''Resume suspended domain.
|
'''Resume suspended domain.
|
||||||
|
|
||||||
:raises qubes.exc.QubesVMNotSuspendedError: when machine is not paused
|
:raises qubes.exc.QubesVMNotSuspendedError: when machine is not paused
|
||||||
@ -960,7 +971,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def unpause(self):
|
async def unpause(self):
|
||||||
'''Resume (unpause) a domain'''
|
'''Resume (unpause) a domain'''
|
||||||
if not self.is_paused():
|
if not self.is_paused():
|
||||||
raise qubes.exc.QubesVMNotPausedError(self)
|
raise qubes.exc.QubesVMNotPausedError(self)
|
||||||
@ -969,6 +980,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# TODO async def
|
||||||
|
# TODO def run_for_retcode, factor out passio
|
||||||
def run(self, command, user=None, autostart=False, notify_function=None,
|
def run(self, command, user=None, autostart=False, notify_function=None,
|
||||||
passio=False, passio_popen=False, passio_stderr=False,
|
passio=False, passio_popen=False, passio_stderr=False,
|
||||||
ignore_stderr=False, localcmd=None, wait=False, gui=True,
|
ignore_stderr=False, localcmd=None, wait=False, gui=True,
|
||||||
@ -1018,6 +1031,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
self.fire_event_pre('domain-cmd-pre-run', start_guid=gui)
|
self.fire_event_pre('domain-cmd-pre-run', start_guid=gui)
|
||||||
|
|
||||||
|
if not self.have_session.is_set():
|
||||||
|
raise qubes.exc.QubesVMError(self, 'don\'t have session yet')
|
||||||
|
|
||||||
args = [qubes.config.system_path['qrexec_client_path'],
|
args = [qubes.config.system_path['qrexec_client_path'],
|
||||||
'-d', str(self.name),
|
'-d', str(self.name),
|
||||||
'{}:{}'.format(user, command)]
|
'{}:{}'.format(user, command)]
|
||||||
@ -1127,7 +1143,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
return qmemman_client
|
return qmemman_client
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def start_daemon(command, **kwargs):
|
async def start_daemon(*command, input=None, **kwargs):
|
||||||
'''Start a daemon for the VM
|
'''Start a daemon for the VM
|
||||||
|
|
||||||
This function take care to run it as appropriate user.
|
This function take care to run it as appropriate user.
|
||||||
@ -1138,16 +1154,19 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
:return: None
|
:return: None
|
||||||
'''
|
'''
|
||||||
|
|
||||||
prefix_cmd = []
|
|
||||||
if os.getuid() == 0:
|
if os.getuid() == 0:
|
||||||
# try to always have VM daemons running as normal user, otherwise
|
# try to always have VM daemons running as normal user, otherwise
|
||||||
# some files (like clipboard) may be created as root and cause
|
# some files (like clipboard) may be created as root and cause
|
||||||
# permission problems
|
# permission problems
|
||||||
qubes_group = grp.getgrnam('qubes')
|
qubes_group = grp.getgrnam('qubes')
|
||||||
prefix_cmd = ['runuser', '-u', qubes_group.gr_mem[0], '--']
|
command = ['runuser', '-u', qubes_group.gr_mem[0], '--'] + command
|
||||||
subprocess.check_call(prefix_cmd + command, **kwargs)
|
p = await asyncio.create_subprocess_exec(*command, **kwargs)
|
||||||
|
stdout, stderr = await p.communicate(input=input)
|
||||||
|
if p.returncode:
|
||||||
|
raise subprocess.CalledProcessError(p.returncode, command,
|
||||||
|
output=stdout, stderr=stderr)
|
||||||
|
|
||||||
def start_qrexec_daemon(self):
|
async def start_qrexec_daemon(self):
|
||||||
'''Start qrexec daemon.
|
'''Start qrexec daemon.
|
||||||
|
|
||||||
:raises OSError: when starting fails.
|
:raises OSError: when starting fails.
|
||||||
@ -1167,13 +1186,13 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
qrexec_env['QREXEC_STARTUP_TIMEOUT'] = str(self.qrexec_timeout)
|
qrexec_env['QREXEC_STARTUP_TIMEOUT'] = str(self.qrexec_timeout)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.start_daemon(
|
await self.start_daemon(
|
||||||
[qubes.config.system_path["qrexec_daemon_path"]] + qrexec_args,
|
qubes.config.system_path['qrexec_daemon_path'], *qrexec_args,
|
||||||
env=qrexec_env)
|
env=qrexec_env)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
raise qubes.exc.QubesVMError(self, 'Cannot execute qrexec-daemon!')
|
raise qubes.exc.QubesVMError(self, 'Cannot execute qrexec-daemon!')
|
||||||
|
|
||||||
def start_qubesdb(self):
|
async def start_qubesdb(self):
|
||||||
'''Start QubesDB daemon.
|
'''Start QubesDB daemon.
|
||||||
|
|
||||||
:raises OSError: when starting fails.
|
:raises OSError: when starting fails.
|
||||||
@ -1184,14 +1203,14 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
self.log.info('Starting Qubes DB')
|
self.log.info('Starting Qubes DB')
|
||||||
try:
|
try:
|
||||||
self.start_daemon([
|
await self.start_daemon(
|
||||||
qubes.config.system_path["qubesdb_daemon_path"],
|
qubes.config.system_path['qubesdb_daemon_path'],
|
||||||
str(self.xid),
|
str(self.xid),
|
||||||
self.name])
|
self.name)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
raise qubes.exc.QubesException('Cannot execute qubesdb-daemon')
|
raise qubes.exc.QubesException('Cannot execute qubesdb-daemon')
|
||||||
|
|
||||||
def wait_for_session(self):
|
async def wait_for_session(self):
|
||||||
'''Wait until machine finished boot sequence.
|
'''Wait until machine finished boot sequence.
|
||||||
|
|
||||||
This is done by executing qubes RPC call that checks if dummy system
|
This is done by executing qubes RPC call that checks if dummy system
|
||||||
@ -1201,9 +1220,12 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
self.log.info('Waiting for qubes-session')
|
self.log.info('Waiting for qubes-session')
|
||||||
|
|
||||||
# Note : User root is redefined to SYSTEM in the Windows agent code
|
# Note : User root is redefined to SYSTEM in the Windows agent code
|
||||||
p = self.run('QUBESRPC qubes.WaitForSession none',
|
p = await asyncio.get_event_loop().run_in_executor(
|
||||||
user="root", passio_popen=True, gui=False, wait=True)
|
functools.partial(self.run, 'QUBESRPC qubes.WaitForSession none',
|
||||||
p.communicate(input=self.default_user.encode())
|
user='root', passio_popen=True, gui=False, wait=True))
|
||||||
|
await asyncio.get_event_loop().run_in_executor(functools.partial(
|
||||||
|
p.communicate, input=self.default_user.encode()))
|
||||||
|
self.have_session.set()
|
||||||
|
|
||||||
def create_on_disk(self, pool=None, pools=None):
|
def create_on_disk(self, pool=None, pools=None):
|
||||||
'''Create files needed for VM.
|
'''Create files needed for VM.
|
||||||
@ -1714,6 +1736,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
|
|
||||||
self.fire_event('domain-qdb-create')
|
self.fire_event('domain-qdb-create')
|
||||||
|
|
||||||
|
# TODO async; update this in constructor
|
||||||
def _update_libvirt_domain(self):
|
def _update_libvirt_domain(self):
|
||||||
'''Re-initialise :py:attr:`libvirt_domain`.'''
|
'''Re-initialise :py:attr:`libvirt_domain`.'''
|
||||||
domain_config = self.create_config_file()
|
domain_config = self.create_config_file()
|
||||||
|
Loading…
Reference in New Issue
Block a user