File indexing completed on 2024-05-05 17:34:41

0001 /*
0002     SPDX-FileCopyrightText: 2019 David Edmundson <davidedmundson@kde.org>
0003     SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "daemon.h"
0009 
0010 #include <algorithm>
0011 #include <chrono>
0012 
0013 #include <QDBusConnection>
0014 #include <QDBusMetaType>
0015 #include <QDBusServiceWatcher>
0016 
0017 #include <QTimer>
0018 
0019 #include <systemstats/SensorPlugin.h>
0020 #include <systemstats/SensorObject.h>
0021 #include <systemstats/SensorContainer.h>
0022 #include <systemstats/SensorProperty.h>
0023 
0024 #include <KDBusService>
0025 #include <KPluginMetaData>
0026 #include <KPluginFactory>
0027 
0028 #ifdef Q_OS_LINUX
0029 #include <sensors/sensors.h>
0030 #endif
0031 
0032 #include "ksystemstatsadaptor.h"
0033 
0034 #include "client.h"
0035 
0036 constexpr auto UpdateRate = std::chrono::milliseconds{500};
0037 
0038 Daemon::Daemon()
0039     : m_serviceWatcher(new QDBusServiceWatcher(this))
0040 {
0041     qDBusRegisterMetaType<KSysGuard::SensorData>();
0042     qDBusRegisterMetaType<KSysGuard::SensorInfo>();
0043     qRegisterMetaType<KSysGuard::SensorDataList>("SDL");
0044     qDBusRegisterMetaType<KSysGuard::SensorDataList>();
0045     qDBusRegisterMetaType<KSysGuard::SensorInfoMap>();
0046     qDBusRegisterMetaType<QStringList>();
0047 
0048     new KsystemstatsAdaptor(this);
0049 
0050     m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
0051     connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &Daemon::onServiceDisconnected);
0052 
0053     auto timer = new QTimer(this);
0054     timer->setInterval(UpdateRate);
0055     connect(timer, &QTimer::timeout, this, &Daemon::sendFrame);
0056     timer->start();
0057 }
0058 
0059 Daemon::~Daemon()
0060 {
0061     for (Client* c : m_clients) {
0062         delete c;
0063     }
0064 #ifdef Q_OS_LINUX
0065     sensors_cleanup();
0066 #endif
0067 }
0068 
0069 void Daemon::init(ReplaceIfRunning replaceIfRunning)
0070 {
0071 #ifdef Q_OS_LINUX
0072     sensors_init(nullptr);
0073 #endif
0074     loadProviders();
0075     KDBusService::StartupOptions options = KDBusService::Unique;
0076     if (replaceIfRunning == ReplaceIfRunning::Replace) {
0077         options |= KDBusService::Replace;
0078     }
0079     QDBusConnection::sessionBus().registerObject("/", this, QDBusConnection::ExportAdaptors);
0080     auto service = new KDBusService(options , this);
0081     service->setExitValue(1);
0082 }
0083 
0084 void Daemon::setQuitOnLastClientDisconnect(bool quit)
0085 {
0086     m_quitOnLastClientDisconnect = quit;
0087 }
0088 
0089 void Daemon::loadProviders()
0090 {
0091     const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("ksystemstats"));
0092     if (plugins.isEmpty()) {
0093         qWarning() << "No plugins found";
0094     }
0095     for (const KPluginMetaData &metaData : plugins) {
0096         auto provider = KPluginFactory::instantiatePlugin<KSysGuard::SensorPlugin>(metaData);
0097         if (!provider.plugin) {
0098             qWarning() << "Could not load plugin:" << metaData.pluginId() << "with file name" << metaData.fileName();
0099         } else {
0100             registerProvider(provider.plugin);
0101             qDebug() << "Loaded plugin" << metaData.pluginId() << "from file" << metaData.fileName();
0102         }
0103     }
0104 }
0105 
0106 void Daemon::registerProvider(KSysGuard::SensorPlugin *provider) {
0107     bool alreadyHasProvider = std::any_of(m_providers.begin(), m_providers.end(), [provider](KSysGuard::SensorPlugin *provider2){
0108         return provider2->providerName() == provider->providerName();
0109     });
0110     if (alreadyHasProvider) {
0111         qWarning() << "Provider" << provider->providerName() << "is already registered";
0112         return;
0113     }
0114     m_providers.append(provider);
0115     const auto containers = provider->containers();
0116     for (auto container : containers) {
0117         m_containers[container->id()] = container;
0118         connect(container, &KSysGuard::SensorContainer::objectAdded, this, [this](KSysGuard::SensorObject *obj) {
0119             for (auto sensor: obj->sensors()) {
0120                 emit sensorAdded(sensor->path());
0121             }
0122         });
0123         connect(container, &KSysGuard::SensorContainer::objectRemoved, this, [this](KSysGuard::SensorObject *obj) {
0124             for (auto sensor: obj->sensors()) {
0125                 emit sensorRemoved(sensor->path());
0126             }
0127         });
0128     }
0129 }
0130 
0131 KSysGuard::SensorInfoMap Daemon::allSensors() const
0132 {
0133     KSysGuard::SensorInfoMap infoMap;
0134     for (auto c : qAsConst(m_containers)) {
0135         auto containerInfo = KSysGuard::SensorInfo{};
0136         containerInfo.name = c->name();
0137         infoMap.insert(c->id(), containerInfo);
0138 
0139         const auto objects = c->objects();
0140         for(auto object : objects) {
0141             auto objectInfo = KSysGuard::SensorInfo{};
0142             objectInfo.name = object->name();
0143             infoMap.insert(object->path(), objectInfo);
0144 
0145             const auto sensors = object->sensors();
0146             for (auto sensor : sensors) {
0147                 infoMap[sensor->path()] = sensor->info();
0148             }
0149         }
0150     }
0151     return infoMap;
0152 }
0153 
0154 KSysGuard::SensorInfoMap Daemon::sensors(const QStringList &sensorPaths) const
0155 {
0156     KSysGuard::SensorInfoMap si;
0157     for (const QString &path : sensorPaths) {
0158         if (auto sensor = findSensor(path)) {
0159             si[path] = sensor->info();
0160         }
0161     }
0162     return si;
0163 }
0164 
0165 void Daemon::subscribe(const QStringList &sensorIds)
0166 {
0167     const QString sender = QDBusContext::message().service();
0168     m_serviceWatcher->addWatchedService(sender);
0169 
0170     Client *client = m_clients.value(sender);
0171     if (!client) {
0172         client = new Client(this, sender);
0173         m_clients[sender] = client;
0174     }
0175     client->subscribeSensors(sensorIds);
0176 }
0177 
0178 void Daemon::unsubscribe(const QStringList &sensorIds)
0179 {
0180     const QString sender = QDBusContext::message().service();
0181     Client *client = m_clients.value(sender);
0182     if (!client) {
0183         return;
0184     }
0185     client->unsubscribeSensors(sensorIds);
0186 }
0187 
0188 KSysGuard::SensorDataList Daemon::sensorData(const QStringList &sensorIds)
0189 {
0190     KSysGuard::SensorDataList sensorData;
0191     for (const QString &sensorId: sensorIds) {
0192         if (KSysGuard::SensorProperty *sensorProperty = findSensor(sensorId)) {
0193             const QVariant value = sensorProperty->value();
0194             if (value.isValid()) {
0195                 sensorData << KSysGuard::SensorData(sensorId, value);
0196             }
0197         }
0198     }
0199     return sensorData;
0200 }
0201 
0202 KSysGuard::SensorProperty *Daemon::findSensor(const QString &path) const
0203 {
0204     int subsystemIndex = path.indexOf('/');
0205     int propertyIndex = path.lastIndexOf('/');
0206 
0207     const QString subsystem = path.left(subsystemIndex);
0208     const QString object = path.mid(subsystemIndex + 1, propertyIndex - (subsystemIndex + 1));
0209     const QString property = path.mid(propertyIndex + 1);
0210 
0211     auto c = m_containers.value(subsystem);
0212     if (!c) {
0213         return nullptr;
0214     }
0215     auto o = c->object(object);
0216     if (!o) {
0217         return nullptr;
0218     }
0219     return o->sensor(property);
0220 }
0221 
0222 void Daemon::onServiceDisconnected(const QString &service)
0223 {
0224     delete m_clients.take(service);
0225     if (m_clients.isEmpty() && m_quitOnLastClientDisconnect) {
0226         QCoreApplication::quit();
0227     };
0228 }
0229 
0230 void Daemon::sendFrame()
0231 {
0232     for (auto provider : qAsConst(m_providers)) {
0233         provider->update();
0234     }
0235 
0236     for (auto client: qAsConst(m_clients)) {
0237         client->sendFrame();
0238     }
0239 }