Browse Source

HVM part 2

Wojtek Porczyk 8 years ago
parent
commit
04cc2099f7
6 changed files with 108 additions and 209 deletions
  1. 0 188
      core-modules/01QubesHVm.py
  2. 1 0
      qubes/config.py
  3. 70 2
      qubes/ext/gui.py
  4. 9 2
      qubes/vm/mix/net.py
  5. 20 13
      qubes/vm/qubesvm.py
  6. 8 4
      templates/libvirt/xen.xml

+ 0 - 188
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)

+ 1 - 0
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",
 

+ 70 - 2
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

+ 9 - 2
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):

+ 20 - 13
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):

+ 8 - 4
templates/libvirt/xen.xml

@@ -27,10 +27,14 @@
 			<viridian/>
 		</features>
 
-        {# TODO
-        <clock offset="variable"
-            adjustment='{timeoffset}' basis='{time_basis}'/>
-        #}
+        {% set timezone = vm.features.check_with_template('timezone', 'localtime').lower() %}
+        {% if timezone == 'localtime' %}
+            <clock offset="variable" adjustment="0" basis="localtime" />
+        {% elif timezone.isdigit() %}
+            <clock offset="variable" adjustment="{{ timezone }}" basis="UTC" />
+        {% else %}
+            <clock offset="variable" adjustment="0" basis="UTC" />
+        {% endif %}
 	{% else %}
 		<clock offset='utc' adjustment='reset'>
 			<timer name="tsc" mode="native"/>