@@ -521,6 +521,37 @@ def __init__(self):
521521 "/etc/qubes-rpc/qubes.USB"
522522 )
523523 self .devices_cache = collections .defaultdict (dict )
524+ self .autoattach_locks = collections .defaultdict (asyncio .Lock )
525+
526+ async def _auto_attach_devices (self , vm ):
527+ async with self .autoattach_locks [vm .uuid ]:
528+ to_attach = {}
529+ assignments = get_assigned_devices (vm .devices ["usb" ])
530+ # the most specific assignments first
531+ for assignment in reversed (sorted (assignments )):
532+ for device in assignment .devices :
533+ if isinstance (device , qubes .device_protocol .UnknownDevice ):
534+ continue
535+ if device .attachment :
536+ continue
537+ if not assignment .matches (device ):
538+ print (
539+ "Unrecognized identity, skipping attachment of device "
540+ f"from the port { assignment } " ,
541+ file = sys .stderr ,
542+ )
543+ continue
544+ # chose first assignment (the most specific) and ignore rest
545+ if device not in to_attach :
546+ # make it unique
547+ to_attach [device ] = assignment .clone (device = device )
548+ in_progress = set ()
549+ for assignment in to_attach .values ():
550+ in_progress .add (
551+ asyncio .ensure_future (self .attach_and_notify (vm , assignment ))
552+ )
553+ if in_progress :
554+ await asyncio .wait (in_progress )
524555
525556 @qubes .ext .handler ("domain-init" , "domain-load" )
526557 def on_domain_init_load (self , vm , event ):
@@ -744,41 +775,22 @@ async def on_device_assign_usb(self, vm, event, device, options):
744775 @qubes .ext .handler ("domain-start" )
745776 async def on_domain_start (self , vm , _event , ** _kwargs ):
746777 # pylint: disable=unused-argument
747- to_attach = {}
748- assignments = get_assigned_devices (vm .devices ["usb" ])
749- # the most specific assignments first
750- for assignment in reversed (sorted (assignments )):
751- for device in assignment .devices :
752- if isinstance (device , qubes .device_protocol .UnknownDevice ):
753- continue
754- if device .attachment :
755- continue
756- if not assignment .matches (device ):
757- print (
758- "Unrecognized identity, skipping attachment of device "
759- f"from the port { assignment } " ,
760- file = sys .stderr ,
761- )
762- continue
763- # chose first assignment (the most specific) and ignore rest
764- if device not in to_attach :
765- # make it unique
766- to_attach [device ] = assignment .clone (device = device )
767- in_progress = set ()
768- for assignment in to_attach .values ():
769- in_progress .add (
770- asyncio .ensure_future (self .attach_and_notify (vm , assignment ))
771- )
772- if in_progress :
773- await asyncio .wait (in_progress )
778+ await self ._auto_attach_devices (vm )
774779
775780 @qubes .ext .handler ("domain-shutdown" )
776781 async def on_domain_shutdown (self , vm , _event , ** _kwargs ):
777782 # pylint: disable=unused-argument
778783 vm .fire_event ("device-list-change:usb" )
779784 utils .device_list_change (self , {}, vm , None , USBDevice )
785+ del self .autoattach_locks [vm .uuid ]
786+
787+ @qubes .ext .handler ("domain-resumed" )
788+ async def on_domain_resumed (self , vm , _event , ** _kwargs ):
789+ # pylint: disable=unused-argument
790+ await self ._auto_attach_devices (vm )
780791
781792 @qubes .ext .handler ("qubes-close" , system = True )
782793 def on_qubes_close (self , app , event ):
783794 # pylint: disable=unused-argument
784795 self .devices_cache .clear ()
796+ self .autoattach_locks .clear ()
0 commit comments