UNPKG

dbus-sdk

Version:

A Node.js SDK for interacting with DBus, enabling seamless service calling and exposure with TypeScript support

830 lines (829 loc) 38.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DBus = void 0; const DBusService_1 = require("./DBusService"); const DBusConnection_1 = require("./lib/DBusConnection"); const DBusMessage_1 = require("./lib/DBusMessage"); const DBusMessageFlags_1 = require("./lib/enums/DBusMessageFlags"); const DBusMessageType_1 = require("./lib/enums/DBusMessageType"); const DBusSignedValue_1 = require("./lib/DBusSignedValue"); const DBusSignalEmitter_1 = require("./lib/DBusSignalEmitter"); const RequestNameFlags_1 = require("./lib/enums/RequestNameFlags"); const Errors_1 = require("./lib/Errors"); const node_events_1 = __importDefault(require("node:events")); const CreateDBusError_1 = require("./lib/CreateDBusError"); const DBusTypeClass_1 = require("./lib/DBusTypeClass"); const DBusVariant_1 = require("./lib/datatypes/DBusVariant"); /** * Main class for interacting with a DBus connection. * Provides methods for connecting to DBus, invoking methods, handling signals, * managing services, objects, and interfaces, and other DBus operations. * Extends EventEmitter to emit events for connection status and DBus signals. */ class DBus extends node_events_1.default { /** * The underlying DBus connection instance. * Manages the low-level communication with the DBus daemon. */ #connection; /** * The unique name assigned to this connection by the DBus daemon (e.g., ':1.123'). * Set after a successful 'Hello' call during initialization. */ #uniqueName; /** * A counter for message serial numbers. * Incremented for each message sent to ensure unique identification. */ #serial = 1; /** * A record of pending method calls awaiting responses. * Maps serial numbers to callback functions for resolving or rejecting the call. */ #inflightCalls = {}; /** * A set of WeakRef objects referencing DBusSignalEmitter instances. * Used for garbage collection of signal emitters when they are no longer needed. */ #signalEmitterWeakRefSet = new Set(); /** * A set of active DBusSignalEmitter instances. * Tracks signal emitters for dispatching incoming signals. */ #signalEmitters = new Set(); /** * A map of signal matching rules to their formatted rule strings. * Used to manage DBus signal subscription rules. */ #signalRulesMap = new Map(); /** * A set of WeakRef objects referencing DBusService instances. * Used for garbage collection of services and updating their unique names. */ #weakServiceSet = new Set(); /** * The DBus management interface for interacting with the DBus daemon. * Provides access to standard DBus methods like name management and signal subscription. */ #dbusManageInterface; /** * Getter for the unique name of this DBus connection. * * @returns The unique name assigned by the DBus daemon (e.g., ':1.123'). */ get uniqueName() { return this.#uniqueName; } /** * Static method to connect to a DBus instance. * Creates a new DBus connection with the provided options and initializes it. * * @param opts - Connection options (e.g., socket path, TCP details, or stream settings). * @returns A Promise resolving to an initialized DBus instance ready for use. */ static async connect(opts) { const bus = new DBus(await DBusConnection_1.DBusConnection.createConnection(opts)); return await bus.initialize(); } /** * Initializes the DBus connection. * Performs a 'Hello' call to obtain a unique name, sets up the management interface, * updates services and signal emitters with current name owners, and registers * event listeners for DBus daemon signals. * * @returns A Promise resolving to this DBus instance after initialization is complete. */ async initialize() { await this.hello(); if (this.#dbusManageInterface) this.#dbusManageInterface.signal.removeAllListeners(); if (this.#weakServiceSet.size || this.#signalEmitterWeakRefSet.size) { const nameInfos = await this.listBusNames(); const names = nameInfos.map((nameInfo) => nameInfo.name); this.#weakServiceSet.forEach((serviceWeakRef) => { const dbusService = serviceWeakRef.deref(); if (!dbusService) { this.#weakServiceSet.delete(serviceWeakRef); return; } if (!names.includes(dbusService.name)) return; const index = names.indexOf(dbusService.name); if (dbusService.uniqueName !== nameInfos[index].uniqueName) updateServiceUniqueName.call(dbusService, nameInfos[index].uniqueName ? nameInfos[index].uniqueName : ''); }); this.#signalEmitterWeakRefSet.forEach((emitterRef) => { const emitter = emitterRef.deref(); if (!emitter) { this.#signalEmitterWeakRefSet.delete(emitterRef); return; } if (!names.includes(emitter.service)) return; const index = names.indexOf(emitter.service); if (emitter.uniqueName !== nameInfos[index].uniqueName) return; updateEmitterUniqueName.call(emitter, nameInfos[index].uniqueName); }); } this.#dbusManageInterface = await this.getInterface('org.freedesktop.DBus', '/org/freedesktop/DBus', 'org.freedesktop.DBus'); const updateServiceUniqueName = function (newUniqueName) { this.updateUniqueName(newUniqueName); }; const updateEmitterUniqueName = function (newUniqueName) { this.updateUniqueName(newUniqueName); }; this.#dbusManageInterface.signal .on('NameOwnerChanged', (name, oldOwner, newOwner) => { this.emit('NameOwnerChanged', name, oldOwner, newOwner); this.#weakServiceSet.forEach((serviceWeakRef) => { const dbusService = serviceWeakRef.deref(); if (!dbusService) { this.#weakServiceSet.delete(serviceWeakRef); return; } if (dbusService.name !== name) return; if (dbusService.uniqueName !== newOwner) updateServiceUniqueName.call(dbusService, newOwner); }); this.#signalEmitterWeakRefSet.forEach((emitterRef) => { const emitter = emitterRef.deref(); if (!emitter) { this.#signalEmitterWeakRefSet.delete(emitterRef); return; } if (emitter.uniqueName !== oldOwner) return; updateEmitterUniqueName.call(emitter, newOwner); }); if (!!name && !oldOwner && !!newOwner) this.emit('online', name); if (!!name && !!oldOwner && !newOwner) this.emit('offline', name); if (!!name && !!oldOwner && !!newOwner) this.emit('replaced', name); }) .on('NameLost', (name) => { this.emit('NameLost', name); }) .on('NameAcquired', (name) => { this.emit('NameAcquired', name); }); return this; } /** * Sends a 'Hello' message to the DBus daemon to obtain a unique connection name. * Sets the unique name for this connection upon successful response. * This is typically the first step in DBus connection initialization. * * @returns A Promise that resolves when the unique name is set. */ async hello() { const [uniqueName] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', method: 'Hello' }); this.#uniqueName = uniqueName instanceof DBusTypeClass_1.DBusTypeClass ? uniqueName.value : uniqueName; } /** * Writes raw data to the DBus socket. * Throws an error if the connection is not active. * Used internally to send encoded DBus messages to the daemon. * * @param data - The Buffer containing the data to write to the DBus socket. * @throws {Error} If the DBus connection is disconnected, with a specific DBus error code. */ write(data) { if (!this.#connection.connected) throw (0, CreateDBusError_1.CreateDBusError)('org.freedesktop.DBus.Error.Disconnected', 'DBus disconnected'); this.#connection.write(data); } /** * Emits a DBus signal with the specified options. * Encodes the signal message using the provided options and writes it to the DBus socket. * Signals are broadcast messages that do not expect a reply. * * @param opts - Options for emitting the signal, including object path, interface, signal name, destination, signature, and data. */ emitSignal(opts) { this.write(DBusMessage_1.DBusMessage.encode({ serial: this.#serial++, type: DBusMessageType_1.DBusMessageType.SIGNAL, flags: DBusMessageFlags_1.DBusMessageFlags.NO_REPLY_EXPECTED, sender: this.#uniqueName, path: opts.objectPath, interfaceName: opts.interface, member: opts.signal, destination: opts.destination, signature: opts.signature ? opts.signature : undefined }, ...opts.data ? opts.data : [])); } /** * Sends a reply to a DBus method call. * Handles both successful responses (METHOD_RETURN) and error replies (ERROR). * Encodes the reply message and writes it to the DBus socket. * * @param opts - Options for the reply, including reply serial number, destination, signature, and data or error object. */ reply(opts) { if (opts.data instanceof Error) { this.write(DBusMessage_1.DBusMessage.encode({ serial: this.#serial++, replySerial: opts.replySerial, type: DBusMessageType_1.DBusMessageType.ERROR, flags: DBusMessageFlags_1.DBusMessageFlags.NO_REPLY_EXPECTED, sender: this.#uniqueName, destination: opts.destination, errorName: opts.data.name, signature: opts.signature ? opts.signature : undefined }, opts.data.message)); } else { this.write(DBusMessage_1.DBusMessage.encode({ serial: this.#serial++, replySerial: opts.replySerial, type: DBusMessageType_1.DBusMessageType.METHOD_RETURN, flags: DBusMessageFlags_1.DBusMessageFlags.NO_REPLY_EXPECTED, sender: this.#uniqueName, destination: opts.destination, signature: opts.signature ? opts.signature : undefined }, ...(opts.data ? opts.data : []))); } } /** * Invokes a DBus method call with the specified options, with a configurable reply expectation. * Sends the method call message to the DBus daemon and optionally waits for a response. * This is the implementation signature that handles the logic for all overloads. * * @param opts - Options for the method call, including service, object path, interface, method, signature, and arguments. * @param noReply - Boolean indicating if a reply is expected (default: false, meaning a reply is expected). * @returns A Promise resolving to the response data if a reply is expected, otherwise void. */ invoke(opts, noReply = false) { if (noReply) { this.write(DBusMessage_1.DBusMessage.encode({ serial: this.#serial++, type: DBusMessageType_1.DBusMessageType.METHOD_CALL, flags: DBusMessageFlags_1.DBusMessageFlags.NO_REPLY_EXPECTED, destination: opts.service, path: opts.objectPath, interfaceName: opts.interface, member: opts.method, signature: opts.signature ? opts.signature : undefined }, ...opts.args ? opts.args : [])); } else { return new Promise((resolve, reject) => { const message = new DBusMessage_1.DBusMessage({ serial: this.#serial++, type: DBusMessageType_1.DBusMessageType.METHOD_CALL, flags: DBusMessageFlags_1.DBusMessageFlags.REPLY_EXPECTED, destination: opts.service, path: opts.objectPath, interfaceName: opts.interface, member: opts.method, signature: opts.signature ? opts.signature : undefined }, ...opts.args ? opts.args : []); this.#inflightCalls[message.header.serial] = [resolve, reject]; this.write(message.toBuffer()); }); } } /** * Retrieves the value of a DBus property using the Properties interface. * Calls the 'Get' method of 'org.freedesktop.DBus.Properties' to fetch a property value. * * @param opts - Options for getting the property, including service, object path, interface name, and property name. * @returns A Promise resolving to the value of the requested property. */ async getProperty(opts) { const [value] = await this.invoke({ service: opts.service, objectPath: opts.objectPath, interface: 'org.freedesktop.DBus.Properties', method: 'Get', signature: 'ss', args: [opts.interface, opts.property] }); return value; } /** * Sets the value of a DBus property using the Properties interface. * Calls the 'Set' method of 'org.freedesktop.DBus.Properties' to update a property value. * * @param opts - Options for setting the property, including service, object path, interface name, property name, value, and optional signature. * @returns A Promise that resolves when the property is successfully set. */ async setProperty(opts) { let signedValue; if (opts.value instanceof DBusSignedValue_1.DBusSignedValue) { if (opts.value.$signature !== DBusVariant_1.DBusVariant.type) { signedValue = new DBusSignedValue_1.DBusSignedValue('v', opts.value); } else { signedValue = opts.value; } } else { signedValue = opts.signature ? new DBusSignedValue_1.DBusSignedValue('v', new DBusSignedValue_1.DBusSignedValue(opts.signature, opts.value)) : new DBusSignedValue_1.DBusSignedValue('v', opts.value); } await this.invoke({ service: opts.service, objectPath: opts.objectPath, interface: 'org.freedesktop.DBus.Properties', method: 'Set', signature: 'ssv', args: [opts.interface, opts.property, signedValue] }); } /** * Formats a DBus match rule string for signal subscription. * Constructs a rule string to filter incoming signals based on sender, path, interface, and signal name. * * @param uniqueName - The sender's unique name or '*' to match any sender. * @param objectPath - The object path or '*' to match any path. * @param interfaceName - The interface name or '*' to match any interface. * @param signalName - The signal name or '*' to match any signal. * @returns The formatted match rule string (e.g., 'type=signal,sender=:1.123,interface=org.test'). */ formatMatchSignalRule(uniqueName, objectPath, interfaceName, signalName) { const matchSignalRules = ['type=signal']; if (uniqueName !== '*') matchSignalRules.push(`sender=${uniqueName}`); if (objectPath !== '*') matchSignalRules.push(`path=${objectPath}`); if (interfaceName !== '*') matchSignalRules.push(`interface=${interfaceName}`); if (signalName !== '*') matchSignalRules.push(`member=${signalName}`); return matchSignalRules.join(','); } /** * Registers a signal subscription rule with the DBus daemon. * Adds a match rule to receive signals matching the specified criteria and stores it for management. * * @param uniqueName - The sender's unique name or '*' to match any sender. * @param objectPath - The object path or '*' to match any path. * @param interfaceName - The interface name or '*' to match any interface. * @param signalName - The signal name or '*' to match any signal. */ onSignal(uniqueName, objectPath, interfaceName, signalName) { const rules = { uniqueName: uniqueName, objectPath: objectPath, interfaceName: interfaceName, signalName: signalName }; this.#signalRulesMap.set(rules, this.formatMatchSignalRule(uniqueName, objectPath, interfaceName, signalName)); return this.addMatch(this.#signalRulesMap.get(rules)); } /** * Removes a signal subscription rule from the DBus daemon. * Removes a previously added match rule to stop receiving specific signals. * * @param signalRuleString - The formatted rule string to remove from the DBus daemon. */ offSignal(signalRuleString) { return this.removeMatch(signalRuleString); } /** * Creates a signal emitter for listening to DBus signals. * Constructs a DBusSignalEmitter instance to handle signal events for specific criteria. * * @param opts - Options for creating the signal emitter, including service name, object path, and interface. * @returns A DBusSignalEmitter instance for subscribing to and handling signals. */ createSignalEmitter(opts) { const signalEmitter = new DBusSignalEmitter_1.DBusSignalEmitter(opts, this.#signalEmitters, (service, objectPath, interfaceName, signalName) => this.onSignal(service, objectPath, interfaceName, signalName)); this.#signalEmitterWeakRefSet.add(new WeakRef(signalEmitter)); return signalEmitter; } /** * Constructor for the DBus class. * Initializes the DBus instance with a connection and sets up event listeners for * incoming messages, connection closure, and errors. Dispatches signals to emitters. * * @param connection - The DBusConnection instance to use for low-level communication. */ constructor(connection) { super(); this.#connection = connection; this.#connection .on('message', (message) => { switch (message.header.type) { case DBusMessageType_1.DBusMessageType.METHOD_RETURN: if (!message.header.replySerial) return; if (!this.#inflightCalls[message.header.replySerial]) return; return this.#inflightCalls[message.header.replySerial][0](message.body); case DBusMessageType_1.DBusMessageType.ERROR: if (!message.header.replySerial) return; if (!this.#inflightCalls[message.header.replySerial]) return; const error = new Error(message.body[0] ? message.body[0] instanceof DBusTypeClass_1.DBusTypeClass ? message.body[0].value : message.body[0] : ''); error.name = message.header.errorName ? message.header.errorName : error.name; return this.#inflightCalls[message.header.replySerial][1](error); case DBusMessageType_1.DBusMessageType.SIGNAL: const sender = message.header.sender; const objectPath = message.header.path; const interfaceName = message.header.interfaceName; const signalName = message.header.member; const signalArgs = message.body; const emitResults = []; this.#signalEmitters.forEach((emitter) => { emitResults.push((() => { if (emitter.uniqueName !== '*' && emitter.uniqueName !== sender) return false; if (emitter.objectPath !== '*' && emitter.objectPath !== objectPath) return false; if (emitter.interface !== '*' && emitter.interface !== interfaceName) return false; if (!emitter.eventNames().includes(signalName) && !emitter.eventNames().includes('*')) return false; const emitDirectly = emitter.emit(signalName, ...signalArgs); const emitWildcard = emitter.emit('*', signalName, ...signalArgs); return emitDirectly || emitWildcard; })()); }); if (emitResults.find((result) => result)) return; const deprecatedSignalRuleStrings = []; this.#signalRulesMap.forEach((signalRuleString, rule) => { if (rule.uniqueName !== '*' && rule.uniqueName !== sender) return; if (rule.objectPath !== '*' && rule.objectPath !== objectPath) return; if (rule.interfaceName !== '*' && rule.interfaceName !== interfaceName) return; if (rule.signalName !== '*' && rule.signalName !== signalName) return; deprecatedSignalRuleStrings.push(signalRuleString); this.#signalRulesMap.delete(rule); }); return deprecatedSignalRuleStrings.forEach((deprecatedSignalRuleString) => this.offSignal(deprecatedSignalRuleString)); case DBusMessageType_1.DBusMessageType.METHOD_CALL: this.emit('methodCall', message); return; } }) .on('close', () => this.emit('connectionClose')) .on('error', (error) => this.emit('connectionError', error)); } /** * Fallback overload for the 'on' method to ensure compatibility with the base EventEmitter class. * This is a catch-all signature for any event name and listener combination. * * @param eventName - Any string representing an event name. * @param listener - Any callback function with variable arguments. * @returns This instance for method chaining. */ on(eventName, listener) { super.on(eventName, listener); return this; } /** * Fallback overload for the 'once' method to ensure compatibility with the base EventEmitter class. * This is a catch-all signature for any event name and listener combination. * * @param eventName - Any string representing an event name. * @param listener - Any callback function with variable arguments. * @returns This instance for method chaining. */ once(eventName, listener) { super.once(eventName, listener); return this; } /** * Fallback overload for the 'off' method to ensure compatibility with the base EventEmitter class. * This is a catch-all signature for any event name and listener combination. * * @param eventName - Any string representing an event name. * @param listener - Any callback function with variable arguments. * @returns This instance for method chaining. */ off(eventName, listener) { super.off(eventName, listener); return this; } /** * Fallback overload for the 'removeListener' method to ensure compatibility with the base EventEmitter class. * This is a catch-all signature for any event name and listener combination. * * @param eventName - Any string representing an event name. * @param listener - Any callback function with variable arguments. * @returns This instance for method chaining. */ removeListener(eventName, listener) { super.removeListener(eventName, listener); return this; } /** * Adds a match rule to receive specific signals from the DBus daemon. * Sends a method call to the DBus daemon to register the rule without waiting for a reply. * * @param rule - The match rule string specifying which signals to receive (e.g., 'type=signal,interface=org.test'). */ addMatch(rule) { this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', method: 'AddMatch', signature: 's', args: [rule] }, true); } /** * Removes a previously added match rule from the DBus daemon. * Sends a method call to the DBus daemon to unregister the rule without waiting for a reply. * * @param rule - The match rule string to remove, previously added with addMatch. */ removeMatch(rule) { this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', method: 'RemoveMatch', signature: 's', args: [rule] }, true); } /** * Retrieves the unique name of the owner of a given bus name. * Queries the DBus daemon to get the current owner of a well-known bus name. * * @param name - The bus name to query (e.g., 'org.freedesktop.DBus'). * @returns A Promise resolving to the unique name of the owner (e.g., ':1.123') or undefined if not found or an error occurs. */ async getNameOwner(name) { try { const [owner] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', method: 'GetNameOwner', signature: 's', args: [name] }); return owner instanceof DBusTypeClass_1.DBusTypeClass ? owner.value : owner; } catch (e) { return undefined; } } /** * Lists all activatable bus names (services that can be started). * Retrieves a list of service names that are registered as activatable with the DBus daemon. * * @returns A Promise resolving to an array of activatable bus names (e.g., ['org.test.Service']). */ async listActivatableNames() { const [activatableNames] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', method: 'ListActivatableNames' }); return activatableNames instanceof DBusTypeClass_1.DBusTypeClass ? activatableNames.value : activatableNames; } /** * Lists all currently active bus names. * Retrieves a list of service names that are currently active (have an owner) on the bus. * * @returns A Promise resolving to an array of active bus names (e.g., ['org.test.Service', ':1.123']). */ async listNames() { const [names] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', method: 'ListNames' }); return names instanceof DBusTypeClass_1.DBusTypeClass ? names.value : names; } /** * Checks if a given bus name currently has an owner. * Queries the DBus daemon to determine if a specific bus name is currently owned by a connection. * * @param name - The bus name to check (e.g., 'org.test.Service'). * @returns A Promise resolving to a boolean indicating if the name has an owner (true) or not (false). */ async nameHasOwner(name) { const [hasOwner] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', signature: 's', method: 'NameHasOwner', args: [name] }); return hasOwner instanceof DBusTypeClass_1.DBusTypeClass ? hasOwner.value : hasOwner; } /** * Requests ownership of a bus name with specified flags. * Attempts to acquire a well-known bus name for this connection, with options for behavior on conflict. * * @param name - The bus name to request (e.g., 'org.my.Service'). * @param flags - Optional flags for the request behavior (default: RequestNameFlags.DBUS_NAME_FLAG_DEFAULT). * @returns A Promise resolving to a result code indicating success or the reason for failure (e.g., RequestNameResultCode.DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER). */ async requestName(name, flags = RequestNameFlags_1.RequestNameFlags.DBUS_NAME_FLAG_DEFAULT) { const [res] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', signature: 'su', method: 'RequestName', args: [name, flags] }); return res instanceof DBusTypeClass_1.DBusTypeClass ? res.value : res; } /** * Releases ownership of a bus name. * Releases a previously acquired well-known bus name, allowing others to claim it. * * @param name - The bus name to release (e.g., 'org.my.Service'). * @returns A Promise resolving to a numeric result code indicating the outcome of the release operation. */ async releaseName(name) { const [res] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', signature: 's', method: 'ReleaseName', args: [name] }); return res instanceof DBusTypeClass_1.DBusTypeClass ? res.value : res; } /** * Reloads the DBus daemon configuration. * Requests the DBus daemon to reload its configuration files, typically for administrative purposes. * * @returns A Promise that resolves when the configuration reload request is sent and processed. */ async reloadConfig() { await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', method: 'ReloadConfig' }); } /** * Starts a service by its bus name. * Requests the DBus daemon to activate a service if it is activatable and not currently running. * * @param name - The bus name of the service to start (e.g., 'org.my.Service'). * @param flags - Optional flags for starting the service (default: 0, no specific flags). * @returns A Promise resolving to a numeric result code indicating the outcome of the start operation. */ async startServiceByName(name, flags = 0) { const [res] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', signature: 'su', method: 'StartServiceByName', args: [name, flags] }); return res instanceof DBusTypeClass_1.DBusTypeClass ? res.value : res; } /** * Retrieves the Unix process ID of a connection by its bus name. * Queries the DBus daemon to get the PID of the process owning a specific bus name. * * @param name - The bus name of the connection (e.g., 'org.my.Service' or ':1.123'). * @returns A Promise resolving to the process ID as a number, or undefined if not found or an error occurs. */ async getConnectionUnixProcessID(name) { try { const [pid] = await this.invoke({ service: 'org.freedesktop.DBus', objectPath: '/org/freedesktop/DBus', interface: 'org.freedesktop.DBus', method: 'GetConnectionUnixProcessID', signature: 's', args: [name] }); return pid instanceof DBusTypeClass_1.DBusTypeClass ? pid.value : pid; } catch (e) { return undefined; } } /** * Disconnects from the DBus daemon. * Closes the underlying connection stream, ending communication with the daemon. * * @returns A Promise that resolves when the connection is fully closed. */ async disconnect() { await new Promise(resolve => this.#connection.end(resolve)); } /** * Lists all bus names, including active and activatable ones, with detailed information. * Combines active and activatable names, fetching additional details like owner and PID. * * @returns A Promise resolving to an array of bus name information objects, including name, unique name, active status, activatable status, and PID. */ async listBusNames() { const [activeNames, activatableNames] = await Promise.all([ this.listNames(), this.listActivatableNames() ]); const names = [...new Set([...activeNames, ...activatableNames])]; return await Promise.all(names.map((name) => { return new Promise(async (resolve) => { const [uniqueName, pid] = await Promise.all([ this.getNameOwner(name), this.getConnectionUnixProcessID(name) ]); return resolve({ name: name, uniqueName: uniqueName, active: activeNames.includes(name), activatable: activatableNames.includes(name), pid: pid }); }); })); } /** * Lists all services (bus names that are not unique connection names). * Filters out unique connection names (e.g., ':1.123') to return only well-known service names. * * @returns A Promise resolving to an array of service information objects, excluding unique connection names. */ async listServices() { const busNameInfos = await this.listBusNames(); return busNameInfos.filter((busNameInfo) => busNameInfo.name !== busNameInfo.uniqueName); } /** * Retrieves all services as DBusService instances. * Converts the list of service information into instantiated DBusService objects for interaction. * * @returns A Promise resolving to an array of DBusService instances representing all available services. */ async getServices() { const serviceBasicInfos = await this.listServices(); return Promise.all(serviceBasicInfos.map((serviceBasicInfo) => this.getService(serviceBasicInfo.name))); } /** * Retrieves a specific service by its bus name. * Starts the service if it is activatable but not currently active, and throws an error if not found. * * @param service - The bus name of the service to retrieve (e.g., 'org.my.Service'). * @returns A Promise resolving to a DBusService instance for the specified service. * @throws {ServiceNotFoundError} If the service is not found in active or activatable lists, or if it has no connection. */ async getService(service) { const [activeNames, activatableNames] = await Promise.all([ this.listNames(), this.listActivatableNames() ]); if (!activeNames.includes(service) && !activatableNames.includes(service)) throw new Errors_1.ServiceNotFoundError(`Service ${service} not found`); if (activatableNames.includes(service) && !activeNames.includes(service)) await this.startServiceByName(service); const uniqueName = await this.getNameOwner(service); if (!uniqueName) throw new Errors_1.ServiceNotFoundError(`Service ${service} has not connection`); const dbusService = new DBusService_1.DBusService({ dbus: this, service: service, uniqueName: uniqueName }); this.#weakServiceSet.add(new WeakRef(dbusService)); return dbusService; } /** * Retrieves a specific DBus object by service and object path. * Fetches the service first, then constructs or retrieves the object at the specified path. * * @param service - The bus name of the service (e.g., 'org.my.Service'). * @param objectPath - The object path to retrieve (e.g., '/org/my/Object'). * @returns A Promise resolving to a DBusObject instance for the specified path under the service. */ async getObject(service, objectPath) { return (await this.getService(service)).getObject(objectPath); } /** * Retrieves a specific DBus interface by service, object path, and interface name. * Fetches the object first, then retrieves or constructs the specified interface on that object. * * @param service - The bus name of the service (e.g., 'org.my.Service'). * @param objectPath - The object path of the object (e.g., '/org/my/Object'). * @param iface - The interface name to retrieve (e.g., 'org.my.Interface'). * @returns A Promise resolving to a DBusInterface instance for the specified interface. */ async getInterface(service, objectPath, iface) { return (await this.getObject(service, objectPath)).getInterface(iface); } } exports.DBus = DBus;