diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a4fc0b..11d6346 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,6 +98,8 @@ set(LOGOS_CORE_SOURCES logos_core/dependency_resolver.h logos_core/plugin_manager.cpp logos_core/plugin_manager.h + logos_core/plugin_launcher.cpp + logos_core/plugin_launcher.h logos_core/qt/qt_app_context.cpp logos_core/qt/qt_app_context.h logos_core/qt/qt_process_manager.cpp diff --git a/src/logos_core/plugin_launcher.cpp b/src/logos_core/plugin_launcher.cpp new file mode 100644 index 0000000..833e40a --- /dev/null +++ b/src/logos_core/plugin_launcher.cpp @@ -0,0 +1,116 @@ +#include "plugin_launcher.h" +#include "qt/qt_process_manager.h" +#include +#include +#include +#include + +namespace { + + QString resolveLogosHostPath(const QStringList& pluginsDirs) { + QString logosHostPath; + + QByteArray envPathBytes = qgetenv("LOGOS_HOST_PATH"); + if (!envPathBytes.isEmpty()) { + logosHostPath = QString::fromUtf8(envPathBytes); + } + + if (logosHostPath.isEmpty()) { + logosHostPath = QDir::cleanPath(QCoreApplication::applicationDirPath() + "/logos_host"); + } + + if (!QFile::exists(logosHostPath)) { + if (!pluginsDirs.isEmpty()) { + QDir pluginsDirCandidate(pluginsDirs.first()); + QString candidate = QDir::cleanPath(pluginsDirCandidate.absoluteFilePath("../bin/logos_host")); + if (QFile::exists(candidate)) { + logosHostPath = candidate; + } + } + } + + if (!QFile::exists(logosHostPath)) { + qCritical() << "logos_host not found at:" << logosHostPath + << "- set LOGOS_HOST_PATH or place it next to the executable"; + return QString(); + } + + return logosHostPath; + } + +} + +namespace PluginLauncher { + + bool launch(const QString& name, const QString& pluginPath, + const QStringList& pluginsDirs, OnTerminatedFn onTerminated) { + QString logosHostPath = resolveLogosHostPath(pluginsDirs); + if (logosHostPath.isEmpty()) + return false; + + std::vector arguments = { + "--name", name.toStdString(), + "--path", pluginPath.toStdString() + }; + + QtProcessManager::ProcessCallbacks callbacks; + + callbacks.onFinished = [onTerminated](const std::string& pName, int exitCode, bool crashed) { + Q_UNUSED(exitCode); + QString qName = QString::fromStdString(pName); + if (crashed) { + qCritical() << "Plugin process crashed:" << qName; + exit(1); + } + if (onTerminated) + onTerminated(qName); + }; + + callbacks.onError = [](const std::string& pName, bool crashed) { + if (crashed) { + qCritical() << "Plugin process crashed:" << QString::fromStdString(pName); + exit(1); + } + }; + + callbacks.onOutput = [](const std::string& pName, const std::string& line, bool isStderr) { + QString qName = QString::fromStdString(pName); + QString qLine = QString::fromStdString(line); + if (isStderr) { + qCritical() << "[" << qName << "]" << qLine; + } else if (qLine.contains("Warning:") || qLine.contains("WARNING:")) { + qWarning() << "[" << qName << "]" << qLine; + } else if (qLine.contains("Critical:") || qLine.contains("FAILED:") || qLine.contains("ERROR:")) { + qCritical() << "[" << qName << "]" << qLine; + } + }; + + return QtProcessManager::startProcess(name.toStdString(), logosHostPath.toStdString(), arguments, callbacks); + } + + bool sendToken(const QString& name, const QString& token) { + return QtProcessManager::sendToken(name.toStdString(), token.toStdString()); + } + + void terminate(const QString& name) { + QtProcessManager::terminateProcess(name.toStdString()); + } + + void terminateAll() { + QtProcessManager::terminateAll(); + } + + bool hasProcess(const QString& name) { + return QtProcessManager::hasProcess(name.toStdString()); + } + + QHash getAllProcessIds() { + auto stdMap = QtProcessManager::getAllProcessIds(); + QHash result; + for (const auto& [name, pid] : stdMap) { + result.insert(QString::fromStdString(name), pid); + } + return result; + } + +} diff --git a/src/logos_core/plugin_launcher.h b/src/logos_core/plugin_launcher.h new file mode 100644 index 0000000..ecd59b4 --- /dev/null +++ b/src/logos_core/plugin_launcher.h @@ -0,0 +1,21 @@ +#ifndef PLUGIN_LAUNCHER_H +#define PLUGIN_LAUNCHER_H + +#include +#include +#include +#include + +namespace PluginLauncher { + using OnTerminatedFn = std::function; + + bool launch(const QString& name, const QString& pluginPath, + const QStringList& pluginsDirs, OnTerminatedFn onTerminated); + bool sendToken(const QString& name, const QString& token); + void terminate(const QString& name); + void terminateAll(); + bool hasProcess(const QString& name); + QHash getAllProcessIds(); +} + +#endif // PLUGIN_LAUNCHER_H diff --git a/src/logos_core/plugin_manager.cpp b/src/logos_core/plugin_manager.cpp index c0a2216..e72e10d 100644 --- a/src/logos_core/plugin_manager.cpp +++ b/src/logos_core/plugin_manager.cpp @@ -1,12 +1,9 @@ #include "plugin_manager.h" #include "plugin_registry.h" #include "dependency_resolver.h" -#include "qt/qt_process_manager.h" +#include "plugin_launcher.h" #include -#include -#include #include -#include #include #include #include "logos_api.h" @@ -37,38 +34,6 @@ namespace { return result; } - QString resolveLogosHostPath() { - QString logosHostPath; - - QByteArray envPathBytes = qgetenv("LOGOS_HOST_PATH"); - if (!envPathBytes.isEmpty()) { - logosHostPath = QString::fromUtf8(envPathBytes); - } - - if (logosHostPath.isEmpty()) { - logosHostPath = QDir::cleanPath(QCoreApplication::applicationDirPath() + "/logos_host"); - } - - if (!QFile::exists(logosHostPath)) { - QStringList dirs = registryInstance().pluginsDirs(); - if (!dirs.isEmpty()) { - QDir pluginsDirCandidate(dirs.first()); - QString candidate = QDir::cleanPath(pluginsDirCandidate.absoluteFilePath("../bin/logos_host")); - if (QFile::exists(candidate)) { - logosHostPath = candidate; - } - } - } - - if (!QFile::exists(logosHostPath)) { - qCritical() << "logos_host not found at:" << logosHostPath - << "- set LOGOS_HOST_PATH or place it next to the executable"; - return QString(); - } - - return logosHostPath; - } - void notifyCapabilityModule(const QString& name, const QString& token) { if (!registryInstance().isLoaded("capability_module")) return; @@ -141,63 +106,23 @@ namespace PluginManager { QString pluginPath = registryInstance().pluginPath(name); - QString logosHostPath = resolveLogosHostPath(); - if (logosHostPath.isEmpty()) + auto onTerminated = [](const QString& n) { + registryInstance().markUnloaded(n); + }; + + if (!PluginLauncher::launch(name, pluginPath, registryInstance().pluginsDirs(), onTerminated)) return false; - std::vector arguments = { - "--name", name.toStdString(), - "--path", pluginPath.toStdString() - }; + QString authToken = QUuid::createUuid().toString(QUuid::WithoutBraces); - QtProcessManager::ProcessCallbacks callbacks; - - callbacks.onFinished = [](const std::string& pName, int exitCode, bool crashed) { - Q_UNUSED(exitCode); - QString qName = QString::fromStdString(pName); - if (crashed) { - qCritical() << "Plugin process crashed:" << qName; - exit(1); - } - registryInstance().markUnloaded(qName); - }; - - callbacks.onError = [](const std::string& pName, bool crashed) { - if (crashed) { - qCritical() << "Plugin process crashed:" << QString::fromStdString(pName); - exit(1); - } - }; - - callbacks.onOutput = [](const std::string& pName, const std::string& line, bool isStderr) { - QString qName = QString::fromStdString(pName); - QString qLine = QString::fromStdString(line); - if (isStderr) { - qCritical() << "[" << qName << "]" << qLine; - } else if (qLine.contains("Warning:") || qLine.contains("WARNING:")) { - qWarning() << "[" << qName << "]" << qLine; - } else if (qLine.contains("Critical:") || qLine.contains("FAILED:") || qLine.contains("ERROR:")) { - qCritical() << "[" << qName << "]" << qLine; - } - }; - - if (!QtProcessManager::startProcess(name.toStdString(), logosHostPath.toStdString(), arguments, callbacks)) { + if (!PluginLauncher::sendToken(name, authToken)) return false; - } - - QUuid authToken = QUuid::createUuid(); - QString authTokenString = authToken.toString(QUuid::WithoutBraces); - - if (!QtProcessManager::sendToken(name.toStdString(), authTokenString.toStdString())) { - return false; - } registryInstance().markLoaded(name); - TokenManager& tokenManager = TokenManager::instance(); - tokenManager.saveToken(name, authTokenString); + TokenManager::instance().saveToken(name, authToken); - notifyCapabilityModule(name, authTokenString); + notifyCapabilityModule(name, authToken); qInfo() << "Plugin loaded:" << name; @@ -254,12 +179,12 @@ namespace PluginManager { return false; } - if (!QtProcessManager::hasProcess(name.toStdString())) { + if (!PluginLauncher::hasProcess(name)) { qWarning() << "No process found for plugin:" << name; return false; } - QtProcessManager::terminateProcess(name.toStdString()); + PluginLauncher::terminate(name); registryInstance().markUnloaded(name); qInfo() << "Plugin unloaded:" << name; @@ -267,7 +192,7 @@ namespace PluginManager { } void terminateAll() { - QtProcessManager::terminateAll(); + PluginLauncher::terminateAll(); registryInstance().clearLoaded(); } @@ -288,12 +213,7 @@ namespace PluginManager { } QHash getPluginProcessIds() { - auto stdMap = QtProcessManager::getAllProcessIds(); - QHash result; - for (const auto& [name, pid] : stdMap) { - result.insert(QString::fromStdString(name), pid); - } - return result; + return PluginLauncher::getAllProcessIds(); } QStringList resolveDependencies(const QStringList& requestedModules) {