File indexing completed on 2024-04-28 13:41:41

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2017-2021 Harald Sitter <sitter@kde.org>
0003 
0004 #include "FileResolver.h"
0005 
0006 #include <QDebug>
0007 #include <QProcess>
0008 
0009 #include <PackageKit/Daemon>
0010 #include <utility>
0011 
0012 #include "Debug.h"
0013 #include "DebugResolver.h"
0014 #include "DiagnosticResolver.h"
0015 
0016 static QString usrMergedAlternative(const QString &path)
0017 {
0018     const QString usr = QStringLiteral("/usr/");
0019     if (path.startsWith(usr)) {
0020         return path.mid(usr.length() - 1 /* keep trailing slash as new root */);
0021     }
0022     return usr.chopped(1 /* drop trailing slash, path already has one */) + path;
0023 }
0024 
0025 FileResolver::FileResolver(std::shared_ptr<File> file, QObject *parent)
0026     : QObject(parent)
0027     , m_file(std::move(file))
0028 {
0029 }
0030 
0031 void FileResolver::resolve()
0032 {
0033     resolve(m_file->path());
0034 }
0035 
0036 void FileResolver::resolve(const QString &path)
0037 {
0038     auto *transaction = PackageKit::Daemon::searchFiles(path, PackageKit::Transaction::FilterInstalled);
0039     connect(transaction, &PackageKit::Transaction::package, this, &FileResolver::packageFound);
0040     connect(transaction, &PackageKit::Transaction::finished, this, [this, path, transaction]() {
0041         transaction->deleteLater();
0042         if (!m_file->packageID().isEmpty()) {
0043             if (m_triedUsrMerge) { // update the file path with its real variant
0044                 m_file->setPath(path);
0045             }
0046             return;
0047         }
0048 
0049         if (!m_triedUsrMerge) {
0050             // Retry with usr merge mangling. On a dpkg level /lib/foo.so is different from /usr/lib/foo.so but we
0051             // cannot really tell which of the two it is. Neither the disk nor gdb knows. So, in the event that
0052             // resolving the input path failed we'll try again with the mangled path. If that too doesn't work we
0053             // are out of options.
0054             m_triedUsrMerge = true;
0055             const QString alternative = usrMergedAlternative(path);
0056             qCDebug(INSTALLER) << this << "Retrying resolution with usr-merged path mutation" << alternative;
0057             resolve(alternative);
0058             return;
0059         }
0060 
0061         // If we failed to resolve the effective package we'll still want
0062         // to get cleaned up, naturally.
0063         // This is kinda error handled in the Installer as well as the QML.
0064         // If we failed to resolve any debug packages that is a fatal error otherwise we'll install
0065         // the ones that managed to resolve by mark the broken packages in the UI as being broken.
0066         // We'll also grab some diagnostics on the way out though...
0067 
0068         qCDebug(INSTALLER) << this << "Getting diagnostics for" << m_file->path();
0069         // if we failed to resolve the file's package ID then attach diagnostic data. This is in particular to help
0070         // diagnose why a given file may have failed to resolve when the user would expect it to.
0071         auto resolver = new DiagnosticResolver(m_file, this);
0072         connect(resolver, &DiagnosticResolver::finished, this, [resolver, this] {
0073             resolver->deleteLater();
0074             m_file->setDiagnosticData(resolver->data());
0075 
0076             m_file->setResolved();
0077             Q_EMIT finished();
0078         });
0079         resolver->resolve();
0080     });
0081 }
0082 
0083 void FileResolver::packageFound(PackageKit::Transaction::Info, const QString &packageID, const QString &)
0084 {
0085     qCDebug(INSTALLER) << this << "found" << packageID;
0086 
0087     m_file->setPackageID(packageID);
0088 
0089     auto package = m_file->package();
0090     const auto packageArch = PackageKit::Daemon::packageArch(packageID);
0091     if (!packageArch.isEmpty()) {
0092         package = package + QLatin1Char(':') + packageArch;
0093     }
0094 
0095     // Packagekit has no notion of source packages, we'll need to resolve it manually.
0096     // This is legacy compatibility for the most part. These days debug symbols are generally generated for each
0097     // binary. Originally manually created dbg packages were per source though, I suppose they can still appear.
0098     auto proc = new QProcess(this);
0099     connect(proc, &QProcess::finished, this, [this, proc](int exitCode, QProcess::ExitStatus) {
0100         proc->deleteLater();
0101 
0102         if (exitCode == 0) {
0103             m_file->setSourcePackage(QString::fromLatin1(proc->readLine()).trimmed());
0104         }
0105         // else isn't a fatal problem as most (or even all) modern packages have -dbgsym packges
0106         // instead of source-derived -dbg packages.
0107 
0108         auto resolver = new DebugResolver(m_file, this);
0109         connect(resolver, &DebugResolver::finished, this, &FileResolver::debugResolverFinished);
0110         resolver->resolve();
0111     });
0112 
0113     proc->start(QStringLiteral("dpkg-query"),
0114                QStringList() << QStringLiteral("-f=${source:Package}\n") << QStringLiteral("-W") << package);
0115 }
0116 
0117 void FileResolver::debugResolverFinished()
0118 {
0119     sender()->deleteLater();
0120     qCDebug(INSTALLER) << this << "++ packages for" << m_file->packageID() << m_file->debugPackageID();
0121     m_file->setResolved();
0122     Q_EMIT finished();
0123 }