#include "./upgradelookup.h" #include "./manager.h" #include "./alpmdatabase.h" #include "./config.h" #include "./utilities.h" #include #include #include using namespace std; namespace RepoIndex { using namespace Utilities; /*! * \class UpgradeResult * \brief The UpgradeResult class wraps and upgrade/downgrade package and the current version. */ /*! * \brief Returns a JSON object for the current instance. */ QJsonObject UpgradeResult::toJson() const { QJsonObject obj; obj.insert(QStringLiteral("name"), package->name()); if(package->repository()) { obj.insert(QStringLiteral("repo"), package->repository()->name()); } obj.insert(QStringLiteral("pkg"), package->basicInfo()); obj.insert(QStringLiteral("curVer"), currentVersion); return obj; } /*! * \class UpgradeLookupProcess * \brief The UpgradeLookupProcess class performs an async upgrade lookup for a particular upgrade source. */ /*! * \brief Constructs a new upgrade lookup process. The upgrade lookup process is started immediately. * \remarks \a upgradeLookup and \a upgradeSource musted be locked by caller */ UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, Repository *upgradeSource) : QObject(upgradeLookup), m_toCheck(upgradeLookup->toCheck()), m_upgradeSource(upgradeSource), m_reply(nullptr), m_watcher(new QFutureWatcher(this)) { connect(this, &UpgradeLookupProcess::finished, upgradeLookup, &UpgradeLookup::processFinished); requestSources(); } /*! * \brief Returns the results. Results are available, after the finished() signal has been emitted. */ inline const UpgradeLookupResults &UpgradeLookupProcess::results() const { return m_results; } /*! * \brief Internally called to request the sources. */ void UpgradeLookupProcess::requestSources() { // ensure the repository to check and the upgrade source are both not busy if(m_toCheck->isBusy()) { connect(m_toCheck, &Repository::available, this, &UpgradeLookupProcess::requestSources); } else { disconnect(m_toCheck, nullptr, this, nullptr); } if(m_upgradeSource->isBusy()) { connect(m_upgradeSource, &Repository::available, this, &UpgradeLookupProcess::requestSources); } else { disconnect(m_upgradeSource, nullptr, this, nullptr); } // request sources if required switch(m_upgradeSource->requestsRequired()) { case PackageDetailAvailability::Request: m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames()); break; case PackageDetailAvailability::FullRequest: m_reply = m_upgradeSource->requestFullPackageInfo(m_toCheck->packageNames()); break; case PackageDetailAvailability::Never: m_results.errors << QStringLiteral("Repository \"%1\" does not provide the required information.").arg(m_upgradeSource->name()); emit finished(); return; case PackageDetailAvailability::Immediately: break; } if(m_reply) { // a request is required -> wait until results are available m_reply->setParent(this); connect(m_reply, &PackageReply::resultsAvailable, this, &UpgradeLookupProcess::sourceReady); } else { // no request required -> call sourceReady immidiately sourceReady(); } } /*! * \brief Internally called when the upgrade source is ready. */ void UpgradeLookupProcess::sourceReady() { // if a request was required, check whether an error occured if(m_reply && !m_reply->error().isEmpty()) { m_results.errors << m_reply->error(); emit finished(); } else { // invoke the actual upgrade lookup connect(m_watcher, &QFutureWatcher::finished, this, &UpgradeLookupProcess::finished); m_watcher->setFuture(QtConcurrent::run(this, &UpgradeLookupProcess::checkUpgrades)); } } /*! * \brief Internally called to perform the upgrade lookup. */ void UpgradeLookupProcess::checkUpgrades() { QReadLocker toCheckLocker(m_toCheck->lock()), srcLocker(m_upgradeSource->lock()); m_toCheck->checkForUpgrades(m_results, QList() << m_upgradeSource); } /*! * \class UpgradeLookup * \brief The UpgradeLookup class performs an async upgrade lookup for using multiple upgrade sources. */ /*! * \brief Constructs a new upgrade lookup (protected since this is a pure virtual class). */ UpgradeLookup::UpgradeLookup(QObject *parent) : QObject(parent), m_toCheck(nullptr), m_remainingProcesses(0), m_firstFinished(false) {} /*! * \fn UpgradeLookup::processFinished() * \brief Internally called when the finished() signal of an UpgradeLookupProcess instance is emitted. */ /*! * \class UpgradeLookupJson * \remarks The object deletes itself after the lookup is done (hence it must be created using new). * \brief The UpgradeLookupJson class performs an async upgrade lookup for using multiple upgrade sources. * * The request and the results are in JSON. * * Example request: * { * db: "repo", repository to be checked * syncdbs: ["extra", "community"] upgrade sources to be used * } * * Example result: * { * softwareUpdates: [ software upgrades * {pkg: ..., basic info of package providing upgrade * curVer: current version * ], * packageOnlyUpdates: [ package-only upgrades * {pkg: ..., basic info of package providing upgrade * curVer: current version * ], * downgrades: [ downgrades * {pkg: ..., basic info of package providing downgrade * curVer: current version * ] * orphanedPackages: [ ... ] basic info of orphaned packages * warnings: [ ... ] warnings * errors: [ ... ] errors * } */ /*! * \fn UpgradeLookupJson::resultsAvailable() * \brief Emitted when all results are availabe (also in the error case). */ /*! * \brief Constructs a new upgrade lookup for the specified \a request using the specified \a manager. */ UpgradeLookupJson::UpgradeLookupJson(Manager &manager, const QJsonObject &request, QObject *parent) : UpgradeLookup(parent), m_request(request) { const auto toCheckName = request.value(QStringLiteral("db")).toString(); if((m_toCheck = manager.repositoryByName(toCheckName))) { QReadLocker toCheckLocker(m_toCheck->lock()); // construct upgrade lookup processes const auto syncDbsArray = request.value(QStringLiteral("syncdbs")).toArray(); if(syncDbsArray.isEmpty()) { for(auto *src : m_toCheck->upgradeSources()) { QReadLocker srcLocker(src->lock()); new UpgradeLookupProcess(this, src); ++m_remainingProcesses; } } else { for(const auto &syncDbValue : syncDbsArray) { const auto syncDbName = syncDbValue.toString(); if(auto *src = manager.repositoryByName(syncDbName)) { QReadLocker srcLocker(src->lock()); new UpgradeLookupProcess(this, src); ++m_remainingProcesses; } else { m_warningsArray << QStringLiteral("The specified upgrade source \"%1\" can not be found.").arg(syncDbName); } } } // check whether any processes could be constructed if(!m_remainingProcesses) { m_errorsArray << QStringLiteral("No upgrade sources associated for repository \"%1\".").arg(m_toCheck->name()); } else { return; // no errors so far } } else { m_errorsArray << QStringLiteral("Repository \"%1\" can not be found.").arg(toCheckName); } deleteLater(); } void UpgradeLookupJson::processFinished() { #ifdef DEBUG_BUILD assert(m_remainingProcesses); #endif // add results const auto &results = static_cast(sender())->results(); for(const auto &res : results.newVersions) { m_softwareUpgradesArray << res.toJson(); } for(const auto &res : results.newReleases) { m_packageOnlyUpgradesArray << res.toJson(); } for(const auto &res : results.downgrades) { m_downgradesArray << res.toJson(); } for(const auto &warning : results.warnings) { m_warningsArray << warning; } for(const auto &error : results.errors) { m_errorsArray << error; } if(m_firstFinished) { m_orphanedPackages = m_orphanedPackages.intersect(results.orphaned); } else { m_firstFinished = true; m_orphanedPackages = results.orphaned; } // check whether all processes are finished if(--m_remainingProcesses == 0) { // finally make info for orphanded packages for(const auto *pkg : m_orphanedPackages) { QJsonObject obj; obj.insert(QStringLiteral("name"), pkg->name()); if(pkg->repository()) { obj.insert(QStringLiteral("repo"), pkg->repository()->name()); } obj.insert(QStringLiteral("pkg"), pkg->basicInfo()); m_orphanedPackagesArray << obj; } // add results to results QJsonObject QJsonObject results; results.insert(QStringLiteral("softwareUpgrades"), m_softwareUpgradesArray); results.insert(QStringLiteral("packageOnlyUpgrades"), m_packageOnlyUpgradesArray); results.insert(QStringLiteral("downgrades"), m_downgradesArray); results.insert(QStringLiteral("orphanedPackages"), m_orphanedPackagesArray); if(!m_warningsArray.isEmpty()) { results.insert(QStringLiteral("warnings"), m_warningsArray); } if(!m_errorsArray.isEmpty()) { results.insert(QStringLiteral("errors"), m_errorsArray); } emit resultsAvailable(m_request.value(QStringLiteral("what")), m_request.value(QStringLiteral("id")), results); // lookup done, delete this helper object deleteLater(); } } /*! * \class UpgradeLookupCli * \brief The UpgradeLookupJson class performs an async upgrade lookup for using multiple upgrade sources. * * The repository to be checked is specified as string. Results are printed to cerr/cout. */ /*! * \brief Constructs a new upgrade lookup for the specified \a db using the specified \a manager. */ UpgradeLookupCli::UpgradeLookupCli(Manager &manager, const string &repo, QObject *parent) : UpgradeLookup(parent) { cerr << shchar << "Checking upgrades for \"" << repo << "\" ..." << endl; const auto toCheckName = qstr(repo); if((m_toCheck = manager.repositoryByName(toCheckName))) { QReadLocker toCheckLocker(m_toCheck->lock()); // construct upgrade lookup processes for(auto *src : m_toCheck->upgradeSources()) { QReadLocker srcLocker(src->lock()); new UpgradeLookupProcess(this, src); ++m_remainingProcesses; } // check whether any processes could be constructed if(!m_remainingProcesses) { m_errorsArray << QStringLiteral("No upgrade sources associated for repository \"%1\".").arg(m_toCheck->name()); } else { return; // no errors so far } } else { m_errorsArray << QStringLiteral("Repository \"%1\" can not be found.").arg(toCheckName); } printResults(); // print errors } void UpgradeLookupCli::processFinished() { #ifdef DEBUG_BUILD assert(m_remainingProcesses); #endif // add results const auto &results = static_cast(sender())->results(); m_softwareUpgradesArray.reserve(m_softwareUpgradesArray.size() + results.newVersions.size()); m_packageOnlyUpgradesArray.reserve(m_packageOnlyUpgradesArray.size() + results.newReleases.size()); m_downgradesArray.reserve(m_downgradesArray.size() + results.downgrades.size()); m_warningsArray.reserve(m_warningsArray.size() + results.warnings.size()); m_errorsArray.reserve(m_errorsArray.size() + results.errors.size()); for(const auto &res : results.newVersions) { m_softwareUpgradesArray << res.package->name(); } for(const auto &res : results.newReleases) { m_packageOnlyUpgradesArray << res.package->name(); } for(const auto &res : results.downgrades) { m_downgradesArray << res.package->name(); } for(const auto &warning : results.warnings) { m_warningsArray << warning; } for(const auto &error : results.errors) { m_errorsArray << error; } if(m_firstFinished) { m_orphanedPackages = m_orphanedPackages.intersect(results.orphaned); } else { m_firstFinished = true; m_orphanedPackages = results.orphaned; } // check whether all processes are finished if(--m_remainingProcesses == 0) { // finally make info for orphanded packages m_orphanedPackagesArray.reserve(m_orphanedPackages.size()); for(const auto *pkg : m_orphanedPackages) { m_orphanedPackagesArray << pkg->name(); } printResults(); } } void UpgradeLookupCli::printResults() { if(useShSyntax) { if(!m_errorsArray.isEmpty()) { Utilities::printBashArray(cerr, "REPOINDEX_ERRORS", m_errorsArray); } if(!m_warningsArray.isEmpty()) { Utilities::printBashArray(cerr, "REPOINDEX_WARNINGS", m_warningsArray); } if(!m_softwareUpgradesArray.isEmpty()) { Utilities::printBashArray(cout, "REPOINDEX_RESULTS_SOFTWARE_UPGRADES", m_softwareUpgradesArray); } if(!m_packageOnlyUpgradesArray.isEmpty()) { Utilities::printBashArray(cout, "REPOINDEX_RESULTS_PACKAGE_UPGRADES", m_packageOnlyUpgradesArray); } if(!m_downgradesArray.isEmpty()) { Utilities::printBashArray(cout, "REPOINDEX_RESULTS_DOWNGRADES", m_downgradesArray); } if(!m_orphanedPackagesArray.isEmpty()) { Utilities::printBashArray(cout, "REPOINDEX_RESULTS_ORPHANED", m_orphanedPackagesArray); } } else { if(!m_errorsArray.isEmpty()) { Utilities::printValues(cerr, "Errors", m_errorsArray); } if(!m_warningsArray.isEmpty()) { Utilities::printValues(cerr, "Warnings", m_warningsArray); } if(!m_softwareUpgradesArray.isEmpty()) { Utilities::printValues(cout, "Software upgrades", m_softwareUpgradesArray); } if(!m_packageOnlyUpgradesArray.isEmpty()) { Utilities::printValues(cout, "Package upgrades", m_packageOnlyUpgradesArray); } if(!m_downgradesArray.isEmpty()) { Utilities::printValues(cout, "Downgrades", m_downgradesArray); } if(!m_orphanedPackagesArray.isEmpty()) { Utilities::printValues(cout, "Orphaned packages", m_orphanedPackagesArray); } } QCoreApplication::exit(); } } // namespace PackageManagement