Warning, file /plasma/ksystemstats/src/daemon.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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/DBusInterface.h>
0020 #include <systemstats/SensorPlugin.h>
0021 #include <systemstats/SensorObject.h>
0022 #include <systemstats/SensorContainer.h>
0023 #include <systemstats/SensorProperty.h>
0024 
0025 #include <KPluginMetaData>
0026 #include <KPluginFactory>
0027 
0028 #ifdef Q_OS_LINUX
0029 #include <sensors/sensors.h>
0030 #endif
0031 
0032 #include "ksystemstats1adaptor.h"
0033 
0034 #include "client.h"
0035 
0036 using namespace Qt::StringLiterals;
0037 
0038 constexpr auto UpdateRate = std::chrono::milliseconds{500};
0039 
0040 Daemon::Daemon()
0041     : m_serviceWatcher(new QDBusServiceWatcher(this))
0042 {
0043     qDBusRegisterMetaType<KSysGuard::SensorData>();
0044     qDBusRegisterMetaType<KSysGuard::SensorInfo>();
0045     qRegisterMetaType<KSysGuard::SensorDataList>("SDL");
0046     qDBusRegisterMetaType<KSysGuard::SensorDataList>();
0047     qDBusRegisterMetaType<KSysGuard::SensorInfoMap>();
0048     qDBusRegisterMetaType<QStringList>();
0049 
0050     new Ksystemstats1Adaptor(this);
0051 
0052     m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
0053     connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &Daemon::onServiceDisconnected);
0054 
0055     auto timer = new QTimer(this);
0056     timer->setInterval(UpdateRate);
0057     connect(timer, &QTimer::timeout, this, &Daemon::sendFrame);
0058     timer->start();
0059 }
0060 
0061 Daemon::~Daemon()
0062 {
0063     for (Client* c : m_clients) {
0064         delete c;
0065     }
0066 #ifdef Q_OS_LINUX
0067     sensors_cleanup();
0068 #endif
0069 }
0070 
0071 bool Daemon::init(ReplaceIfRunning replaceIfRunning)
0072 {
0073 #ifdef Q_OS_LINUX
0074     sensors_init(nullptr);
0075 #endif
0076     loadProviders();
0077 
0078     QDBusConnection::sessionBus().registerObject(KSysGuard::SystemStats::ObjectPath, this, QDBusConnection::ExportAdaptors);
0079 
0080     if (!registerDBusService(KSysGuard::SystemStats::ServiceName, replaceIfRunning)) {
0081         return false;
0082     }
0083 
0084     return true;
0085 }
0086 
0087 void Daemon::setQuitOnLastClientDisconnect(bool quit)
0088 {
0089     m_quitOnLastClientDisconnect = quit;
0090 }
0091 
0092 void Daemon::loadProviders()
0093 {
0094     const QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("ksystemstats"));
0095     if (plugins.isEmpty()) {
0096         qWarning() << "No plugins found";
0097     }
0098     for (const KPluginMetaData &metaData : plugins) {
0099         auto provider = KPluginFactory::instantiatePlugin<KSysGuard::SensorPlugin>(metaData);
0100         if (!provider.plugin) {
0101             qWarning() << "Could not load plugin:" << metaData.pluginId() << "with file name" << metaData.fileName();
0102         } else {
0103             registerProvider(provider.plugin);
0104             qDebug() << "Loaded plugin" << metaData.pluginId() << "from file" << metaData.fileName();
0105         }
0106     }
0107 }
0108 
0109 void Daemon::registerProvider(KSysGuard::SensorPlugin *provider) {
0110     bool alreadyHasProvider = std::any_of(m_providers.begin(), m_providers.end(), [provider](KSysGuard::SensorPlugin *provider2){
0111         return provider2->providerName() == provider->providerName();
0112     });
0113     if (alreadyHasProvider) {
0114         qWarning() << "Provider" << provider->providerName() << "is already registered";
0115         return;
0116     }
0117     m_providers.append(provider);
0118     const auto containers = provider->containers();
0119     for (auto container : containers) {
0120         m_containers[container->id()] = container;
0121         connect(container, &KSysGuard::SensorContainer::objectAdded, this, [this](KSysGuard::SensorObject *obj) {
0122             for (auto sensor: obj->sensors()) {
0123                 emit sensorAdded(sensor->path());
0124             }
0125         });
0126         connect(container, &KSysGuard::SensorContainer::objectRemoved, this, [this](KSysGuard::SensorObject *obj) {
0127             for (auto sensor: obj->sensors()) {
0128                 emit sensorRemoved(sensor->path());
0129             }
0130         });
0131     }
0132 }
0133 
0134 KSysGuard::SensorInfoMap Daemon::allSensors() const
0135 {
0136     KSysGuard::SensorInfoMap infoMap;
0137     for (auto c : std::as_const(m_containers)) {
0138         auto containerInfo = KSysGuard::SensorInfo{};
0139         containerInfo.name = c->name();
0140         infoMap.insert(c->id(), containerInfo);
0141 
0142         const auto objects = c->objects();
0143         for(auto object : objects) {
0144             auto objectInfo = KSysGuard::SensorInfo{};
0145             objectInfo.name = object->name();
0146             infoMap.insert(object->path(), objectInfo);
0147 
0148             const auto sensors = object->sensors();
0149             for (auto sensor : sensors) {
0150                 infoMap[sensor->path()] = sensor->info();
0151             }
0152         }
0153     }
0154     return infoMap;
0155 }
0156 
0157 KSysGuard::SensorInfoMap Daemon::sensors(const QStringList &sensorPaths) const
0158 {
0159     KSysGuard::SensorInfoMap si;
0160     for (const QString &path : sensorPaths) {
0161         if (auto sensor = findSensor(path)) {
0162             si[path] = sensor->info();
0163         }
0164     }
0165     return si;
0166 }
0167 
0168 void Daemon::subscribe(const QStringList &sensorIds)
0169 {
0170     const QString sender = QDBusContext::message().service();
0171     m_serviceWatcher->addWatchedService(sender);
0172 
0173     Client *client = m_clients.value(sender);
0174     if (!client) {
0175         client = new Client(this, sender);
0176         m_clients[sender] = client;
0177     }
0178     client->subscribeSensors(sensorIds);
0179 }
0180 
0181 void Daemon::unsubscribe(const QStringList &sensorIds)
0182 {
0183     const QString sender = QDBusContext::message().service();
0184     Client *client = m_clients.value(sender);
0185     if (!client) {
0186         return;
0187     }
0188     client->unsubscribeSensors(sensorIds);
0189 }
0190 
0191 KSysGuard::SensorDataList Daemon::sensorData(const QStringList &sensorIds)
0192 {
0193     KSysGuard::SensorDataList sensorData;
0194     for (const QString &sensorId: sensorIds) {
0195         if (KSysGuard::SensorProperty *sensorProperty = findSensor(sensorId)) {
0196             const QVariant value = sensorProperty->value();
0197             if (value.isValid()) {
0198                 sensorData << KSysGuard::SensorData(sensorId, value);
0199             }
0200         }
0201     }
0202     return sensorData;
0203 }
0204 
0205 KSysGuard::SensorProperty *Daemon::findSensor(const QString &path) const
0206 {
0207     int subsystemIndex = path.indexOf('/');
0208     int propertyIndex = path.lastIndexOf('/');
0209 
0210     const QString subsystem = path.left(subsystemIndex);
0211     const QString object = path.mid(subsystemIndex + 1, propertyIndex - (subsystemIndex + 1));
0212     const QString property = path.mid(propertyIndex + 1);
0213 
0214     auto c = m_containers.value(subsystem);
0215     if (!c) {
0216         return nullptr;
0217     }
0218     auto o = c->object(object);
0219     if (!o) {
0220         return nullptr;
0221     }
0222     return o->sensor(property);
0223 }
0224 
0225 void Daemon::onServiceDisconnected(const QString &service)
0226 {
0227     if (service == KSysGuard::SystemStats::ServiceName) {
0228         return;
0229     }
0230 
0231     delete m_clients.take(service);
0232     if (m_clients.isEmpty() && m_quitOnLastClientDisconnect) {
0233         QCoreApplication::quit();
0234     };
0235 }
0236 
0237 bool Daemon::registerDBusService(const QString& serviceName, ReplaceIfRunning replace)
0238 {
0239     auto interface = QDBusConnection::sessionBus().interface();
0240 
0241     if (interface->isServiceRegistered(serviceName) && replace != ReplaceIfRunning::Replace) {
0242         qWarning() << "ksystemstats is already running";
0243         return false;
0244     }
0245 
0246     connect(interface, &QDBusConnectionInterface::serviceUnregistered, this, [](const QString &service) {
0247         if (service == KSysGuard::SystemStats::ServiceName) {
0248             QCoreApplication::instance()->quit();
0249         }
0250     });
0251 
0252     auto result = interface->registerService(KSysGuard::SystemStats::ServiceName, QDBusConnectionInterface::ReplaceExistingService, QDBusConnectionInterface::AllowReplacement);
0253     if (result != QDBusConnectionInterface::ServiceRegistered) {
0254         qWarning() << "Could not register name" << KSysGuard::SystemStats::ServiceName;
0255         return false;
0256     }
0257 
0258     return true;
0259 }
0260 
0261 void Daemon::sendFrame()
0262 {
0263     for (auto provider : std::as_const(m_providers)) {
0264         provider->update();
0265     }
0266 
0267     for (auto client: std::as_const(m_clients)) {
0268         client->sendFrame();
0269     }
0270 }
0271 
0272 #include "moc_daemon.cpp"