diff --git a/core-modules/01QubesHVm.py b/core-modules/01QubesHVm.py
index 2a6e9f04..8613e8ee 100644
--- a/core-modules/01QubesHVm.py
+++ b/core-modules/01QubesHVm.py
@@ -85,28 +85,8 @@ class QubesHVm(QubesResizableVm):
'save': lambda: str(self._seamless_gui_mode) }
attrs['services']['default'] = "{'meminfo-writer': False}"
- attrs['memory']['default'] = defaults["hvm_memory"]
-
return attrs
- def __init__(self, **kwargs):
-
- super(QubesHVm, self).__init__(**kwargs)
-
- # Default for meminfo-writer have changed to (correct) False in the
- # same version as introduction of guiagent_installed, so for older VMs
- # with wrong setting, change is based on 'guiagent_installed' presence
- if "guiagent_installed" not in kwargs and \
- (not 'xml_element' in kwargs or kwargs['xml_element'].get('guiagent_installed') is None):
- self.services['meminfo-writer'] = False
-
- @property
- def type(self):
- return "HVM"
-
- def is_appvm(self):
- return True
-
@classmethod
def is_template_compatible(cls, template):
if template and (not template.is_template() or template.type != "TemplateHVM"):
@@ -124,28 +104,6 @@ class QubesHVm(QubesResizableVm):
attrs += [ 'guiagent_installed' ]
return attrs
- @property
- def qrexec_installed(self):
- return self._qrexec_installed or \
- bool(self.template and self.template.qrexec_installed)
-
- @qrexec_installed.setter
- def qrexec_installed(self, value):
- if self.template and self.template.qrexec_installed and not value:
- print >>sys.stderr, "WARNING: When qrexec_installed set in template, it will be propagated to the VM"
- self._qrexec_installed = value
-
- @property
- def guiagent_installed(self):
- return self._guiagent_installed or \
- bool(self.template and self.template.guiagent_installed)
-
- @guiagent_installed.setter
- def guiagent_installed(self, value):
- if self.template and self.template.guiagent_installed and not value:
- print >>sys.stderr, "WARNING: When guiagent_installed set in template, it will be propagated to the VM"
- self._guiagent_installed = value
-
@property
def seamless_gui_mode(self):
if not self.guiagent_installed:
@@ -237,59 +195,6 @@ class QubesHVm(QubesResizableVm):
self.storage.resize_private_img(size)
- def get_config_params(self):
-
- params = super(QubesHVm, self).get_config_params()
-
- self.storage.drive = self.drive
- params.update(self.storage.get_config_params())
- params['volatiledev'] = ''
-
- if self.timezone.lower() == 'localtime':
- params['time_basis'] = 'localtime'
- params['timeoffset'] = '0'
- elif self.timezone.isdigit():
- params['time_basis'] = 'UTC'
- params['timeoffset'] = self.timezone
- else:
- print >>sys.stderr, "WARNING: invalid 'timezone' value: %s" % self.timezone
- params['time_basis'] = 'UTC'
- params['timeoffset'] = '0'
- return params
-
- def verify_files(self):
- if dry_run:
- return
-
- self.storage.verify_files()
-
- # fire hooks
- for hook in self.hooks_verify_files:
- hook(self)
-
- return True
-
- @property
- def vif(self):
- if self.xid < 0:
- return None
- if self.netvm is None:
- return None
- return "vif{0}.+".format(self.stubdom_xid)
-
- @property
- def mac(self):
- if self._mac is not None:
- return self._mac
- elif self.template is not None:
- return self.template.mac
- else:
- return "00:16:3E:5E:6C:{qid:02X}".format(qid=self.qid)
-
- @mac.setter
- def mac(self, value):
- self._mac = value
-
def run(self, command, **kwargs):
if self.qrexec_installed:
if 'gui' in kwargs and kwargs['gui']==False:
@@ -298,20 +203,6 @@ class QubesHVm(QubesResizableVm):
else:
raise QubesException("Needs qrexec agent installed in VM to use this function. See also qvm-prefs.")
- @property
- def stubdom_xid(self):
- if self.xid < 0:
- return -1
-
- if vmm.xs is None:
- return -1
-
- stubdom_xid_str = vmm.xs.read('', '/local/domain/%d/image/device-model-domid' % self.xid)
- if stubdom_xid_str is not None:
- return int(stubdom_xid_str)
- else:
- return -1
-
def start(self, *args, **kwargs):
# make it available to storage.prepare_for_vm_startup, which is
# called before actually building VM libvirt configuration
@@ -333,74 +224,6 @@ class QubesHVm(QubesResizableVm):
else:
raise
- def start_stubdom_guid(self, verbose=True):
-
- guid_cmd = [system_path["qubes_guid_path"],
- "-d", str(self.stubdom_xid),
- "-t", str(self.xid),
- "-N", self.name,
- "-c", self.label.color,
- "-i", self.label.icon_path,
- "-l", str(self.label.index)]
- if self.debug:
- guid_cmd += ['-v', '-v']
- elif not verbose:
- guid_cmd += ['-q']
- retcode = subprocess.call (guid_cmd)
- if (retcode != 0) :
- raise QubesException("Cannot start qubes-guid!")
-
- def start_guid(self, verbose=True, notify_function=None,
- before_qrexec=False, **kwargs):
- if not before_qrexec:
- return
-
- if not self.guiagent_installed or self.debug:
- if verbose:
- print >> sys.stderr, "--> Starting Qubes GUId (full screen)..."
- self.start_stubdom_guid(verbose=verbose)
-
- kwargs['extra_guid_args'] = kwargs.get('extra_guid_args', []) + \
- ['-Q', '-n']
-
- stubdom_guid_pidfile = \
- '/var/run/qubes/guid-running.%d' % self.stubdom_xid
- if not self.debug and os.path.exists(stubdom_guid_pidfile):
- # Terminate stubdom guid once "real" gui agent connects
- stubdom_guid_pid = int(open(stubdom_guid_pidfile, 'r').read())
- kwargs['extra_guid_args'] += ['-K', str(stubdom_guid_pid)]
-
- super(QubesHVm, self).start_guid(verbose, notify_function, **kwargs)
-
- def start_qrexec_daemon(self, **kwargs):
- if not self.qrexec_installed:
- if kwargs.get('verbose', False):
- print >> sys.stderr, "--> Starting the qrexec daemon..."
- xid = self.get_xid()
- qrexec_env = os.environ.copy()
- qrexec_env['QREXEC_STARTUP_NOWAIT'] = '1'
- retcode = subprocess.call ([system_path["qrexec_daemon_path"], str(xid), self.name, self.default_user], env=qrexec_env)
- if (retcode != 0) :
- self.force_shutdown(xid=xid)
- raise OSError ("ERROR: Cannot execute qrexec-daemon!")
- else:
- super(QubesHVm, self).start_qrexec_daemon(**kwargs)
-
- if self.guiagent_installed:
- if kwargs.get('verbose'):
- print >> sys.stderr, "--> Waiting for user '%s' login..." % self.default_user
-
- self.wait_for_session(notify_function=kwargs.get('notify_function', None))
- self.send_gui_mode()
-
- def send_gui_mode(self):
- if self.seamless_gui_mode:
- service_input = "SEAMLESS"
- else:
- service_input = "FULLSCREEN"
-
- self.run_service("qubes.SetGuiMode", input=service_input)
-
def _cleanup_zombie_domains(self):
super(QubesHVm, self)._cleanup_zombie_domains()
if not self.is_running():
@@ -416,15 +239,6 @@ class QubesHVm(QubesResizableVm):
guid_pid = open(guid_pidfile).read().strip()
os.kill(int(guid_pid), 15)
- def suspend(self):
- if dry_run:
- return
-
- if not self.is_running() and not self.is_paused():
- raise QubesException ("VM not running!")
-
- self.pause()
-
def is_guid_running(self):
# If user force the guiagent, is_guid_running will mimic a standard QubesVM
if self.guiagent_installed:
@@ -444,5 +258,3 @@ class QubesHVm(QubesResizableVm):
if self.qrexec_installed and not self.is_qrexec_running():
return False
return True
-
-register_qubes_vm_class(QubesHVm)
diff --git a/qubes/config.py b/qubes/config.py
index 467f7342..62af4dbb 100644
--- a/qubes/config.py
+++ b/qubes/config.py
@@ -74,6 +74,7 @@ vm_files = {
defaults = {
'libvirt_uri': 'xen:///',
'memory': 400,
+ 'hvm_memory': 512,
'kernelopts': "nopat",
'kernelopts_pcidevs': "nopat iommu=soft swiotlb=8192",
diff --git a/qubes/ext/gui.py b/qubes/ext/gui.py
index 14aa223c..2bc46edf 100644
--- a/qubes/ext/gui.py
+++ b/qubes/ext/gui.py
@@ -58,6 +58,7 @@ class GUI(qubes.ext.Extension):
'-c', vm.label.color,
'-i', vm.label.icon_path,
'-l', str(vm.label.index)]
+
if extra_guid_args is not None:
guid_cmd += extra_guid_args
@@ -68,8 +69,19 @@ class GUI(qubes.ext.Extension):
else:
guid_cmd += ['-q']
- retcode = subprocess.call(guid_cmd)
- if retcode != 0:
+ if vm.hvm:
+ guid_cmd += ['-Q', '-n']
+
+ stubdom_guid_pidfile = \
+ '/var/run/qubes/guid-running.{}'.format(self.get_stubdom_xid(vm))
+ if not vm.debug and os.path.exists(stubdom_guid_pidfile):
+ # Terminate stubdom guid once "real" gui agent connects
+ stubdom_guid_pid = open(stubdom_guid_pidfile, 'r').read().strip()
+ guid_cmd += ['-K', stubdom_guid_pid]
+
+ try:
+ subprocess.check_call(guid_cmd)
+ except subprocess.CalledProcessError:
raise qubes.exc.QubesVMError(vm,
'Cannot start qubes-guid for domain {!r}'.format(vm.name))
@@ -77,6 +89,62 @@ class GUI(qubes.ext.Extension):
vm.wait_for_session()
+ @staticmethod
+ def get_stubdom_xid(vm):
+ if vm.xid < 0:
+ return -1
+
+ if vm.app.vmm.xs is None:
+ return -1
+
+ stubdom_xid_str = vm.app.vmm.xs.read('',
+ '/local/domain/{}/image/device-model-domid'.format(vm.xid))
+ if stubdom_xid_str is None or not stubdom_xid_str.isdigit():
+ return -1
+
+ return int(stubdom_xid_str)
+
+
+ @staticmethod
+ def send_gui_mode(vm):
+ vm.run_service('qubes.SetGuiMode',
+ input=('SEAMLESS'
+ if vm.features.get('gui-seamless', False)
+ else 'FULLSCREEN'))
+
+
+ @qubes.ext.handler('domain-spawn')
+ def on_domain_spawn(self, vm, event, start_guid=True, **kwargs):
+ if not start_guid:
+ return
+
+ if not vm.hvm:
+ return
+
+ if not os.getenv('DISPLAY'):
+ vm.log.error('Not starting gui daemon, no DISPLAY set')
+ return
+
+ guid_cmd = [qubes.config.system_path['qubes_guid_path'],
+ '-d', str(self.get_stubdom_xid(vm)),
+ '-t', str(vm.xid),
+ '-N', vm.name,
+ '-c', vm.label.color,
+ '-i', vm.label.icon_path,
+ '-l', str(vm.label.index),
+ ]
+
+ if vm.debug:
+ guid_cmd += ['-v', '-v']
+ else:
+ guid_cmd += ['-q']
+
+ try:
+ subprocess.check_call(guid_cmd)
+ except subprocess.CalledProcesException:
+ raise qubes.exc.QubesVMError(vm, 'Cannot start gui daemon')
+
+
@qubes.ext.handler('monitor-layout-change')
def on_monitor_layout_change(self, vm, event, monitor_layout):
# pylint: disable=no-self-use
diff --git a/qubes/vm/mix/net.py b/qubes/vm/mix/net.py
index a05994f4..c27b8fa9 100644
--- a/qubes/vm/mix/net.py
+++ b/qubes/vm/mix/net.py
@@ -36,7 +36,11 @@ import qubes.exc
class NetVMMixin(object):
mac = qubes.property('mac', type=str,
- default=(lambda self: '00:16:3E:5E:6C:{:02X}'.format(self.qid)),
+ default=(lambda self:
+ self.template.mac if self.hvm
+ and hasattr(self, 'template')
+ and self.template is not None
+ else '00:16:3E:5E:6C:{:02X}'.format(self.qid)),
ls_width=17,
doc='MAC address of the NIC emulated inside VM')
@@ -109,7 +113,10 @@ class NetVMMixin(object):
return None
if self.netvm is None:
return None
- return "vif{0}.+".format(self.xid)
+
+ # XXX ugly hack ahead
+ # stubdom_xid is one more than self.xid
+ return 'vif{0}.+'.format(self.xid + int(self.hvm))
@property
def connected_vms(self):
diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py
index d5a6d70d..d9f6fa99 100644
--- a/qubes/vm/qubesvm.py
+++ b/qubes/vm/qubesvm.py
@@ -183,7 +183,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
package manager.''')
memory = qubes.property('memory', type=int,
- default=qubes.config.defaults['memory'],
+ default=(lambda self:
+ qubes.config.defaults['hvm_memory' if self.hvm else 'memory']),
doc='Memory currently available for this VM.')
maxmem = qubes.property('maxmem', type=int,
@@ -766,7 +767,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
'Cannot suspend domain {!r} which has PCI devices attached' \
.format(self.name))
else:
- self.libvirt_domain.suspend()
+ if self.hvm:
+ self.libvirt_domain.pause()
+ else:
+ self.libvirt_domain.suspend()
def pause(self):
@@ -953,22 +957,25 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
:raises OSError: when starting fails.
'''
- if not self.features.check_with_template('qrexec', not self.hvm):
- self.log.debug(
- 'Not starting the qrexec daemon, disabled by features')
- return
-
self.log.debug('Starting the qrexec daemon')
qrexec_args = [str(self.xid), self.name, self.default_user]
if not self.debug:
qrexec_args.insert(0, "-q")
+
qrexec_env = os.environ.copy()
- qrexec_env['QREXEC_STARTUP_TIMEOUT'] = str(self.qrexec_timeout)
- retcode = subprocess.call(
- [qubes.config.system_path["qrexec_daemon_path"]] + qrexec_args,
- env=qrexec_env)
- if retcode != 0:
- raise OSError('Cannot execute qrexec-daemon!')
+ if not self.features.check_with_template('qrexec', not self.hvm):
+ self.log.debug(
+ 'Starting the qrexec daemon in background, because of features')
+ qrexec_env['QREXEC_STARTUP_NOWAIT'] = '1'
+ else:
+ qrexec_env['QREXEC_STARTUP_TIMEOUT'] = str(self.qrexec_timeout)
+
+ try:
+ subprocess.check_call(
+ [qubes.config.system_path["qrexec_daemon_path"]] + qrexec_args,
+ env=qrexec_env)
+ except subprocess.CalledProcessError:
+ raise qubes.exc.QubesVMError(self, 'Cannot execute qrexec-daemon!')
def start_qubesdb(self):
diff --git a/templates/libvirt/xen.xml b/templates/libvirt/xen.xml
index 3d6ceeff..13b15e51 100644
--- a/templates/libvirt/xen.xml
+++ b/templates/libvirt/xen.xml
@@ -27,10 +27,14 @@
- {# TODO
-
- #}
+ {% set timezone = vm.features.check_with_template('timezone', 'localtime').lower() %}
+ {% if timezone == 'localtime' %}
+
+ {% elif timezone.isdigit() %}
+
+ {% else %}
+
+ {% endif %}
{% else %}