add demo application

This commit is contained in:
Evgenii Alekseev 2017-03-05 03:20:09 +03:00
parent 6646400027
commit 84b8632ae8
30 changed files with 906 additions and 123 deletions

View File

@ -72,9 +72,10 @@ include(cppcheck.cmake)
include(paths.cmake)
get_directory_property(CMAKE_DEFINITIONS COMPILE_DEFINITIONS)
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
add_subdirectory(queued)
configure_file("${CMAKE_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h")
add_subdirectory("queued")
add_subdirectory("queued-daemon")
if (BUILD_TESTING)
enable_testing()
add_subdirectory(test)
add_subdirectory("test")
endif ()

View File

@ -0,0 +1,13 @@
# set project name
set (SUBPROJECT "queued-daemon")
message (STATUS "Subproject ${SUBPROJECT}")
add_subdirectory ("src")
# build man
file (GLOB SUBPROJECT_MAN_IN "*.1")
file (RELATIVE_PATH SUBPROJECT_MAN "${CMAKE_SOURCE_DIR}" "${SUBPROJECT_MAN_IN}")
configure_file ("${SUBPROJECT_MAN_IN}" "${CMAKE_CURRENT_BINARY_DIR}/${SUBPROJECT_MAN}")
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/${SUBPROJECT_MAN}" DESTINATION "${DATA_INSTALL_DIR}/man/man1")
install (FILES "bash-completions" DESTINATION "${DATA_INSTALL_DIR}/bash-completion/completions" RENAME "${SUBPROJECT}")
install (FILES "zsh-completions" DESTINATION "${DATA_INSTALL_DIR}/zsh/site-functions" RENAME "_${SUBPROJECT}")

View File

View File

View File

@ -0,0 +1,11 @@
[Administrator]
Username = root
Password = 0dd3e512642c97ca3f747f9a76e374fbda73f9292823c0313be9d78add7cdd8f72235af0c553dd26797e78e1854edee0ae002f8aba074b066dfce1af114e32f8
[Database]
Driver = QSQLITE
Hostname =
Password =
Path = /tmp/queued.db
Port =
Username =

View File

