872
class DBusObjectWithProperties(dbus.service.Object):
873
"""A D-Bus object with properties.
874
class DBusObjectWithAnnotations(dbus.service.Object):
875
"""A D-Bus object with annotations.
875
Classes inheriting from this can use the dbus_service_property
876
decorator to expose methods as D-Bus properties. It exposes the
877
standard Get(), Set(), and GetAll() methods on the D-Bus.
877
Classes inheriting from this can use the dbus_annotations
878
decorator to add annotations to methods or signals.
896
897
for name, athing in
897
898
inspect.getmembers(cls, self._is_dbus_thing(thing)))
900
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
902
path_keyword = 'object_path',
903
connection_keyword = 'connection')
904
def Introspect(self, object_path, connection):
905
"""Overloading of standard D-Bus method.
907
Inserts annotation tags on methods and signals.
909
xmlstring = dbus.service.Object.Introspect(self, object_path,
912
document = xml.dom.minidom.parseString(xmlstring)
914
for if_tag in document.getElementsByTagName("interface"):
915
# Add annotation tags
916
for typ in ("method", "signal"):
917
for tag in if_tag.getElementsByTagName(typ):
919
for name, prop in (self.
920
_get_all_dbus_things(typ)):
921
if (name == tag.getAttribute("name")
922
and prop._dbus_interface
923
== if_tag.getAttribute("name")):
924
annots.update(getattr(
925
prop, "_dbus_annotations", {}))
926
for name, value in annots.items():
927
ann_tag = document.createElement(
929
ann_tag.setAttribute("name", name)
930
ann_tag.setAttribute("value", value)
931
tag.appendChild(ann_tag)
932
# Add interface annotation tags
933
for annotation, value in dict(
934
itertools.chain.from_iterable(
935
annotations().items()
936
for name, annotations
937
in self._get_all_dbus_things("interface")
938
if name == if_tag.getAttribute("name")
940
ann_tag = document.createElement("annotation")
941
ann_tag.setAttribute("name", annotation)
942
ann_tag.setAttribute("value", value)
943
if_tag.appendChild(ann_tag)
944
# Fix argument name for the Introspect method itself
945
if (if_tag.getAttribute("name")
946
== dbus.INTROSPECTABLE_IFACE):
947
for cn in if_tag.getElementsByTagName("method"):
948
if cn.getAttribute("name") == "Introspect":
949
for arg in cn.getElementsByTagName("arg"):
950
if (arg.getAttribute("direction")
952
arg.setAttribute("name",
954
xmlstring = document.toxml("utf-8")
956
except (AttributeError, xml.dom.DOMException,
957
xml.parsers.expat.ExpatError) as error:
958
logger.error("Failed to override Introspection method",
963
class DBusObjectWithProperties(DBusObjectWithAnnotations):
964
"""A D-Bus object with properties.
966
Classes inheriting from this can use the dbus_service_property
967
decorator to expose methods as D-Bus properties. It exposes the
968
standard Get(), Set(), and GetAll() methods on the D-Bus.
899
971
def _get_dbus_property(self, interface_name, property_name):
900
972
"""Returns a bound method if one exists which is a D-Bus
901
973
property with the specified name and interface.
911
983
raise DBusPropertyNotFound("{}:{}.{}".format(
912
984
self.dbus_object_path, interface_name, property_name))
987
def _get_all_interface_names(cls):
988
"""Get a sequence of all interfaces supported by an object"""
989
return (name for name in set(getattr(getattr(x, attr),
990
"_dbus_interface", None)
991
for x in (inspect.getmro(cls))
914
995
@dbus.service.method(dbus.PROPERTIES_IFACE,
915
996
in_signature="ss",
916
997
out_signature="v")
1006
1088
if prop._dbus_interface
1007
1089
== if_tag.getAttribute("name")):
1008
1090
if_tag.appendChild(tag)
1009
# Add annotation tags
1010
for typ in ("method", "signal", "property"):
1011
for tag in if_tag.getElementsByTagName(typ):
1013
for name, prop in (self.
1014
_get_all_dbus_things(typ)):
1015
if (name == tag.getAttribute("name")
1016
and prop._dbus_interface
1017
== if_tag.getAttribute("name")):
1018
annots.update(getattr(
1019
prop, "_dbus_annotations", {}))
1020
for name, value in annots.items():
1021
ann_tag = document.createElement(
1023
ann_tag.setAttribute("name", name)
1024
ann_tag.setAttribute("value", value)
1025
tag.appendChild(ann_tag)
1026
# Add interface annotation tags
1027
for annotation, value in dict(
1028
itertools.chain.from_iterable(
1029
annotations().items()
1030
for name, annotations
1031
in self._get_all_dbus_things("interface")
1032
if name == if_tag.getAttribute("name")
1034
ann_tag = document.createElement("annotation")
1035
ann_tag.setAttribute("name", annotation)
1036
ann_tag.setAttribute("value", value)
1037
if_tag.appendChild(ann_tag)
1091
# Add annotation tags for properties
1092
for tag in if_tag.getElementsByTagName("property"):
1094
for name, prop in self._get_all_dbus_things(
1096
if (name == tag.getAttribute("name")
1097
and prop._dbus_interface
1098
== if_tag.getAttribute("name")):
1099
annots.update(getattr(
1100
prop, "_dbus_annotations", {}))
1101
for name, value in annots.items():
1102
ann_tag = document.createElement(
1104
ann_tag.setAttribute("name", name)
1105
ann_tag.setAttribute("value", value)
1106
tag.appendChild(ann_tag)
1038
1107
# Add the names to the return values for the
1039
1108
# "org.freedesktop.DBus.Properties" methods
1040
1109
if (if_tag.getAttribute("name")
1058
1127
exc_info=error)
1059
1128
return xmlstring
1131
dbus.OBJECT_MANAGER_IFACE
1132
except AttributeError:
1133
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
1135
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
1136
"""A D-Bus object with an ObjectManager.
1138
Classes inheriting from this exposes the standard
1139
GetManagedObjects call and the InterfacesAdded and
1140
InterfacesRemoved signals on the standard
1141
"org.freedesktop.DBus.ObjectManager" interface.
1143
Note: No signals are sent automatically; they must be sent
1146
@dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
1147
out_signature = "a{oa{sa{sv}}}")
1148
def GetManagedObjects(self):
1149
"""This function must be overridden"""
1150
raise NotImplementedError()
1152
@dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
1153
signature = "oa{sa{sv}}")
1154
def InterfacesAdded(self, object_path, interfaces_and_properties):
1157
@dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
1159
def InterfacesRemoved(self, object_path, interfaces):
1162
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1163
out_signature = "s",
1164
path_keyword = 'object_path',
1165
connection_keyword = 'connection')
1166
def Introspect(self, object_path, connection):
1167
"""Overloading of standard D-Bus method.
1169
Override return argument name of GetManagedObjects to be
1170
"objpath_interfaces_and_properties"
1172
xmlstring = DBusObjectWithAnnotations(self, object_path,
1175
document = xml.dom.minidom.parseString(xmlstring)
1177
for if_tag in document.getElementsByTagName("interface"):
1178
# Fix argument name for the GetManagedObjects method
1179
if (if_tag.getAttribute("name")
1180
== dbus.OBJECT_MANAGER_IFACE):
1181
for cn in if_tag.getElementsByTagName("method"):
1182
if (cn.getAttribute("name")
1183
== "GetManagedObjects"):
1184
for arg in cn.getElementsByTagName("arg"):
1185
if (arg.getAttribute("direction")
1189
"objpath_interfaces"
1191
xmlstring = document.toxml("utf-8")
1193
except (AttributeError, xml.dom.DOMException,
1194
xml.parsers.expat.ExpatError) as error:
1195
logger.error("Failed to override Introspection method",
1062
1199
def datetime_to_dbus(dt, variant_level=0):
1063
1200
"""Convert a UTC datetime.datetime() to a D-Bus type."""
1376
1513
if exitstatus >= 0:
1377
1514
# Emit D-Bus signal
1378
1515
self.CheckerCompleted(dbus.Int16(exitstatus),
1516
# This is specific to GNU libC
1517
dbus.Int64(exitstatus << 8),
1380
1518
dbus.String(command))
1382
1520
# Emit D-Bus signal
1383
1521
self.CheckerCompleted(dbus.Int16(-1),
1385
self.last_checker_signal),
1523
# This is specific to GNU libC
1525
| self.last_checker_signal),
1386
1526
dbus.String(command))
1524
1668
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1526
1670
# Name - property
1672
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1527
1673
@dbus_service_property(_interface, signature="s", access="read")
1528
1674
def Name_dbus_property(self):
1529
1675
return dbus.String(self.name)
1531
1677
# Fingerprint - property
1679
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1532
1680
@dbus_service_property(_interface, signature="s", access="read")
1533
1681
def Fingerprint_dbus_property(self):
1534
1682
return dbus.String(self.fingerprint)
1663
1813
self.stop_checker()
1665
1815
# ObjectPath - property
1817
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const",
1818
"org.freedesktop.DBus.Deprecated": "true"})
1666
1819
@dbus_service_property(_interface, signature="o", access="read")
1667
1820
def ObjectPath_dbus_property(self):
1668
1821
return self.dbus_object_path # is already a dbus.ObjectPath
1670
1823
# Secret = property
1825
{"org.freedesktop.DBus.Property.EmitsChangedSignal":
1671
1827
@dbus_service_property(_interface,
1672
1828
signature="ay",
1673
1829
access="write",
2911
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2761
2913
@dbus.service.signal(_interface, signature="os")
2762
2914
def ClientRemoved(self, objpath, name):
2918
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2766
2920
@dbus.service.method(_interface, out_signature="ao")
2767
2921
def GetAllClients(self):
2769
2923
return dbus.Array(c.dbus_object_path for c in
2770
2924
tcp_server.clients.itervalues())
2926
@dbus_annotations({"org.freedesktop.DBus.Deprecated":
2772
2928
@dbus.service.method(_interface,
2773
2929
out_signature="a{oa{sv}}")
2774
2930
def GetAllClientsWithProperties(self):
2776
2932
return dbus.Dictionary(
2777
{ c.dbus_object_path: c.GetAll("")
2933
{ c.dbus_object_path: c.GetAll(
2934
"se.recompile.Mandos.Client")
2778
2935
for c in tcp_server.clients.itervalues() },
2779
2936
signature="oa{sv}")
2785
2942
if c.dbus_object_path == object_path:
2786
2943
del tcp_server.clients[c.name]
2787
2944
c.remove_from_connection()
2788
# Don't signal anything except ClientRemoved
2945
# Don't signal the disabling
2789
2946
c.disable(quiet=True)
2791
self.ClientRemoved(object_path, c.name)
2947
# Emit D-Bus signal for removal
2948
self.client_removed_signal(c)
2793
2950
raise KeyError(object_path)
2954
@dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
2955
out_signature = "a{oa{sa{sv}}}")
2956
def GetManagedObjects(self):
2958
return dbus.Dictionary(
2959
{ client.dbus_object_path:
2961
{ interface: client.GetAll(interface)
2963
client._get_all_interface_names()})
2964
for client in tcp_server.clients.values()})
2966
def client_added_signal(self, client):
2967
"""Send the new standard signal and the old signal"""
2969
# New standard signal
2970
self.InterfacesAdded(
2971
client.dbus_object_path,
2973
{ interface: client.GetAll(interface)
2975
client._get_all_interface_names()}))
2977
self.ClientAdded(client.dbus_object_path)
2979
def client_removed_signal(self, client):
2980
"""Send the new standard signal and the old signal"""
2982
# New standard signal
2983
self.InterfacesRemoved(
2984
client.dbus_object_path,
2985
client._get_all_interface_names())
2987
self.ClientRemoved(client.dbus_object_path,
2797
2990
mandos_dbus_service = MandosDBusService()
2863
3056
name, client = tcp_server.clients.popitem()
2865
3058
client.remove_from_connection()
2866
# Don't signal anything except ClientRemoved
3059
# Don't signal the disabling
2867
3060
client.disable(quiet=True)
2870
mandos_dbus_service.ClientRemoved(
2871
client.dbus_object_path, client.name)
3061
# Emit D-Bus signal for removal
3062
mandos_dbus_service.client_removed_signal(client)
2872
3063
client_settings.clear()
2874
3065
atexit.register(cleanup)
2876
3067
for client in tcp_server.clients.itervalues():
2879
mandos_dbus_service.ClientAdded(client.dbus_object_path)
3069
# Emit D-Bus signal for adding
3070
mandos_dbus_service.client_added_signal(client)
2880
3071
# Need to initiate checking of clients
2881
3072
if client.enabled:
2882
3073
client.init_checker()