repoindex/alpm/manager.cpp

827 lines
31 KiB
C++
Raw Normal View History

#include "./manager.h"
#include "./utilities.h"
#include "./list.h"
#include "./config.h"
#include "./alpmdatabase.h"
2015-08-10 22:46:01 +02:00
#include "../network/userrepository.h"
2015-09-04 14:37:01 +02:00
2015-08-10 22:46:01 +02:00
#include <c++utilities/io/inifile.h>
#include <c++utilities/conversion/stringconversion.h>
2015-09-04 14:37:01 +02:00
#include <c++utilities/misc/memory.h>
2015-08-10 22:46:01 +02:00
#include <QList>
#include <QSysInfo>
#include <QMutexLocker>
#include <QStringBuilder>
#include <QFile>
#include <QDataStream>
2016-01-18 20:34:29 +01:00
#include <QTimer>
2015-08-10 22:46:01 +02:00
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <algorithm>
using namespace std;
using namespace IoUtilities;
using namespace ConversionUtilities;
namespace RepoIndex {
2015-08-10 22:46:01 +02:00
2015-09-04 14:37:01 +02:00
/*!
* \cond
*/
2015-08-10 22:46:01 +02:00
constexpr int defaultSigLevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
2015-09-04 14:37:01 +02:00
inline ostream &operator <<(ostream &stream, const QString &str)
{
stream << str.toLocal8Bit().data();
return stream;
}
/*!
* \endcond
*/
2015-08-10 22:46:01 +02:00
/*!
* \brief The Manager class helps accessing ALPM.
*
* - It queries the ALPM for database and package information.
* - It serializes the information as JSON objects used by the network classes and the web interface.
*/
/*!
* \brief Creates a new manager class; initializes a new ALPM handle.
* \param rootdir Specifies the root directory.
* \param dbpath Specifies the database directory.
*/
Manager::Manager(const Config &config) :
2015-08-10 22:46:01 +02:00
m_config(config),
m_writeCacheBeforeGone(true),
2015-08-10 22:46:01 +02:00
m_sigLevel(defaultSigLevel),
2015-09-04 14:37:01 +02:00
m_localFileSigLevel(ALPM_SIG_USE_DEFAULT)
2015-08-10 22:46:01 +02:00
{
initAlpmHandle();
2015-09-04 14:37:01 +02:00
if(config.isAurEnabled()) {
m_userRepo = make_unique<UserRepository>(m_networkAccessManager);
2015-08-10 22:46:01 +02:00
}
}
/*!
* \brief Releases the associated ALPM handle.
*/
Manager::~Manager()
{
if(m_writeCacheBeforeGone) {
writeCache();
}
cleanupAlpm();
2015-08-10 22:46:01 +02:00
}
/*!
* \brief Returns the first package with the specified name from the specified database.
2015-08-10 22:46:01 +02:00
*/
AlpmPackage *Manager::packageFromDatabase(const QString &dbName, const QString &pkgName)
2015-08-10 22:46:01 +02:00
{
if(auto *db = databaseByName(dbName)) {
2015-09-04 14:37:01 +02:00
return static_cast<AlpmPackage *>(db->packageByName(pkgName));
} else {
return nullptr;
}
}
/*!
* \brief Returns the first package with the specified name from the specified database.
2015-09-04 14:37:01 +02:00
*/
const AlpmPackage *Manager::packageFromDatabase(const QString &dbName, const QString &pkgName) const
2015-09-04 14:37:01 +02:00
{
if(const auto *db = databaseByName(dbName)) {
2015-09-04 14:37:01 +02:00
return static_cast<const AlpmPackage *>(db->packageByName(pkgName));
} else {
return nullptr;
}
}
/*!
* \brief Returns the first package with the specified \a name from one of the sync databases.
2015-09-04 14:37:01 +02:00
*/
AlpmPackage *Manager::packageFromSyncDatabases(const QString &pkgName)
2015-09-04 14:37:01 +02:00
{
2015-12-08 18:59:17 +01:00
for(const auto &dbEntry : m_syncDbs) {
if(auto *pkg = dbEntry->packageByName(pkgName)) {
2015-09-04 14:37:01 +02:00
return static_cast<AlpmPackage *>(pkg);
}
}
return nullptr;
}
/*!
* \brief Returns the first package with the specified \a name from one of the sync databases.
2015-09-04 14:37:01 +02:00
*/
const AlpmPackage *Manager::packageFromSyncDatabases(const QString &pkgName) const
2015-09-04 14:37:01 +02:00
{
2015-12-08 18:59:17 +01:00
for(const auto &dbEntry : m_syncDbs) {
if(const auto *pkg = dbEntry->packageByName(pkgName)) {
2015-09-04 14:37:01 +02:00
return static_cast<const AlpmPackage *>(pkg);
2015-08-10 22:46:01 +02:00
}
}
2015-09-04 14:37:01 +02:00
return nullptr;
2015-08-10 22:46:01 +02:00
}
/*!
* \brief Creates a new package instance for the specified package file.
*
* Verifies the integrity of the file if the option is set.
*/
2015-09-04 14:37:01 +02:00
unique_ptr<AlpmOwnershipPackage> Manager::packageFromFile(const char *fileName, bool verifyIntegrity)
2015-08-10 22:46:01 +02:00
{
alpm_pkg_t *pkg;
if(alpm_pkg_load(m_handle, fileName, verifyIntegrity, static_cast<alpm_siglevel_t>(m_localFileSigLevel), &pkg) != 0) {
throw runtime_error(string("Unable to load package file: ") + alpm_strerror(alpm_errno(m_handle)));
} else {
2015-09-04 14:37:01 +02:00
return make_unique<AlpmOwnershipPackage>(pkg);
2015-08-10 22:46:01 +02:00
}
}
/*!
* \brief Returns the first package satisfiing the specified dependency from one of the available package sources (excluding the local database
* and sources requirering requests such as the AUR).
*/
Package *Manager::packageProviding(const Dependency &dependency)
{
2015-12-08 18:59:17 +01:00
for(auto &dbEntry : m_syncDbs) {
QReadLocker locker(dbEntry->lock());
if(auto *pkg = dbEntry->packageProviding(dependency)) {
return pkg;
}
}
return nullptr;
}
/*!
* \brief Returns the first package satisfiing the specified dependency from one of the available package sources (excluding the local database
* and sources requirering requests such as the AUR).
*/
const Package *Manager::packageProviding(const Dependency &dependency) const
{
2015-12-08 18:59:17 +01:00
for(const auto &dbEntry : m_syncDbs) {
QReadLocker locker(dbEntry->lock());
if(const auto *pkg = dbEntry->packageProviding(dependency)) {
return pkg;
}
}
return nullptr;
}
2015-08-10 22:46:01 +02:00
/*!
* \brief Sets the install reason for the specified \a package.
*/
2015-09-04 14:37:01 +02:00
void Manager::setInstallReason(AlpmPackage *package, alpm_pkgreason_t reason)
2015-08-10 22:46:01 +02:00
{
2015-09-04 14:37:01 +02:00
if(alpm_pkg_set_reason(package->ptr(), reason)) {
throw runtime_error(string("Unable to set install reason of the package ") + package->name().toLocal8Bit().data() + ": " + alpm_strerror(alpm_errno(m_handle)));
2015-08-10 22:46:01 +02:00
}
}
/*!
* \brief Returns the last value with the specified \a key in the specified multimap.
*/
const string &lastValue(const multimap<string, string> &mm, const string &key)
{
static const string defaultValue;
const auto i = find_if(mm.crbegin(), mm.crend(), [&key] (const pair<string, string> &i) {
return i.first == key;
});
return i != mm.crend() ? i->second : defaultValue;
}
/*!
* \brief Parses a "SigLevel" denotation from pacman config file.
*/
int Manager::parseSigLevel(const string &sigLevelStr)
2015-08-10 22:46:01 +02:00
{
int sigLevel = defaultSigLevel;
// split sig level denotation into parts
const auto parts = splitString<list<string> >(sigLevelStr, " ");
for(const auto &part : parts) {
// determine whether part affect packages, databases or both
bool package = true, db = true;
const char *partStart = part.data();
if(!strncmp(partStart, "Package", 7)) {
db = false; // package only part
partStart += 7;
} else if(!strncmp(partStart, "Database", 8)) {
package = false; // db only part
partStart += 8;
}
// set sig level according part
if(!strcmp(partStart, "Never")) {
if(package) {
sigLevel &= ~ALPM_SIG_PACKAGE;
}
if(db) {
sigLevel &= ~ALPM_SIG_DATABASE;
}
} else if(!strcmp(partStart, "Optional")) {
if(package) {
sigLevel |= ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL;
}
if(db) {
sigLevel |= ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
}
} else if(!strcmp(partStart, "Required")) {
if(package) {
sigLevel |= ALPM_SIG_PACKAGE;
sigLevel &= ~ALPM_SIG_PACKAGE_OPTIONAL;
}
if(db) {
sigLevel |= ALPM_SIG_DATABASE;
sigLevel &= ~ALPM_SIG_DATABASE_OPTIONAL;
}
} else if(!strcmp(partStart, "TrustedOnly")) {
if(package) {
sigLevel &= ~(ALPM_SIG_PACKAGE_MARGINAL_OK | ALPM_SIG_PACKAGE_UNKNOWN_OK);
}
if(db) {
sigLevel &= ~(ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK);
}
} else if(!strcmp(partStart, "TrustAll")) {
if(package) {
sigLevel |= ALPM_SIG_PACKAGE_MARGINAL_OK | ALPM_SIG_PACKAGE_UNKNOWN_OK;
}
if(db) {
sigLevel |= ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK;
}
} else {
cerr << shchar << "Warning: Invalid value \"" << part << "\" for \"SigLevel\" in pacman config file will be ignored." << endl;
2015-08-10 22:46:01 +02:00
}
}
return sigLevel;
}
/*!
* \brief Parses a "Usage" denotation from pacman config file.
*/
int Manager::parseUsage(const string &usageStr)
2015-08-10 22:46:01 +02:00
{
int usage = 0;
const auto parts = splitString<list<string> >(usageStr, " ");
for(const auto &part : parts) {
if(part == "Sync") {
usage |= ALPM_DB_USAGE_SYNC;
} else if(part == "Search") {
usage |= ALPM_DB_USAGE_SEARCH;
} else if(part == "Install") {
usage |= ALPM_DB_USAGE_INSTALL;
} else if(part == "Upgrade") {
usage |= ALPM_DB_USAGE_UPGRADE;
} else {
cerr << shchar << "Warning: Invalid value \"" << part << "\" for \"Usage\" in pacman config file will be ignored." << endl;
2015-08-10 22:46:01 +02:00
}
}
return usage ? usage : ALPM_DB_USAGE_ALL;
}
/*!
* \brief Registers sync databases listed in the Pacman config file. Also reads the cache dir.
2015-08-10 22:46:01 +02:00
*/
void Manager::registerDataBasesFromPacmanConfig()
2015-08-10 22:46:01 +02:00
{
// open config file and parse as ini
try {
IniFile configIni;
{
fstream configFile;
configFile.exceptions(ios_base::failbit | ios_base::badbit);
configFile.open(m_config.pacmanConfFile().toLocal8Bit().data(), ios_base::in);
configIni.parse(configFile);
}
// determine current cpu archtitecture (required for server URLs)
static const string sysArch(QSysInfo::currentCpuArchitecture().toStdString());
string arch = sysArch;
const auto &config = configIni.data();
// read relevant options
static const string sigLevelKey("SigLevel");
static const string usageKey("Usage");
int globalSigLevel = defaultSigLevel;
for(auto &scope : config) {
if(scope.first == "options") {
// iterate through all "config" scopes (just to cover the case that there are multiple "options" scopes)
const auto &options = scope.second;
const auto &specifiedArch = lastValue(options, "Architecture");
if(!specifiedArch.empty() && specifiedArch != "auto") {
arch = specifiedArch;
}
const auto &specifiedDir = lastValue(options, "CacheDir");
if(!specifiedDir.empty()) {
m_pacmanCacheDir = QString::fromStdString(specifiedDir);
}
globalSigLevel = parseSigLevel(lastValue(options, sigLevelKey));
2015-08-10 22:46:01 +02:00
}
}
// register sync databases
unordered_map<string, IniFile> includedInis;
for(const auto &scope : config) {
if(scope.first != "options") {
// read and validate database name
QString dbName = QString::fromLocal8Bit(scope.first.c_str());
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
cerr << shchar << "Error: Unable to add database from pacman config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl;
} else if(dbName.startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
cerr << shchar << "Error: Unable to add database from pacman config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
2015-12-08 18:59:17 +01:00
} else if(m_syncDbMap.count(dbName)) {
cerr << shchar << "Error: Unable to add database from pacman config: Database names must be unique. Ignoring second occurance of database \"" << scope.first << "\"." << endl;
2015-08-10 22:46:01 +02:00
} else {
// read sig level and usage
const auto &sigLevelStr = lastValue(scope.second, sigLevelKey);
int sigLevel = sigLevelStr.empty() ? globalSigLevel : parseSigLevel(sigLevelStr);
int usage = parseUsage(lastValue(scope.second, usageKey));
// try to register database in the ALPM system
if(alpm_db_t *db = alpm_register_syncdb(m_handle, scope.first.c_str(), static_cast<alpm_siglevel_t>(sigLevel))) {
// set usage
if(alpm_db_set_usage(db, static_cast<alpm_db_usage_t>(usage)) == 0) {
if(m_config.isVerbose() || m_config.runServer()) {
cerr << shchar << "Added database [" << scope.first << ']' << endl;
}
2015-08-10 22:46:01 +02:00
} else {
if(m_config.isVerbose() || m_config.runServer()) {
cerr << shchar << "Warning: Added database [" << scope.first << "] but failed to set usage" << endl;
}
2015-08-10 22:46:01 +02:00
}
// add servers
for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) {
string url = range.first->second;
findAndReplace<string>(url, "$repo", scope.first);
findAndReplace<string>(url, "$arch", arch);
alpm_db_add_server(db, url.c_str());
if(m_config.isVerbose() || m_config.runServer()) {
cerr << shchar << "Added server: " << url << endl;
}
2015-08-10 22:46:01 +02:00
}
// add included servers
for(auto range = scope.second.equal_range("Include"); range.first != range.second; ++range.first) {
const auto &path = range.first->second;
auto &includedIni = includedInis[path];
if(includedIni.data().empty()) {
try {
fstream includedFile;
includedFile.exceptions(ios_base::failbit | ios_base::badbit);
includedFile.open(path, ios_base::in);
includedIni.parse(includedFile);
} catch (const ios_base::failure &) {
cerr << shchar << "Error: An IO exception occured when parsing the included file \"" << path << "\"." << endl;
2015-08-10 22:46:01 +02:00
}
}
try {
for(auto &scope : includedIni.data()) {
if(scope.first.empty()) {
for(auto range = scope.second.equal_range("Server"); range.first != range.second; ++range.first) {
string url = range.first->second;
findAndReplace<string>(url, "$repo", scope.first);
findAndReplace<string>(url, "$arch", arch);
alpm_db_add_server(db, url.c_str());
if(m_config.isVerbose() || m_config.runServer()) {
cerr << shchar << "Added server: " << url << endl;
}
}
}
2015-08-10 22:46:01 +02:00
}
} catch (const out_of_range &) {
cerr << shchar << "Warning: Included file \"" << path << "\" has no values." << endl;
2015-08-10 22:46:01 +02:00
}
}
// add sync db to internal map (use as index size + 1 because the local database has index 0)
2015-12-08 18:59:17 +01:00
m_syncDbs.emplace_back(make_unique<AlpmDatabase>(db, m_config.alpmDbPath(), m_syncDbs.size() + 1));
auto emplaced = m_syncDbMap.emplace(dbName, m_syncDbs.back().get());
2015-08-10 22:46:01 +02:00
if(usage & ALPM_DB_USAGE_UPGRADE) {
// -> db is used to upgrade local database
2015-12-08 18:59:17 +01:00
localDataBase()->upgradeSources() << emplaced.first->second;
2015-08-10 22:46:01 +02:00
}
} else {
cerr << shchar << "Error: Unable to add sync database [" << scope.first << ']' << endl;
2015-08-10 22:46:01 +02:00
}
}
}
}
} catch (const ios_base::failure &) {
throw ios_base::failure("Error: An IO exception occured when parsing the config file.");
}
}
/*!
* \brief Registers sync databases listed in the repository index configuration.
*/
void Manager::registerDatabasesFromRepoIndexConfig()
{
2015-09-04 14:37:01 +02:00
// check whether an entry already exists, if not create a new one
for(const RepoEntry &repoEntry : m_config.repoEntries()) {
AlpmDatabase *syncDb = nullptr;
try {
2015-12-08 18:59:17 +01:00
syncDb = m_syncDbMap.at(repoEntry.name());
cerr << shchar << "Applying config for database [" << syncDb->name() << ']' << endl;
if(!repoEntry.dataBasePath().isEmpty()) {
cerr << shchar << "Warning: Can't use data base path specified in repo index config because the repo \""
2015-09-04 14:37:01 +02:00
<< repoEntry.name() << "\" has already been added from the Pacman config." << endl;
}
if(repoEntry.sigLevel()) {
cerr << shchar << "Warning: Can't use sig level specified in repo index config because the repo \""
2015-09-04 14:37:01 +02:00
<< repoEntry.name() << "\" has already been added from the Pacman config." << endl;
}
syncDb->setPackagesDirectory(repoEntry.packageDir());
syncDb->setSourcesDirectory(repoEntry.sourceDir());
} catch(const out_of_range &) {
if(repoEntry.name().compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
cerr << shchar << "Error: Unable to add database from repo index config: The database name mustn't be \"local\" because this name is reserved for the local database." << endl;
} else if(repoEntry.name().startsWith(QLatin1String("aur"), Qt::CaseInsensitive)) {
cerr << shchar << "Error: Unable to add database from repo index config: The database name mustn't start with \"aur\" because this name is reserved for the Arch Linux User Repository." << endl;
} else {
2015-09-04 14:37:01 +02:00
// TODO: database path
auto *db = alpm_register_syncdb(m_handle, repoEntry.name().toLocal8Bit().data(), static_cast<alpm_siglevel_t>(repoEntry.sigLevel()));
2015-12-08 18:59:17 +01:00
m_syncDbs.emplace_back(make_unique<AlpmDatabase>(db, m_config.alpmDbPath(), m_syncDbs.size() + 1));
auto emplaced = m_syncDbMap.emplace(repoEntry.name(), m_syncDbs.back().get());
if(emplaced.second) {
2015-12-08 18:59:17 +01:00
syncDb = emplaced.first->second;
2015-09-04 14:37:01 +02:00
syncDb->setSourcesDirectory(repoEntry.sourceDir());
syncDb->setPackagesDirectory(repoEntry.packageDir());
if(m_config.isVerbose() || m_config.runServer()) {
cerr << shchar << "Added database [" << repoEntry.name() << ']' << endl;
}
}
}
}
if(syncDb) {
syncDb->addServerUrls(repoEntry.servers());
}
2015-08-10 22:46:01 +02:00
}
2015-09-04 14:37:01 +02:00
// add upgrade sources
for(const RepoEntry &repoEntry : m_config.repoEntries()) {
try {
2015-12-08 18:59:17 +01:00
auto &upgradeSources = m_syncDbMap.at(repoEntry.name())->upgradeSources();
2015-09-04 14:37:01 +02:00
for(const auto &upgradeSourceName : repoEntry.upgradeSources()) {
if(auto *source = repositoryByName(upgradeSourceName)) {
upgradeSources << source;
} else {
cerr << shchar << "Warning: The specified upgrade source \"" << upgradeSourceName << "\" can not be found and will be ignored." << endl;
2015-09-04 14:37:01 +02:00
}
}
} catch(const out_of_range &) {
// entry should have been added before
}
}
2015-08-10 22:46:01 +02:00
}
/*!
* \brief Initiates all ALPM data bases.
* \remarks Must be called, after all relevant sync data bases have been registered (eg. via applyPacmanConfig()).
*/
void Manager::initAlpmDataBases(bool computeRequiredBy)
{
// call the init method
{
QList<PackageLoader *> loaders;
2015-12-08 18:59:17 +01:00
loaders.reserve(m_syncDbMap.size() + 1);
loaders << localDataBase()->init();
2015-12-08 18:59:17 +01:00
for(auto &syncDbEntry : m_syncDbMap) {
loaders << syncDbEntry.second->init();
}
for(auto *loader : loaders) {
loader->future().waitForFinished();
delete loader;
}
}
// compute required-by and optional-for
if(computeRequiredBy) {
QList<QFuture<void> > futures;
2015-12-08 18:59:17 +01:00
futures.reserve(m_syncDbMap.size() + 1);
futures << localDataBase()->computeRequiredBy(*this);
2015-12-08 18:59:17 +01:00
for(auto &syncDbEntry : m_syncDbMap) {
futures << syncDbEntry.second->computeRequiredBy(*this);
}
for(auto &future : futures) {
future.waitForFinished();
}
}
}
2016-01-18 20:34:29 +01:00
/*!
* \brief Returns whether automatic cache maintenance is enabled.
* \remarks If enabled, the cache will be cleaned and saved frequently.
*/
bool Manager::isAutoCacheMaintenanceEnabled() const
{
return m_cacheTimer && m_cacheTimer->isActive();
}
/*!
* \brief Sets whether automatic cache maintenacne is enabled.
* \sa isAutoCacheMaintenanceEnabled()
*/
void Manager::setAutoCacheMaintenanceEnabled(bool enabled)
{
if(isAutoCacheMaintenanceEnabled() != enabled) {
if(enabled) {
if(!m_cacheTimer) {
m_cacheTimer = make_unique<QTimer>();
m_cacheTimer->setInterval(5 * 60 * 1000);
QObject::connect(m_cacheTimer.get(), &QTimer::timeout, bind(&Manager::maintainCache, this));
}
m_cacheTimer->start();
} else {
m_cacheTimer->stop();
}
}
}
/*!
* \brief Writes the cache for all repositories where caching makes sense.
*/
void Manager::writeCache()
{
// could iterate through all repos and check isCachingUseful() but
// currently its just the AUR which is needed to be cached
if(userRepository()) {
QFile file(config().cacheDir() % QChar('/') % userRepository()->name() % QStringLiteral(".cache"));
if(file.open(QFileDevice::WriteOnly)) {
QDataStream stream(&file);
userRepository()->writeToCacheStream(stream);
// if warnings/errors occur, these will be printed directly by writeToCacheStream()
} else {
cerr << shchar << "Warning: Unable to write cache file for the AUR." << endl;
}
}
}
void Manager::restoreCache()
{
// could iterate through all repos and check isCachingUseful() but
// currently its just the AUR which is needed to be cached
if(userRepository()) {
QFile file(config().cacheDir() % QChar('/') % userRepository()->name() % QStringLiteral(".cache"));
if(file.exists()) {
if(file.open(QFileDevice::ReadOnly)) {
QDataStream stream(&file);
userRepository()->restoreFromCacheStream(stream);
// if warnings/errors occur, these will be printed directly by restoreFromCacheStream()
} else {
cerr << shchar << "Warning: Unable to open cache file for the AUR." << endl;
}
}
}
}
2016-01-18 20:34:29 +01:00
void Manager::cleanCache()
{
// currently clear only AUR cache
if(userRepository()) {
userRepository()->cleanOutdatedPackages();
}
}
void Manager::wipeCache()
{
// currently wipe only AUR cache
if(userRepository()) {
userRepository()->wipePackages();
}
}
void Manager::maintainCache()
{
cleanCache();
// TODO: write cache only when modified
writeCache();
}
2015-08-10 22:46:01 +02:00
/*!
* \brief Unregisters all registred sync databases.
*/
void Manager::unregisterSyncDataBases()
{
if(alpm_unregister_all_syncdbs(m_handle)) {
throw runtime_error(string("Cannot unregister sync databases: ") + alpm_strerror(alpm_errno(m_handle)));
}
}
/*!
* \brief Returns a list of all sync databases.
* \remarks Sync databases must be registered with parsePacmanConfig() before.
*/
2015-12-08 18:59:17 +01:00
const std::map<QString, AlpmDatabase *> &Manager::syncDatabases() const
2015-08-10 22:46:01 +02:00
{
2015-12-08 18:59:17 +01:00
return m_syncDbMap; // m_syncDbs has been filled when the databases were registered
2015-08-10 22:46:01 +02:00
}
/*!
* \brief Returns basic information about all repositories known to the manager.
*
* The results include the local database ("local") and the names of
* the registered sync databases.
*/
const QJsonObject &Manager::basicRepoInfo() const
2015-08-10 22:46:01 +02:00
{
if(m_basicRepoInfo.isEmpty()) {
QMutexLocker locker(&m_basicRepoInfoMutex);
if(m_basicRepoInfo.isEmpty()) {
2015-09-04 14:37:01 +02:00
// add local data base
2015-11-03 18:19:24 +01:00
{
QReadLocker locker(localDataBase()->lock());
m_basicRepoInfo.insert(localDataBase()->name(), localDataBase()->basicInfo());
}
2015-09-04 14:37:01 +02:00
// add sync data bases
for(const auto &syncDb : syncDatabases()) {
// check if the "sync" database is actually used for syncing
2015-11-03 18:19:24 +01:00
QReadLocker locker(syncDb.second->lock());
2015-09-04 14:37:01 +02:00
auto usage = syncDb.second->usage();
if((usage & ALPM_DB_USAGE_SYNC) || (usage & ALPM_DB_USAGE_INSTALL) || (usage & ALPM_DB_USAGE_UPGRADE)) {
m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo());
} else {
m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo());
}
2015-08-10 22:46:01 +02:00
}
2015-09-04 14:37:01 +02:00
// add AUR
if(userRepository()) {
2015-11-03 18:19:24 +01:00
QReadLocker locker(userRepository()->lock());
m_basicRepoInfo.insert(userRepository()->name(), userRepository()->basicInfo());
2015-09-04 14:37:01 +02:00
}
2015-08-10 22:46:01 +02:00
}
}
return m_basicRepoInfo;
}
/*!
* \brief Returns package information for the specified selection of packages.
2015-09-27 19:29:45 +02:00
* \remarks Does not request any information and hence will only return information
* which does not need to be requested or has been requested yet.
2015-08-10 22:46:01 +02:00
*/
const QJsonArray Manager::packageInfo(const QJsonObject &pkgSelection, PackageInfoPart part) const
2015-08-10 22:46:01 +02:00
{
QJsonArray results;
2015-09-04 14:37:01 +02:00
for(auto i = pkgSelection.constBegin(), end = pkgSelection.constEnd(); i != end; ++i) {
if(auto *repo = repositoryByName(i.key())) {
for(const auto &entry : i.value().toArray()) {
const auto entryObj = entry.toObject();
const auto pkgName = entryObj.value(QStringLiteral("name")).toString();
if(!pkgName.isEmpty()) {
QJsonObject res;
2015-09-29 21:52:30 +02:00
res.insert(QStringLiteral("name"), pkgName);
res.insert(QStringLiteral("repo"), repo->name());
const auto index = entryObj.value(QStringLiteral("index"));
if(!index.isNull() && !index.isUndefined()) {
res.insert(QStringLiteral("index"), index);
}
2015-09-04 14:37:01 +02:00
if(auto *pkg = repo->packageByName(pkgName)) {
if(part & Basics) {
res.insert(QStringLiteral("basics"), pkg->basicInfo());
}
if(part & Details) {
res.insert(QStringLiteral("details"), pkg->detailedInfo());
}
2015-09-04 14:37:01 +02:00
} else {
res.insert(QStringLiteral("error"), QStringLiteral("na"));
2015-09-04 14:37:01 +02:00
}
results << res;
2015-09-04 14:37:01 +02:00
}
2015-08-10 22:46:01 +02:00
}
2015-09-04 14:37:01 +02:00
} else {
// specified repository can not be found
QJsonObject errorObj;
errorObj.insert(QStringLiteral("repo"), i.key());
errorObj.insert(QStringLiteral("error"), QStringLiteral("na"));
results << errorObj;
2015-08-10 22:46:01 +02:00
}
2015-09-04 14:37:01 +02:00
2015-08-10 22:46:01 +02:00
}
return results;
2015-08-10 22:46:01 +02:00
}
/*!
* \brief Returns group information for the local database and all registred sync databases.
*/
const QJsonArray &Manager::groupInfo() const
{
if(m_groupInfo.empty()) {
QMutexLocker locker(&m_groupInfoMutex);
if(m_groupInfo.empty()) {
m_groupInfo << localDataBase()->groupInfo();
2015-12-08 18:59:17 +01:00
for(const auto &db : m_syncDbMap) {
2015-09-04 14:37:01 +02:00
m_groupInfo << db.second->groupInfo();
}
2015-08-10 22:46:01 +02:00
}
}
return m_groupInfo;
}
/*!
* \brief Initiates the ALPM library handle and databases.
* \remarks Do not call this function, if ALPM is already initiated.
*/
void Manager::initAlpmHandle()
{
alpm_errno_t err;
if(!(m_handle = alpm_initialize(config().alpmRootDir().toLocal8Bit().data(), config().alpmDbPath().toLocal8Bit().data(), &err))) {
throw runtime_error(string("Cannot initialize ALPM: ") + alpm_strerror(err));
}
m_localDb = make_unique<AlpmDatabase>(alpm_get_localdb(m_handle), config().alpmDbPath(), 0);
}
/*!
* \brief Frees the ALPM library handle and databases.
* \remarks Do not call any ALPM related methods except initAlpmHandle() to reinit ALPM.
*/
void Manager::cleanupAlpm()
{
if(m_handle) {
alpm_release(m_handle);
2015-10-28 19:55:36 +01:00
m_handle = nullptr;
m_localDb.reset();
m_syncDbs.clear();
2015-12-08 18:59:17 +01:00
m_syncDbMap.clear();
}
}
2015-08-10 22:46:01 +02:00
/*!
* \brief Returns the ALPM database with the specified name.
*/
const AlpmDatabase *Manager::databaseByName(const QString &dbName) const
2015-08-10 22:46:01 +02:00
{
2015-09-04 14:37:01 +02:00
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
return localDataBase();
2015-08-10 22:46:01 +02:00
} else {
2015-09-04 14:37:01 +02:00
try {
2015-12-08 18:59:17 +01:00
return m_syncDbMap.at(dbName);
2015-09-04 14:37:01 +02:00
} catch(const out_of_range &) {
return nullptr;
}
2015-08-10 22:46:01 +02:00
}
}
/*!
* \brief Returns the ALPM database with the specified name.
*/
AlpmDatabase *Manager::databaseByName(const QString &dbName)
2015-08-10 22:46:01 +02:00
{
2015-09-04 14:37:01 +02:00
if(dbName.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
return localDataBase();
2015-08-10 22:46:01 +02:00
} else {
2015-09-04 14:37:01 +02:00
try {
2015-12-08 18:59:17 +01:00
return m_syncDbMap.at(dbName);
2015-09-04 14:37:01 +02:00
} catch(const out_of_range &) {
return nullptr;
}
2015-08-10 22:46:01 +02:00
}
}
/*!
2015-09-04 14:37:01 +02:00
* \brief Returns the package source with the specified name.
2015-08-10 22:46:01 +02:00
*/
2015-09-04 14:37:01 +02:00
const Repository *Manager::repositoryByName(const QString &name) const
2015-08-10 22:46:01 +02:00
{
2015-09-04 14:37:01 +02:00
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
return localDataBase();
2015-09-04 14:37:01 +02:00
} else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) {
return userRepository();
} else {
try {
2015-12-08 18:59:17 +01:00
return m_syncDbMap.at(name);
2015-09-04 14:37:01 +02:00
} catch(const out_of_range &) {
return nullptr;
}
}
2015-08-10 22:46:01 +02:00
}
/*!
2015-09-04 14:37:01 +02:00
* \brief Returns the package source with the specified name.
2015-08-10 22:46:01 +02:00
*/
2015-09-04 14:37:01 +02:00
Repository *Manager::repositoryByName(const QString &name)
2015-08-10 22:46:01 +02:00
{
2015-09-04 14:37:01 +02:00
if(name.compare(QLatin1String("local"), Qt::CaseInsensitive) == 0) {
return localDataBase();
2015-09-04 14:37:01 +02:00
} else if(config().isAurEnabled() && (name.compare(QLatin1String("aur"), Qt::CaseInsensitive) == 0)) {
return userRepository();
} else {
2015-08-10 22:46:01 +02:00
try {
2015-12-08 18:59:17 +01:00
return m_syncDbMap.at(name);
2015-09-04 14:37:01 +02:00
} catch(const out_of_range &) {
return nullptr;
2015-08-10 22:46:01 +02:00
}
}
2015-09-04 14:37:01 +02:00
}
/*!
* \brief Checks the specified database for upgrades.
*
* Appropriate upgrade sources will be determined automatically; does not check the AUR.
*/
const UpgradeLookupResults Manager::checkForUpgrades(AlpmDatabase *db) const
2015-09-04 14:37:01 +02:00
{
UpgradeLookupResults results;
db->checkForUpgrades(results);
2015-08-10 22:46:01 +02:00
return results;
}
}