|
| 1 | +const { cfg, getData, setData, runAsShell, runAsPkg } = require('./common'), |
| 2 | + { randomFillSync } = require('crypto'), |
| 3 | + { parseString } = require('xml2js'), |
| 4 | + { parse: parseGetAll } = require('./GetAll'); |
| 5 | + |
| 6 | +const cnames = ['INVALID', 'BYTE', 'BOOLEAN', 'INT16', 'UINT16', 'INT32', 'UINT32', 'INT64', 'UINT64', 'DOUBLE', 'STRING', 'OBJECT_PATH', 'SIGNATURE', 'ARRAY', 'STRUCT', 'VARIANT', 'DICT ENTRY', 'UNIX FD']; |
| 7 | + |
| 8 | +async function parseXML(xml) { |
| 9 | + return new Promise((resolve, reject) => { |
| 10 | + parseString(xml, { |
| 11 | + mergeAttrs: true, |
| 12 | + // explicitArray: false |
| 13 | + }, (err, result) => { |
| 14 | + if (err) { |
| 15 | + return reject(err); |
| 16 | + } else { |
| 17 | + return resolve(result); |
| 18 | + } |
| 19 | + }); |
| 20 | + }); |
| 21 | +} |
| 22 | + |
| 23 | +async function introspect(runner) { |
| 24 | +/* |
| 25 | + _root = { |
| 26 | + bus.na.me: { // Level 1: Destination |
| 27 | + /obj/ect: { // Level 2: Object |
| 28 | + in.ter.face: { // Level 3: Interface |
| 29 | + 'method': { // Level 4: 'method' |
| 30 | + MethodName: [ |
| 31 | + {name: "argument_name", type: "a{type}", direction: "in/out"}, |
| 32 | + ... |
| 33 | + ], |
| 34 | + ... |
| 35 | + }, |
| 36 | + 'signal': { // Level 4: 'signal' |
| 37 | + SignalName: [ |
| 38 | + {name: "argument_name", type: "a{type}"}, |
| 39 | + ... |
| 40 | + ], |
| 41 | + ... |
| 42 | + }, |
| 43 | + 'property': { // Level 4: 'property' |
| 44 | + PropertyName1: "property_value", |
| 45 | + PropertyName2: null, |
| 46 | + ... |
| 47 | + } |
| 48 | + } |
| 49 | + }, |
| 50 | + ... |
| 51 | + }, |
| 52 | + ... |
| 53 | + } |
| 54 | +*/ |
| 55 | + const _root = {}; |
| 56 | + |
| 57 | +/* |
| 58 | + shelf = [ |
| 59 | + {dest: "bus.na.me", object: "/", interface: null}, |
| 60 | + {dest: "bus.na.me", object: "/", interface: "in.ter.face"}, |
| 61 | + ] |
| 62 | +
|
| 63 | + Temporary shelf for a batch of commands |
| 64 | + - If interface is null, call Introspectable.Introspect |
| 65 | + - If interface is not null, call Properties.GetAll |
| 66 | +*/ |
| 67 | + |
| 68 | + // let shelf = getData('names').map(name => ({ dest: name, object: '/', interface: null })); |
| 69 | + let shelf = [ |
| 70 | + { dest: 'fi.w1.wpa_supplicant1', object: '/fi/w1/wpa_supplicant1', interface: null }, |
| 71 | + ];//debug |
| 72 | + |
| 73 | + while (shelf.length) { |
| 74 | + console.log(JSON.stringify(shelf)); |
| 75 | + |
| 76 | + // A random delimiter for this iteration |
| 77 | + const delimiter = `[:${randomFillSync(Buffer.alloc(20)).toString('base64')}:]`; |
| 78 | + |
| 79 | + const cmd = shelf.reduce((cmd, v) => { |
| 80 | + const { dest, object, interface } = v; |
| 81 | + |
| 82 | + // Delimiter |
| 83 | + cmd += `\necho '${delimiter}';\n`; |
| 84 | + |
| 85 | + // Metadata |
| 86 | + cmd += `echo '${JSON.stringify(v)}';\n`; |
| 87 | + |
| 88 | + // If interface is null, call Introspectable.Introspect |
| 89 | + if (interface == null) { |
| 90 | + return cmd + `dbus-send --system --type=method_call --print-reply --dest=${dest} ${object} org.freedesktop.DBus.Introspectable.Introspect;\n`; |
| 91 | + } |
| 92 | + |
| 93 | + // If interface is not null, call Properties.GetAll |
| 94 | + else { |
| 95 | + return cmd + `dbus-send --system --type=method_call --print-reply --dest=${dest} ${object} org.freedesktop.DBus.Properties.GetAll string:${interface};\n`; |
| 96 | + } |
| 97 | + }, ''); |
| 98 | + |
| 99 | + // Run the command |
| 100 | + const res = await runner(cmd); |
| 101 | + |
| 102 | + // Flush shelf |
| 103 | + shelf = []; |
| 104 | + |
| 105 | + // Split by delimiter |
| 106 | + for (const block of res.split(delimiter)) { |
| 107 | + // Parse metadata |
| 108 | + const [, metadata] = /^({.+})$/m.exec(block) || [, '{}'], |
| 109 | + { dest, object, interface } = JSON.parseXML(metadata); |
| 110 | + if (!dest) { |
| 111 | + continue; |
| 112 | + } |
| 113 | + |
| 114 | + // Initialize _root[dest] (Level 1) |
| 115 | + if (!_root[dest]) { |
| 116 | + _root[dest] = {}; |
| 117 | + } |
| 118 | + |
| 119 | + // Initialize _root[dest][object] (Level 2) |
| 120 | + if (!_root[dest][object]) { |
| 121 | + _root[dest][object] = {}; |
| 122 | + } |
| 123 | + |
| 124 | + // Parse the message |
| 125 | + const [, string] = /\n\s+string "(.+)"[\n\s]*$/s.exec(block) || [], // Introspect |
| 126 | + [, array] = /\n\s+array \[(.+)\][\n\s]*$/s.exec(block) || []; // GetAll |
| 127 | + |
| 128 | + // If interface is null, Introspect is expected |
| 129 | + if (interface == null && string) { |
| 130 | + // Parse XML, grab interfaces and children |
| 131 | + const { |
| 132 | + node: { |
| 133 | + interface: interfaces, |
| 134 | + node: children |
| 135 | + } = {} |
| 136 | + } = await parseXML(string) || {}; |
| 137 | + |
| 138 | + // interfaces: Enumerate and register |
| 139 | + (interfaces || []).forEach(v => { |
| 140 | + const { |
| 141 | + name: [ interface ], |
| 142 | + method: methods, |
| 143 | + signal: signals, |
| 144 | + property |
| 145 | + } = v; |
| 146 | + |
| 147 | + // Initialize _root[dest][object][interface] (Level 3) |
| 148 | + _root[dest][object][interface] = {}; |
| 149 | + |
| 150 | + // Found methods, |
| 151 | + if (methods) { |
| 152 | + // Initialize _root[dest][object][interface]['method'] (Level 4) |
| 153 | + _root[dest][object][interface]['method'] = {}; |
| 154 | + |
| 155 | + // methods: Enumerate and register |
| 156 | + methods.forEach(v => { |
| 157 | + const { |
| 158 | + name: [ method ], |
| 159 | + arg |
| 160 | + } = v; |
| 161 | + |
| 162 | + _root[dest][object][interface]['method'][method] = arg; |
| 163 | + }); |
| 164 | + } |
| 165 | + |
| 166 | + // Found signals, |
| 167 | + if (signals) { |
| 168 | + // Initialize _root[dest][object][interface]['signal'] (Level 4) |
| 169 | + _root[dest][object][interface]['signal'] = {}; |
| 170 | + |
| 171 | + // signals: Enumerate and register |
| 172 | + signals.forEach(v => { |
| 173 | + const { |
| 174 | + name: [ signal ], |
| 175 | + arg |
| 176 | + } = v; |
| 177 | + |
| 178 | + _root[dest][object][interface]['signal'][signal] = arg; |
| 179 | + }); |
| 180 | + } |
| 181 | + |
| 182 | + // Found property, |
| 183 | + if (property) { |
| 184 | + // Initialize _root[dest][object][interface]['property'] (Level 4) |
| 185 | + _root[dest][object][interface]['property'] = {}; |
| 186 | + |
| 187 | + // Put it on shelf for later GetAll |
| 188 | + shelf.push({dest: dest, object: object, interface: interface}); |
| 189 | + } |
| 190 | + }); |
| 191 | + |
| 192 | + // children: Enumerate and register |
| 193 | + (children || []).forEach(v => { |
| 194 | + const { |
| 195 | + name: [ child ] |
| 196 | + } = v; |
| 197 | + |
| 198 | + // Put it on shelf for later Introspect |
| 199 | + shelf.push({ |
| 200 | + dest: dest, |
| 201 | + object: `${object}${object == '/' ? '' : '/'}${child}`, |
| 202 | + interface: null |
| 203 | + }); |
| 204 | + }); |
| 205 | + } |
| 206 | + // If interface is not null, GetAll is expected |
| 207 | + else if (array) { |
| 208 | + // FIXME: Use GetAll.jison |
| 209 | + } |
| 210 | + // Else: Invalid |
| 211 | + else { |
| 212 | + console.log(`Invalid: ${block}`); |
| 213 | + } |
| 214 | + } |
| 215 | + } |
| 216 | + |
| 217 | + return _root; |
| 218 | +} |
| 219 | + |
| 220 | +async function main() { |
| 221 | + // Introspect and acquire root objects |
| 222 | + const shell = await introspect(runAsShell), |
| 223 | + pkg = await introspect(runAsPkg); |
| 224 | + |
| 225 | + // Save root |
| 226 | + setData('root.shell', shell); |
| 227 | + setData('root.pkg', pkg); |
| 228 | +} |
| 229 | + |
| 230 | +main(); |
0 commit comments