130 lines
6.9 KiB
C++
130 lines
6.9 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#include "hardware.h"
|
|
#include "fan.h"
|
|
#include "cpu.h"
|
|
#include <KAuth/ActionReply>
|
|
#include <KAuth/HelperSupport>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QJsonDocument>
|
|
#include <QProcess>
|
|
#include <QSaveFile>
|
|
|
|
class Helper : public QObject {
|
|
Q_OBJECT
|
|
KAuth::ActionReply reply(const QString &error, const QVariantMap &data = {}) {
|
|
if (error.isEmpty()) { auto r = KAuth::ActionReply::SuccessReply(); r.setData(data); return r; }
|
|
auto r = KAuth::ActionReply::HelperErrorReply(); r.setErrorDescription(error); return r;
|
|
}
|
|
QString service(const QString &verb, const QString &unit = "framework-laptop-tools-fan.service") {
|
|
QProcess process;
|
|
process.start("/usr/bin/systemctl", {verb, unit});
|
|
if (!process.waitForFinished(25000)) { process.kill(); process.waitForFinished(); return "Service did not respond."; }
|
|
return process.exitCode() == 0 ? QString() : QString::fromUtf8(process.readAllStandardError());
|
|
}
|
|
public Q_SLOTS:
|
|
KAuth::ActionReply inspect(const QVariantMap &) {
|
|
const auto error = compatibilityError();
|
|
if (!error.isEmpty()) return reply(error);
|
|
auto data = firmwareStatus();
|
|
QString cpuError;
|
|
data["cpuConfig"] = readCpuConfig(cpuError);
|
|
if (!cpuError.isEmpty()) data["cpuError"] = cpuError;
|
|
const auto path = ecHwmon();
|
|
const auto mode = readNumber(path + "/pwm1_enable");
|
|
if (mode == std::optional<double>(2)) data["fanMode"] = "auto";
|
|
else if (mode == std::optional<double>(1)) {
|
|
data["fanMode"] = "manual";
|
|
const auto pwm = readNumber(path + "/pwm1");
|
|
if (pwm) data["fanDuty"] = qRound(*pwm * 100 / 255);
|
|
QString fanError;
|
|
const auto config = readFanConfig(fanError);
|
|
if (!fanError.isEmpty()) data["fanError"] = fanError;
|
|
if (fanError.isEmpty()) {
|
|
if (config.value("mode") == "curve") {
|
|
data["fanMode"] = "curve";
|
|
data["fanCurve"] = config["curve"];
|
|
QVariantList temperatures;
|
|
for (const double t : fanTemperatures(config)) temperatures.append(t);
|
|
data["fanTemperatures"] = temperatures;
|
|
}
|
|
}
|
|
}
|
|
data["fanPersistent"] = QFile::exists(savedFanConfigPath);
|
|
return reply({}, data);
|
|
}
|
|
KAuth::ActionReply configure(const QVariantMap &args) {
|
|
QString error = compatibilityError();
|
|
if (!error.isEmpty()) return reply(error);
|
|
const QString operation = args.value("operation").toString();
|
|
if (operation == "batch") {
|
|
const auto operations = args.value("operations").toList();
|
|
if (operations.isEmpty() || operations.size() > 6) return reply("Invalid settings batch.");
|
|
const QStringList allowed{"keyboard", "powerAuto", "powerBrightness", "chargeLimit", "chargeWatts", "fan", "cpuProfiles"};
|
|
for (const auto &entry : operations)
|
|
if (!allowed.contains(entry.toMap().value("operation").toString())) return reply("Unknown operation in settings batch.");
|
|
QVariantList completed;
|
|
for (const auto &entry : operations) {
|
|
const auto result = configure(entry.toMap());
|
|
if (result.failed()) return reply({}, {{"completed", completed}, {"applyError", result.errorDescription()}});
|
|
completed.append(entry);
|
|
}
|
|
return reply({}, {{"completed", completed}});
|
|
}
|
|
bool valid;
|
|
const double raw = args.value("value").toDouble(&valid);
|
|
const int value = valid && raw >= 0 && raw <= 10000 && raw == int(raw) ? int(raw) : -1;
|
|
if (operation == "keyboard" && value >= 0 && value <= 100)
|
|
error = writeNumber("/sys/class/leds/chromeos::kbd_backlight/brightness", value);
|
|
else if (operation == "cpuProfiles") {
|
|
const auto config = normalizeCpuConfig(args.value("config").toMap());
|
|
error = validateCpuConfig(config);
|
|
if (!error.isEmpty()) return reply(error);
|
|
if (!QDir().mkpath("/etc/framework-laptop-tools")) return reply("Cannot create CPU configuration directory.");
|
|
QSaveFile file(cpuConfigPath);
|
|
if (!file.open(QIODevice::WriteOnly)) return reply(file.errorString());
|
|
file.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
|
|
const auto data = QJsonDocument::fromVariant(config).toJson();
|
|
if (file.write(data) != data.size() || !file.commit()) return reply(file.errorString());
|
|
const QString unit = "framework-laptop-tools-cpu.service";
|
|
if (hasCpuOverrides(config)) {
|
|
error = service("enable", unit);
|
|
if (error.isEmpty()) error = service("restart", unit);
|
|
} else {
|
|
error = service("disable", unit);
|
|
if (error.isEmpty()) error = service("stop", unit);
|
|
}
|
|
if (!error.isEmpty()) error = "CPU settings were saved, but applying them failed: " + error;
|
|
} else if (operation == "fan") {
|
|
error = validateFan(args);
|
|
if (!error.isEmpty()) return reply(error);
|
|
// Restore the old worker before replacing its configuration.
|
|
error = service("stop");
|
|
if (!error.isEmpty()) return reply(error);
|
|
if (QFile::exists(savedFanConfigPath) && !QFile::remove(savedFanConfigPath)) return reply("Cannot remove saved fan settings.");
|
|
const bool persistent = args.value("mode") != "auto";
|
|
error = service(persistent ? "enable" : "disable");
|
|
if (error.isEmpty()) error = service(persistent ? "enable" : "disable", "framework-laptop-tools-fan-resume.service");
|
|
if (!error.isEmpty()) return reply(error);
|
|
if (args.value("mode") == "auto") error = restoreFan();
|
|
else {
|
|
const QString path = persistent ? savedFanConfigPath : fanConfigPath;
|
|
if (!QDir().mkpath(QFileInfo(path).absolutePath())) error = "Cannot create fan settings directory.";
|
|
if (error.isEmpty()) {
|
|
QSaveFile file(path);
|
|
if (!file.open(QIODevice::WriteOnly)) return reply(file.errorString());
|
|
file.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
|
|
const auto data = QJsonDocument::fromVariant(args).toJson(QJsonDocument::Compact);
|
|
if (file.write(data) != data.size() || !file.commit()) return reply(file.errorString());
|
|
error = service("start");
|
|
}
|
|
}
|
|
} else if (operation == "powerAuto") error = setFirmwareValue(operation, 0);
|
|
else if (value >= 0) error = setFirmwareValue(operation, value);
|
|
else error = "Unknown operation or invalid value.";
|
|
return reply(error);
|
|
}
|
|
};
|
|
KAUTH_HELPER_MAIN("se.ajpanton.frameworktools", Helper)
|
|
#include "helper.moc"
|