diff --git a/.gitignore b/.gitignore index 9a0d19c..8c25584 100644 --- a/.gitignore +++ b/.gitignore @@ -41,8 +41,6 @@ build # archives *src.tar.[gx]z *pkg.tar.[gx]z -src -pkg *.deb # clion settings diff --git a/sources/CMakeLists.txt b/sources/CMakeLists.txt index 3ad6598..32738c9 100644 --- a/sources/CMakeLists.txt +++ b/sources/CMakeLists.txt @@ -9,7 +9,7 @@ if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif () -project(awesomewidgets) +project(queued) set(PROJECT_AUTHOR "Evgeniy Alekseev") set(PROJECT_CONTACT "esalexeev@gmail.com") set(PROJECT_LICENSE "MIT") diff --git a/sources/libraries.cmake b/sources/libraries.cmake index 20d761d..1330025 100644 --- a/sources/libraries.cmake +++ b/sources/libraries.cmake @@ -1,12 +1,12 @@ # main qt libraries -find_package(Qt5 5.6.0 REQUIRED COMPONENTS Core DBus Test) +find_package(Qt5 5.6.0 REQUIRED COMPONENTS Core DBus Sql Test) add_definitions( - ${Qt5Core_DEFINITIONS} ${Qt5DBus_DEFINITIONS} + ${Qt5Core_DEFINITIONS} ${Qt5DBus_DEFINITIONS} ${Qt5Sql_DEFINITIONS} ) set(Qt_INCLUDE - ${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS} + ${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS} ) set(Qt_LIBRARIES - ${Qt5Core_LIBRARIES} ${Qt5DBus_LIBRARIES} + ${Qt5Core_LIBRARIES} ${Qt5DBus_LIBRARIES} ${Qt5Sql_LIBRARIES} ) diff --git a/sources/queued/include/queued/Queued.h b/sources/queued/include/queued/Queued.h index 1a5cee7..e374e71 100644 --- a/sources/queued/include/queued/Queued.h +++ b/sources/queued/include/queued/Queued.h @@ -24,6 +24,7 @@ #ifndef QUEUED_H #define QUEUED_H +#include "QueuedDatabase.h" #include "QueuedDebug.h" #include "QueuedProcess.h" #include "QueuedProcessManager.h" diff --git a/sources/queued/include/queued/QueuedDatabase.h b/sources/queued/include/queued/QueuedDatabase.h new file mode 100644 index 0000000..4a01cd0 --- /dev/null +++ b/sources/queued/include/queued/QueuedDatabase.h @@ -0,0 +1,103 @@ +/* + * 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 QueuedDatabase.h + * Header of Queued library + * @author Evgeniy Alekseev + * @copyright MIT + * @bug https://github.com/arcan1s/queued/issues + */ + + +#ifndef QUEUEDDATABASE_H +#define QUEUEDDATABASE_H + +#include +#include +#include + + +typedef QHash QueuedDBSchema; + +/** + * @brief queued adaptor to databases + */ +class QueuedDatabase : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString path READ path) + + // TODO add fields SQL parameters (type whatever), Qt specific types + const QueuedDBSchema DBSchema = { + {"users", + {"_id", "name", "uid", "gid", "password_sha512", "email", "cpu", "gpu", + "memory", "gpumemory", "storage"}}, + {"tasks", {"_id", "userId", "command", "arguments", "workDirectory"}}}; + +public: + /** + * @brief QueuedDatabase class constructor + * @param parent pointer to parent item + * @param path path to database + * @param driver database driver + */ + explicit QueuedDatabase(QObject *parent, const QString path, + const QString driver); + /** + * @brief QueuedDatabase class destructor + */ + virtual ~QueuedDatabase(); + /** + * @brief check and create database + */ + void checkDatabase(); + /** + * @brief open database + * @param _hostname hostname to connect, may be empty + * @param _port port to connect, may be 0 + * @param _username username to connect, may be empty + * @param _password password to connect, will be ignored if _username + * is empty + */ + void open(const QString _hostname, const int _port, const QString _username, + const QString _password); + /** + * @brief path to database + * @return path to used database + */ + QString path() const; + +private: + /** + * @brief database + */ + QSqlDatabase m_database; + /** + * @brief database path + */ + QString m_path; + /** + * @brief create or update actual schema in table + * @param _table table name + */ + void createSchema(const QString _table); + /** + * @brief create given table + * @param _table table name + */ + void createTable(const QString _table); +}; + +#endif /* QUEUEDDATABASE_H */ diff --git a/sources/queued/include/queued/QueuedProcessManager.h b/sources/queued/include/queued/QueuedProcessManager.h index 7ac9ade..c52e7f0 100644 --- a/sources/queued/include/queued/QueuedProcessManager.h +++ b/sources/queued/include/queued/QueuedProcessManager.h @@ -31,6 +31,7 @@ typedef QHash QueuedProcessMap; +typedef QHash QueuedProcessConnectionMap; /** * @brief implementation over QProcess to run processes @@ -103,6 +104,10 @@ private slots: const long long _index); private: + /** + * @brief connection map + */ + QueuedProcessConnectionMap m_connections; /** * @brief action on exit */ diff --git a/sources/queued/src/CMakeLists.txt b/sources/queued/src/CMakeLists.txt new file mode 100644 index 0000000..f232d36 --- /dev/null +++ b/sources/queued/src/CMakeLists.txt @@ -0,0 +1,20 @@ +# set files +file (GLOB_RECURSE SUBPROJECT_SOURCES "*.cpp") +file (GLOB_RECURSE SUBPROJECT_HEADERS "*.h" "${PROJECT_LIBRARY_DIR}/*h") + +# include_path +include_directories ("${PROJECT_LIBRARY_DIR}/include" + "${CMAKE_CURRENT_BINARY_DIR}" + "${CMAKE_BINARY_DIR}" + "${Qt_INCLUDE}") + +qt5_wrap_cpp (SUBPROJECT_MOC_SOURCES "${SUBPROJECT_HEADERS}") + +add_library ("${SUBPROJECT}" SHARED "${SUBPROJECT_SOURCES}" "${SUBPROJECT_HEADERS}" + "${SUBPROJECT_MOC_SOURCES}") +set_target_properties ("${SUBPROJECT}" PROPERTIES + SOVERSION "${PROJECT_VERSION_MAJOR}" + VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +target_link_libraries ("${SUBPROJECT}" "${Qt_LIBRARIES}") +# install properties +install (TARGETS "${SUBPROJECT}" DESTINATION "${LIB_INSTALL_DIR}") diff --git a/sources/queued/src/QueuedDatabase.cpp b/sources/queued/src/QueuedDatabase.cpp new file mode 100644 index 0000000..bbbc597 --- /dev/null +++ b/sources/queued/src/QueuedDatabase.cpp @@ -0,0 +1,146 @@ +/* + * 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 QueuedDatabase.cpp + * Source code of queued library + * @author Evgeniy Alekseev + * @copyright GPLv3 + * @bug https://github.com/arcan1s/queued/issues + */ + + +#include "queued/Queued.h" + +#include +#include +#include + + +/** + * @fn QueuedDatabase + */ +QueuedDatabase::QueuedDatabase(QObject *parent, const QString path, + const QString driver) + : QObject(parent) + , m_path(path) +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; + + m_database = QSqlDatabase::addDatabase(driver); + m_database.setDatabaseName(path); +} + + +/** + * @fn ~QueuedDatabase + */ +QueuedDatabase::~QueuedDatabase() +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; +} + + +/** + * @fn open + */ +void QueuedDatabase::open(const QString _hostname, const int _port, + const QString _username, const QString _password) +{ + qCDebug(LOG_LIB) << "Open database at" << _hostname << _port << "as user" + << _username; + + if (!_hostname.isEmpty()) + m_database.setHostName(_hostname); + if (_port > 0) + m_database.setPort(_port); + bool status = _username.isEmpty() ? m_database.open() + : m_database.open(_username, _password); + + qCDebug(LOG_LIB) << "Open database status" << status; + if (status) + return checkDatabase(); +} + + +/** + * @fn checkDatabase + */ +void QueuedDatabase::checkDatabase() +{ + QStringList tables = m_database.tables(); + for (auto table : DBSchema.keys()) { + // create table if does not exist + if (!tables.contains(table)) + createTable(table); + // update schema + createSchema(table); + } +} + + +/** + * @fn path + */ +QString QueuedDatabase::path() const +{ + return m_path; +} + + +/** + * @fn createSchema + */ +void QueuedDatabase::createSchema(const QString _table) +{ + qCDebug(LOG_LIB) << "Create schema for" << _table; + + QSqlRecord record = m_database.record(_table); + // get column names + QStringList columns; + for (int i = 0; i < record.count(); i++) + columns.append(record.fieldName(i)); + + // check and append if any + for (auto column : DBSchema[_table]) { + if (columns.contains(column)) + continue; + QSqlQuery query + = m_database.exec(QString("ALTER TABLE '%1' add column `%2`") + .arg(_table) + .arg(column)); + QSqlError error = query.lastError(); + if (error.isValid()) + qCCritical(LOG_LIB) << "Could not insert column" << column + << "to table" << _table + << "error:" << error.text(); + } +} + + +/** + * @fn createTable + */ +void QueuedDatabase::createTable(const QString _table) +{ + qCDebug(LOG_LIB) << "Create table" << _table; + + QSqlQuery query = m_database.exec( + QString("CREATE TABLE '%1' (`_id` INTEGER PRIMARY KEY AUTOINCREMENT)") + .arg(_table)); + QSqlError error = query.lastError(); + if (error.isValid()) + qCCritical(LOG_LIB) << "Could not create table" << _table + << "error:" << error.text(); +} diff --git a/sources/queued/src/QueuedDebug.cpp b/sources/queued/src/QueuedDebug.cpp new file mode 100644 index 0000000..7781184 --- /dev/null +++ b/sources/queued/src/QueuedDebug.cpp @@ -0,0 +1,44 @@ +/* + * 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 QueuedDebug.cpp + * Source code of queued library + * @author Evgeniy Alekseev + * @copyright GPLv3 + * @bug https://github.com/arcan1s/queued/issues + */ + + +#include "queued/Queued.h" + +#include "version.h" + + +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) +Q_LOGGING_CATEGORY(LOG_PL, "org.queued.plugin", QtMsgType::QtWarningMsg) +Q_LOGGING_CATEGORY(LOG_SERV, "org.queued.server", QtMsgType::QtWarningMsg) + + +/** + * @fn getBuildData + */ +QStringList QueuedDebug::getBuildData() +{ + QStringList metadata; + + return metadata; +} diff --git a/sources/queued/src/QueuedProcess.cpp b/sources/queued/src/QueuedProcess.cpp new file mode 100644 index 0000000..ef958e7 --- /dev/null +++ b/sources/queued/src/QueuedProcess.cpp @@ -0,0 +1,161 @@ +/* + * 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 QueuedProcess.cpp + * Source code of queued library + * @author Evgeniy Alekseev + * @copyright GPLv3 + * @bug https://github.com/arcan1s/queued/issues + */ + + +#include "queued/Queued.h" + +#include + + +/** + * @class QueuedProcess + */ +/** + * @fn QueuedProcess + */ +QueuedProcess::QueuedProcess(QObject *parent, + const QueuedProcessDefinitions definitions, + const long long index) + : QProcess(parent) + , m_definitions(definitions) + , m_index(index) +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; + + setArguments(m_definitions.args); + setProgram(m_definitions.cmd); + setWorkingDirectory(m_definitions.workingDirectory); +} + + +/** + * @fn ~QueuedProcess + */ +QueuedProcess::~QueuedProcess() +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; +} + + +/** + * @fn index + */ +long long QueuedProcess::index() const +{ + return m_index; +} + + +/** + * @fn name + */ +QString QueuedProcess::name() const +{ + return QString("queued-%1").arg(index()); +} + + +/** + * @fn removeLimit + */ +void QueuedProcess::removeLimit(const LimitType _limitType) +{ + qCDebug(LOG_LIB) << "Remove limit" << static_cast(_limitType); + + m_limits.remove(_limitType); +} + + +/** + * @fn setLimit + */ +void QueuedProcess::setLimit(const LimitType _limitType, const QVariant _value) +{ + qCDebug(LOG_LIB) << "Set limit" << static_cast(_limitType) << "to" + << _value; + + if (!_value.isValid()) + return removeLimit(_limitType); + + bool status = false; + long long intValue = _value.type() == QVariant::String + ? convertMemory(_value.toString(), status) + : _value.toLongLong(&status); + + if (!status) + removeLimit(_limitType); + else + m_limits[_limitType] = intValue; +} + + +/** + * @fn operator== + */ +bool QueuedProcess::operator==(const QueuedProcess _other) +{ + return name() == _other.name(); +} + + +/** + * @fn setupChildProcess + */ +void QueuedProcess::setupChildProcess() +{ + ::setgid(m_definitions.gid); + ::setuid(m_definitions.uid); +} + + +/** + * @fn run + */ +void QueuedProcess::run() +{ + // TODO set limits to child process and etc + return start(); +} + + +/** + * @fn convertMemory + */ +long long QueuedProcess::convertMemory(QString _value, bool &_status) const +{ + qCDebug(LOG_LIB) << "Convert memory value" << _value; + + long long intValue; + if (_value.endsWith(QString("K"))) + intValue = _value.remove(QString("K")).toLongLong(&_status) * 1024; + else if (_value.endsWith(QString("M"))) + intValue + = _value.remove(QString("M")).toLongLong(&_status) * 1024 * 1024; + else if (_value.endsWith(QString("G"))) + intValue = _value.remove(QString("G")).toLongLong(&_status) * 1024 + * 1024 * 1024; + else + intValue = _value.toInt(&_status); + + qCInfo(LOG_LIB) << "Converted value" << intValue; + return intValue; +} diff --git a/sources/queued/src/QueuedProcessManager.cpp b/sources/queued/src/QueuedProcessManager.cpp new file mode 100644 index 0000000..613342f --- /dev/null +++ b/sources/queued/src/QueuedProcessManager.cpp @@ -0,0 +1,146 @@ +/* + * 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 QueuedProcessManager.cpp + * Source code of queued library + * @author Evgeniy Alekseev + * @copyright GPLv3 + * @bug https://github.com/arcan1s/queued/issues + */ + + +#include "queued/Queued.h" + + +/** + * @fn QueuedProcessManager + */ +QueuedProcessManager::QueuedProcessManager(QObject *parent, + const OnExitAction onExit) + : QObject(parent) + , m_onExit(onExit) +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; +} + + +/** + * @fn ~QueuedProcessManager + */ +QueuedProcessManager::~QueuedProcessManager() +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; + + QList indices = m_processes.keys(); + for (auto index : indices) + remove(index); +} + + +/** + * @fn add + */ +QueuedProcess * +QueuedProcessManager::add(const long long _index, + const QueuedProcessDefinitions _definitions) +{ + qCDebug(LOG_LIB) << "Add new process" << _definitions.cmd << "with index" + << _index; + + if (m_processes.contains(_index)) + return m_processes[_index]; + + QueuedProcess *process = new QueuedProcess(this, _definitions, _index); + m_processes[_index] = process; + // connect to signal + m_connections[_index] = connect( + process, static_cast( + &QProcess::finished), + [=](const int exitCode, const QProcess::ExitStatus exitStatus) { + return taskFinished(exitCode, exitStatus, _index); + }); + + return process; +} + + +/** + * @fn onExit + */ +QueuedProcessManager::OnExitAction QueuedProcessManager::onExit() const +{ + return m_onExit; +} + + +/** + * @fn process + */ +QueuedProcess *QueuedProcessManager::process(const long long _index) +{ + qCDebug(LOG_LIB) << "Get process by index" << _index; + + return m_processes.contains(_index) ? m_processes[_index] : nullptr; +} + + +/** + * @fn processes + */ +QueuedProcessMap QueuedProcessManager::processes() +{ + return m_processes; +} + + +/** + * @fn remove + */ +void QueuedProcessManager::remove(const long long _index) +{ + qCDebug(LOG_LIB) << "Remove process by index" << _index; + + if (!m_processes.contains(_index)) + return; + + QueuedProcess *process = m_processes.take(_index); + auto connection = m_connections.take(_index); + disconnect(connection); + + switch (m_onExit) { + case OnExitAction::Kill: + process->kill(); + break; + case OnExitAction::Terminate: + process->terminate(); + break; + } + + process->deleteLater(); +} + + +/** + * @fn taskFinished + */ +void QueuedProcessManager::taskFinished(const int _exitCode, + const QProcess::ExitStatus _exitStatus, + const long long _index) +{ + qCDebug(LOG_LIB) << "Process" << _index << "finished with code" << _exitCode + << "and status" << _exitStatus; + + // TODO implementation +}