From 5f0d5640515e2224e2e9aa9258c3a9fd9e9556ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 25 Sep 2015 20:37:44 +0200 Subject: [PATCH 1/2] utils/QubesWatch: use timers to retry QubesDB watch registration QubesWatch._register_watches is called from libvirt event callback, asynchronously to qvm-start. This means that `qubesdb-daemon` may not be running or populated yet. If first QubesDB connection (or watch registration) fails, schedule next try using timers in libvirt event API (as it is base of QubesWatch mainloop), instead of some sleep loop. This way other events will be processed in the meantime. QubesOS/qubes-issues#1110 --- core/qubesutils.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/core/qubesutils.py b/core/qubesutils.py index 041a66c8..120d4a98 100644 --- a/core/qubesutils.py +++ b/core/qubesutils.py @@ -790,29 +790,41 @@ class QubesWatch(object): name = libvirt_domain.name() if name in self._qdb: return + if not libvirt_domain.isActive(): + return # open separate connection to Qubes DB: # 1. to not confuse pull() with responses to real commands sent from # other threads (like read, write etc) with watch events # 2. to not think whether QubesDB is thread-safe (it isn't) - while libvirt_domain.isActive() and name not in self._qdb: - try: - self._qdb[name] = QubesDB(name) - except Error as e: - if e.args[0] != 2: - raise - time.sleep(0.5) - if name not in self._qdb: - # domain no longer active + try: + self._qdb[name] = QubesDB(name) + except Error as e: + if e.args[0] != 2: + raise + libvirt.virEventAddTimeout(500, self._retry_register_watches, + libvirt_domain) return else: name = "dom0" self._qdb[name] = QubesDB(name) - self._qdb[name].watch('/qubes-block-devices') + try: + self._qdb[name].watch('/qubes-block-devices') + except Error as e: + if e.args[0] == 102: # Connection reset by peer + # QubesDB daemon not running - most likely we've connected to + # stale daemon which just exited; retry later + libvirt.virEventAddTimeout(500, self._retry_register_watches, + libvirt_domain) + return self._qdb_events[name] = libvirt.virEventAddHandle( self._qdb[name].watch_fd(), libvirt.VIR_EVENT_HANDLE_READABLE, self._qdb_handler, name) + def _retry_register_watches(self, timer, libvirt_domain): + libvirt.virRemoveTimeout(timer) + self._register_watches(libvirt_domain) + def _unregister_watches(self, libvirt_domain): name = libvirt_domain.name() if name in self._qdb_events: From ef6095662e9ddeb101c0065b86bac3602f9b8fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Fri, 25 Sep 2015 22:02:53 +0200 Subject: [PATCH 2/2] utils/QubesWatch: register domain watches on VIR_DOMAIN_EVENT_RESUMED QubesVM.start() first creates domain as paused, completes its setup (including starting qubesdb-daemon and creating appropriate entries), then resumes the domain. So wait for that resume to be sure that `qubesdb-daemon` is already running and populated. QubesOS/qubes-issues#1110 --- core/qubesutils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/qubesutils.py b/core/qubesutils.py index 120d4a98..feaf1407 100644 --- a/core/qubesutils.py +++ b/core/qubesutils.py @@ -835,7 +835,9 @@ class QubesWatch(object): del(self._qdb[name]) def _domain_list_changed(self, conn, domain, event, reason, param): - if event == libvirt.VIR_DOMAIN_EVENT_STARTED: + # use VIR_DOMAIN_EVENT_RESUMED instead of VIR_DOMAIN_EVENT_STARTED to + # make sure that qubesdb daemon is already running + if event == libvirt.VIR_DOMAIN_EVENT_RESUMED: self._register_watches(domain) elif event == libvirt.VIR_DOMAIN_EVENT_STOPPED: self._unregister_watches(domain)