1# 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 17import xml.etree.ElementTree as ET 18import 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( 101 ET.fromstring(data), 102 self.tag_match, 103 self.intf_match) 104 105 def _discover_flat(self, path, parser): 106 items = {} 107 interfaces = list(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 137