Commit f3b9f473 authored by Lukas Appelhans's avatar Lukas Appelhans
Browse files

Integrate Polkit into AkabeiCore

Make it at least halfway work

main.cpp

Try to make it work

Remove the minus and make it work

Try to do a PolKitArchiveHandler

Fix compilation and add test

Check permissions plus use the PolKitArchiveHandler already in akabei-create-db

wip, not compiling

Make it compile again, make it able to upgrade, add filesystemhandler and also start sqliteone

all untested though

Finish sqlite

Create a PolKitSQLiteConnection

work on the polkitsqliteconnection

Add filesystemhelper

Check for uid and gid before assuming all is right

Use polkit for lock file

Damn

Correct connect!

Try more things that do not work

Transfer more to PolKitSQLiteConnection and fix a couple of crashes

Make the lock only on transaction

Make sure the helper actually locks the lockfile

Fix a bunch of crashes

Add comment

Fix 2 FIXMEs, still untested

Actually make ze locking work

A bunch of fixes everywhere

More abstraction

More abstraction

Rename

Fix processingOptions and also make the lock work much better hopefully
parent 7e1bebad
......@@ -24,6 +24,7 @@ set(QT_MIN_VERSION "4.4.0")
find_package(Qt4 REQUIRED)
find_package(LibArchive REQUIRED)
find_package(Sqlite REQUIRED)
find_package(PolkitQt-1 REQUIRED)
add_definitions(${QT_DEFINITIONS} -W -Wall)
......@@ -31,10 +32,13 @@ include_directories(
BEFORE SYSTEM
${QT_INCLUDES}
${LIBARCHIVE_INCLUDE_DIR}
${POLKITQT-1_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/lib
${CMAKE_CURRENT_BINARY_DIR}/tools/polkit-helper
${CMAKE_SOURCE_DIR}/lib
${CMAKE_SOURCE_DIR}/tools/polkit-helper
)
# Define install directories
......@@ -43,7 +47,7 @@ set(BIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE "Akabei binary installat
set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE "Akabei library installation directory" STRING)
set(INCLUDES_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include/akabeicore" CACHE "Akabei includes installation directory" STRING)
set(STATE_DIR "/var" CACHE "Directory to store cache data and log files, usually under ${STATE_DIR}/{cache,log,etc...})" STRING)
set(CONFIGURATION_DIR "/var" CACHE "Directory to store Akabei’s configuration files" STRING)
set(CONFIGURATION_DIR "/etc" CACHE "Directory to store Akabei’s configuration files" STRING)
option(BUILD_TESTS "Builds various tests and examples for Akabei" ON)
......
# - Try to find PolkitQt-1
# Once done this will define
#
# POLKITQT-1_FOUND - system has Polkit-qt
# POLKITQT-1_INCLUDE_DIR - the Polkit-qt include directory
# POLKITQT-1_LIBRARIES - Link these to use all Polkit-qt libs
# POLKITQT-1_CORE_LIBRARY - Link this to use the polkit-qt-core library only
# POLKITQT-1_GUI_LIBRARY - Link this to use GUI elements in polkit-qt (polkit-qt-gui)
# POLKITQT-1_AGENT_LIBRARY - Link this to use the agent wrapper in polkit-qt
# POLKITQT-1_DEFINITIONS - Compiler switches required for using Polkit-qt
#
# The minimum required version of PolkitQt-1 can be specified using the
# standard syntax, e.g. find_package(PolkitQt-1 1.0)
# Copyright (c) 2010, Dario Freddi, <drf@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# Support POLKITQT-1_MIN_VERSION for compatibility:
if ( NOT PolkitQt-1_FIND_VERSION AND POLKITQT-1_MIN_VERSION )
set ( PolkitQt-1_FIND_VERSION ${POLKITQT-1_MIN_VERSION} )
endif ( NOT PolkitQt-1_FIND_VERSION AND POLKITQT-1_MIN_VERSION )
set( _PolkitQt-1_FIND_QUIETLY ${PolkitQt-1_FIND_QUIETLY} )
find_package( PolkitQt-1 ${PolkitQt-1_FIND_VERSION} QUIET NO_MODULE PATHS ${LIB_INSTALL_DIR}/PolkitQt-1/cmake )
set( PolkitQt-1_FIND_QUIETLY ${_PolkitQt-1_FIND_QUIETLY} )
include( FindPackageHandleStandardArgs )
find_package_handle_standard_args( PolkitQt-1 DEFAULT_MSG PolkitQt-1_CONFIG )
if (POLKITQT-1_FOUND)
if (NOT POLKITQT-1_INSTALL_DIR STREQUAL CMAKE_INSTALL_PREFIX)
message("WARNING: Installation prefix does not match PolicyKit install prefixes. You probably will need to move files installed "
"in POLICY_FILES_INSTALL_DIR and by dbus_add_activation_system_service to the ${POLKITQT-1_INSTALL_DIR} prefix")
endif (NOT POLKITQT-1_INSTALL_DIR STREQUAL CMAKE_INSTALL_PREFIX)
endif (POLKITQT-1_FOUND)
......@@ -24,7 +24,9 @@ akabeirunnerrunnable_p.cpp
akabeivalidatorrunnable_p.cpp
md5.c
SQLiteConnection.cpp
PolKitSQLiteConnection.cpp
libarchive++/archivehandler.cpp
libarchive++/polkitarchivehandler.cpp
)
set(AKABEI_OPERATION_SRCS
......@@ -55,6 +57,7 @@ akabeiquery.h
akabeicache.h
akabeicore_global.h
SQLiteConnection.h
PolKitSQLiteConnection.h
md5.h
)
......@@ -62,6 +65,7 @@ set(AKABEI_ARCHIVE_HDRS
libarchive++/archivehandler.h
libarchive++/archiveentry.h
libarchive++/archiveoperations.h
libarchive++/polkitarchivehandler.h
)
set(AKABEI_OPERATION_HDRS
......@@ -90,7 +94,8 @@ set_target_properties(akabeicore PROPERTIES VERSION ${MAJOR_AKABEI_VERSION}.${MI
target_link_libraries(akabeicore
${QT_QTCORE_LIBRARY}
${LIBARCHIVE_LIBRARY}
${SQLITE_LIBRARIES})
${SQLITE_LIBRARIES}
${POLKITQT-1_LIBRARIES})
install(TARGETS akabeicore DESTINATION ${LIB_INSTALL_DIR})
install(FILES ${AKABEI_CORE_HDRS} DESTINATION ${INCLUDES_INSTALL_DIR})
......
/* This file is part of the Chakra project
Copyright (C) 2012 Lukas Appelhans <boom1992@chakra-project.org>
Copyright (C) 2011 Lisa Vitolo <shainer@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 "PolKitSQLiteConnection.h"
#include <QCoreApplication>
#include <QDBusMessage>
#include <QDBusInterface>
#include <akabeiconfig.h>
#include <akabeihelpers.h>
#include <PolkitQt1/Authority>
#define SETUP_DBUS_DATABASE \
if (!d->setupDbusDatabase) { \
iface.setProperty("filename", d->filename); \
d->setupDbusDatabase = true; \
}
class PolKitSQLiteConnection::Private
{
public:
enum Access {
NoAccess,
Read,
ReadWrite
};
Private()
: connection(0), setupDbusDatabase(false)
{
qRegisterMetaType< QList<Row> >("QList<Row>");
}
~Private() {
delete connection;
}
SQLiteConnection * connection;
Akabei::Helpers::Permissions access;
QString filename;
bool setupDbusDatabase;
};
PolKitSQLiteConnection::PolKitSQLiteConnection(QString const& name, bool readonly)
: d(new Private())
{
connectToDB(name, readonly);
}
PolKitSQLiteConnection::PolKitSQLiteConnection()
: d(new Private())
{
}
PolKitSQLiteConnection::~PolKitSQLiteConnection()
{
delete d;
}
void PolKitSQLiteConnection::connectToDB(QString const& name, bool readonly)
{
d->access = Akabei::Helpers::permissionsOfPath(name);
if (d->access != Akabei::Helpers::NoPermission) {
d->connection = new SQLiteConnection(name, readonly && d->access.testFlag(Akabei::Helpers::WritePermission));
}
d->filename = name;
d->setupDbusDatabase = false;
}
SQLiteResource PolKitSQLiteConnection::query(const QString &q)
{
if ((d->access.testFlag(Akabei::Helpers::ReadWritePermission)) || ((d->access.testFlag(Akabei::Helpers::ReadPermission)) && q.startsWith("SELECT"))) {
return d->connection->query(q);
}
if (Akabei::Helpers::checkAuthorizationSync("org.chakraproject.akabeicorehelper.sqlite.query")) {
QDBusInterface iface("org.chakraproject.akabeicorehelper", "/archive", "org.chakraproject.akabeicorehelper.sqlite", QDBusConnection::systemBus());
SETUP_DBUS_DATABASE
QDBusMessage mes = iface.call("query", q);
if (!mes.arguments().isEmpty())
return SQLiteResource(mes.arguments().first().value< QList<Row> >());
} else {
qDebug() << "Authorization not granted" << PolkitQt1::Authority::instance()->errorDetails();//TODO: error handling
}
return SQLiteResource(QList<Row>());
}
qint64 PolKitSQLiteConnection::getLastRowId()
{
if (d->access.testFlag(Akabei::Helpers::ReadPermission))
return d->connection->getLastRowId();
QDBusInterface iface("org.chakraproject.akabeicorehelper", "/archive", "org.chakraproject.akabeicorehelper.sqlite", QDBusConnection::systemBus());
SETUP_DBUS_DATABASE
QDBusMessage mes = iface.call("lastRowId");
if (!mes.arguments().isEmpty())
return mes.arguments().first().toInt();
return -1;
}
void PolKitSQLiteConnection::bind(QString const& key, int value)
{
if (!d->access)
d->connection->bind(key, value);
if (d->access & Akabei::Helpers::ReadWritePermission)
return;
//We bind to both databases, as we have no information yet if we have a read or write query
QDBusInterface iface("org.chakraproject.akabeicorehelper", "/archive", "org.chakraproject.akabeicorehelper.sqlite", QDBusConnection::systemBus());
SETUP_DBUS_DATABASE
QDBusMessage mes = iface.call("bind", key, value);
}
void PolKitSQLiteConnection::bind(QString const& key, QString const& value)
{
if (!d->access)
d->connection->bind(key, value);
if (d->access & Akabei::Helpers::ReadWritePermission)
return;
//We bind to both databases, as we have no information yet if we have a read or write query
QDBusInterface iface("org.chakraproject.akabeicorehelper", "/archive", "org.chakraproject.akabeicorehelper.sqlite", QDBusConnection::systemBus());
SETUP_DBUS_DATABASE
QDBusMessage mes = iface.call("bind", key, value);
}
void PolKitSQLiteConnection::bind(QString const& key, QByteArray const& value)
{
if (!d->access)
d->connection->bind(key, value);
if (d->access & Akabei::Helpers::ReadWritePermission)
return;
//We bind to both databases, as we have no information yet if we have a read or write query
QDBusInterface iface("org.chakraproject.akabeicorehelper", "/archive", "org.chakraproject.akabeicorehelper.sqlite", QDBusConnection::systemBus());
SETUP_DBUS_DATABASE
QDBusMessage mes = iface.call("bind", key, value);
}
/* This file is part of the Chakra project
Copyright (C) 2012 Lukas Appelhans <boom1992@chakra-project.org>
Copyright (C) 2011 Lisa Vitolo <shainer@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.
*/
#ifndef POLKITSQLITECONNECTION_H
#define POLKITSQLITECONNECTION_H
#include <QVariantMap>
#include <QString>
#include <SQLiteConnection.h>
/**
* @class SQLiteConnection
* @brief manages a connection with a database using the sqlite C library
*
* This class is not thread-safe.
*/
class PolKitSQLiteConnection
{
typedef QVariant Value;
typedef QVariantMap Row;
public:
/**
* Constructs an object and establishes the connection
*
* @param name the database filename
* @param readonly true if read-only access, false for read-write
*/
PolKitSQLiteConnection(QString const& name, bool readonly);
/**
* Constructs an object without establishing any connection
*/
PolKitSQLiteConnection();
~PolKitSQLiteConnection();
/**
* Establishes a new connection with a database
*
* @param name the database filename
* @param readonly true if read-only access, false for read-write
*/
void connectToDB(QString const&, bool);
/**
* Executes a query on the database
*
* @param q the query string
* @returns a SQLiteResource object with the result, if present
*/
SQLiteResource query(const QString &q);
/**
* If the table interested by the last query as an autoincremented primary key,
* gets the last value assigned to the key
*/
qint64 getLastRowId();
/**
* Creates a "macro" (fake name) that maps into an integer, a QString, or a QByteArray
* The next query can contain the defined macros in place of the actual values.
* @note All bindings stay valid only for the subsequent call of query().
*/
void bind(QString const&, int);
void bind(QString const&, QString const&);
void bind(QString const&, QByteArray const&);
private:
// Disable copy constructor and default constructor
PolKitSQLiteConnection(PolKitSQLiteConnection const&);
class Private;
Private *d;
};
Q_DECLARE_METATYPE(QList<Row>);
#endif
......@@ -38,15 +38,18 @@ class SQLiteConnection::Private
public:
Private()
: isConnected(false)
, dbHandle(0)
, statement(0)
{}
~Private()
{
int retvalue(sqlite3_close(dbHandle));
if (dbHandle) {
int retvalue(sqlite3_close(dbHandle));
if (retvalue != SQLITE_OK) {
logWarning() << "Connection with database not closed properly; it's possible to still have some resources allocated";
if (retvalue != SQLITE_OK) {
logWarning() << "Connection with database not closed properly; it's possible to still have some resources allocated";
}
}
}
......@@ -109,7 +112,7 @@ void SQLiteConnection::connectToDB(QString const& dbName, bool readonly)
/*
* Executes a query
*/
SQLiteResource SQLiteConnection::query(const QString &q)
QList<Row> SQLiteConnection::rowQuery(const QString &q)
{
QMutexLocker locker(&d->mutex);
d->rows.clear();
......@@ -162,8 +165,13 @@ SQLiteResource SQLiteConnection::query(const QString &q)
throw SQLiteException(sqlite3_errmsg(d->dbHandle));
}
SQLiteResource table(d->rows);
d->checkForErrors(sqlite3_finalize(d->statement));
return d->rows;
}
SQLiteResource SQLiteConnection::query(const QString &q)
{
SQLiteResource table(rowQuery(q));
return table;
}
......
......@@ -30,6 +30,9 @@
#include <exception>
#include <QSharedDataPointer>
typedef QVariantMap Row;
/**
* @class SQLiteResource
* @brief Used by SQLiteConnection to store the result of a query
......@@ -37,7 +40,6 @@
class SQLiteResource
{
typedef QVariant Value;
typedef QVariantMap Row;
public:
SQLiteResource(QList<Row>);
SQLiteResource(const SQLiteResource &other);
......@@ -80,7 +82,6 @@ class SQLiteConnection
typedef QVariantMap Row;
public:
/**
* Constructs an object and establishes the connection
*
......@@ -111,6 +112,14 @@ public:
* @returns a SQLiteResource object with the result, if present
*/
SQLiteResource query(const QString &q);
/**
* Executes a query on the database
*
* @param q the query string
* @returns a QList<Row> object with the result, if present
*/
QList<Row> rowQuery(const QString &q);
/**
* If the table interested by the last query as an autoincremented primary key,
......
......@@ -28,11 +28,15 @@
#include <QVariant>
#include <QTranslator>
#include <QCoreApplication>
#include <QDBusInterface>
#include <QDBusConnectionInterface>
#include <libarchive++/archivehandler.h>
#include <akabeihook.h>
#include <fcntl.h>
#include <polkit-qt-1/polkitqt1-authority.h>
Q_DECLARE_METATYPE(QUuid)
Q_DECLARE_METATYPE(QList< Akabei::Package* >)
Q_DECLARE_METATYPE(QList< Akabei::Group* >)
......@@ -191,18 +195,102 @@ void BackendPrivate::addDatabase(const QString &name)
}
}
void BackendPrivate::setStatus(Backend::Status s)
void Backend::setStatus(Backend::Status s, QObject * cO, const char * cS)
{
Q_Q(Backend);
Q_D(Backend);
qDebug() << "CALLED WITH" << s << d->status;
if (s == d->status)
return;
qDebug() << "Now let's call this";
if (s == Backend::StatusOnTransaction && d->status != Akabei::Backend::StatusWaitingForLock) {
d->callbackObject = cO;
d->callbackSlot = cS;
if (Config::instance()->needsPrivileges()) {
qDebug() << "We need privileges to get lock";
if (Akabei::Helpers::checkAuthorizationSync("org.chakraproject.akabeicorehelper.filesystem.lock")) {
setStatus(Backend::StatusWaitingForLock);
qDebug() << "Authorization granted";
QDBusInterface iface("org.chakraproject.akabeicorehelper", "/filesystem", "org.chakraproject.akabeicorehelper.filesystem", QDBusConnection::systemBus());
iface.setProperty("root", Config::instance()->root());
iface.connection().connect("org.chakraproject.akabeicorehelper", "/filesystem", "org.chakraproject.akabeicorehelper.filesystem", "lockGranted", this, SLOT(__k__lockGranted(qint64)));
QDBusMessage mes = iface.call("getLock", QCoreApplication::applicationPid());
} else {
qDebug() << "Authorization not granted" << PolkitQt1::Authority::instance()->errorDetails();
setStatus(Backend::StatusBroken);
}
return;
}
qDebug() << "WE HERE";
if (!d->lockFileHandle) {
d->lockFileHandle = new QFile(Akabei::Config::instance()->rootDir().absoluteFilePath("var/local/akabei.lck"));
if (!d->lockFileHandle->open(QFile::ReadWrite)) {
d->initError.setType(Error::BackendInitializationError);
d->initError.setDescription("Could not open lock file for writing.");
setStatus(Backend::StatusBroken);
return;
}
}
struct flock lockinfo;
memset(&lockinfo, 0, sizeof(struct flock));
lockinfo.l_type = F_WRLCK;
lockinfo.l_whence = SEEK_SET;
lockinfo.l_start = 0;
lockinfo.l_len = 0;
if (fcntl(d->lockFileHandle->handle(), F_SETLK, &lockinfo) == -1) {
akabeiDebug() << "No lock for us!";
setStatus(Backend::StatusWaitingForLock);
connect(&d->lockWatcher, SIGNAL(finished()), this, SLOT(__k__lockGranted()));//TODO: Check if this works!!!!!
QFuture<void> lockF = QtConcurrent::run(d, &BackendPrivate::getLock);
d->lockWatcher.setFuture(lockF);
} else {
d->__k__lockGranted();
}
return;
} else if (d->status == StatusOnTransaction) {
if (Config::instance()->needsPrivileges()) {
qDebug() << "We need privileges to remove lock";
if (Akabei::Helpers::checkAuthorizationSync("org.chakraproject.akabeicorehelper.filesystem.lock")) {
qDebug() << "Authorization granted";
QDBusInterface iface("org.chakraproject.akabeicorehelper", "/filesystem", "org.chakraproject.akabeicorehelper.filesystem", QDBusConnection::systemBus());
QDBusMessage mes = iface.call("removeLock", QCoreApplication::applicationPid());
} else {
qDebug() << "Authorization not granted" << PolkitQt1::Authority::instance()->errorDetails();
setStatus(Backend::StatusBroken);
}
return;
}
struct flock lockinfo;
memset(&lockinfo, 0, sizeof(struct flock));
lockinfo.l_type = F_UNLCK;
lockinfo.l_whence = SEEK_SET;
lockinfo.l_start = 0;
lockinfo.l_len = 0;
if (s != status) {
status = s;
emit q->statusChanged(status);
if (fcntl(d->lockFileHandle->handle(), F_SETLK, &lockinfo) == -1) {
akabeiDebug() << "lock could not get released?!";
}
}
d->status = s;
emit statusChanged(d->status);
}
void BackendPrivate::__k__initializationFinished()
{
Q_Q(Backend);
databases.clear();
localDatabase = 0;
......@@ -214,14 +302,14 @@ void BackendPrivate::__k__initializationFinished()
if (rethash.isEmpty()) {
// Broken
setStatus(Akabei::Backend::StatusBroken);
q->setStatus(Akabei::Backend::StatusBroken);
return;
}
localDatabase = rethash["local"];
if (!localDatabase) {
// Something very wrong here
setStatus(Akabei::Backend::StatusBroken);
q->setStatus(Akabei::Backend::StatusBroken);
return;
}
......@@ -236,7 +324,7 @@ void BackendPrivate::__k__initializationFinished()
}
// The backend is now ready
setStatus(Akabei::Backend::StatusReady);
q->setStatus(Akabei::Backend::StatusReady);
}
void BackendPrivate::packageQueryFinished()
......@@ -308,16 +396,16 @@ void BackendPrivate::groupQueryFinished()
emit q->queryGroupsCompleted(uuid, result);
}
void BackendPrivate::__k__lockGranted()
void BackendPrivate::__k__lockGranted(qint64 pid)
{
Q_Q(Backend);
setStatus(Backend::StatusInitializing);
initializationWatcher = new QFutureWatcher< QHash< QString, Database* > >(this);
QObject::connect(initializationWatcher, SIGNAL(finished()), q, SLOT(__k__initializationFinished()));
qDebug() << "LOCK GRANTED TO" << pid << "We got" << QCoreApplication::applicationPid();
if (pid != QCoreApplication::applicationPid())
return;
q->setStatus(Backend::StatusOnTransaction);
QFuture< QHash< QString, Database* > > future = QtConcurrent::run(this, &BackendPrivate::performInitialization);
initializationWatcher->setFuture(future);