@ -0,0 +1,18 @@
# set files
file (GLOB_RECURSE SUBPROJECT_SOURCES "*.cpp")
file (GLOB_RECURSE SUBPROJECT_HEADERS "*.h")
# include_path
include_directories ("${PROJECT_LIBRARY_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_BINARY_DIR}"
"${PROJECT_TRDPARTY_DIR}"
"${Qt_INCLUDE}")
qt5_wrap_cpp (SUBPROJECT_MOC_SOURCES "${SUBPROJECT_HEADERS}")
add_executable ("${SUBPROJECT}" "${SUBPROJECT_HEADERS}" "${SUBPROJECT_SOURCES}"
"${SUBPROJECT_MOC_SOURCES}")
target_link_libraries ("${SUBPROJECT}" "${PROJECT_LIBRARY}" "${Qt_LIBRARIES}")
# install properties
install (TARGETS "${SUBPROJECT}" DESTINATION "${BIN_INSTALL_DIR}")

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2016 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#include "QueuedApplication.h"
#include <QDBusConnection>
#include <QDBusMessage>
#include "queued/Queued.h"
#include "QueuedApplicationInterface.h"
QueuedApplication::QueuedApplication(QObject *parent, const QVariantHash &args)
: QObject(parent)
, m_configuration(args)
{
qSetMessagePattern(QueuedDebug::LOG_FORMAT);
qCDebug(LOG_APP) << __PRETTY_FUNCTION__;
for (auto &metadata : QueuedDebug::getBuildData())
qCDebug(LOG_APP) << metadata;
init();
}
QueuedApplication::~QueuedApplication()
{
qCDebug(LOG_APP) << __PRETTY_FUNCTION__;
deinit();
}
void QueuedApplication::deinit()
{
QDBusConnection::sessionBus().unregisterObject(
QueuedConfig::DBUS_APPLICATION_PATH);
if (m_core)
delete m_core;
}
void QueuedApplication::init()
{
deinit();
initCore();
initDBus();
}
void QueuedApplication::initCore()
{
m_core = new QueuedCore(this);
// init objects
m_core->init(m_configuration[QString("config")].toString());
}
void QueuedApplication::initDBus()
{
QDBusConnection bus = QDBusConnection::systemBus();
if (!bus.registerObject(QueuedConfig::DBUS_APPLICATION_PATH,
new QueuedApplicationInterface(this),
QDBusConnection::ExportAllContents)) {
QString message = QString("Could not register application object %1")
.arg(bus.lastError().message());
qCCritical(LOG_DBUS) << message;
throw QueuedDBusException(message);
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2016 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#ifndef QUEUEDAPPLICATION_H
#define QUEUEDAPPLICATION_H
#include <QObject>
#include <QVariant>
class QueuedCore;
class QueuedApplication : public QObject
{
Q_OBJECT
public:
explicit QueuedApplication(QObject *parent, const QVariantHash &args);
virtual ~QueuedApplication();
void deinit();
void init();
private:
// backend
void initDBus();
void initCore();
// library
QueuedCore *m_core = nullptr;
// configuration
QVariantHash m_configuration;
};
#endif /* QUEUEDAPPLICATION_H */

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2016 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#include "QueuedApplicationInterface.h"
#include <QCoreApplication>
#include <unistd.h>
#include "queued/Queued.h"
#include "QueuedApplication.h"
QueuedApplicationInterface::QueuedApplicationInterface(
QueuedApplication *parent)
: QDBusAbstractAdaptor(parent)
, m_application(parent)
{
qCDebug(LOG_DBUS) << __PRETTY_FUNCTION__;
}
QueuedApplicationInterface::~QueuedApplicationInterface()
{
qCDebug(LOG_DBUS) << __PRETTY_FUNCTION__;
}
bool QueuedApplicationInterface::Active() const
{
return true;
}
void QueuedApplicationInterface::Close() const
{
return QCoreApplication::exit(0);
}
QStringList QueuedApplicationInterface::UIDs() const
{
QStringList uids;
uids.append(QString::number(getuid()));
uids.append(QString::number(geteuid()));
return uids;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2016 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#ifndef QUEUEDAPPLICATIONINTERFACE_H
#define QUEUEDAPPLICATIONINTERFACE_H
#include <QDBusAbstractAdaptor>
#include "QueuedConfig.h"
class QueuedApplication;
class QueuedApplicationInterface : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", DBUS_SERVICE_NAME)
public:
explicit QueuedApplicationInterface(QueuedApplication *parent);
virtual ~QueuedApplicationInterface();
public slots:
bool Active() const;
Q_NOREPLY void Close() const;
QStringList UIDs() const;
private:
QueuedApplication *m_application = nullptr;
};
#endif /* QUEUEDAPPLICATIONINTERFACE_H */

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2016 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusMessage>
#include <queued/Queued.h>
#include "QueuedApplication.h"
#include "version.h"
bool existingSessionOperation(const QString &operation)
{
QVariantList arguments = QueuedCoreAdaptor::sendRequest(
QueuedConfig::DBUS_SERVICE, QueuedConfig::DBUS_APPLICATION_PATH,
QueuedConfig::DBUS_SERVICE, operation, QVariantList());
return (!arguments.isEmpty() && arguments.at(0).type() == QVariant::Bool
&& arguments[0].toBool());
}
int main(int argc, char *argv[])
{
// daemon(0, 0);
QCoreApplication app(argc, argv);
app.setApplicationName(NAME);
app.setApplicationVersion(VERSION);
// parser
QCommandLineParser parser;
parser.setApplicationDescription(
"Daemon for starting jobs to queue of calculations");
parser.addHelpOption();
parser.addVersionOption();
// configuration option
QCommandLineOption configOption(QStringList() << "c"
<< "config",
"Read initial configuration from file",
"config", QueuedSettings::defaultPath());
parser.addOption(configOption);
// debug mode
QCommandLineOption debugOption(QStringList() << "d"
<< "debug",
"Print debug information");
parser.addOption(debugOption);
parser.process(app);
// check if exists
if (existingSessionOperation(QString("Active"))) {
qCWarning(LOG_APP) << "Another session is active";
return 1;
}
// enable debug
if (parser.isSet(debugOption))
QueuedDebug::enableDebug();
// build initial options hash
QVariantHash arguments = {{"config", parser.value(configOption)}};
// start application
QueuedApplication instance(nullptr, arguments);
return app.exec();
}

View File

View File

@ -35,17 +35,27 @@ namespace QueuedConfig
* @brief DBus service name for library and application
* @remark required by Qt macro
*/
#define DBUS_SERVICE_NAME "org.quadro.core"
#define DBUS_SERVICE_NAME "org.queued.core"
/**
* @ingroup QueuedConfig
* @brief DBus service name for library and application
*/
const char DBUS_SERVICE[] = DBUS_SERVICE_NAME;
/**
* @ingroup QueuedConfig
* @brief DBus object path for applicaiton
*/
const char DBUS_APPLICATION_PATH[] = "/application";
/**
* @ingroup QueuedConfig
* @brief DBus object path for library
*/
const char DBUS_OBJECT_PATH[] = "/queued";
/**
* @ingroup QueuedConfig
* @brief DBus properties path for library
*/
const char DBUS_PROPERTY_PATH[] = "/property";
// path configuration
// common paths

View File

@ -29,6 +29,7 @@
#include "QueuedCore.h"
#include "QueuedCoreAdaptor.h"
#include "QueuedCoreInterface.h"
#include "QueuedCorePropertiesInterface.h"
#include "QueuedDatabase.h"
#include "QueuedDebug.h"
#include "QueuedEnums.h"

View File

@ -33,6 +33,7 @@
class QueuedAdvancedSettings;
class QueuedDatabase;
class QueuedProcess;
class QueuedProcessManager;
class QueuedReportManager;
class QueuedSettings;
@ -185,6 +186,20 @@ public:
*/
bool stopTask(const long long _id,
const QueuedUserManager::QueuedUserAuthorization &_auth);
/**
* @brief get task by ID
* @param _id
* task ID
* @return task object or nullptr if no task found
*/
const QueuedProcess *task(const long long _id);
/**
* @brief get user by ID
* @param _id
* user ID
* @return user object or nullptr if no user found
*/
const QueuedUser *user(const long long _id);
// control methods
/**
* @brief deinit subclasses

View File

@ -24,30 +24,49 @@
#ifndef QUEUEDCOREADAPTOR_H
#define QUEUEDCOREADAPTOR_H
#include <QObject>
#include <QDBusArgument>
#include <QVariant>
/**
* @brief DBus adaptor for core interface
* @defgroup QueuedCoreAdaptor
* @brief adaptor to DBus methods
*/
class QueuedCoreAdaptor : public QObject
namespace QueuedCoreAdaptor
{
Q_OBJECT
public:
/**
* @brief QueuedCoreAdaptor class constructor
* @param parent
* pointer to parent item
/**
* @ingroup QueuedCoreAdaptor
* @brief common DBus request
* @param _service
* DBus service name
* @param _path
* DBus object path
* @param _interface
* DBus interface name
* @param _cmd
* command which will be sent to DBus
* @param _args
* command arguments
* @return reply object from DBus request
*/
explicit QueuedCoreAdaptor(QObject *parent);
/**
* @brief QueuedCoreAdaptor class destructor
QVariantList sendRequest(const QString &_service, const QString &_path,
const QString &_interface, const QString &_cmd,
const QVariantList &_args);
/**
* @ingroup QueuedCoreAdaptor
* @brief additional method to avoid conversion from DBus type to native ones
* @tparam T
* type to which DBus data should be converted
* @param _data
* source data
* @return converted value
*/
virtual ~QueuedCoreAdaptor();
private:
template <class T> T toNativeType(const QVariant &_data)
{
return qdbus_cast<T>(
_data.value<QDBusVariant>().variant().value<QDBusArgument>());
};
}
#endif /* QUEUEDCOREADAPTOR_H */

View File

@ -114,16 +114,45 @@ public slots:
* @brief edit task
* @param id
* task ID
* @param data
* new task data
* @param command
* new command or empty
* @param arguments
* command line arguments or empty
* @param directory
* working directory or empty
* @param nice
* nice level or 0
* @param uid
* new uid or 0
* @param state
* new state or 0
* @param cpu
* new limit by CPU cores or -1
* @param gpu
* limit by GPU cores or -1
* @param memory
* new limit by memory or -1
* @param gpumemory
* new limit by GPU memory or -1
* @param storage
* new limit by storage or -1
* @param start
* new start time in ISO format or empty
* @param end
* new end time in ISO format or empty
* @param whoAmI
* auth user name
* @param token
* auth user token
* @return true on successful task edition
*/
bool TaskEdit(const qlonglong id, const QDBusVariant &data,
const QString &whoAmI, const QString &token);
bool TaskEdit(const qlonglong id, const QString &command,
const QStringList &arguments, const QString &directory,
const uint nice, const uint uid, const uint gid,
const uint state, const long long cpu, const long long gpu,
const QString &memory, const QString &gpumemory,
const QString &storage, const QString &whoAmI,
const QString &token);
/**
* @brief force start task
* @param id
@ -184,16 +213,34 @@ public slots:
* @brief edit user
* @param id
* user ID
* @param data
* new user data
* @param name
* new user name or empty
* @param password
* new user password or empty
* @param email
* new user email or empty
* @param cpu
* new limit by CPU cores or -1
* @param gpu
* limit by GPU cores or -1
* @param memory
* new limit by memory or -1
* @param gpumemory
* new limit by GPU memory or -1
* @param storage
* new limit by storage or -1
* @param whoAmI
* auth user name
* @param token
* auth user token
* @return true on successful user edition
*/
bool UserEdit(const qlonglong id, const QDBusVariant &data,
const QString &whoAmI, const QString &token);
bool UserEdit(const qlonglong id, const QString &name,
const QString &password, const QString &email,
const long long cpu, const long long gpu,
const QString &memory, const QString &gpumemory,
const QString &storage, const QString &whoAmI,
const QString &token);
/**
* @brief add permission to user
* @param id

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2016 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
/**
* @file QueuedCorePropertiesInterface.h
* Header of Queued library
* @author Evgeniy Alekseev
* @copyright MIT
* @bug https://github.com/arcan1s/queued/issues
*/
#ifndef QUEUEDCOREPROPERTIESINTERFACE_H
#define QUEUEDCOREPROPERTIESINTERFACE_H
#include <QDBusAbstractAdaptor>
#include <QDBusVariant>
#include "QueuedConfig.h"
class QueuedCore;
/**
* @brief DBus interface for QueuedCore class
*/
class QueuedCorePropertiesInterface : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", DBUS_SERVICE_NAME)
public:
/**
* @brief QueuedCorePropertiesInterface class constructor
* @param parent
* pointer to QueuedCore object
*/
explicit QueuedCorePropertiesInterface(QueuedCore *parent);
/**
* @brief QueuedCorePropertiesInterface class destructor
*/
virtual ~QueuedCorePropertiesInterface();
public slots:
/**
* @brief get task property
* @param id
* task ID
* @param property
* property name
* @return property value or empty if task or property not found
*/
QDBusVariant TaskProperty(const long long id, const QString &property);
/**
* @brief get user property
* @param id
* user ID
* @param property
* property name
* @return property value or empty if user or property not found
*/
QDBusVariant UserProperty(const long long id, const QString &property);
private:
/**
* @brief pointer to database object
*/
QueuedCore *m_core = nullptr;
};
#endif /* QUEUEDCOREPROPERTIESINTERFACE_H */

View File

@ -88,8 +88,7 @@ const QueuedDBSchema DBSchema = {
{{"_id",
{"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong,
true}},
{"key",
{"key", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String, true}},
{"key", {"key", "TEXT NOT NULL DEFAULT '0'", QVariant::String, true}},
{"value", {"value", "TEXT", QVariant::String, true}}}},
{TASKS_TABLE,
{{"_id",
@ -111,16 +110,14 @@ const QueuedDBSchema DBSchema = {
{{"_id",
{"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong,
true}},
{"token",
{"token", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String, true}},
{"token", {"token", "TEXT NOT NULL DEFAULT '0'", QVariant::String, true}},
{"validUntil",
{"validUntil", "TEXT NOT NULL DEFAULT '0'", QVariant::String, true}}}},
{USERS_TABLE,
{{"_id",
{"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong,
true}},
{"name",
{"name", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String, true}},
{"name", {"name", "TEXT NOT NULL DEFAULT '0'", QVariant::String, true}},
{"password", {"password", "TEXT", QVariant::String, false}},
{"email", {"email", "TEXT", QVariant::String, false}},
{"lastLogin", {"lastLogin", "TEXT", QVariant::String, true}},

View File

@ -27,6 +27,10 @@
#include <QLoggingCategory>
/**
* @brief daemon logging category
*/
Q_DECLARE_LOGGING_CATEGORY(LOG_APP)
/**
* @brief control application logging category
*/
@ -63,7 +67,11 @@ const char LOG_FORMAT[] = "[%{time "
"{if-warning}WW%{endif}%{if-critical}CC%{endif}%{if-"
"fatal}FF%{endif}][%{category}][%{function}] "
"%{message}";
/**
* @ingroup QueuedDebug
* @brief method to enable debug messages
*/
void enableDebug();
/**
* @ingroup QueuedDebug
* @brief additional method to get build details declared in version.h

View File

@ -31,30 +31,6 @@
*/
namespace QueuedEnums
{
/**
* @ingroup QueuedEnums
* @enum LimitType
* @brief available limit types
* @var LimitType::CPUThreads
* limit on CPU threads count
* @var LimitType::GPUThreads
* limit on GPU threads count
* @var LimitType::Memory
* limit on physical memory
* @var LimitType::GPUMemory
* limit on GPU memory
* @var LimitType::Storage
* limit on storage
*/
enum LimitType {
CPUThreads = 1 << 0,
GPUThreads = 1 << 1,
Memory = 1 << 2,
GPUMemory = 1 << 3,
Storage = 1 << 4
};
Q_DECLARE_FLAGS(LimitTypes, LimitType)
Q_DECLARE_OPERATORS_FOR_FLAGS(LimitTypes)
/**
* @ingroup QueuedEnums
* @enum Permissions
@ -71,10 +47,9 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(LimitTypes)
* access to reports
*/
enum class Permission {
SuperAdmin = 1 << 0,
Admin = 1 << 1,
JobOwner = 1 << 2,
User = 1 << 3,
SuperAdmin = 1 << 1,
Admin = 1 << 2,
JobOwner = 1 << 3,
Web = 1 << 4,
Reports = 1 << 5
};
@ -92,9 +67,9 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(Permissions)
* process exit
*/
enum class ProcessState {
NotRunning = 1 << 0,
Running = 1 << 1,
Exited = 1 << 2
NotRunning = 1 << 1,
Running = 1 << 2,
Exited = 1 << 3
};
};

View File

@ -88,7 +88,7 @@ struct Limits {
Limits(const QString &_stringLimits)
{
QStringList limits = _stringLimits.split(QChar('\x01'));
while (limits.count() < 4)
while (limits.count() < 5)
limits.append(QString("0"));
cpu = limits.at(0).toLongLong();

View File

@ -65,7 +65,12 @@ bool QueuedCore::addTask(
qCDebug(LOG_LIB) << "Add task" << _command << "with arguments" << _arguments
<< "from user" << _userId;
long long userAuthId = m_users->user(_auth.user)->index();
auto authUser = m_users->user(_auth.user);
if (!authUser) {
qCWarning(LOG_LIB) << "Could not find auth user" << _auth.user;
return false;
}
long long userAuthId = authUser->index();
long long actualUserId = (_userId == -1) ? userAuthId : _userId;
// check permissions
@ -89,8 +94,13 @@ bool QueuedCore::addTask(
// add to database
auto ids = m_users->ids(_userId);
auto user = m_users->user(_userId);
if (!user) {
qCWarning(LOG_LIB) << "Could not find task user" << _userId;
return false;
}
auto taskLimits = QueuedLimits::minimalLimits(
_limits, m_users->user(_userId)->limits(),
_limits, user->limits(),
QueuedLimits::Limits(
m_advancedSettings->get(QString("DefaultLimits")).toString()));
QVariantHash properties = {{"user", _userId},
@ -131,6 +141,13 @@ bool QueuedCore::addUser(
return false;
}
// check if already exists
auto user = m_users->user(_name);
if (user) {
qCWarning(LOG_LIB) << "User" << _name << "already exists";
return false;
}
// add to dababase
QVariantHash properties
= {{"name", _name},
@ -158,6 +175,7 @@ QueuedCore::authorization(const QString &_name, const QString &_password)
QString token = m_users->authorize(_name, _password);
QueuedUserManager::QueuedUserAuthorization auth;
auth.user = _name;
auth.token = token;
if (!token.isEmpty()) {
QVariantHash payload = {
@ -225,7 +243,12 @@ bool QueuedCore::editTask(
}
// check permissions
long long userAuthId = m_users->user(_auth.user)->index();
auto authUser = m_users->user(_auth.user);
if (!authUser) {
qCWarning(LOG_LIB) << "Could not find auth user" << _auth.user;
return false;
}
long long userAuthId = authUser->index();
bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin);
bool isUser = m_users->authorize(_auth, QueuedEnums::Permission::JobOwner);
if (userAuthId == task->user()) {
@ -290,8 +313,13 @@ bool QueuedCore::editUser(
}
// check permissions
auto authUser = m_users->user(_auth.user);
if (!authUser) {
qCWarning(LOG_LIB) << "Could not find auth user" << _auth.user;
return false;
}
long long userAuthId = authUser->index();
bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin);
long long userAuthId = m_users->user(_auth.user)->index();
if (userAuthId != _id) {
if (!isAdmin) {
qCInfo(LOG_LIB) << "User" << _auth.user
@ -339,8 +367,13 @@ bool QueuedCore::editUserPermission(
}
// check permissions
auto authUser = m_users->user(_auth.user);
if (!authUser) {
qCWarning(LOG_LIB) << "Could not find auth user" << _auth.user;
return false;
}
long long userAuthId = authUser->index();
bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin);
long long userAuthId = m_users->user(_auth.user)->index();
if (userAuthId != _id) {
if (!isAdmin) {
qCInfo(LOG_LIB) << "User" << _auth.user
@ -385,8 +418,13 @@ bool QueuedCore::startTask(
qCDebug(LOG_LIB) << "Force start task with ID" << _id;
// check permissions
auto authUser = m_users->user(_auth.user);
if (!authUser) {
qCWarning(LOG_LIB) << "Could not find auth user" << _auth.user;
return false;
}
long long userAuthId = authUser->index();
bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin);
long long userAuthId = m_users->user(_auth.user)->index();
if (userAuthId != _id) {
if (!isAdmin) {
qCInfo(LOG_LIB) << "User" << _auth.user
@ -417,7 +455,12 @@ bool QueuedCore::stopTask(
}
// check permissions
long long userAuthId = m_users->user(_auth.user)->index();
auto authUser = m_users->user(_auth.user);
if (!authUser) {
qCWarning(LOG_LIB) << "Could not find auth user" << _auth.user;
return false;
}
long long userAuthId = authUser->index();
bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin);
bool isUser = m_users->authorize(_auth, QueuedEnums::Permission::JobOwner);
if (userAuthId == task->user()) {
@ -442,6 +485,28 @@ bool QueuedCore::stopTask(
}
/**
* @fn task
*/
const QueuedProcess *QueuedCore::task(const long long _id)
{
qCDebug(LOG_LIB) << "Get task by ID" << _id;
return m_processes->process(_id);
}
/**
* @fn user
*/
const QueuedUser *QueuedCore::user(const long long _id)
{
qCDebug(LOG_LIB) << "Get user by ID" << _id;
return m_users->user(_id);
}
/**
* @fn deinit
*/
@ -455,6 +520,8 @@ void QueuedCore::deinit()
// dbus cleanup
QDBusConnection::sessionBus().unregisterObject(
QueuedConfig::DBUS_OBJECT_PATH);
QDBusConnection::sessionBus().unregisterObject(
QueuedConfig::DBUS_PROPERTY_PATH);
QDBusConnection::sessionBus().unregisterService(QueuedConfig::DBUS_SERVICE);
// delete objects now
@ -583,7 +650,7 @@ QVariantHash QueuedCore::dropAdminFields(const QString &_table,
*/
void QueuedCore::initDBus()
{
QDBusConnection bus = QDBusConnection::sessionBus();
QDBusConnection bus = QDBusConnection::systemBus();
if (!bus.registerService(QueuedConfig::DBUS_SERVICE)) {
QString message = QString("Could not register service %1")
@ -600,6 +667,14 @@ void QueuedCore::initDBus()
qCCritical(LOG_DBUS) << message;
throw QueuedDBusException(message);
}
if (!bus.registerObject(QueuedConfig::DBUS_PROPERTY_PATH,
new QueuedCorePropertiesInterface(this),
QDBusConnection::ExportAllContents)) {
QString message = QString("Could not register properties object %1")
.arg(bus.lastError().message());
qCCritical(LOG_DBUS) << message;
throw QueuedDBusException(message);
}
}
@ -677,7 +752,7 @@ void QueuedCore::initUsers()
QString now = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
auto dbTokens = m_database->get(
QueuedDB::TOKENS_TABLE,
QString("WHERE datetime(validUntil) > datetime(%2)").arg(now));
QString("WHERE datetime(validUntil) > datetime('%1')").arg(now));
m_users->loadTokens(dbTokens);
auto dbUsers = m_database->get(QueuedDB::USERS_TABLE);
m_users->loadUsers(dbUsers);

View File

@ -23,24 +23,35 @@
#include "queued/Queued.h"
#include <QDBusConnection>
#include <QDBusMessage>
/**
* @class QueuedCoreAdaptor
* @fn sendRequest
*/
/**
* @fn QueuedCoreAdaptor
*/
QueuedCoreAdaptor::QueuedCoreAdaptor(QObject *parent)
: QObject(parent)
QVariantList QueuedCoreAdaptor::sendRequest(const QString &_service,
const QString &_path,
const QString &_interface,
const QString &_cmd,
const QVariantList &_args)
{
qCDebug(LOG_LIB) << __PRETTY_FUNCTION__;
}
qCDebug(LOG_DBUS) << "Send request to service" << _service << "by interface"
<< _interface << "to" << _path << "command" << _cmd
<< "with args" << _args;
QDBusConnection bus = QDBusConnection::systemBus();
QDBusMessage request
= QDBusMessage::createMethodCall(_service, _path, _interface, _cmd);
if (!_args.isEmpty())
request.setArguments(_args);
/**
* @fn ~QueuedCoreAdaptor
*/
QueuedCoreAdaptor::~QueuedCoreAdaptor()
{
qCDebug(LOG_LIB) << __PRETTY_FUNCTION__;
QDBusMessage response = bus.call(request, QDBus::BlockWithGui);
QVariantList arguments = response.arguments();
QString error = response.errorMessage();
if (!error.isEmpty())
qCWarning(LOG_DBUS) << "Error message" << error;
return arguments;
}

View File

@ -100,14 +100,54 @@ bool QueuedCoreInterface::TaskAdd(
/**
* @fn TaskEdit
*/
bool QueuedCoreInterface::TaskEdit(const qlonglong id, const QDBusVariant &data,
bool QueuedCoreInterface::TaskEdit(
const qlonglong id, const QString &command, const QStringList &arguments,
const QString &directory, const uint nice, const uint uid, const uint gid,
const uint state, const long long cpu, const long long gpu,
const QString &memory, const QString &gpumemory, const QString &storage,
const QString &whoAmI, const QString &token)
{
qCDebug(LOG_DBUS) << "Edit task" << id << data.variant() << "auth by"
<< whoAmI;
qCDebug(LOG_DBUS) << "Edit task" << id << command << arguments << directory
<< nice << uid << gid << state << cpu << gpu << memory
<< gpumemory << storage << "auth by" << whoAmI;
return m_core->editTask(id, data.variant().toHash(),
QueuedUserManager::auth(whoAmI, token));
auto task = m_core->task(id);
if (!task) {
qCWarning(LOG_DBUS) << "Could not find task" << id;
return false;
}
// build payload
QVariantHash data;
if (!command.isEmpty())
data[QString("command")] = command;
if (!arguments.isEmpty())
data[QString("arguments")] = arguments;
if (!directory.isEmpty())
data[QString("directory")] = directory;
if (nice > 0)
data[QString("nice")] = nice;
if (uid > 0)
data[QString("uid")] = uid;
if (gid > 0)
data[QString("gid")] = gid;
if (state > 0)
data[QString("state")] = state;
// append limits now
auto limits = task->limits();
if (cpu > -1)
limits.cpu = cpu;
if (gpu > -1)
limits.gpu = gpu;
if (memory > -1)
limits.memory = QueuedLimits::convertMemory(memory);
if (gpumemory > -1)
limits.gpumemory = QueuedLimits::convertMemory(gpumemory);
if (storage > -1)
limits.storage = QueuedLimits::convertMemory(storage);
data[QString("limits")] = limits.toString();
return m_core->editTask(id, data, QueuedUserManager::auth(whoAmI, token));
}
@ -161,14 +201,47 @@ bool QueuedCoreInterface::UserAdd(const QString &name, const QString &email,
/**
* @fn UserEdit
*/
bool QueuedCoreInterface::UserEdit(const qlonglong id, const QDBusVariant &data,
bool QueuedCoreInterface::UserEdit(const qlonglong id, const QString &name,
const QString &password,
const QString &email, const long long cpu,
const long long gpu, const QString &memory,
const QString &gpumemory,
const QString &storage,
const QString &whoAmI, const QString &token)
{
qCDebug(LOG_DBUS) << "Edit user" << id << data.variant() << "auth by"
<< whoAmI;
qCDebug(LOG_DBUS) << "Edit user" << id << name << email << cpu << gpu
<< memory << gpumemory << storage << "auth by" << whoAmI;
return m_core->editUser(id, data.variant().toHash(),
QueuedUserManager::auth(whoAmI, token));
// get user object first to match limits
auto user = m_core->user(id);
if (!user) {
qCWarning(LOG_DBUS) << "Could not find user" << id;
return false;
}
// build payload
QVariantHash data;
if (!name.isEmpty())
data[QString("name")] = name;
if (!password.isEmpty())
data[QString("password")] = password;
if (!email.isEmpty())
data[QString("email")] = email;
// append limits now
auto limits = user->limits();
if (cpu > -1)
limits.cpu = cpu;
if (gpu > -1)
limits.gpu = gpu;
if (memory > -1)
limits.memory = QueuedLimits::convertMemory(memory);
if (gpumemory > -1)
limits.gpumemory = QueuedLimits::convertMemory(gpumemory);
if (storage > -1)
limits.storage = QueuedLimits::convertMemory(storage);
data[QString("limits")] = limits.toString();
return m_core->editUser(id, data, QueuedUserManager::auth(whoAmI, token));
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2016 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
/**
* @file QueuedCorePropertiesInterface.cpp
* Source code of queued library
* @author Evgeniy Alekseev
* @copyright GPLv3
* @bug https://github.com/arcan1s/queued/issues
*/
#include "queued/Queued.h"
/**
* @class QueuedCorePropertiesInterface
*/
/**
* @fn QueuedCorePropertiesInterface
*/
QueuedCorePropertiesInterface::QueuedCorePropertiesInterface(QueuedCore *parent)
: QDBusAbstractAdaptor(parent)
, m_core(parent)
{
qCDebug(LOG_DBUS) << __PRETTY_FUNCTION__;
}
/**
* @fn ~QueuedCorePropertiesInterface
*/
QueuedCorePropertiesInterface::~QueuedCorePropertiesInterface()
{
qCDebug(LOG_DBUS) << __PRETTY_FUNCTION__;
}
/**
* @fn TaskProperty
*/
QDBusVariant
QueuedCorePropertiesInterface::TaskProperty(const long long id,
const QString &property)
{
qCDebug(LOG_DBUS) << "Get property" << property << "from task" << id;
auto task = m_core->task(id);
if (!task) {
qCWarning(LOG_DBUS) << "Could not find task" << id;
return QDBusVariant();
}
return QDBusVariant(task->property(property.toLocal8Bit().constData()));
}
/**
* @fn UserProperty
*/
QDBusVariant
QueuedCorePropertiesInterface::UserProperty(const long long id,
const QString &property)
{
qCDebug(LOG_DBUS) << "Get property" << property << "from user" << id;
auto user = m_core->user(id);
if (!user) {
qCWarning(LOG_DBUS) << "Could not find user" << id;
return QDBusVariant();
}
return QDBusVariant(user->property(property.toLocal8Bit().constData()));
}

View File

@ -80,15 +80,17 @@ void QueuedDatabase::createAdministrator(const QString &_user,
const QString &_password)
{
qCDebug(LOG_LIB) << "Check for user" << _user;
QString table("users");
QSqlQuery query = m_database.exec(
QString("SELECT * FROM '%1' WHERE name='%2'").arg(table).arg(_user));
QSqlQuery query
= m_database.exec(QString("SELECT * FROM '%1' WHERE name='%2'")
.arg(QueuedDB::USERS_TABLE)
.arg(_user));
QSqlError error = query.lastError();
if (error.isValid())
qCWarning(LOG_LIB) << "Could not get record" << _user << "from" << table
<< "message" << error.text();
else if (query.size() > 0)
qCWarning(LOG_LIB) << "Could not get record" << _user << "from"
<< QueuedDB::USERS_TABLE << "message"
<< error.text();
else if (query.next())
return;
qCInfo(LOG_LIB) << "Create administrator user" << _user;
@ -97,7 +99,7 @@ void QueuedDatabase::createAdministrator(const QString &_user,
{"password", _password},
{"permissions", static_cast<int>(QueuedEnums::Permission::SuperAdmin)}};
if (!add(table, payload))
if (!add(QueuedDB::USERS_TABLE, payload))
qCCritical(LOG_LIB) << "Could not create administrator";
}
@ -113,7 +115,7 @@ QList<QVariantHash> QueuedDatabase::get(const QString &_table,
QList<QVariantHash> output;
QSqlQuery query
= m_database.exec(QString("SELECT * FROM '%1' ORDER BY _id DESC %1")
= m_database.exec(QString("SELECT * FROM '%1' %2 ORDER BY _id DESC")
.arg(_table)
.arg(_condition));
@ -227,7 +229,7 @@ bool QueuedDatabase::modify(const QString &_table, const long long _id,
auto payload = getQueryPayload(_table, _value);
QStringList stringPayload;
for (int i = 0; i < payload.first.count(); i++)
stringPayload.append(QString("%1='%2'")
stringPayload.append(QString("%1=%2")
.arg(payload.first.at(i))
.arg(payload.second.at(i)));
// build query
@ -274,7 +276,7 @@ void QueuedDatabase::removeTasks(const QDateTime &_endTime)
qCDebug(LOG_LIB) << "Remove all tasks which are older than" << _endTime;
QSqlQuery query = m_database.exec(
QString("DELETE FROM %1 WHERE datetime(endTime) < datetime(%2)")
QString("DELETE FROM %1 WHERE datetime(endTime) < datetime('%2')")
.arg(QueuedDB::TASKS_TABLE)
.arg(_endTime.toString(Qt::ISODate)));
@ -292,7 +294,7 @@ void QueuedDatabase::removeTokens()
{
QString now = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
QSqlQuery query = m_database.exec(
QString("DELETE FROM %1 WHERE datetime(validUntil) > datetime(%2)")
QString("DELETE FROM %1 WHERE datetime(validUntil) > datetime('%2')")
.arg(QueuedDB::TOKENS_TABLE)
.arg(now));
@ -312,7 +314,7 @@ void QueuedDatabase::removeUsers(const QDateTime &_lastLogin)
<< _lastLogin;
QSqlQuery query = m_database.exec(
QString("DELETE FROM %1 WHERE datetime(lastLogin) < datetime(%2)")
QString("DELETE FROM %1 WHERE datetime(lastLogin) < datetime('%2')")
.arg(QueuedDB::USERS_TABLE)
.arg(_lastLogin.toString(Qt::ISODate)));

View File

@ -26,6 +26,7 @@
#include "version.h"
Q_LOGGING_CATEGORY(LOG_APP, "org.queued.application", QtMsgType::QtWarningMsg)
Q_LOGGING_CATEGORY(LOG_CTL, "org.queued.control", QtMsgType::QtWarningMsg)
Q_LOGGING_CATEGORY(LOG_DBUS, "org.queued.dbus", QtMsgType::QtWarningMsg)
Q_LOGGING_CATEGORY(LOG_LIB, "org.queued.library", QtMsgType::QtWarningMsg)
@ -33,6 +34,15 @@ Q_LOGGING_CATEGORY(LOG_PL, "org.queued.plugin", QtMsgType::QtWarningMsg)
Q_LOGGING_CATEGORY(LOG_SERV, "org.queued.server", QtMsgType::QtWarningMsg)
/**
* @fn enableDebug
*/
void QueuedDebug::enableDebug()
{
QLoggingCategory::setFilterRules(QString("org.queued.*=true"));
}
/**
* @fn getBuildData
*/

View File

@ -73,10 +73,14 @@ void QueuedTokenManager::loadTokens(const QList<QVariantHash> &_values)
token[QString("validUntil")].toString(), Qt::ISODate);
QString tokenId = token[QString("token")].toString();
m_tokens[tokenId] = validUntil;
QTimer::singleShot(
// create timer
std::chrono::milliseconds duration(
validUntil.toMSecsSinceEpoch()
- QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(),
Qt::VeryCoarseTimer,
- QDateTime::currentDateTimeUtc().toMSecsSinceEpoch());
QTimer timer;
timer.setSingleShot(true);
timer.setInterval(duration);
connect(&timer, &QTimer::timeout,
[this, tokenId]() { return expireToken(tokenId); });
}
}

View File

@ -79,7 +79,8 @@ QueuedUser::addPermissions(const QueuedEnums::Permissions _permissions)
QString QueuedUser::hashFromPassword(const QString &_password)
{
return QCryptographicHash::hash(_password.toUtf8(),
QCryptographicHash::Sha512);
QCryptographicHash::Sha512)
.toHex();
}
@ -122,9 +123,7 @@ QPair<uint, uint> QueuedUser::ids()
*/
bool QueuedUser::isPasswordValid(const QString &_password) const
{
return (m_definitions.password.toUtf8()
== QCryptographicHash::hash(_password.toUtf8(),
QCryptographicHash::Sha512));
return (m_definitions.password.toUtf8() == hashFromPassword(_password));
}