Browse Source

mgmt: rework mgmt.Events to use per-instance handlers

QubesOS/qubes-issues#2622
Marek Marczykowski-Górecki 7 years ago
parent
commit
3bc8facc0b
1 changed files with 44 additions and 14 deletions
  1. 44 14
      qubes/mgmt.py

+ 44 - 14
qubes/mgmt.py

@@ -179,6 +179,34 @@ class AbstractQubesMgmt(object):
             self.fire_event_for_permission(**kwargs))
 
 
+class QubesMgmtEventsDispatcher(object):
+    def __init__(self, filters, send_event):
+        self.filters = filters
+        self.send_event = send_event
+
+    def vm_handler(self, subject, event, **kwargs):
+        if event.startswith('mgmt-permission:'):
+            return
+        if not list(apply_filters([(subject, event, kwargs)],
+                self.filters)):
+            return
+        self.send_event(subject, event, **kwargs)
+
+    def app_handler(self, subject, event, **kwargs):
+        if not list(apply_filters([(subject, event, kwargs)],
+                self.filters)):
+            return
+        self.send_event(subject, event, **kwargs)
+
+    def on_domain_add(self, subject, event, vm):
+        # pylint: disable=unused-argument
+        vm.add_handler('*', self.vm_handler)
+
+    def on_domain_delete(self, subject, event, vm):
+        # pylint: disable=unused-argument
+        vm.remove_handler('*', self.vm_handler)
+
+
 class QubesMgmt(AbstractQubesMgmt):
     '''Implementation of Qubes Management API calls
 
@@ -590,19 +618,15 @@ class QubesMgmt(AbstractQubesMgmt):
         # cache event filters, to not call an event each time an event arrives
         event_filters = self.fire_event_for_permission()
 
-        def handler(subject, event, **kwargs):
-            if self.dest.name != 'dom0' and subject != self.dest:
-                return
-            if event.startswith('mgmt-permission:'):
-                return
-            if not list(apply_filters([(subject, event, kwargs)],
-                    event_filters)):
-                return
-            self.send_event(subject, event, **kwargs)
-
+        dispatcher = QubesMgmtEventsDispatcher(event_filters, self.send_event)
         if self.dest.name == 'dom0':
-            type(self.app).add_handler('*', handler)
-        qubes.vm.BaseVM.add_handler('*', handler)
+            self.app.add_handler('*', dispatcher.app_handler)
+            self.app.add_handler('domain-add', dispatcher.on_domain_add)
+            self.app.add_handler('domain-delete', dispatcher.on_domain_delete)
+            for vm in self.app.domains:
+                vm.add_handler('*', dispatcher.vm_handler)
+        else:
+            self.dest.add_handler('*', dispatcher.vm_handler)
 
         # send artificial event as a confirmation that connection is established
         self.send_event(self.app, 'connection-established')
@@ -614,8 +638,14 @@ class QubesMgmt(AbstractQubesMgmt):
             pass
 
         if self.dest.name == 'dom0':
-            type(self.app).remove_handler('*', handler)
-        qubes.vm.BaseVM.remove_handler('*', handler)
+            self.app.remove_handler('*', dispatcher.app_handler)
+            self.app.remove_handler('domain-add', dispatcher.on_domain_add)
+            self.app.remove_handler('domain-delete',
+                dispatcher.on_domain_delete)
+            for vm in self.app.domains:
+                vm.remove_handler('*', dispatcher.vm_handler)
+        else:
+            self.dest.remove_handler('*', dispatcher.vm_handler)
 
     @api('mgmt.vm.feature.List', no_payload=True)
     @asyncio.coroutine