File indexing completed on 2024-05-12 05:29:10

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 
0003 #include "automaticcoredumpexcavator.h"
0004 
0005 #include <fcntl.h>
0006 #include <unistd.h>
0007 
0008 #include <chrono>
0009 #include <filesystem>
0010 
0011 #include <QDBusConnection>
0012 #include <QDBusConnectionInterface>
0013 #include <QDBusMessage>
0014 #include <QDBusPendingCallWatcher>
0015 #include <QDBusUnixFileDescriptor>
0016 #include <QDebug>
0017 
0018 #include "coredumpexcavator.h"
0019 
0020 using namespace Qt::StringLiterals;
0021 using namespace std::chrono_literals;
0022 
0023 void AutomaticCoredumpExcavator::excavateFrom(const QString &coredumpFilename)
0024 {
0025     if (!m_coreDir) {
0026         m_coreDir = std::make_unique<QTemporaryDir>(QDir::tempPath() + u"/drkonqi-core"_s);
0027         Q_ASSERT(m_coreDir->isValid());
0028         if (!m_coreDir->isValid()) {
0029             Q_EMIT failed();
0030             return;
0031         }
0032         // Keep the core to ourself.
0033         std::filesystem::permissions(m_coreDir->path().toStdString(), std::filesystem::perms::owner_all, std::filesystem::perm_options::replace);
0034     }
0035 
0036     const auto coreFileTarget = m_coreDir->filePath(u"core"_s);
0037     const auto coredumpFileInfo = QFileInfo(coredumpFilename);
0038 
0039     if (coredumpFileInfo.isReadable()) {
0040         auto excavator = new CoredumpExcavator(this);
0041         connect(excavator, &CoredumpExcavator::excavated, this, [this, coreFileTarget](int exitCode) {
0042             if (exitCode != 0) {
0043                 qWarning() << "Failed to excavate core from file:" << exitCode;
0044                 Q_EMIT failed();
0045                 return;
0046             }
0047             Q_EMIT excavated(coreFileTarget);
0048         });
0049         excavator->excavateFromTo(coredumpFilename, coreFileTarget);
0050     } else {
0051         if (QFile::exists(coreFileTarget)) {
0052             qDebug() << "Core already exists, returning early";
0053             Q_EMIT excavated(coreFileTarget);
0054             return;
0055         }
0056 
0057         auto msg = QDBusMessage::createMethodCall("org.kde.drkonqi"_L1, "/"_L1, "org.kde.drkonqi"_L1, "excavateFromToDirFd"_L1);
0058 
0059         int fd = open(qUtf8Printable(m_coreDir->path()), O_RDONLY | O_CLOEXEC | O_DIRECTORY);
0060         if (fd < 0) {
0061             int err = errno;
0062             qWarning() << "Failed to open m_coreDir" << m_coreDir->path() << strerror(err);
0063             Q_EMIT failed();
0064             return;
0065         }
0066         auto closeFd = qScopeGuard([fd] {
0067             close(fd);
0068         });
0069 
0070         msg << coredumpFileInfo.fileName() << QVariant::fromValue(QDBusUnixFileDescriptor(fd));
0071         static const auto connection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "drkonqi-polkit-system-connection"_L1);
0072         connection.interface()->setTimeout(std::chrono::milliseconds(5min).count());
0073         auto watcher = new QDBusPendingCallWatcher(connection.asyncCall(msg));
0074         QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, coreFileTarget] {
0075             watcher->deleteLater();
0076             QDBusReply<QString> reply = *watcher;
0077             qWarning() << reply.isValid() << reply.error() << reply.value();
0078             if (!reply.isValid() || reply.value().isEmpty()) {
0079                 qWarning() << "Failed to excavate core as admin:" << reply.error();
0080                 Q_EMIT failed();
0081                 return;
0082             }
0083             Q_EMIT excavated(coreFileTarget);
0084         });
0085     }
0086 }