File indexing completed on 2024-04-14 05:26:59
0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0002 // SPDX-FileCopyrightText: 2020-2021 Harald Sitter <sitter@kde.org> 0003 0004 #include "smartmonitor.h" 0005 0006 #include <chrono> 0007 0008 #include <KLocalizedString> 0009 0010 #include "device.h" 0011 #include "devicenotifier.h" 0012 #include "instabilities.h" 0013 #include "kded_debug.h" 0014 #include "smartctl.h" 0015 #include "smartdata.h" 0016 0017 using namespace std::chrono_literals; 0018 0019 SMARTMonitor::SMARTMonitor(std::unique_ptr<AbstractSMARTCtl> ctl, std::unique_ptr<DeviceNotifier> deviceNotifier, QObject *parent) 0020 : QObject(parent) 0021 , m_ctl(std::move(ctl)) 0022 , m_deviceNotifier(std::move(deviceNotifier)) 0023 { 0024 connect(&m_reloadTimer, &QTimer::timeout, this, &SMARTMonitor::reloadData); 0025 connect(m_ctl.get(), &AbstractSMARTCtl::finished, this, &SMARTMonitor::onSMARTCtlFinished); 0026 m_reloadTimer.setInterval(24h); 0027 } 0028 0029 void SMARTMonitor::start() 0030 { 0031 qCDebug(KDED) << "starting"; 0032 connect(m_deviceNotifier.get(), &DeviceNotifier::addDevice, this, &SMARTMonitor::addDevice); 0033 connect(m_deviceNotifier.get(), &DeviceNotifier::removeUDI, this, &SMARTMonitor::removeUDI); 0034 QMetaObject::invokeMethod(m_deviceNotifier.get(), &DeviceNotifier::start, Qt::QueuedConnection); // async to ensure listeners are ready 0035 m_reloadTimer.start(); 0036 } 0037 0038 QList<Device *> SMARTMonitor::devices() const 0039 { 0040 return m_devices; 0041 } 0042 0043 void SMARTMonitor::removeUDI(const QString &udi) 0044 { 0045 auto newEnd = std::remove_if(m_devices.begin(), m_devices.end(), [this, udi](Device *dev) { 0046 if (dev->udi() != udi) { 0047 return false; 0048 } 0049 0050 Q_EMIT deviceRemoved(dev); 0051 dev->deleteLater(); 0052 return true; 0053 }); 0054 m_devices.erase(newEnd, m_devices.end()); 0055 } 0056 0057 void SMARTMonitor::reloadData() 0058 { 0059 m_deviceNotifier->loadData(); 0060 m_reloadTimer.start(); 0061 } 0062 0063 void SMARTMonitor::onSMARTCtlFinished(const QString &devicePath, const QJsonDocument &document, const QString &textDocument) 0064 { 0065 auto pendingIt = m_pendingDevices.find(devicePath); 0066 if (pendingIt == m_pendingDevices.end()) { 0067 qCDebug(KDED) << "unexpected pending result for" << devicePath; 0068 return; 0069 } 0070 Device *device = *pendingIt; 0071 m_pendingDevices.erase(pendingIt); 0072 0073 if (document.isEmpty()) { // failed to get data, ignore the device 0074 qCDebug(KDED) << "Received no data for" << devicePath; 0075 device->deleteLater(); 0076 return; 0077 } 0078 0079 SMARTData data(document); 0080 if (!data.m_valid) { 0081 qCDebug(KDED) << "Invalid SMART data; skipping" << devicePath; 0082 device->deleteLater(); 0083 return; 0084 } 0085 0086 if (!devicePath.endsWith(QStringLiteral(".json"))) { // simulation data 0087 Q_ASSERT(devicePath == data.m_device); 0088 } 0089 0090 auto existingIt = std::find_if(m_devices.begin(), m_devices.end(), [&device](Device *existing) { 0091 return *existing == *device; 0092 }); 0093 if (existingIt != m_devices.cend()) { 0094 device->deleteLater(); // won't be needing this 0095 0096 Device *existing = *existingIt; 0097 // update failure and call it a day. Notification is handled by the Device. 0098 existing->setInstabilities(Instabilities::from(data)); 0099 existing->setFailed(!data.m_status.m_passed); 0100 existing->setAdvancedReport(textDocument); 0101 0102 return; 0103 } 0104 device->setInstabilities(Instabilities::from(data)); 0105 device->setFailed(!data.m_status.m_passed); 0106 device->setAdvancedReport(textDocument); 0107 0108 m_devices << device; 0109 Q_EMIT deviceAdded(device); 0110 } 0111 0112 void SMARTMonitor::addDevice(Device *device) 0113 { 0114 m_pendingDevices[device->path()] = device; 0115 m_ctl->run(device->path()); 0116 } 0117 0118 #include "moc_smartmonitor.cpp"