File indexing completed on 2024-04-21 05:33:27

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
0003 
0004 #include "smartctl.h"
0005 
0006 #include <KAuth/Action>
0007 #include <KAuth/ExecuteJob>
0008 #include <KAuth/HelperSupport>
0009 #include <KLocalizedString>
0010 #include <QDebug>
0011 #include <QFileInfo>
0012 
0013 #include "kded_debug.h"
0014 #include "smartfailure.h"
0015 
0016 void SMARTCtl::run(const QString &devicePath)
0017 {
0018     // https://bugs.kde.org/show_bug.cgi?id=379215
0019     // One cannot make any kind of concurrent requests to kauth, so we need
0020     // a fairly awkward busy state and request queuing system.
0021     if (m_busy) {
0022         m_requestQueue.push(devicePath);
0023         return;
0024     }
0025     m_busy = true;
0026 
0027     KAuth::Action action(QStringLiteral("org.kde.kded.smart.smartctl"));
0028     // This is technically never used unless the sysadmin forces our action
0029     // to require authentication. In that case we'll want to give request context
0030     // as we do requests per-device though.
0031     action.setDetailsV2({{KAuth::Action::AuthDetail::DetailMessage,
0032                           i18nc("@label description of authentication request to read SMART data. %1 is a device path e.g. /dev/sda",
0033                                 "Read SMART report for storage device %1",
0034                                 devicePath)}});
0035     action.setHelperId(QStringLiteral("org.kde.kded.smart"));
0036 
0037     // The helper only consumes names, ensure we fully resolve the name of the
0038     // device to /dev/$name.
0039     const QString canonicalDevicePath = QFileInfo(devicePath).canonicalFilePath();
0040     Q_ASSERT(!canonicalDevicePath.isEmpty());
0041     const QFileInfo canonicalDeviceInfo(canonicalDevicePath);
0042     Q_ASSERT(canonicalDeviceInfo.absolutePath() == QLatin1String("/dev"));
0043 
0044     action.addArgument(QStringLiteral("deviceName"), canonicalDeviceInfo.fileName());
0045     qCDebug(KDED) << action.isValid() << action.hasHelper() << action.helperId() << action.status();
0046     KAuth::ExecuteJob *job = action.execute();
0047     connect(job, &KJob::result, this, [this, job, devicePath] {
0048         const auto data = job->data();
0049         const auto code = SMART::Failures(data.value(QStringLiteral("exitCode"), QByteArray()).toInt());
0050         const auto json = data.value(QStringLiteral("data"), QByteArray()).toByteArray();
0051 
0052         QJsonDocument document;
0053         if (json.isEmpty()) {
0054             qCDebug(KDED) << "looks like we got no data back for" << devicePath << code << json.isEmpty();
0055         } else {
0056             document = QJsonDocument::fromJson(json);
0057         }
0058 
0059         // Queue the next pending request if there is any.
0060         m_busy = false;
0061         if (!m_requestQueue.empty()) {
0062             auto request = m_requestQueue.front();
0063             run(request);
0064             m_requestQueue.pop();
0065         }
0066 
0067         Q_EMIT finished(devicePath, document, data.value(QStringLiteral("cliData")).toString());
0068     });
0069     job->start();
0070 }
0071 
0072 #include "moc_smartctl.cpp"