File indexing completed on 2024-03-24 17:10:31

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