122 lines
4.5 KiB
JavaScript
122 lines
4.5 KiB
JavaScript
// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
import Gio from 'gi://Gio';
|
|
import GLib from 'gi://GLib';
|
|
|
|
import {Logger} from './logger.js';
|
|
|
|
export const BUS_ADDRESS_REGEX = /([a-zA-Z0-9._-]+\.[a-zA-Z0-9.-]+)|(:[0-9]+\.[0-9]+)$/;
|
|
|
|
Gio._promisify(Gio.DBusConnection.prototype, 'call');
|
|
|
|
export async function getUniqueBusName(bus, name, cancellable) {
|
|
if (name[0] === ':')
|
|
return name;
|
|
|
|
if (!bus)
|
|
bus = Gio.DBus.session;
|
|
|
|
const variantName = new GLib.Variant('(s)', [name]);
|
|
const [unique] = (await bus.call('org.freedesktop.DBus', '/', 'org.freedesktop.DBus',
|
|
'GetNameOwner', variantName, new GLib.VariantType('(s)'),
|
|
Gio.DBusCallFlags.NONE, -1, cancellable)).deep_unpack();
|
|
|
|
return unique;
|
|
}
|
|
|
|
export async function getBusNames(bus, cancellable) {
|
|
if (!bus)
|
|
bus = Gio.DBus.session;
|
|
|
|
const [names] = (await bus.call('org.freedesktop.DBus', '/', 'org.freedesktop.DBus',
|
|
'ListNames', null, new GLib.VariantType('(as)'), Gio.DBusCallFlags.NONE,
|
|
-1, cancellable)).deep_unpack();
|
|
|
|
const uniqueNames = new Map();
|
|
const requests = names.map(name => getUniqueBusName(bus, name, cancellable));
|
|
const results = await Promise.allSettled(requests);
|
|
|
|
for (let i = 0; i < results.length; i++) {
|
|
const result = results[i];
|
|
if (result.status === 'fulfilled') {
|
|
let namesForBus = uniqueNames.get(result.value);
|
|
if (!namesForBus) {
|
|
namesForBus = new Set();
|
|
uniqueNames.set(result.value, namesForBus);
|
|
}
|
|
if (result.value !== names[i])
|
|
namesForBus.add(names[i]);
|
|
} else if (!result.reason.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
|
Logger.debug(`Impossible to get the unique name of ${names[i]}: ${result.reason}`);
|
|
}
|
|
}
|
|
|
|
return uniqueNames;
|
|
}
|
|
|
|
async function getProcessId(connectionName, cancellable = null, bus = Gio.DBus.session) {
|
|
const res = await bus.call('org.freedesktop.DBus', '/',
|
|
'org.freedesktop.DBus', 'GetConnectionUnixProcessID',
|
|
new GLib.Variant('(s)', [connectionName]),
|
|
new GLib.VariantType('(u)'),
|
|
Gio.DBusCallFlags.NONE,
|
|
-1,
|
|
cancellable);
|
|
const [pid] = res.deepUnpack();
|
|
return pid;
|
|
}
|
|
|
|
export async function getProcessName(connectionName, cancellable = null,
|
|
priority = GLib.PRIORITY_DEFAULT, bus = Gio.DBus.session) {
|
|
const pid = await getProcessId(connectionName, cancellable, bus);
|
|
const cmdFile = Gio.File.new_for_path(`/proc/${pid}/cmdline`);
|
|
const inputStream = await cmdFile.read_async(priority, cancellable);
|
|
const bytes = await inputStream.read_bytes_async(2048, priority, cancellable);
|
|
const textDecoder = new TextDecoder();
|
|
return textDecoder.decode(bytes.toArray().map(v => !v ? 0x20 : v));
|
|
}
|
|
|
|
export async function* introspectBusObject(bus, name, cancellable,
|
|
interfaces = undefined, path = undefined) {
|
|
if (!path)
|
|
path = '/';
|
|
|
|
const [introspection] = (await bus.call(name, path, 'org.freedesktop.DBus.Introspectable',
|
|
'Introspect', null, new GLib.VariantType('(s)'), Gio.DBusCallFlags.NONE,
|
|
5000, cancellable)).deep_unpack();
|
|
|
|
const nodeInfo = Gio.DBusNodeInfo.new_for_xml(introspection);
|
|
|
|
if (!interfaces || dbusNodeImplementsInterfaces(nodeInfo, interfaces))
|
|
yield {nodeInfo, path};
|
|
|
|
if (path === '/')
|
|
path = '';
|
|
|
|
for (const subNodeInfo of nodeInfo.nodes) {
|
|
const subPath = `${path}/${subNodeInfo.path}`;
|
|
yield* introspectBusObject(bus, name, cancellable, interfaces, subPath);
|
|
}
|
|
}
|
|
|
|
function dbusNodeImplementsInterfaces(nodeInfo, interfaces) {
|
|
if (!(nodeInfo instanceof Gio.DBusNodeInfo) || !Array.isArray(interfaces))
|
|
return false;
|
|
|
|
return interfaces.some(iface => nodeInfo.lookup_interface(iface));
|
|
}
|