5
5
# Copyright (C) 2010-2016 Joanna Rutkowska <[email protected] >
6
6
# Copyright (C) 2015-2016 Wojtek Porczyk <[email protected] >
7
7
# Copyright (C) 2016 Bahtiar `kalkin-` Gadimov <[email protected] >
8
- # Copyright (C) 2017 Marek Marczykowski-Górecki
8
+ # Copyright (C) 2017 Marek Marczykowski-Górecki
9
9
10
10
# Copyright (C) 2024 Piotr Bartman-Szwarc
11
-
11
+
12
12
#
13
13
# This library is free software; you can redistribute it and/or
14
14
# modify it under the terms of the GNU Lesser General Public
@@ -53,101 +53,82 @@ def qbool(value):
53
53
return qubes .property .bool (None , None , value )
54
54
55
55
56
- class Device :
56
+ class Port :
57
57
"""
58
- Basic class of a *bus* device with *ident* exposed by a *backend domain*.
58
+ Class of a *bus* device port with *ident* exposed by a *backend domain*.
59
59
60
60
Attributes:
61
61
backend_domain (QubesVM): The domain which exposes devices,
62
62
e.g.`sys-usb`.
63
- ident (str): A unique identifier for the device within
64
- the backend domain.
65
- devclass (str, optional): The class of the device (e.g., 'usb', 'pci').
63
+ ident (str): A unique identifier for the port within the backend domain.
64
+ devclass (str): The class of the port (e.g., 'usb', 'pci').
66
65
"""
67
66
ALLOWED_CHARS_KEY = set (
68
67
string .digits + string .ascii_letters
69
68
+ r"!#$%&()*+,-./:;<>?@[\]^_{|}~" )
70
69
ALLOWED_CHARS_PARAM = ALLOWED_CHARS_KEY .union (set (string .punctuation + ' ' ))
71
70
72
- def __init__ (self , backend_domain , ident , devclass = None ):
71
+ def __init__ (self , backend_domain , ident , devclass ):
73
72
self .__backend_domain = backend_domain
74
73
self .__ident = ident
75
- self .__bus = devclass
74
+ self .__devclass = devclass
76
75
77
76
def __hash__ (self ):
78
- return hash ((str ( self .backend_domain ) , self .ident ))
77
+ return hash ((self .backend_domain . name , self .ident , self . devclass ))
79
78
80
79
def __eq__ (self , other ):
81
- if isinstance (other , Device ):
80
+ if isinstance (other , Port ):
82
81
return (
83
82
self .backend_domain == other .backend_domain and
84
- self .ident == other .ident
83
+ self .ident == other .ident and
84
+ self .devclass == other .devclass
85
85
)
86
- raise TypeError (f"Comparing instances of 'Device ' and '{ type (other )} ' "
86
+ raise TypeError (f"Comparing instances of 'Port ' and '{ type (other )} ' "
87
87
"is not supported" )
88
88
89
89
def __lt__ (self , other ):
90
- if isinstance (other , Device ):
91
- return (self .backend_domain .name , self .ident ) < \
92
- (other .backend_domain .name , other .ident )
93
- raise TypeError (f"Comparing instances of 'Device ' and '{ type (other )} ' "
90
+ if isinstance (other , Port ):
91
+ return (self .backend_domain .name , self .devclass , self . ident ) < \
92
+ (other .backend_domain .name , other .devclass , other . ident )
93
+ raise TypeError (f"Comparing instances of 'Port ' and '{ type (other )} ' "
94
94
"is not supported" )
95
95
96
96
def __repr__ (self ):
97
- return "[%s]:%s" % ( self .backend_domain , self .ident )
97
+ return f"[ { self . backend_domain . name } ]: { self .devclass } : { self .ident } "
98
98
99
99
def __str__ (self ):
100
- return '{!s }:{!s}' . format ( self .backend_domain , self . ident )
100
+ return f" { self . backend_domain . name } :{ self .ident } "
101
101
102
102
@property
103
103
def ident (self ) -> str :
104
104
"""
105
- Immutable device identifier.
105
+ Immutable port identifier.
106
106
107
- Unique for given domain and device type .
107
+ Unique for given domain and devclass .
108
108
"""
109
109
return self .__ident
110
110
111
111
@property
112
112
def backend_domain (self ) -> QubesVM :
113
- """ Which domain provides this device . (immutable)"""
113
+ """ Which domain exposed this port . (immutable)"""
114
114
return self .__backend_domain
115
115
116
116
@property
117
117
def devclass (self ) -> str :
118
- """ Immutable* Device class such like: 'usb', 'pci' etc.
118
+ """ Immutable port class such like: 'usb', 'pci' etc.
119
119
120
- For unknown devices "peripheral" is returned.
121
-
122
- *see `@devclass.setter`
120
+ For unknown classes "peripheral" is returned.
123
121
"""
124
- if self .__bus :
125
- return self .__bus
122
+ if self .__devclass :
123
+ return self .__devclass
126
124
return "peripheral"
127
125
128
- @property
129
- def devclass_is_set (self ) -> bool :
130
- """
131
- Returns true if devclass is already initialised.
132
- """
133
- return bool (self .__bus )
134
-
135
- @devclass .setter
136
- def devclass (self , devclass : str ):
137
- """ Once a value is set, it should not be overridden.
138
-
139
- However, if it has not been set, i.e., the value is `None`,
140
- we can override it."""
141
- if self .__bus is not None :
142
- raise TypeError ("Attribute devclass is immutable" )
143
- self .__bus = devclass
144
-
145
126
@classmethod
146
127
def unpack_properties (
147
128
cls , untrusted_serialization : bytes
148
129
) -> Tuple [Dict , Dict ]:
149
130
"""
150
- Unpacks basic device properties from a serialized encoded string.
131
+ Unpacks basic port properties from a serialized encoded string.
151
132
152
133
Returns:
153
134
tuple: A tuple containing two dictionaries, properties and options,
@@ -215,17 +196,17 @@ def pack_property(cls, key: str, value: str):
215
196
216
197
@staticmethod
217
198
def check_device_properties (
218
- expected_device : 'Device ' , properties : Dict [str , Any ]):
199
+ expected_port : 'Port ' , properties : Dict [str , Any ]):
219
200
"""
220
- Validates properties against an expected device configuration.
201
+ Validates properties against an expected port configuration.
221
202
222
203
Modifies `properties`.
223
204
224
205
Raises:
225
206
UnexpectedDeviceProperty: If any property does not match
226
207
the expected values.
227
208
"""
228
- expected = expected_device
209
+ expected = expected_port
229
210
exp_vm_name = expected .backend_domain .name
230
211
if properties .get ('backend_domain' , exp_vm_name ) != exp_vm_name :
231
212
raise UnexpectedDeviceProperty (
@@ -239,13 +220,11 @@ def check_device_properties(
239
220
f"when expected id: { expected .ident } ." )
240
221
properties ['ident' ] = expected .ident
241
222
242
- if expected .devclass_is_set :
243
- if (properties .get ('devclass' , expected .devclass )
244
- != expected .devclass ):
245
- raise UnexpectedDeviceProperty (
246
- f"Got { properties ['devclass' ]} device "
247
- f"when expected { expected .devclass } ." )
248
- properties ['devclass' ] = expected .devclass
223
+ if properties .get ('devclass' , expected .devclass ) != expected .devclass :
224
+ raise UnexpectedDeviceProperty (
225
+ f"Got { properties ['devclass' ]} device "
226
+ f"when expected { expected .devclass } ." )
227
+ properties ['devclass' ] = expected .devclass
249
228
250
229
251
230
class DeviceCategory (Enum ):
@@ -428,27 +407,24 @@ def _load_classes(bus: str):
428
407
return result
429
408
430
409
431
- class DeviceInfo (Device ):
410
+ class DeviceInfo (Port ):
432
411
""" Holds all information about a device """
433
412
434
413
def __init__ (
435
414
self ,
436
- backend_domain : QubesVM ,
437
- ident : str ,
438
- * ,
439
- devclass : Optional [str ] = None ,
415
+ port : Port ,
440
416
vendor : Optional [str ] = None ,
441
417
product : Optional [str ] = None ,
442
418
manufacturer : Optional [str ] = None ,
443
419
name : Optional [str ] = None ,
444
420
serial : Optional [str ] = None ,
445
421
interfaces : Optional [List [DeviceInterface ]] = None ,
446
- parent : Optional [Device ] = None ,
422
+ parent : Optional [Port ] = None ,
447
423
attachment : Optional [QubesVM ] = None ,
448
424
self_identity : Optional [str ] = None ,
449
425
** kwargs
450
426
):
451
- super ().__init__ (backend_domain , ident , devclass )
427
+ super ().__init__ (port . backend_domain , port . ident , port . devclass )
452
428
453
429
self ._vendor = vendor
454
430
self ._product = product
@@ -462,6 +438,13 @@ def __init__(
462
438
463
439
self .data = kwargs
464
440
441
+ @property
442
+ def port (self ) -> Port :
443
+ """
444
+ Device port visible in Qubes.
445
+ """
446
+ return Port (self .backend_domain , self .ident , self .devclass )
447
+
465
448
@property
466
449
def vendor (self ) -> str :
467
450
"""
@@ -570,7 +553,7 @@ def interfaces(self) -> List[DeviceInterface]:
570
553
return self ._interfaces
571
554
572
555
@property
573
- def parent_device (self ) -> Optional [Device ]:
556
+ def parent_device (self ) -> Optional [Port ]:
574
557
"""
575
558
The parent device, if any.
576
559
@@ -663,28 +646,27 @@ def deserialize(
663
646
def _deserialize (
664
647
cls ,
665
648
untrusted_serialization : bytes ,
666
- expected_device : Device
649
+ expected_port : Port
667
650
) -> 'DeviceInfo' :
668
651
"""
669
652
Actually deserializes the object.
670
653
"""
671
654
properties , options = cls .unpack_properties (untrusted_serialization )
672
655
properties .update (options )
673
656
674
- cls .check_device_properties (expected_device , properties )
657
+ cls .check_device_properties (expected_port , properties )
675
658
676
659
if 'attachment' not in properties or not properties ['attachment' ]:
677
660
properties ['attachment' ] = None
678
661
else :
679
- app = expected_device .backend_domain .app
662
+ app = expected_port .backend_domain .app
680
663
properties ['attachment' ] = app .domains .get_blind (
681
664
properties ['attachment' ])
682
665
683
- if (expected_device .devclass_is_set
684
- and properties ['devclass' ] != expected_device .devclass ):
666
+ if properties ['devclass' ] != expected_port .devclass :
685
667
raise UnexpectedDeviceProperty (
686
668
f"Got { properties ['devclass' ]} device "
687
- f"when expected { expected_device .devclass } ." )
669
+ f"when expected { expected_port .devclass } ." )
688
670
689
671
if 'interfaces' in properties :
690
672
interfaces = properties ['interfaces' ]
@@ -694,15 +676,23 @@ def _deserialize(
694
676
properties ['interfaces' ] = interfaces
695
677
696
678
if 'parent_ident' in properties :
697
- properties ['parent' ] = Device (
698
- backend_domain = expected_device .backend_domain ,
679
+ properties ['parent' ] = Port (
680
+ backend_domain = expected_port .backend_domain ,
699
681
ident = properties ['parent_ident' ],
700
682
devclass = properties ['parent_devclass' ],
701
683
)
702
684
del properties ['parent_ident' ]
703
685
del properties ['parent_devclass' ]
704
686
705
- return cls (** properties )
687
+ port = Port (
688
+ properties ['backend_domain' ],
689
+ properties ['ident' ],
690
+ properties ['devclass' ])
691
+ del properties ['backend_domain' ]
692
+ del properties ['ident' ]
693
+ del properties ['devclass' ]
694
+
695
+ return cls (port , ** properties )
706
696
707
697
@property
708
698
def self_identity (self ) -> str :
@@ -770,10 +760,11 @@ class UnknownDevice(DeviceInfo):
770
760
"""Unknown device - for example, exposed by domain not running currently"""
771
761
772
762
def __init__ (self , backend_domain , ident , * , devclass , ** kwargs ):
773
- super ().__init__ (backend_domain , ident , devclass = devclass , ** kwargs )
763
+ port = Port (backend_domain , ident , devclass )
764
+ super ().__init__ (port , ** kwargs )
774
765
775
766
776
- class DeviceAssignment (Device ):
767
+ class DeviceAssignment (Port ):
777
768
""" Maps a device to a frontend_domain.
778
769
779
770
There are 3 flags `attached`, `automatically_attached` and `required`.
@@ -821,7 +812,7 @@ def clone(self, **kwargs):
821
812
return self .__class__ (** attr )
822
813
823
814
@classmethod
824
- def from_device (cls , device : Device , ** kwargs ) -> 'DeviceAssignment' :
815
+ def from_device (cls , device : Port , ** kwargs ) -> 'DeviceAssignment' :
825
816
"""
826
817
Get assignment of the device.
827
818
"""
@@ -923,13 +914,13 @@ def serialize(self) -> bytes:
923
914
def deserialize (
924
915
cls ,
925
916
serialization : bytes ,
926
- expected_device : Device ,
917
+ expected_port : Port ,
927
918
) -> 'DeviceAssignment' :
928
919
"""
929
920
Recovers a serialized object, see: :py:meth:`serialize`.
930
921
"""
931
922
try :
932
- result = cls ._deserialize (serialization , expected_device )
923
+ result = cls ._deserialize (serialization , expected_port )
933
924
except Exception as exc :
934
925
raise ProtocolError () from exc
935
926
return result
@@ -938,15 +929,15 @@ def deserialize(
938
929
def _deserialize (
939
930
cls ,
940
931
untrusted_serialization : bytes ,
941
- expected_device : Device ,
932
+ expected_port : Port ,
942
933
) -> 'DeviceAssignment' :
943
934
"""
944
935
Actually deserializes the object.
945
936
"""
946
937
properties , options = cls .unpack_properties (untrusted_serialization )
947
938
properties ['options' ] = options
948
939
949
- cls .check_device_properties (expected_device , properties )
940
+ cls .check_device_properties (expected_port , properties )
950
941
951
942
properties ['attach_automatically' ] = qbool (
952
943
properties .get ('attach_automatically' , 'no' ))
0 commit comments