18ffe1e44SBrad Bishop# Contributors Listed Below - COPYRIGHT 2016 28ffe1e44SBrad Bishop# [+] International Business Machines Corp. 38ffe1e44SBrad Bishop# 48ffe1e44SBrad Bishop# 58ffe1e44SBrad Bishop# Licensed under the Apache License, Version 2.0 (the "License"); 68ffe1e44SBrad Bishop# you may not use this file except in compliance with the License. 78ffe1e44SBrad Bishop# You may obtain a copy of the License at 88ffe1e44SBrad Bishop# 98ffe1e44SBrad Bishop# http://www.apache.org/licenses/LICENSE-2.0 108ffe1e44SBrad Bishop# 118ffe1e44SBrad Bishop# Unless required by applicable law or agreed to in writing, software 128ffe1e44SBrad Bishop# distributed under the License is distributed on an "AS IS" BASIS, 138ffe1e44SBrad Bishop# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148ffe1e44SBrad Bishop# implied. See the License for the specific language governing 158ffe1e44SBrad Bishop# permissions and limitations under the License. 168ffe1e44SBrad Bishop 17c66de763SBrad Bishopimport xml.etree.ElementTree as ET 188ffe1e44SBrad Bishopimport dbus 198ffe1e44SBrad Bishop 208ffe1e44SBrad Bishop 218ffe1e44SBrad Bishopclass IntrospectionNodeParser: 228ffe1e44SBrad Bishop def __init__(self, data, tag_match=bool, intf_match=bool): 238ffe1e44SBrad Bishop self.data = data 248ffe1e44SBrad Bishop self.cache = {} 258ffe1e44SBrad Bishop self.tag_match = tag_match 268ffe1e44SBrad Bishop self.intf_match = intf_match 278ffe1e44SBrad Bishop 288ffe1e44SBrad Bishop def parse_args(self): 298ffe1e44SBrad Bishop return [x.attrib for x in self.data.findall('arg')] 308ffe1e44SBrad Bishop 318ffe1e44SBrad Bishop def parse_children(self): 328ffe1e44SBrad Bishop return [x.attrib['name'] for x in self.data.findall('node')] 338ffe1e44SBrad Bishop 348ffe1e44SBrad Bishop def parse_method_or_signal(self): 358ffe1e44SBrad Bishop name = self.data.attrib['name'] 368ffe1e44SBrad Bishop return name, self.parse_args() 378ffe1e44SBrad Bishop 388ffe1e44SBrad Bishop def parse_interface(self): 398ffe1e44SBrad Bishop iface = {} 408ffe1e44SBrad Bishop iface['method'] = {} 418ffe1e44SBrad Bishop iface['signal'] = {} 428ffe1e44SBrad Bishop 438ffe1e44SBrad Bishop for node in self.data: 448ffe1e44SBrad Bishop if node.tag not in ['method', 'signal']: 458ffe1e44SBrad Bishop continue 468ffe1e44SBrad Bishop if not self.tag_match(node.tag): 478ffe1e44SBrad Bishop continue 488ffe1e44SBrad Bishop p = IntrospectionNodeParser( 498ffe1e44SBrad Bishop node, self.tag_match, self.intf_match) 508ffe1e44SBrad Bishop n, element = p.parse_method_or_signal() 518ffe1e44SBrad Bishop iface[node.tag][n] = element 528ffe1e44SBrad Bishop 538ffe1e44SBrad Bishop return iface 548ffe1e44SBrad Bishop 558ffe1e44SBrad Bishop def parse_node(self): 568ffe1e44SBrad Bishop if self.cache: 578ffe1e44SBrad Bishop return self.cache 588ffe1e44SBrad Bishop 598ffe1e44SBrad Bishop self.cache['interfaces'] = {} 608ffe1e44SBrad Bishop self.cache['children'] = [] 618ffe1e44SBrad Bishop 628ffe1e44SBrad Bishop for node in self.data: 638ffe1e44SBrad Bishop if node.tag == 'interface': 648ffe1e44SBrad Bishop p = IntrospectionNodeParser( 658ffe1e44SBrad Bishop node, self.tag_match, self.intf_match) 668ffe1e44SBrad Bishop name = p.data.attrib['name'] 678ffe1e44SBrad Bishop if not self.intf_match(name): 688ffe1e44SBrad Bishop continue 698ffe1e44SBrad Bishop self.cache['interfaces'][name] = p.parse_interface() 708ffe1e44SBrad Bishop elif node.tag == 'node': 718ffe1e44SBrad Bishop self.cache['children'] = self.parse_children() 728ffe1e44SBrad Bishop 738ffe1e44SBrad Bishop return self.cache 748ffe1e44SBrad Bishop 758ffe1e44SBrad Bishop def get_interfaces(self): 768ffe1e44SBrad Bishop return self.parse_node()['interfaces'] 778ffe1e44SBrad Bishop 788ffe1e44SBrad Bishop def get_children(self): 798ffe1e44SBrad Bishop return self.parse_node()['children'] 808ffe1e44SBrad Bishop 818ffe1e44SBrad Bishop def recursive_binding(self): 828ffe1e44SBrad Bishop return any('/' in s for s in self.get_children()) 838ffe1e44SBrad Bishop 848ffe1e44SBrad Bishop 858ffe1e44SBrad Bishopclass IntrospectionParser: 868ffe1e44SBrad Bishop def __init__(self, name, bus, tag_match=bool, intf_match=bool): 878ffe1e44SBrad Bishop self.name = name 888ffe1e44SBrad Bishop self.bus = bus 898ffe1e44SBrad Bishop self.tag_match = tag_match 908ffe1e44SBrad Bishop self.intf_match = intf_match 918ffe1e44SBrad Bishop 928ffe1e44SBrad Bishop def _introspect(self, path): 938ffe1e44SBrad Bishop try: 948ffe1e44SBrad Bishop obj = self.bus.get_object(self.name, path, introspect=False) 958ffe1e44SBrad Bishop iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE) 968ffe1e44SBrad Bishop data = iface.Introspect() 978ffe1e44SBrad Bishop except dbus.DBusException: 988ffe1e44SBrad Bishop return None 998ffe1e44SBrad Bishop 1008ffe1e44SBrad Bishop return IntrospectionNodeParser( 101c66de763SBrad Bishop ET.fromstring(data), 1028ffe1e44SBrad Bishop self.tag_match, 1038ffe1e44SBrad Bishop self.intf_match) 1048ffe1e44SBrad Bishop 1058ffe1e44SBrad Bishop def _discover_flat(self, path, parser): 1068ffe1e44SBrad Bishop items = {} 107*9172f3e7SCamVan Nguyen interfaces = list(parser.get_interfaces().keys()) 1088ffe1e44SBrad Bishop if interfaces: 1098ffe1e44SBrad Bishop items[path] = {} 1108ffe1e44SBrad Bishop items[path]['interfaces'] = interfaces 1118ffe1e44SBrad Bishop 1128ffe1e44SBrad Bishop return items 1138ffe1e44SBrad Bishop 1148ffe1e44SBrad Bishop def introspect(self, path='/', parser=None): 1158ffe1e44SBrad Bishop items = {} 1168ffe1e44SBrad Bishop if not parser: 1178ffe1e44SBrad Bishop parser = self._introspect(path) 1188ffe1e44SBrad Bishop if not parser: 1198ffe1e44SBrad Bishop return {} 1208ffe1e44SBrad Bishop items.update(self._discover_flat(path, parser)) 1218ffe1e44SBrad Bishop 1228ffe1e44SBrad Bishop if path != '/': 1238ffe1e44SBrad Bishop path += '/' 1248ffe1e44SBrad Bishop 1258ffe1e44SBrad Bishop if parser.recursive_binding(): 1268ffe1e44SBrad Bishop callback = self._discover_flat 1278ffe1e44SBrad Bishop else: 1288ffe1e44SBrad Bishop callback = self.introspect 1298ffe1e44SBrad Bishop 1308ffe1e44SBrad Bishop for k in parser.get_children(): 1318ffe1e44SBrad Bishop parser = self._introspect(path + k) 1328ffe1e44SBrad Bishop if not parser: 1338ffe1e44SBrad Bishop continue 1348ffe1e44SBrad Bishop items.update(callback(path + k, parser)) 1358ffe1e44SBrad Bishop 1368ffe1e44SBrad Bishop return items 137