Skip to content

Commit 1cf336d

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
1 parent 80438d7 commit 1cf336d

File tree

3 files changed

+63
-14
lines changed

3 files changed

+63
-14
lines changed

doc/manpages/qvm-features.rst

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -429,10 +429,10 @@ on it.
429429
preload-dispvm-max
430430
^^^^^^^^^^^^^^^^^^
431431

432-
Number of disposables to preload. Upon setting, the number of running preloaded
433-
disposables will be adjusted to match the maximum configured, if there is not
434-
enough of them and there is enough available memory on the system, new ones will
435-
be created, if there are more than enough, the excess will be removed.
432+
Number of disposables to preload. Upon setting, the quantity of running
433+
preloaded disposables will be adjusted to match the maximum configured, if there
434+
is not enough of them and there is enough available memory on the system, new
435+
ones will be created, if there are more than enough, the excess will be removed.
436436

437437
|
438438
| **Valid on**: disposable template
@@ -455,12 +455,6 @@ disposable in the list. As soon as the preloaded disposable is requested to be
455455
used, it is removed from the `preload-dispvm` list, GUI applications entries
456456
become visible, followed by a new disposable being preloaded.
457457

458-
.. warning::
459-
460-
Applications configured to autostart by the disposable template or the
461-
template itself will be interactive before the preloaded disposable can be
462-
paused.
463-
464458
|
465459
| **Managed by**: system
466460
| **Valid on**: disposable template

