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 %}