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 }