From e738a7596d6e69cbc8cbeecd07512119516268b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 28 Jul 2017 15:05:52 +0200 Subject: [PATCH 1/5] app: update handling features/service os ClockVM Threis no more ntpd service used - new approach do not conflict with ntpd. Because of this, new feature is named 'service.clocksync', and should be _enabled_ in ClockVM ('ntpd' was disabled there). QubesOS/qubes-issues#1230 --- qubes/app.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/qubes/app.py b/qubes/app.py index fec7788f..7548f221 100644 --- a/qubes/app.py +++ b/qubes/app.py @@ -663,7 +663,7 @@ class Qubes(qubes.PropertyHolder): doc='''Which VM to use as `yum` proxy for updating AdminVM and TemplateVMs''') clockvm = qubes.VMProperty('clockvm', load_stage=3, - allow_none=True, + default=None, allow_none=True, doc='Which VM to use as NTP proxy for updating AdminVM') default_kernel = qubes.property('default_kernel', load_stage=3, doc='Which kernel to use when not overriden in VM') @@ -803,17 +803,6 @@ class Qubes(qubes.PropertyHolder): self.property_require('clockvm', allow_none=True) self.property_require('updatevm', allow_none=True) - # Disable ntpd in ClockVM - to not conflict with ntpdate (both are - # using 123/udp port) - if hasattr(self, 'clockvm') and self.clockvm is not None: - if self.clockvm.features.get('service.ntpd', False): - self.log.warning( - 'VM set as clockvm (%r) has enabled \'ntpd\' service! ' - 'Expect failure when syncing time in dom0.', - self.clockvm) - else: - self.clockvm.features['service.ntpd'] = '' - for vm in self.domains: vm.events_enabled = True vm.fire_event('domain-load') @@ -1149,13 +1138,14 @@ class Qubes(qubes.PropertyHolder): # pylint: disable=unused-argument,no-self-use if newvalue is None: return - if newvalue.features.get('service.ntpd', False): - raise qubes.exc.QubesVMError(newvalue, - 'Cannot set {!r} as {!r} since it has ntpd enabled.'.format( - newvalue.name, name)) - else: - newvalue.features['service.ntpd'] = '' + if 'service.clocksync' not in newvalue.features: + newvalue.features['service.clocksync'] = True + @qubes.events.handler('property-set:clockvm') + def on_property_set_clockvm(self, event, name, newvalue, oldvalue=None): + # pylint: disable=unused-argument,no-self-use + if oldvalue and oldvalue.features.get('service.clocksync', False): + del oldvalue.features['service.clocksync'] @qubes.events.handler( 'property-pre-set:default_netvm', From 1a6728cb12ccfbc1711129d3f91a183badc4804b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 28 Jul 2017 15:08:33 +0200 Subject: [PATCH 2/5] ext/services: move exporting 'service.*' features to extensions This means core code will not publish any features by default. --- qubes/ext/services.py | 62 +++++++++++++++++++++++++++++++++++++++++ qubes/vm/qubesvm.py | 8 ------ rpm_spec/core-dom0.spec | 1 + 3 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 qubes/ext/services.py diff --git a/qubes/ext/services.py b/qubes/ext/services.py new file mode 100644 index 00000000..85f29dde --- /dev/null +++ b/qubes/ext/services.py @@ -0,0 +1,62 @@ +# -*- encoding: utf-8 -*- +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2017 Marek Marczykowski-Górecki +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see . + +'''Extension responsible for qvm-service framework''' + +import qubes.ext + +class ServicesExtension(qubes.ext.Extension): + '''This extension export features with 'service.' prefix to QubesDB in + /qubes-service/ tree. + ''' + # pylint: disable=no-self-use + @qubes.ext.handler('domain-qdb-create') + def on_domain_qdb_create(self, vm): + '''Actually export features''' + for feature, value in vm.features.items(): + if not feature.startswith('service.'): + continue + service = feature[len('service.'):] + # forcefully convert to '0' or '1' + vm.untrusted_qdb.write('/qubes-service/{}'.format(service), + str(int(bool(value)))) + + @qubes.ext.handler('domain-feature-set') + def on_domain_feature_set(self, vm, feature, value, oldvalue=None): + '''Update /qubes-service/ QubesDB tree in runtime''' + # pylint: disable=unused-argument + if not vm.is_running(): + return + if not feature.startswith('service.'): + return + service = feature[len('service.'):] + # forcefully convert to '0' or '1' + vm.untrusted_qdb.write('/qubes-service/{}'.format(service), + str(int(bool(value)))) + + @qubes.ext.handler('domain-feature-delete') + def on_domain_feature_delete(self, vm, feature): + '''Update /qubes-service/ QubesDB tree in runtime''' + if not vm.is_running(): + return + if not feature.startswith('service.'): + return + service = feature[len('service.'):] + vm.untrusted_qdb.rm('/qubes-service/{}'.format(service)) diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index 69febe4a..fcd8fd7a 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -1750,14 +1750,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): if tzname: self.qdb.write('/qubes-timezone', tzname) - for feature, value in self.features.items(): - if not feature.startswith('service.'): - continue - service = feature[len('service.'):] - # forcefully convert to '0' or '1' - self.qdb.write('/qubes-service/{}'.format(service), - str(int(bool(value)))) - self.qdb.write('/qubes-block-devices', '') self.qdb.write('/qubes-usb-devices', '') diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index b1b36492..aacb6caa 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -289,6 +289,7 @@ fi %{python3_sitelib}/qubes/ext/pci.py %{python3_sitelib}/qubes/ext/qubesmanager.py %{python3_sitelib}/qubes/ext/r3compatibility.py +%{python3_sitelib}/qubes/ext/services.py %dir %{python3_sitelib}/qubes/tests %dir %{python3_sitelib}/qubes/tests/__pycache__ From 5321e11003052b417af821ff77e06fe15ea3a8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 28 Jul 2017 15:09:19 +0200 Subject: [PATCH 3/5] qubesvm: fix docstring --- qubes/vm/qubesvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index fcd8fd7a..548fad7f 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -278,7 +278,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): :param value: new value :param oldvalue: old value, if any - .. event:: domain-feature-delete (subject, event, key) + .. event:: domain-feature-delete (subject, event, feature) A feature was removed. From 3c052d9e3cec6800d96cb9af5f263ae6d281dfc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 28 Jul 2017 16:16:00 +0200 Subject: [PATCH 4/5] doc: include list of extensions --- doc/qubes-ext.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/qubes-ext.rst b/doc/qubes-ext.rst index 0947efe4..00d3e4b6 100644 --- a/doc/qubes-ext.rst +++ b/doc/qubes-ext.rst @@ -1,8 +1,22 @@ :py:mod:`qubes.ext` -- Qubes extensions ======================================= +Module contents +--------------- + .. automodule:: qubes.ext :members: :show-inheritance: +Extensions defined here +----------------------- + +.. autoclass:: qubes.ext.admin.AdminExtension +.. autoclass:: qubes.ext.block.BlockDeviceExtension +.. autoclass:: qubes.ext.core_features.CoreFeatures +.. autoclass:: qubes.ext.gui.GUI +.. autoclass:: qubes.ext.pci.PCIDeviceExtension +.. autoclass:: qubes.ext.r3compatibility.R3Compatibility +.. autoclass:: qubes.ext.services.ServicesExtension + .. vim: ts=3 sw=3 et From 73fb16dbda0fc5acd30d37e2a47c9f9941f6ee1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 28 Jul 2017 16:32:19 +0200 Subject: [PATCH 5/5] tests: check clockvm-related handlers --- qubes/tests/app.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/qubes/tests/app.py b/qubes/tests/app.py index f9c6987f..599e051b 100644 --- a/qubes/tests/app.py +++ b/qubes/tests/app.py @@ -147,15 +147,41 @@ class TC_30_VMCollection(qubes.tests.QubesTestCase): class TC_90_Qubes(qubes.tests.QubesTestCase): + def tearDown(self): + try: + os.unlink('/tmp/qubestest.xml') + except: + pass + super(TC_90_Qubes, self).tearDown() + @qubes.tests.skipUnlessDom0 def test_000_init_empty(self): # pylint: disable=no-self-use,unused-variable,bare-except try: os.unlink('/tmp/qubestest.xml') - except: + except FileNotFoundError: pass qubes.Qubes.create_empty_store('/tmp/qubestest.xml') + def test_100_clockvm(self): + app = qubes.Qubes('/tmp/qubestest.xml', load=False, offline_mode=True) + app.load_initial_values() + + template = app.add_new_vm('TemplateVM', name='test-template', + label='green') + appvm = app.add_new_vm('AppVM', name='test-vm', template=template, + label='red') + self.assertIsNone(app.clockvm) + self.assertNotIn('service.clocksync', appvm.features) + self.assertNotIn('service.clocksync', template.features) + app.clockvm = appvm + self.assertIn('service.clocksync', appvm.features) + self.assertTrue(appvm.features['service.clocksync']) + app.clockvm = template + self.assertNotIn('service.clocksync', appvm.features) + self.assertIn('service.clocksync', template.features) + self.assertTrue(template.features['service.clocksync']) + @qubes.tests.skipUnlessGit def test_900_example_xml_in_doc(self): self.assertXMLIsValid(