blob: ec252c69e2aa4c07f0cc8db135789487f5518d3a [file] [log] [blame]
Brad Bishop8ffe1e42016-02-11 16:15:40 -05001# Contributors Listed Below - COPYRIGHT 2016
2# [+] International Business Machines Corp.
3#
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14# implied. See the License for the specific language governing
15# permissions and limitations under the License.
16
Brad Bishopc66de762016-06-28 16:47:42 -040017import xml.etree.ElementTree as ET
Brad Bishop8ffe1e42016-02-11 16:15:40 -050018import dbus
19
20
21class IntrospectionNodeParser:
22 def __init__(self, data, tag_match=bool, intf_match=bool):
23 self.data = data
24 self.cache = {}
25 self.tag_match = tag_match
26 self.intf_match = intf_match
27
28 def parse_args(self):
29 return [x.attrib for x in self.data.findall('arg')]
30
31 def parse_children(self):
32 return [x.attrib['name'] for x in self.data.findall('node')]
33
34 def parse_method_or_signal(self):
35 name = self.data.attrib['name']
36 return name, self.parse_args()
37
38 def parse_interface(self):
39 iface = {}
40 iface['method'] = {}
41 iface['signal'] = {}
42
43 for node in self.data:
44 if node.tag not in ['method', 'signal']:
45 continue
46 if not self.tag_match(node.tag):
47 continue
48 p = IntrospectionNodeParser(
49 node, self.tag_match, self.intf_match)
50 n, element = p.parse_method_or_signal()
51 iface[node.tag][n] = element
52
53 return iface
54
55 def parse_node(self):
56 if self.cache:
57 return self.cache
58
59 self.cache['interfaces'] = {}
60 self.cache['children'] = []
61
62 for node in self.data:
63 if node.tag == 'interface':
64 p = IntrospectionNodeParser(
65 node, self.tag_match, self.intf_match)
66 name = p.data.attrib['name']
67 if not self.intf_match(name):
68 continue
69 self.cache['interfaces'][name] = p.parse_interface()
70 elif node.tag == 'node':
71 self.cache['children'] = self.parse_children()
72
73 return self.cache
74
75 def get_interfaces(self):
76 return self.parse_node()['interfaces']
77
78 def get_children(self):
79 return self.parse_node()['children']
80
81 def recursive_binding(self):
82 return any('/' in s for s in self.get_children())
83
84
85class IntrospectionParser:
86 def __init__(self, name, bus, tag_match=bool, intf_match=bool):
87 self.name = name
88 self.bus = bus
89 self.tag_match = tag_match
90 self.intf_match = intf_match
91
92 def _introspect(self, path):
93 try:
94 obj = self.bus.get_object(self.name, path, introspect=False)
95 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
96 data = iface.Introspect()
97 except dbus.DBusException:
98 return None
99
100 return IntrospectionNodeParser(
Brad Bishopc66de762016-06-28 16:47:42 -0400101 ET.fromstring(data),
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500102 self.tag_match,
103 self.intf_match)
104
105 def _discover_flat(self, path, parser):
106 items = {}
107 interfaces = parser.get_interfaces().keys()
108 if interfaces:
109 items[path] = {}
110 items[path]['interfaces'] = interfaces
111
112 return items
113
114 def introspect(self, path='/', parser=None):
115 items = {}
116 if not parser:
117 parser = self._introspect(path)
118 if not parser:
119 return {}
120 items.update(self._discover_flat(path, parser))
121
122 if path != '/':
123 path += '/'
124
125 if parser.recursive_binding():
126 callback = self._discover_flat
127 else:
128 callback = self.introspect
129
130 for k in parser.get_children():
131 parser = self._introspect(path + k)
132 if not parser:
133 continue
134 items.update(callback(path + k, parser))
135
136 return items
Brad Bishopc66de762016-06-28 16:47:42 -0400137
138
139def find_dbus_interfaces(conn, service, path, match):
Brad Bishop737ea402016-06-28 16:30:43 -0400140 class _FindInterfaces(object):
Brad Bishopc66de762016-06-28 16:47:42 -0400141 def __init__(self):
142 self.results = {}
143
144 @staticmethod
Brad Bishop737ea402016-06-28 16:30:43 -0400145 def _introspect(service, path):
Brad Bishopc66de762016-06-28 16:47:42 -0400146 obj = conn.get_object(service, path, introspect=False)
147 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
148 return iface.Introspect()
149
150 @staticmethod
Brad Bishop737ea402016-06-28 16:30:43 -0400151 def _get_managed_objects(service, om):
Brad Bishopc66de762016-06-28 16:47:42 -0400152 obj = conn.get_object(service, om, introspect=False)
153 iface = dbus.Interface(
154 obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
155 return iface.GetManagedObjects()
156
157 @staticmethod
Brad Bishop737ea402016-06-28 16:30:43 -0400158 def _to_path(elements):
Brad Bishopc66de762016-06-28 16:47:42 -0400159 return '/' + '/'.join(elements)
160
161 @staticmethod
Brad Bishop737ea402016-06-28 16:30:43 -0400162 def _to_path_elements(path):
Brad Bishopc66de762016-06-28 16:47:42 -0400163 return filter(bool, path.split('/'))
164
165 def __call__(self, service, path):
166 self.results = {}
Brad Bishop737ea402016-06-28 16:30:43 -0400167 self._find_interfaces(service, path)
Brad Bishopc66de762016-06-28 16:47:42 -0400168 return self.results
169
170 @staticmethod
Brad Bishop737ea402016-06-28 16:30:43 -0400171 def _match(iface):
Brad Bishopc66de762016-06-28 16:47:42 -0400172 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
173 or match(iface)
174
Brad Bishop737ea402016-06-28 16:30:43 -0400175 def _find_interfaces(self, service, path):
176 path_elements = self._to_path_elements(path)
177 path = self._to_path(path_elements)
178 root = ET.fromstring(self._introspect(service, path))
Brad Bishopc66de762016-06-28 16:47:42 -0400179
180 ifaces = filter(
Brad Bishop737ea402016-06-28 16:30:43 -0400181 self._match,
Brad Bishopc66de762016-06-28 16:47:42 -0400182 [x.attrib.get('name') for x in root.findall('interface')])
183 self.results[path] = ifaces
184
185 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
Brad Bishop737ea402016-06-28 16:30:43 -0400186 objs = self._get_managed_objects(service, path)
Brad Bishopc66de762016-06-28 16:47:42 -0400187 for k, v in objs.iteritems():
188 self.results[k] = v
189 else:
190 children = filter(
191 bool,
192 [x.attrib.get('name') for x in root.findall('node')])
193 children = [
Brad Bishop737ea402016-06-28 16:30:43 -0400194 self._to_path(
195 path_elements + self._to_path_elements(x))
Brad Bishopc66de762016-06-28 16:47:42 -0400196 for x in sorted(children)]
197 for child in children:
198 if child not in self.results:
Brad Bishop737ea402016-06-28 16:30:43 -0400199 self._find_interfaces(service, child)
Brad Bishopc66de762016-06-28 16:47:42 -0400200
Brad Bishop737ea402016-06-28 16:30:43 -0400201 return _FindInterfaces()(service, path)