mirror of
https://github.com/tnodir/fort
synced 2024-11-15 05:57:57 +00:00
UI: Add --control <PATH> argument.
Control running instance's conf by executing scripts.
This commit is contained in:
parent
099c3de95f
commit
cfddff8a06
@ -14,6 +14,8 @@ SOURCES += \
|
||||
conf/addressgroup.cpp \
|
||||
conf/appgroup.cpp \
|
||||
conf/firewallconf.cpp \
|
||||
control/controlmanager.cpp \
|
||||
control/controlworker.cpp \
|
||||
db/databasemanager.cpp \
|
||||
db/databasesql.cpp \
|
||||
db/quotamanager.cpp \
|
||||
@ -70,6 +72,8 @@ HEADERS += \
|
||||
conf/addressgroup.h \
|
||||
conf/appgroup.h \
|
||||
conf/firewallconf.h \
|
||||
control/controlmanager.h \
|
||||
control/controlworker.h \
|
||||
db/databasemanager.h \
|
||||
db/databasesql.h \
|
||||
db/quotamanager.h \
|
||||
|
@ -201,6 +201,15 @@ QQmlListProperty<AddressGroup> FirewallConf::addressGroups()
|
||||
return QQmlListProperty<AddressGroup>(this, m_addressGroups);
|
||||
}
|
||||
|
||||
AppGroup *FirewallConf::appGroupByName(const QString &name) const
|
||||
{
|
||||
foreach (AppGroup *appGroup, appGroupsList()) {
|
||||
if (appGroup->name() == name)
|
||||
return appGroup;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QQmlListProperty<AppGroup> FirewallConf::appGroups()
|
||||
{
|
||||
return QQmlListProperty<AppGroup>(this, m_appGroups);
|
||||
|
@ -118,6 +118,8 @@ public:
|
||||
const QList<AddressGroup *> &addressGroupsList() const { return m_addressGroups; }
|
||||
QQmlListProperty<AddressGroup> addressGroups();
|
||||
|
||||
Q_INVOKABLE AppGroup *appGroupByName(const QString &name) const;
|
||||
|
||||
const QList<AppGroup *> &appGroupsList() const { return m_appGroups; }
|
||||
QQmlListProperty<AppGroup> appGroups();
|
||||
|
||||
|
135
src/ui/control/controlmanager.cpp
Normal file
135
src/ui/control/controlmanager.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
#include "controlmanager.h"
|
||||
|
||||
#include <QJSEngine>
|
||||
#include <QLoggingCategory>
|
||||
#include <QThreadPool>
|
||||
|
||||
#include "../conf/firewallconf.h"
|
||||
#include "../util/fileutil.h"
|
||||
#include "controlworker.h"
|
||||
#include "fortmanager.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(CLOG_CONTROL_MANAGER)
|
||||
Q_LOGGING_CATEGORY(CLOG_CONTROL_MANAGER, "fort.controlManager")
|
||||
|
||||
ControlManager::ControlManager(const QString &globalName,
|
||||
const QString &scriptPath,
|
||||
QObject *parent) :
|
||||
QObject(parent),
|
||||
m_isClient(!scriptPath.isEmpty()),
|
||||
m_scriptPath(scriptPath),
|
||||
m_fortManager(nullptr),
|
||||
m_worker(nullptr),
|
||||
m_semaphore(globalName + QLatin1String("_ControlSemaphore"), 0,
|
||||
isClient() ? QSystemSemaphore::Open : QSystemSemaphore::Create),
|
||||
m_sharedMemory(globalName + QLatin1String("_ControlSharedMemory"))
|
||||
{
|
||||
}
|
||||
|
||||
ControlManager::~ControlManager()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
bool ControlManager::listen(FortManager *fortManager)
|
||||
{
|
||||
if (m_sharedMemory.size() > 0)
|
||||
return true;
|
||||
|
||||
if (!m_sharedMemory.create(4096)) {
|
||||
qWarning(CLOG_CONTROL_MANAGER()) << "Shared Memory create error:"
|
||||
<< m_sharedMemory.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fortManager = fortManager;
|
||||
|
||||
if (!m_worker) {
|
||||
setupWorker();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ControlManager::post(const QStringList &args)
|
||||
{
|
||||
if (!m_sharedMemory.attach()) {
|
||||
qWarning(CLOG_CONTROL_MANAGER()) << "Shared Memory attach error:"
|
||||
<< m_sharedMemory.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
ControlWorker worker(&m_semaphore, &m_sharedMemory);
|
||||
|
||||
return worker.post(m_scriptPath, args);
|
||||
}
|
||||
|
||||
void ControlManager::processRequest(const QString &scriptPath,
|
||||
const QStringList &args)
|
||||
{
|
||||
const QString script = FileUtil::readFile(scriptPath);
|
||||
if (script.isEmpty()) {
|
||||
qWarning(CLOG_CONTROL_MANAGER()) << "Script is empty:"
|
||||
<< scriptPath;
|
||||
return;
|
||||
}
|
||||
|
||||
QJSEngine engine;
|
||||
engine.installExtensions(QJSEngine::ConsoleExtension);
|
||||
|
||||
QJSValue globalObject = engine.globalObject();
|
||||
|
||||
// Arguments
|
||||
QJSValue argsJs = engine.newArray(args.size());
|
||||
QJSValue argsMapJs = engine.newObject();
|
||||
for (int i = 0, n = args.size(); i < n; ++i) {
|
||||
const QString &arg = args.at(i);
|
||||
argsJs.setProperty(i, arg);
|
||||
|
||||
const int sepPos = arg.indexOf('=');
|
||||
if (sepPos > 0) {
|
||||
const QString k = arg.left(sepPos);
|
||||
const QString v = arg.mid(sepPos + 1);
|
||||
argsMapJs.setProperty(k, v);
|
||||
}
|
||||
}
|
||||
globalObject.setProperty("args", argsJs);
|
||||
globalObject.setProperty("arg", argsMapJs);
|
||||
|
||||
// FirewallConf
|
||||
QJSValue firewallConfJs = engine.newQObject(
|
||||
m_fortManager->firewallConf());
|
||||
globalObject.setProperty("conf", firewallConfJs);
|
||||
|
||||
// Run the script
|
||||
const QJSValue res = engine.evaluate(script, scriptPath);
|
||||
if (res.isError()) {
|
||||
qWarning(CLOG_CONTROL_MANAGER()) << "Script error:"
|
||||
<< scriptPath << "line"
|
||||
<< res.property("lineNumber").toInt()
|
||||
<< ":" << res.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
m_fortManager->saveOriginConf(tr("Control script executed"));
|
||||
}
|
||||
|
||||
void ControlManager::setupWorker()
|
||||
{
|
||||
m_worker = new ControlWorker(&m_semaphore, &m_sharedMemory); // autoDelete = true
|
||||
|
||||
connect(m_worker, &ControlWorker::requestReady,
|
||||
this, &ControlManager::processRequest);
|
||||
|
||||
QThreadPool::globalInstance()->start(m_worker);
|
||||
}
|
||||
|
||||
void ControlManager::abort()
|
||||
{
|
||||
if (!m_worker) return;
|
||||
|
||||
m_worker->disconnect(this);
|
||||
|
||||
m_worker->abort();
|
||||
m_worker = nullptr;
|
||||
}
|
51
src/ui/control/controlmanager.h
Normal file
51
src/ui/control/controlmanager.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef CONTROLMANAGER_H
|
||||
#define CONTROLMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedMemory>
|
||||
#include <QSystemSemaphore>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(ControlWorker)
|
||||
QT_FORWARD_DECLARE_CLASS(FortManager)
|
||||
|
||||
class ControlManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ControlManager(const QString &globalName,
|
||||
const QString &scriptPath,
|
||||
QObject *parent = nullptr);
|
||||
virtual ~ControlManager();
|
||||
|
||||
bool isClient() const { return m_isClient; }
|
||||
|
||||
bool listen(FortManager *fortManager);
|
||||
bool post(const QStringList &args);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private slots:
|
||||
void processRequest(const QString &scriptPath,
|
||||
const QStringList &args);
|
||||
|
||||
private:
|
||||
void setupWorker();
|
||||
|
||||
void abort();
|
||||
|
||||
private:
|
||||
bool m_isClient;
|
||||
|
||||
QString m_scriptPath;
|
||||
|
||||
FortManager *m_fortManager;
|
||||
ControlWorker *m_worker;
|
||||
|
||||
QSystemSemaphore m_semaphore;
|
||||
QSharedMemory m_sharedMemory;
|
||||
};
|
||||
|
||||
#endif // CONTROLMANAGER_H
|
114
src/ui/control/controlworker.cpp
Normal file
114
src/ui/control/controlworker.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
#include "controlworker.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QSharedMemory>
|
||||
#include <QSystemSemaphore>
|
||||
|
||||
ControlWorker::ControlWorker(QSystemSemaphore *semaphore,
|
||||
QSharedMemory *sharedMemory,
|
||||
QObject *parent) :
|
||||
QObject(parent),
|
||||
m_aborted(false),
|
||||
m_semaphore(semaphore),
|
||||
m_sharedMemory(sharedMemory)
|
||||
{
|
||||
}
|
||||
|
||||
void ControlWorker::run()
|
||||
{
|
||||
while (m_semaphore->acquire() && !m_aborted) {
|
||||
processRequest();
|
||||
}
|
||||
}
|
||||
|
||||
void ControlWorker::abort()
|
||||
{
|
||||
m_aborted = true;
|
||||
|
||||
m_semaphore->release();
|
||||
}
|
||||
|
||||
bool ControlWorker::post(const QString &scriptPath,
|
||||
const QStringList &args)
|
||||
{
|
||||
m_sharedMemory->lock();
|
||||
|
||||
const bool res = writeDataStream(scriptPath, args);
|
||||
|
||||
m_sharedMemory->unlock();
|
||||
|
||||
if (res) {
|
||||
m_semaphore->release();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void ControlWorker::processRequest()
|
||||
{
|
||||
QString scriptPath;
|
||||
QStringList args;
|
||||
|
||||
m_sharedMemory->lock();
|
||||
|
||||
const bool res = readDataStream(scriptPath, args);
|
||||
|
||||
m_sharedMemory->unlock();
|
||||
|
||||
if (res) {
|
||||
emit requestReady(scriptPath, args);
|
||||
}
|
||||
}
|
||||
|
||||
bool ControlWorker::writeData(const QByteArray &data)
|
||||
{
|
||||
const int dataSize = data.size();
|
||||
|
||||
if (dataSize < 0 || int(sizeof(int)) + dataSize > m_sharedMemory->size())
|
||||
return false;
|
||||
|
||||
int *p = static_cast<int *>(m_sharedMemory->data());
|
||||
*p++ = dataSize;
|
||||
|
||||
if (dataSize != 0) {
|
||||
memcpy(p, data.constData(), data.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray ControlWorker::readData() const
|
||||
{
|
||||
const int *p = static_cast<const int *>(m_sharedMemory->constData());
|
||||
const int dataSize = *p++;
|
||||
|
||||
if (dataSize < 0 || int(sizeof(int)) + dataSize > m_sharedMemory->size())
|
||||
return QByteArray();
|
||||
|
||||
return QByteArray::fromRawData((const char *) p, dataSize);
|
||||
}
|
||||
|
||||
bool ControlWorker::writeDataStream(const QString &scriptPath,
|
||||
const QStringList &args)
|
||||
{
|
||||
QByteArray data;
|
||||
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream << scriptPath << args;
|
||||
|
||||
return writeData(data);
|
||||
}
|
||||
|
||||
bool ControlWorker::readDataStream(QString &scriptPath,
|
||||
QStringList &args) const
|
||||
{
|
||||
const QByteArray data = readData();
|
||||
|
||||
if (data.isEmpty())
|
||||
return false;
|
||||
|
||||
QDataStream stream(data);
|
||||
stream >> scriptPath >> args;
|
||||
|
||||
return true;
|
||||
}
|
49
src/ui/control/controlworker.h
Normal file
49
src/ui/control/controlworker.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef CONTROLWORKER_H
|
||||
#define CONTROLWORKER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QRunnable>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QSharedMemory)
|
||||
QT_FORWARD_DECLARE_CLASS(QSystemSemaphore)
|
||||
|
||||
class ControlWorker : public QObject, public QRunnable
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ControlWorker(QSystemSemaphore *semaphore,
|
||||
QSharedMemory *sharedMemory,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
void run() override;
|
||||
|
||||
bool post(const QString &scriptPath,
|
||||
const QStringList &args);
|
||||
|
||||
signals:
|
||||
void requestReady(const QString &scriptPath,
|
||||
const QStringList &args);
|
||||
|
||||
public slots:
|
||||
void abort();
|
||||
|
||||
private:
|
||||
void processRequest();
|
||||
|
||||
bool writeData(const QByteArray &data);
|
||||
QByteArray readData() const;
|
||||
|
||||
bool writeDataStream(const QString &scriptPath,
|
||||
const QStringList &args);
|
||||
bool readDataStream(QString &scriptPath,
|
||||
QStringList &args) const;
|
||||
|
||||
private:
|
||||
volatile bool m_aborted;
|
||||
|
||||
QSystemSemaphore *m_semaphore;
|
||||
QSharedMemory *m_sharedMemory;
|
||||
};
|
||||
|
||||
#endif // CONTROLWORKER_H
|
@ -12,7 +12,7 @@
|
||||
#include "sqlite/sqlitestmt.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(CLOG_DATABASE_MANAGER)
|
||||
Q_LOGGING_CATEGORY(CLOG_DATABASE_MANAGER, CLOG_DATABASE_MANAGER_STR)
|
||||
Q_LOGGING_CATEGORY(CLOG_DATABASE_MANAGER, "fort.databaseManager")
|
||||
|
||||
#define INVALID_APP_INDEX qint16(-1)
|
||||
#define INVALID_APP_ID qint64(-1)
|
||||
|
@ -11,8 +11,6 @@ QT_FORWARD_DECLARE_CLASS(QuotaManager)
|
||||
QT_FORWARD_DECLARE_CLASS(SqliteDb)
|
||||
QT_FORWARD_DECLARE_CLASS(SqliteStmt)
|
||||
|
||||
#define CLOG_DATABASE_MANAGER_STR "fort.databaseManager"
|
||||
|
||||
class DatabaseManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -56,6 +56,11 @@ void FortSettings::processArguments(const QStringList &args)
|
||||
"Directory to store statistics.", "stat");
|
||||
parser.addOption(statOption);
|
||||
|
||||
const QCommandLineOption controlOption(
|
||||
QStringList() << "c" << "control",
|
||||
"Control running instance by executing the JS file.", "control");
|
||||
parser.addOption(controlOption);
|
||||
|
||||
parser.addVersionOption();
|
||||
parser.addHelpOption();
|
||||
|
||||
@ -80,6 +85,15 @@ void FortSettings::processArguments(const QStringList &args)
|
||||
m_statPath = FileUtil::pathSlash(
|
||||
FileUtil::absolutePath(m_statPath));
|
||||
}
|
||||
|
||||
// Control QML file path
|
||||
m_controlPath = parser.value(controlOption);
|
||||
if (!m_controlPath.isEmpty()) {
|
||||
m_controlPath = FileUtil::absolutePath(m_controlPath);
|
||||
}
|
||||
|
||||
// Other Arguments
|
||||
m_args = parser.positionalArguments();
|
||||
}
|
||||
|
||||
void FortSettings::setupIni()
|
||||
|
@ -85,6 +85,10 @@ public:
|
||||
QString statPath() const { return m_statPath; }
|
||||
QString statFilePath() const;
|
||||
|
||||
QString controlPath() const { return m_controlPath; }
|
||||
|
||||
QStringList args() const { return m_args; }
|
||||
|
||||
QString errorMessage() const { return m_errorMessage; }
|
||||
|
||||
QString appUpdatesUrl() const { return APP_UPDATES_URL; }
|
||||
@ -140,6 +144,8 @@ private:
|
||||
|
||||
QString m_profilePath;
|
||||
QString m_statPath;
|
||||
QString m_controlPath;
|
||||
QStringList m_args;
|
||||
|
||||
QString m_errorMessage;
|
||||
|
||||
|
Binary file not shown.
@ -39,6 +39,14 @@
|
||||
<translation>Длина пути приложения должна быть < %1</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ControlManager</name>
|
||||
<message>
|
||||
<location filename="../control/controlmanager.cpp" line="114"/>
|
||||
<source>Control script executed</source>
|
||||
<translation>Управляющий скрипт выполнен</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FortManager</name>
|
||||
<message>
|
||||
@ -80,17 +88,17 @@
|
||||
<context>
|
||||
<name>FortSettings</name>
|
||||
<message>
|
||||
<location filename="../fortsettings.cpp" line="191"/>
|
||||
<location filename="../fortsettings.cpp" line="205"/>
|
||||
<source>Can't write .ini file</source>
|
||||
<translation>Не удалось записать .ini файл</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../fortsettings.cpp" line="206"/>
|
||||
<location filename="../fortsettings.cpp" line="220"/>
|
||||
<source>Can't create .conf file</source>
|
||||
<translation>Не удалось создать .conf файл</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../fortsettings.cpp" line="201"/>
|
||||
<location filename="../fortsettings.cpp" line="215"/>
|
||||
<source>Can't create backup .conf file</source>
|
||||
<translation>Не удалось создать бэкап .conf файла</translation>
|
||||
</message>
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "../common/version.h"
|
||||
#include "control/controlmanager.h"
|
||||
#include "control/controlworker.h"
|
||||
#include "driver/drivermanager.h"
|
||||
#include "fortcommon.h"
|
||||
#include "fortmanager.h"
|
||||
@ -10,6 +12,7 @@
|
||||
|
||||
#define FORT_ERROR_INSTANCE 1
|
||||
#define FORT_ERROR_DEVICE 2
|
||||
#define FORT_ERROR_CONTROL 3
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -31,7 +34,16 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
// To check running instance
|
||||
ControlManager controlManager(app.applicationName(),
|
||||
fortSettings.controlPath());
|
||||
|
||||
// Send control request to running instance
|
||||
if (controlManager.isClient()) {
|
||||
return controlManager.post(fortSettings.args())
|
||||
? 0 : FORT_ERROR_CONTROL;
|
||||
}
|
||||
|
||||
// Check running instance
|
||||
if (!OsUtil::createGlobalMutex(APP_NAME)) {
|
||||
QMessageBox::critical(nullptr, QString(),
|
||||
"Application is already running!");
|
||||
@ -49,5 +61,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
fortManager.showTrayIcon();
|
||||
|
||||
// Process control requests from clients
|
||||
if (!controlManager.listen(&fortManager)) {
|
||||
return FORT_ERROR_CONTROL;
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user