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"