* Copyright (C) 2022 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
import cockpit from "cockpit";
import { getInstallationStatusAction, getPendingErrorAction } from "../actions/boss-actions.js";
import { debug, error } from "../helpers/log.js";
import { _callClient, _getProperty } from "./helpers.js";
import { moduleClients } from "./index.js";
import { installationState } from "./installation_state.js";
import { LocalizationClient } from "./localization.js";
import { NetworkClient } from "./network.js";
import { RuntimeClient } from "./runtime.js";
const OBJECT_PATH = "/org/fedoraproject/Anaconda/Boss";
const INTERFACE_NAME = "org.fedoraproject.Anaconda.Boss";
export const INSTALLATION_STATUS = { FAILED: "failed", NOT_STARTED: "not_started", RUNNING: "running", SUCCEEDED: "succeeded" };
const callClient = (...args) => {
return _callClient(BossClient, OBJECT_PATH, INTERFACE_NAME, ...args);
};
* @param {string} address Anaconda bus address
* @param {Function} dispatch Redux dispatch function
*
* @returns {Object} A DBus client for the Boss bus
*/
export class BossClient {
constructor (address, dispatch) {
if (BossClient.instance && (!address || BossClient.instance.address === address)) {
return BossClient.instance;
}
BossClient.instance?.client.close();
BossClient.instance = this;
this.client = cockpit.dbus(
INTERFACE_NAME,
{ address, bus: "none", superuser: "try" }
);
this.address = address;
this.dispatch = dispatch;
}
async init (args = {}) {
this.client.addEventListener("close", () => error("Boss client closed"));
const status = await getInstallationStatus();
const installationStarted = status !== INSTALLATION_STATUS.NOT_STARTED;
installationState.active = installationStarted;
const clients = installationStarted ? [RuntimeClient, NetworkClient, LocalizationClient] : moduleClients;
return Promise.all([
this.dispatch(getInstallationStatusAction()),
this.dispatch(getPendingErrorAction()),
...clients.map(Client => new Client(this.address, this.dispatch).init(args))
]).then(() => {
this.startEventMonitor();
});
}
startEventMonitor () {
this._subscription = this.client.subscribe(
{ },
(path, iface, signal, args) => {
if (signal === "PropertiesChanged" &&
args[0] === INTERFACE_NAME) {
if (Object.hasOwn(args[1], "InstallationStatus")) {
debug("Boss: InstallationStatus property changed");
this.dispatch(getInstallationStatusAction());
}
if (Object.hasOwn(args[1], "ActiveInstallationTask") &&
args[1].ActiveInstallationTask.v) {
installationState.active = true;
for (const Client of moduleClients) {
Client.instance?.stopEventMonitor();
}
}
}
}
);
}
stopEventMonitor () {
this._subscription?.remove();
}
}
* @param {string} task DBus path to a task
*
* @returns {Promise} Resolves the total number of tasks
*/
export const getSteps = ({ task }) => {
return new BossClient().client.call(
task,
"org.freedesktop.DBus.Properties",
"Get",
["org.fedoraproject.Anaconda.Task", "Steps"]
)
.then(ret => ret[0]);
};
* @returns {Promise} Resolves the object path of the active installation task, or ""
*/
export const getActiveInstallationTask = () => {
return _getProperty(BossClient, OBJECT_PATH, INTERFACE_NAME, "ActiveInstallationTask");
};
* @returns {Promise} Resolves a list of tasks
*/
export const installWithTasks = () => {
return callClient("InstallWithTasks", []);
};
* @param {string} locale Locale id
*/
export const setLocale = ({ locale }) => {
return callClient("SetLocale", [locale]);
};
* @returns {Promise} Resolves the installation status enum value
*/
export const getInstallationStatus = () => {
return _getProperty(BossClient, OBJECT_PATH, INTERFACE_NAME, "InstallationStatus");
};
* @returns {Promise} Resolves the pending error message, or ""
*/
export const getPendingErrorMessage = () => {
return _getProperty(BossClient, OBJECT_PATH, INTERFACE_NAME, "PendingErrorMessage");
};
* @returns {Promise} Resolves the pending error type
*/
export const getPendingErrorType = () => {
return _getProperty(BossClient, OBJECT_PATH, INTERFACE_NAME, "PendingErrorType");
};