File indexing completed on 2024-04-28 09:40:58
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 }