vm/qubesvm: allow 'features-request' to have async handlers

Some handlers may want to call into other VMs (or even the one asking),
but vm.run() functions are coroutines, so needs to be called from
another coroutine. Allow for that.
Also fix typo in documentation.
This commit is contained in:
Marek Marczykowski-Górecki 2018-02-28 05:33:03 +01:00
parent 5840dd76f9
commit 7c4566ec14
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
3 changed files with 17 additions and 10 deletions

View File

@ -61,7 +61,7 @@ class QubesMiscAPI(qubes.api.AbstractQubesAPI):
untrusted_value = untrusted_features[untrusted_key] untrusted_value = untrusted_features[untrusted_key]
assert all((c in safe_set) for c in untrusted_value) assert all((c in safe_set) for c in untrusted_value)
self.src.fire_event('features-request', yield from self.src.fire_event_async('features-request',
untrusted_features=untrusted_features) untrusted_features=untrusted_features)
self.app.save() self.app.save()
@ -87,7 +87,7 @@ class QubesMiscAPI(qubes.api.AbstractQubesAPI):
untrusted_features[feature] = untrusted_value untrusted_features[feature] = untrusted_value
del untrusted_value del untrusted_value
self.src.fire_event('features-request', yield from self.src.fire_event_async('features-request',
untrusted_features=untrusted_features) untrusted_features=untrusted_features)
self.app.save() self.app.save()

View File

@ -69,8 +69,9 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
mock.call.untrusted_qdb.read('/features-request/feature1'), mock.call.untrusted_qdb.read('/features-request/feature1'),
mock.call.untrusted_qdb.read('/features-request/feature2'), mock.call.untrusted_qdb.read('/features-request/feature2'),
mock.call.untrusted_qdb.read('/features-request/feature3'), mock.call.untrusted_qdb.read('/features-request/feature3'),
mock.call.fire_event('features-request', untrusted_features={ mock.call.fire_event_async('features-request', untrusted_features={
'feature1': '1', 'feature2': '', 'feature3': 'other'}) 'feature1': '1', 'feature2': '', 'feature3': 'other'}),
('fire_event_async().__iter__', (), {}),
]) ])
def test_001_features_request_empty(self): def test_001_features_request_empty(self):
@ -82,7 +83,9 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
]) ])
self.assertEqual(self.src.mock_calls, [ self.assertEqual(self.src.mock_calls, [
mock.call.untrusted_qdb.list('/features-request/'), mock.call.untrusted_qdb.list('/features-request/'),
mock.call.fire_event('features-request', untrusted_features={}) mock.call.fire_event_async('features-request',
untrusted_features={}),
('fire_event_async().__iter__', (), {}),
]) ])
def test_002_features_request_invalid1(self): def test_002_features_request_invalid1(self):
@ -129,10 +132,11 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
mock.call.untrusted_qdb.read('/qubes-tools/qrexec'), mock.call.untrusted_qdb.read('/qubes-tools/qrexec'),
mock.call.untrusted_qdb.read('/qubes-tools/gui'), mock.call.untrusted_qdb.read('/qubes-tools/gui'),
mock.call.untrusted_qdb.read('/qubes-tools/default-user'), mock.call.untrusted_qdb.read('/qubes-tools/default-user'),
mock.call.fire_event('features-request', untrusted_features={ mock.call.fire_event_async('features-request', untrusted_features={
'gui': '1', 'gui': '1',
'default-user': 'user', 'default-user': 'user',
'qrexec': '1'}), 'qrexec': '1'}),
('fire_event_async().__iter__', (), {}),
]) ])
self.assertEqual(self.app.mock_calls, [mock.call.save()]) self.assertEqual(self.app.mock_calls, [mock.call.save()])
@ -150,10 +154,11 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
mock.call.untrusted_qdb.read('/qubes-tools/qrexec'), mock.call.untrusted_qdb.read('/qubes-tools/qrexec'),
mock.call.untrusted_qdb.read('/qubes-tools/gui'), mock.call.untrusted_qdb.read('/qubes-tools/gui'),
mock.call.untrusted_qdb.read('/qubes-tools/default-user'), mock.call.untrusted_qdb.read('/qubes-tools/default-user'),
mock.call.fire_event('features-request', untrusted_features={ mock.call.fire_event_async('features-request', untrusted_features={
'gui': '1', 'gui': '1',
'default-user': 'user', 'default-user': 'user',
'qrexec': '1'}), 'qrexec': '1'}),
('fire_event_async().__iter__', (), {}),
]) ])
self.assertEqual(self.app.mock_calls, [mock.call.save()]) self.assertEqual(self.app.mock_calls, [mock.call.save()])

View File

@ -334,12 +334,12 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
:param event: Event name (``'domain-tag-delete:' tag``) :param event: Event name (``'domain-tag-delete:' tag``)
:param tag: tag name :param tag: tag name
.. event:: feature-request (subject, event, *, untrusted_features) .. event:: features-request (subject, event, *, untrusted_features)
The domain is performing a feature request. The domain is performing a features request.
:param subject: Event emitter (the qube object) :param subject: Event emitter (the qube object)
:param event: Event name (``'feature-request'``) :param event: Event name (``'features-request'``)
:param untrusted_features: :py:class:`dict` containing the feature \ :param untrusted_features: :py:class:`dict` containing the feature \
request request
@ -351,6 +351,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
ranging from plainly ignoring the request to verbatim copy into ranging from plainly ignoring the request to verbatim copy into
:py:attr:`features` with only minimal sanitisation. :py:attr:`features` with only minimal sanitisation.
Handler for this event can be asynchronous (a coroutine).
.. event:: firewall-changed (subject, event) .. event:: firewall-changed (subject, event)
Firewall was changed. Firewall was changed.