Skip to content

Commit db354af

Browse files
committed
Late GUID for preloaded disposables
- Autostarted applications don't show when preloading; and - Preloaded qubes paused before the session will have a working GUI when the session starts. For: QubesOS/qubes-issues#1512 For: QubesOS/qubes-issues#9940 For: QubesOS/qubes-issues#9907 Requires: QubesOS/qubes-core-admin-client#359
1 parent e38dfa0 commit db354af

File tree

3 files changed

+26
-43
lines changed

3 files changed

+26
-43
lines changed

qubes/tests/api_internal.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def create_mockvm(self, features=None):
6464
features = {}
6565
vm = mock.Mock()
6666
vm.features.check_with_template.side_effect = features.get
67+
vm.features.get.side_effect = features.get
6768
vm.run_service.return_value.wait = mock_coro(
6869
vm.run_service.return_value.wait
6970
)

qubes/vm/dispvm.py

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ class DispVM(qubes.vm.qubesvm.QubesVM):
7777
7878
- Full start: Preloaded disposable must only be interrupted
7979
(paused/suspended) or used after all basic services in it have been
80-
started. Autostarted applications allows user interaction before the it
81-
should, that is a bug.
80+
started. Failure to complete this step must remove the qube from the
81+
preload list.
8282
8383
- **Prevents accidental tampering**:
8484
@@ -92,12 +92,11 @@ class DispVM(qubes.vm.qubesvm.QubesVM):
9292
GUI applications, that is a bug because features cannot be set before
9393
that event.
9494
95-
- Preloaded qubes must be marked as used when prior to being
96-
unpaused/resumed, even if it was not requested. The goal of
97-
pause/suspend in case of preloaded disposables is mostly detecting
98-
whether a qube was used or not, and not managing resource consumption;
99-
thus, even with abundant system resources, they should not be
100-
unpaused/resumed without being requested.
95+
- Preloaded qubes must be marked as used after being unpaused/resumed,
96+
even if it was not requested. The goal of pause/suspend in case of
97+
preloaded disposables is mostly detecting whether a qube was used or
98+
not, not managing resource consumption; thus, even with abundant system
99+
resources, they should not be unpaused/resumed without being requested.
101100
102101
**Stages**:
103102
@@ -114,14 +113,6 @@ class DispVM(qubes.vm.qubesvm.QubesVM):
114113
applicable). Only in this phase, GUI applications treat the qube as any
115114
other unnamed disposable and the qube object is returned to the caller if
116115
requested.
117-
118-
**Outstanding bugs**:
119-
120-
- GUI applications set to autostart can appear on the screen and be
121-
interactive for a brief moment before the qube is allowed to be used
122-
followed by a sudden freeze.
123-
- Can't interrupt qubes before the GUI session has started if the qube's
124-
usage will require a GUI (GUI daemon cannot handle an interrupted qube).
125116
"""
126117

127118
template = qubes.VMProperty(
@@ -328,37 +319,33 @@ async def on_domain_started_dispvm(
328319
"""
329320
if not self.is_preload:
330321
return
331-
# TODO: pause is late for autostarted GUI applications
332-
# https://github.com/QubesOS/qubes-issues/issues/9907
333322
timeout = self.qrexec_timeout
334-
gui = bool(self.guivm and self.features.get("gui", True))
335-
service = "qubes.WaitForSession"
336-
if not gui:
337-
service = "qubes.WaitForRunningSystem"
323+
# https://github.com/QubesOS/qubes-issues/issues/9964
324+
rpc = "qubes.WaitForRunningSystem"
325+
path = "/run/qubes-rpc:/usr/local/etc/qubes-rpc:/etc/qubes-rpc"
326+
service = '$(PATH="' + path + '" command -v ' + rpc + ")"
338327
try:
339328
self.log.info(
340-
"Waiting '%s' with timeout of '%d' seconds", service, timeout
329+
"Waiting for '%s' with timeout of '%d' seconds",
330+
service,
331+
timeout,
341332
)
342333
await asyncio.wait_for(
343-
self.run_service_for_stdio(
334+
self.run(
344335
service,
345-
user=self.default_user,
346336
stdout=subprocess.DEVNULL,
347337
stderr=subprocess.DEVNULL,
348338
),
349339
timeout=timeout,
350340
)
351341
except asyncio.TimeoutError:
352-
# TODO: if pause occurs before the GUI session starts (on boot
353-
# before login manager), results in an unusable GUI for the qube:
354-
# https://github.com/QubesOS/qubes-issues/issues/9940
355342
raise qubes.exc.QubesException(
356-
"Timed out Qrexec call to '%s' after '%d' seconds during "
357-
"preload startup" % (service, timeout)
343+
"Timed out call to '%s' after '%d' seconds during preload "
344+
"startup" % (service, timeout)
358345
)
359346
except (subprocess.CalledProcessError, qubes.exc.QubesException):
360347
raise qubes.exc.QubesException(
361-
"Error on Qrexec call to '%s' during preload startup" % service
348+
"Error on call to '%s' during preload startup" % service
362349
)
363350

364351
if self.is_preload:
@@ -376,11 +363,11 @@ def on_domain_paused(
376363
if self.is_preload:
377364
self.log.info("Paused preloaded qube")
378365

379-
@qubes.events.handler("domain-pre-unpaused")
380-
def on_domain_pre_unpaused(
366+
@qubes.events.handler("domain-unpaused")
367+
def on_domain_unpaused(
381368
self, event, **kwargs
382369
): # pylint: disable=unused-argument
383-
"""Mark preloaded domains as used before being unpaused."""
370+
"""Mark preloaded disposables as used."""
384371
# Qube start triggers unpause via 'libvirt_domain.resume()'.
385372
if self.is_preload and self.is_fully_usable():
386373
self.log.info("Unpaused preloaded qube will be marked as used")
@@ -490,7 +477,8 @@ async def from_appvm(cls, appvm, preload=False, **kwargs):
490477
# requesting the qube.
491478
appvm.remove_preload_from_list([dispvm.name])
492479
dispvm.features["preload-dispvm-request"] = True
493-
tries, sleep = 1200, 0.1
480+
sleep = 0.1
481+
tries = int(dispvm.qrexec_timeout * 1.2 / sleep)
494482
for _ in range(tries):
495483
if dispvm.features.get(
496484
"preload-dispvm-skip-interrupt", None

qubes/vm/mix/dvmtemplate.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,10 @@ def on_feature_pre_set_preload_dispvm_max(
125125
if not self.features.check_with_template("qrexec", None):
126126
raise qubes.exc.QubesValueError("Qube does not support qrexec")
127127

128-
gui = bool(self.guivm and self.features.get("gui", True))
129-
service = "supported-rpc."
130-
if gui:
131-
service += "qubes.WaitForSession"
132-
else:
133-
service += "qubes.WaitForRunningSystem"
128+
service = "supported-rpc.qubes.WaitForRunningSystem"
134129
if not self.features.check_with_template(service, False):
135130
raise qubes.exc.QubesValueError(
136-
"Qube GUI is '%s' and does not support the RPC '%s'"
137-
% (gui, service)
131+
"Qube does not support the RPC '%s'" % service
138132
)
139133

140134
value = value or "0"

0 commit comments

Comments
 (0)