Commit 63e495fa authored by AlmAck's avatar AlmAck
Browse files

Merge branch 'replace'

parents 29472d27 218cdc64
......@@ -134,6 +134,6 @@ private:
const char *message;
};
Q_DECLARE_METATYPE(QList<Row>);
Q_DECLARE_METATYPE(QList<Row>)
#endif // ABSTRACTSQLITECONNECTION_H
......@@ -41,6 +41,8 @@ operations/akabeiplainscriptletoperation.cpp
operations/akabeiplainupgradeoperation.cpp
operations/akabeipolkitupgradeoperation.cpp
operations/akabeipolkitinstalloperation.cpp
operations/akabeiplainreplaceoperation.cpp
operations/akabeipolkitreplaceoperation.cpp
operations/akabeioperationutils.cpp
)
......@@ -84,6 +86,8 @@ operations/akabeipolkitremoveoperation.h
operations/akabeiplainscriptletoperation.h
operations/akabeiplainupgradeoperation.h
operations/akabeipolkitupgradeoperation.h
operations/akabeiplainreplaceoperation.h
operations/akabeipolkitreplaceoperation.h
operations/akabeiplainhookoperation.h
operations/akabeioperationutils.h
)
......
......@@ -36,7 +36,7 @@ enum Permission {
WritePermission = 0x02,
ReadWritePermission = 0x03
};
Q_DECLARE_FLAGS(Permissions, Permission);
Q_DECLARE_FLAGS(Permissions, Permission)
QByteArray md5sum(const QByteArray &data);
QByteArray md5sumOfFile(const QString &path);
QByteArray processMd5(unsigned char output[]);
......
......@@ -32,7 +32,7 @@ public:
virtual ~OperationPrivate() {
qDeleteAll(postOps);
qDeleteAll(preOps);
};
}
QString targetName;
QByteArray targetVersion;
......
......@@ -100,6 +100,11 @@ QString packageDependencies(const QString& dependency, const QString& comparison
return QString("SELECT * FROM packages JOIN depends ON packages.id=depends.package WHERE depends.dependency %0 \"%1\";").arg(comparison, dependency);
}
QString globalReplaces()
{
return QString("SELECT * FROM replaces JOIN packages ON replaces.package = packages.id");
}
QString begin()
{
return QString("BEGIN;");
......
......@@ -143,6 +143,11 @@ QString AKABEICORESHARED_EXPORT requiredByPackages(const QString &);
*/
QString AKABEICORESHARED_EXPORT packageDependencies(const QString &, const QString &);
/**
* Query all packages with at least one replacement.
*/
QString AKABEICORESHARED_EXPORT globalReplaces();
/**
* Query that begins a transaction.
*/
......
......@@ -335,6 +335,13 @@ void ValidatorWorker::processNextPhase()
}
}
foreach (const QString &target, op->conflictingTargets()) {
// if is a pkg to remove or replace skip the checks
bool skip = false;
foreach (const QString targetRemove, targetRemovals){
if(targetRemove.contains(target)) skip = true;
}
if (skip) continue;
if (targetNames.contains(target)) {
// Corner case: the conflict might be a provides/conflicts problem. Let's check.
if (targetNames[target] == op) {
......
......@@ -38,9 +38,9 @@ class AKABEICORESHARED_EXPORT PlainHookOperation : public Operation
friend class ChrootedProcess;
Q_PRIVATE_SLOT(d, void slotErrors(Akabei::Error::List));
Q_PRIVATE_SLOT(d, void slotFinished(bool));
Q_PRIVATE_SLOT(d, void slotMessage(QString));
Q_PRIVATE_SLOT(d, void slotErrors(Akabei::Error::List))
Q_PRIVATE_SLOT(d, void slotFinished(bool))
Q_PRIVATE_SLOT(d, void slotMessage(QString))
};
}
......
......@@ -182,8 +182,9 @@ void PlainRemoveOperation::validate()
}
if (!found) {
// setErrors(Error::List() << Error());
setErrors(Error::List() << Error(Error::UnknownError, QObject::tr("The package is not installed."), this));
setValidationFinished(false);
return;
}
if (!(processingOptions().testFlag(Akabei::SkipDependencies))) {
setTargetRemovals(QStringList() << d->package->name() << d->package->provides());
......
/* This file is part of the Chakra project
Copyright (C) 2015 Lukas Appelhans <l.appelhans@gmx.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
*/
#include "akabeiplainreplaceoperation.h"
#include "akabeiplainhookoperation.h"
#include "akabeiplainscriptletoperation.h"
#include "akabeioperationutils.h"
#include <akabeipackage.h>
#include <akabeicache.h>
#include <akabeiconfig.h>
#include <akabeihelpers.h>
#include <akabeibackend.h>
#include <akabeierror.h>
#include <akabeilog.h>
#include <libarchive++/archivehandler.h>
#include <akabeidatabase.h>
#include <akabeiquery.h>
#include <QFile>
#include <QDir>
#include <akabeidebug.h>
#include <QDateTime>
#include <QStringList>
#include <cstdio>
#include <stdlib.h>
#include <sqlite3.h>
#include <md5.h>
namespace Akabei {
class PlainReplaceOperation::Private
{
public:
Private(Package *p, const QList<Package*> &r) : package(p), replaces(r) {}
~Private() {}
Package *package;
QList<Package*> replaces;
};
PlainReplaceOperation::PlainReplaceOperation(Package *package, const QList<Package*> &replaces)
: Operation(package->name())
, d(new Private(package, replaces))
{
setPhase(Phase3); // Replacement happens in phase 3.
setPriority(50); // Replacement has a low priority.
setCanBeConcurrent(false); // And it can not be concurrent.
setTargetVersion(package->version().toByteArray());
setDescription(QObject::tr("Replacing packages with %1...").arg(package->name())); //TODO: Give better description
//Basically we replace our own package as well :)
QList<Package*> installed = Backend::instance()->localDatabase()->queryPackages(Queries::selectPackages("name", "=", d->package->name()));
if (!installed.isEmpty())
d->replaces.append(installed.first());
}
PlainReplaceOperation::~PlainReplaceOperation()
{
delete d;
}
void PlainReplaceOperation::run()
{
qDebug() << "Run replace";
OperationUtils util;
if (!(processingOptions() & Akabei::DatabaseOnly)) {
if (!util.validatePackage(d->package)) {
setErrors(Error::List() << Error(Error::UnknownError, util.errorMessage(), this));
setFinished(false);
}
const int numberPackages = d->replaces.count();
for (int i = 0; i != numberPackages; ++i) {
Package * replacedpkg = d->replaces[i];
// Remove first
// Remove all files
int idx = 0;
int filesSize = replacedpkg->retrieveFiles().size();
QMap<QString, QString> backup = replacedpkg->backupFiles();
foreach (QString file, replacedpkg->retrieveFiles()) {
if ((file.endsWith('/') && !QFileInfo(file).isSymLink()) || (backup.contains(file) && !backup[file].isEmpty())) {
++idx;
// It's a directory
continue;
}
QFile f(file);
if (!f.remove() && !(processingOptions().testFlag(Akabei::Force))) {
QString translated = QObject::tr("Removing %1 failed: %2").arg(file, f.errorString());
setErrors(Error::List() << Error(Error::UnknownError, translated, this));
setFinished(false);
}
++idx;
setProgress((idx * 100 / filesSize * (i + 1) / numberPackages) / 2);
}
}
// Run it.
QDir currentDir = QDir::current();
ArchiveHandler handle(d->package->pathToArchive());
try {
// libarchive requires this for extracting hard links.
chdir(Config::instance()->root().toUtf8().data());
QMap<QString, QString> backup = d->package->backupFiles();
/* Sets up the functor to do all the necessary job on the archive entries */
InstallFunctor extractor(backup, d->package->ultimatelyOwnedFiles(), this, handle);
handle.handleEntries(extractor);
// Restore the old cwd is we have it.
if (currentDir.exists()) {
chdir(currentDir.absolutePath().toUtf8().data());
}
} catch (ArchiveException& e) {
akabeiDebug() << e.what();
setErrors(Error::List() << Error(Akabei::Error::UnknownError, QObject::tr("Could not open package file."), this));
setFinished(false);
return;
}
}
// Generate an install date in UTC format.
QDateTime dt = QDateTime::currentDateTime();
Package::InstallReason reason = Package::InstalledAsDependencyReason;
// Create a new package.
Package *p = d->package->generateInstalledPackage(reason, dt);
// Database handling starts.
int ret = Helpers::QueryPerformer::beginTransaction(Backend::instance()->localDatabase());
if (ret != SQLITE_OK) {
// Problems...
setErrors(Error::List() << Error(Error::DatabaseError, QObject::tr("Could not begin database transaction!"), this));
setFinished(false);
return;
}
// Insert the entry into the database.
// Obtain the query from our helpers.
ret = Helpers::QueryPerformer::insertPackage(Backend::instance()->localDatabase(), p);
if (ret != SQLITE_OK) {
// Problems...
Helpers::QueryPerformer::rollbackTransaction(Backend::instance()->localDatabase());
setErrors(Error::List() << Error(Error::DatabaseError, QObject::tr("Could not add package to database!"), this));
setFinished(false);
return;
}
// Insert files.
ret = Helpers::QueryPerformer::insertFiles(Backend::instance()->localDatabase(), p);
if (ret != SQLITE_OK) {
// Problems...
Helpers::QueryPerformer::rollbackTransaction(Backend::instance()->localDatabase());
setErrors(Error::List() << Error(Error::DatabaseError, QObject::tr("Could not add files to database!"), this));
setFinished(false);
return;
}
// Scriptlet?
if (p->hasScriptlet()) {
ret = Helpers::QueryPerformer::insertScriptlet(Backend::instance()->localDatabase(), p);
if (ret != SQLITE_OK) {
// Problems...
Helpers::QueryPerformer::rollbackTransaction(Backend::instance()->localDatabase());
setErrors(Error::List() << Error(Error::DatabaseError, QObject::tr("Could not add scriptlet to database!"), this));
setFinished(false);
return;
}
}
// Commit the transaction.
ret = Helpers::QueryPerformer::commitTransaction(Backend::instance()->localDatabase());
if (ret != SQLITE_OK) {
// Problems...
Helpers::QueryPerformer::rollbackTransaction(Backend::instance()->localDatabase());
setErrors(Error::List() << Error(Error::DatabaseError, QObject::tr("Could not commit the transaction!"), this));
setFinished(false);
return;
}
if (!(processingOptions().testFlag(Akabei::DatabaseOnly))) {
// Add the archive to the cache.
// Simply copy it over to our cachedir.
Cache cache;
cache.writePackage(p->pathToArchive(), p->filename());
}
// Delete Package object.
delete p;
// Done!
log() << "replaced " << d->package->name() << " (" << d->package->version().toByteArray() << ")" << Akabei::endlog;
setProgress(100);
setFinished(true);
}
void PlainReplaceOperation::validate()
{
// Add conflicts.
setConflictingTargets(d->package->conflictsWith());
if (!(processingOptions().testFlag(Akabei::SkipDependencies))) {
// Add additional targets.
QStringList targets = QStringList() << d->package->name() << Helpers::unversionedTargets(d->package->provides());
targets.removeDuplicates();
setTargetAdditions(targets);
QStringList targetsRemoved;
foreach (Package * r, d->replaces)
targetsRemoved << r->name();
setTargetRemovals(targetsRemoved);
}
if (!(processingOptions().testFlag(Akabei::DatabaseOnly))) {
// Now let’s set the conflicting targets & stuff.
// Since this is an replacement, we’re adding stuff to the filesystem.
{
QSet<QString> oldFiles;
foreach (Package * r, d->replaces)
oldFiles.unite(r->retrieveFiles(Package::FilepathNoPrefix).toSet());
QSet<QString> newFiles = d->package->retrieveFiles(Package::FilepathNoPrefix).toSet();
QSet<QString> add = newFiles;
add.subtract(oldFiles);
QSet<QString> remove = oldFiles;
remove.subtract(newFiles);
if (!(processingOptions().testFlag(Akabei::Force))) {
setFileSystemAdditions(add.toList());
}
setFileSystemRemovals(remove.toList());
}
bool upgrade = false;
foreach (Package * package, d->replaces) {
if (package->name() == d->package->name()) {
upgrade = true;
continue;
}
// If it has a scriptlet, we need a new suboperation
if (package->hasScriptlet()) {
// Does it have a pre install function?
if (package->retrieveScriptlet().contains("pre_remove")) {
// Ok, it has
QStringList args;
args << "pre_remove";// << d->package->version().toByteArray();
setPreOperations(Operation::List() << new PlainScriptletOperation(d->package, args));//FIXME: Make this the polkitscriptletoperation
}
// Does it have a post install function?
if (package->retrieveScriptlet().contains("post_remove")) {
// Ok, it has
QStringList args;
args << "post_remove";// << d->package->version().toByteArray();
setPostOperations(Operation::List() << new PlainScriptletOperation(d->package, args));
}
}
}
const QString operation = upgrade ? "upgrade" : "install";
//Install/Upgrade scriptlets, Remove scriplets, hooks
// If it has a scriptlet, we need a new suboperation.
if (d->package->hasScriptlet()) {
// Does it have a pre-install function?
if (d->package->retrieveScriptlet().contains("pre_" + operation)) {
// Ok, it does.
QStringList args;
args << "pre_" + operation;
setPreOperations(Operation::List() << new PlainScriptletOperation(d->package, args));
}
// Does it have a post-install function?
if (d->package->retrieveScriptlet().contains("post_" + operation)) {
// Ok, it does.
QStringList args;
args << "post_" + operation;
setPostOperations(Operation::List() << new PlainScriptletOperation(d->package, args));
}
}
// Same for hooks.
if (d->package->hasHooks()) {
akabeiDebug() << "We have hooks" << d->package->hooks();
PlainHookOperation::instance()->add(d->package->retrieveHooks(), "post_install");
}
QSet<Hook*> removeHooks;
foreach (Package * package, d->replaces) {
removeHooks.unite(package->retrieveHooks().toSet());
}
if (!removeHooks.isEmpty()) {
PlainHookOperation::instance()->add(removeHooks.toList(), "post_remove");
}
}
if (!(processingOptions().testFlag(Akabei::SkipDependencies))) {
// Compute dependencies and stuff.
setTargetDependencies(d->package->dependencies());
}
// Ok, validated!
setValidationFinished(true);
}
}
/* This file is part of the Chakra project
Copyright (C) 2015 Lukas Appelhans <l.appelhans@gmx.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
*/
#ifndef AKABEIPLAINREPLACEOPERATION_H
#define AKABEIPLAINREPLACEOPERATION_H
#include <akabeicore_global.h>
#include <akabeioperation.h>
#include <akabeipackage.h>
namespace Akabei {
class Package;
//TODO: We need error handling (existing files etc) and also custom InstallReasons
class AKABEICORESHARED_EXPORT PlainReplaceOperation : public Operation
{
Q_DISABLE_COPY(PlainReplaceOperation)
public:
explicit PlainReplaceOperation(Package *package, const QList<Package*> &replaces);
virtual ~PlainReplaceOperation();
protected:
void validate();
void run();
private:
class Private;
Private * const d;
};
}
#endif // AKABEIPLAININSTALLOPERATION_H
......@@ -132,7 +132,7 @@ void PlainScriptletOperation::run()
void PlainScriptletOperation::validate()
{
akabeiDebug() << "Validating scriptlet ";
akabeiDebug() << "Validating scriptlet for: " << d->package->name();
// Just check if the package actually has a scriptlet
setValidationFinished(d->package->hasScriptlet());
}
......
......@@ -20,6 +20,10 @@ namespace Akabei {
class Package;
/**
* @class PolkitInstakkOperation
* @brief Operation to install a package from the system using polkit.
*/
class AKABEICORESHARED_EXPORT PolkitInstallOperation : public Operation
{
Q_OBJECT
......
......@@ -12,12 +12,18 @@
#ifndef AKABEIPOLKITREINSTALLOPERATION_H
#define AKABEIPOLKITREINSTALLOPERATION_H
#include <akabeioperation.h>
#include <akabeicore_global.h>
#include <QStringList>
#include <akabeioperation.h>
#include <akabeipackage.h>
namespace Akabei {
class Package;
/**
* @class PolkitReInstallOperation
* @brief Operation to reinstall a package from the system using polkit.
*/
class AKABEICORESHARED_EXPORT PolkitReInstallOperation : public Operation
{
Q_OBJECT
......
......@@ -14,9 +14,10 @@
#include <akabeicore_global.h>
#include <akabeioperation.h>
#include <akabeipackage.h>
namespace Akabei {
namespace Akabei
{
class Package;
/**
......
/*
This file is part of the Chakra project
Copyright (C) 2010 Dario Freddi <drf@chakra-project.org>
Copyright (C) 2013 Lukas Appelhans <boom1992@chakra-project.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
*/
#include "akabeipolkitreplaceoperation.h"
#include "akabeiplainscriptletoperation.h"
#include "akabeioperationutils.h"
#include "akabeiplainhookoperation.h"
#include <akabeipackage.h>
#include <akabeihelpers.h>
#include <akabeiconfig.h>
#include <akabeibackend.h>
#include <akabeilog.h>
#include <akabeierror.h>
#include <akabeidatabase.h>
#include <akabeiquery.h>
#include <QDateTime>
#include <QStringList>
#include <QDBusInterface>
#include <QDBusPendingCall>
namespace Akabei {
class PolkitReplaceOperation::Private
{
public:
Private(Package *p, const QList<Package*> &r, PolkitReplaceOperation * op) : package(p), replaces(r), q(op), iface(0) {}
~Private() {
delete iface;
}
void __k__polkitFinished(const QString &archive, bool success);
void __k__progressUpdated(const QString &archive, int progress);
void __k__newMessage(const QString &archive, const QString &message);
void __k__error(const QString &archive, int type, const QString &error);
void __k__removeFileProgress(int progress);
void __k__removeFileFinished();
void __k__removeFileErrors(const QStringList &errors);
Package *package;
QList<Package*> replaces;
PolkitReplaceOperation * q;
QDBusInterface * iface;
};
PolkitReplaceOperation::PolkitReplaceOperation(Package *package, const QList<Package*> &replaces)
: Operation(package->name())
, d(new Private(package, replaces, this))
{