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
JavaScript
"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;