qubesadmin/tests/tools/qvm_start_daemon.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ def test_020_start_gui_for_vm(self, proc_mock):
231231
self.app.expected_calls[
232232
('test-vm', 'admin.vm.property.Get', 'virt_mode', None)] = \
233233
b'0\x00default=False type=str pv'
234+
self.app.expected_calls[
235+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
236+
b"2QubesNoSuchPropertyErrorInvalid property 'is_preload' of test-vm"
234237
self.app.expected_calls[
235238
('test-vm', 'admin.vm.feature.CheckWithTemplate',
236239
'no-monitor-layout', None)] = \
@@ -272,6 +275,9 @@ def test_021_start_gui_for_vm_hvm(self, proc_mock):
272275
self.app.expected_calls[
273276
('test-vm', 'admin.vm.property.Get', 'debug', None)] = \
274277
b'0\x00default=False type=bool False'
278+
self.app.expected_calls[
279+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
280+
b"2QubesNoSuchPropertyErrorInvalid property 'is_preload' of test-vm"
275281
self.app.expected_calls[
276282
('test-vm', 'admin.vm.feature.CheckWithTemplate',
277283
'no-monitor-layout', None)] = \
@@ -312,6 +318,9 @@ def test_022_start_gui_for_vm_hvm_stubdom(self):
312318
self.app.expected_calls[
313319
('test-vm', 'admin.vm.property.Get', 'debug', None)] = \
314320
b'0\x00default=False type=bool False'
321+
self.app.expected_calls[
322+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
323+
b"2QubesNoSuchPropertyErrorInvalid property 'is_preload' of test-vm"
315324
self.app.expected_calls[
316325
('test-vm', 'admin.vm.feature.CheckWithTemplate',
317326
'no-monitor-layout', None)] = \
@@ -360,6 +369,9 @@ def test_030_start_gui_for_stubdomain(self):
360369
self.app.expected_calls[
361370
('test-vm', 'admin.vm.property.Get', 'stubdom_xid', None)] = \
362371
b'0\x00default=False type=int 3001'
372+
self.app.expected_calls[
373+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
374+
b"2QubesNoSuchPropertyErrorInvalid property 'is_preload' of test-vm"
363375
self.app.expected_calls[
364376
('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \
365377
b'2\x00QubesFeatureNotFoundError\x00\x00Feature not set\x00'
@@ -391,6 +403,9 @@ def test_031_start_gui_for_stubdomain_forced(self):
391403
self.app.expected_calls[
392404
('test-vm', 'admin.vm.property.Get', 'stubdom_xid', None)] = \
393405
b'0\x00default=False type=int 3001'
406+
self.app.expected_calls[
407+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
408+
b"2QubesNoSuchPropertyErrorInvalid property 'is_preload' of test-vm"
394409
# self.app.expected_calls[
395410
# ('test-vm', 'admin.vm.feature.CheckWithTemplate', 'gui', None)] = \
396411
# b'0\x00'
@@ -442,6 +457,9 @@ def test_040_start_gui(self):
442457
self.app.expected_calls[
443458
('test-vm', 'admin.vm.property.Get', 'guivm', None)] = \
444459
b'0\x00default=False type=vm gui-vm'
460+
self.app.expected_calls[
461+
('test-vm', 'admin.vm.property.Get', 'is_preload', None)] = \
462+
b"2QubesNoSuchPropertyErrorInvalid property 'is_preload' of test-vm"
445463

446464
# pylint: disable=protected-access
447465
self.app._local_name = 'gui-vm'

qubesadmin/tools/qvm_start_daemon.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,8 @@ async def start_gui_for_vm(self, vm, monitor_layout=None):
722722
:param monitor_layout: monitor layout to send; if None, fetch it from
723723
local X server.
724724
"""
725+
if getattr(vm, "is_preload", False) is True:
726+
return
725727
guid_cmd = self.common_guid_args(vm)
726728
guid_cmd.extend(["-d", str(vm.xid)])
727729

@@ -748,6 +750,8 @@ async def start_gui_for_stubdomain(self, vm, force=False):
748750
749751
This function is a coroutine.
750752
"""
753+
if getattr(vm, "is_preload", False) is True:
754+
return
751755
want_stubdom = force
752756
if not want_stubdom and vm.features.check_with_template(
753757
"gui-emulated", False
@@ -779,6 +783,8 @@ async def start_audio_for_vm(self, vm):
779783
780784
:param vm: VM for which start AUDIO daemon
781785
"""
786+
if getattr(vm, "is_preload", False) is True:
787+
return
782788
pacat_cmd = [PACAT_DAEMON_PATH, "-l", self.pacat_domid(vm), vm.name]
783789
vm.log.info("Starting AUDIO")
784790

@@ -794,6 +800,8 @@ async def start_gui(self, vm, force_stubdom=False, monitor_layout=None):
794800
one for target AppVM is running.
795801
:param monitor_layout: monitor layout configuration
796802
"""
803+
if getattr(vm, "is_preload", False) is True:
804+
return
797805
guivm = getattr(vm, "guivm", None)
798806
if guivm != vm.app.local_name:
799807
vm.log.info("GUI connected to {}. Skipping.".format(guivm))
@@ -815,6 +823,8 @@ async def start_audio(self, vm):
815823
816824
:param vm: VM for which AUDIO daemon should be started
817825
"""
826+
if getattr(vm, "is_preload", False) is True:
827+
return
818828
audiovm = getattr(vm, "audiovm", None)
819829
if audiovm != vm.app.local_name:
820830
vm.log.info("AUDIO connected to {}. Skipping.".format(audiovm))
@@ -832,7 +842,7 @@ async def start_audio(self, vm):
832842
def on_domain_spawn(self, vm, _event, **kwargs):
833843
"""Handler of 'domain-spawn' event, starts GUI daemon for stubdomain"""
834844

835-
if not self.is_watched(vm):
845+
if not self.is_watched(vm) or getattr(vm, "is_preload", False) is True:
836846
return
837847

838848
try:
@@ -857,7 +867,7 @@ def on_domain_start(self, vm, _event, **kwargs):
857867
"""Handler of 'domain-start' event, starts GUI/AUDIO daemon for
858868
actual VM"""
859869

860-
if not self.is_watched(vm):
870+
if not self.is_watched(vm) or getattr(vm, "is_preload", False) is True:
861871
return
862872

863873
self.xid_cache[vm.name] = vm.xid, vm.stubdom_xid
@@ -887,15 +897,17 @@ def on_domain_start(self, vm, _event, **kwargs):
887897
def on_connection_established(self, _subject, _event, **_kwargs):
888898
"""Handler of 'connection-established' event, used to launch GUI/AUDIO
889899
daemon for domains started before this tool."""
890-
891900
monitor_layout = get_monitor_layout()
892901
self.app.domains.clear_cache()
893902
for vm in self.app.domains:
894903
try:
895904
if vm.klass == "AdminVM":
896905
continue
897906

898-
if not self.is_watched(vm):
907+
if (
908+
not self.is_watched(vm)
909+
or getattr(vm, "is_preload", False) is True
910+
):
899911
continue
900912

901913
power_state = vm.get_power_state()
@@ -937,6 +949,28 @@ def on_domain_stopped(self, vm, _event, **_kwargs):
937949
self.cleanup_guid(stubdom_xid)
938950
self.cleanup_pacat_process(stubdom_xid)
939951

952+
def on_property_preload_set(self, vm, _event, **_kwargs):
953+
"""Handler of 'property-reset:is_preload' event, used to launch
954+
GUI/AUDIO daemon after preload is marked as used."""
955+
if (
956+
not vm.klass == "DispVM"
957+
or not self.is_watched(vm)
958+
or getattr(vm, "is_preload", False) is True
959+
or not vm.is_running()
960+
):
961+
return
962+
monitor_layout = get_monitor_layout()
963+
try:
964+
if "guivm" in self.enabled_services:
965+
asyncio.ensure_future(
966+
self.start_gui(vm, monitor_layout=monitor_layout)
967+
)
968+
if "audiovm" in self.enabled_services:
969+
asyncio.ensure_future(self.start_audio(vm))
970+
self.xid_cache[vm.name] = vm.xid, vm.stubdom_xid
971+
except qubesadmin.exc.QubesDaemonCommunicationError as e:
972+
vm.log.warning("Failed to handle %s: %s", vm.name, str(e))
973+
940974
def on_property_audiovm_set(self, vm, event, **kwargs):
941975
"""Handler for catching event related to dynamic AudioVM set/unset"""
942976
if vm.name not in self.xid_cache:
@@ -1013,6 +1047,9 @@ def register_events(self, events):
10131047
"connection-established", self.on_connection_established
10141048
)
10151049
events.add_handler("domain-stopped", self.on_domain_stopped)
1050+
events.add_handler(
1051+
"property-reset:is_preload", self.on_property_preload_set
1052+
)
10161053

10171054
for event in [
10181055
"property-set:audiovm",

0 commit comments

Comments
 (0)