diff options
| author | Vaxry <43317083+vaxerski@users.noreply.github.com> | 2025-04-29 18:59:43 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-29 18:59:43 +0200 |
| commit | 5bd7ff884dc613ad22ecd227d41ce6ad7ee336df (patch) | |
| tree | 63696482d6ca82db0f123ed53ce798ba500b7c44 /src/plugins | |
| parent | 21184404885cf61f7c10d2eb749478ef6b035dd2 (diff) | |
permissions: add perms for plugin loading (#10184)
Adds permission management for loading plugins
---------
Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
Diffstat (limited to 'src/plugins')
| -rw-r--r-- | src/plugins/PluginSystem.cpp | 119 | ||||
| -rw-r--r-- | src/plugins/PluginSystem.hpp | 29 |
2 files changed, 103 insertions, 45 deletions
diff --git a/src/plugins/PluginSystem.cpp b/src/plugins/PluginSystem.cpp index 87b9e8ef..2bed8dbd 100644 --- a/src/plugins/PluginSystem.cpp +++ b/src/plugins/PluginSystem.cpp @@ -3,22 +3,75 @@ #include <dlfcn.h> #include <ranges> #include "../config/ConfigManager.hpp" +#include "../debug/HyprCtl.hpp" #include "../managers/LayoutManager.hpp" #include "../managers/HookSystemManager.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" +#include "../managers/permissions/DynamicPermissionManager.hpp" +#include "../debug/HyprNotificationOverlay.hpp" CPluginSystem::CPluginSystem() { g_pFunctionHookSystem = makeUnique<CHookSystem>(); } -CPlugin* CPluginSystem::loadPlugin(const std::string& path) { +SP<CPromise<CPlugin*>> CPluginSystem::loadPlugin(const std::string& path) { + + pid_t pid = 0; + + if (g_pHyprCtl->m_currentRequestParams.pid > 0) + pid = g_pHyprCtl->m_currentRequestParams.pid; + + return CPromise<CPlugin*>::make([path, pid, this](SP<CPromiseResolver<CPlugin*>> resolver) { + const auto PERM = g_pDynamicPermissionManager->clientPermissionModeWithString(pid, path, PERMISSION_TYPE_PLUGIN); + if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { + Debug::log(LOG, "CPluginSystem: Waiting for user confirmation to load {}", path); + + auto promise = g_pDynamicPermissionManager->promiseFor(pid, path, PERMISSION_TYPE_PLUGIN); + if (!promise) { // already awaiting or something? + resolver->reject("Failed to get a promise for permission"); + return; + } + + promise->then([this, path, resolver](SP<CPromiseResult<eDynamicPermissionAllowMode>> result) { + if (result->hasError()) { + Debug::log(ERR, "CPluginSystem: Error spawning permission prompt"); + resolver->reject("Error spawning permission prompt"); + return; + } + + if (result->result() != PERMISSION_RULE_ALLOW_MODE_ALLOW) { + Debug::log(ERR, "CPluginSystem: Rejecting plugin load of {}, user denied", path); + resolver->reject("user denied"); + return; + } + + Debug::log(LOG, "CPluginSystem: Loading {}, user allowed", path); + + const auto RESULT = loadPluginInternal(path); + if (RESULT.has_value()) + resolver->resolve(RESULT.value()); + else + resolver->reject(RESULT.error()); + }); + return; + } else if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { + Debug::log(LOG, "CPluginSystem: Rejecting plugin load, permission is disabled"); + resolver->reject("permission is disabled"); + return; + } - m_szLastError = ""; + const auto RESULT = loadPluginInternal(path); + if (RESULT.has_value()) + resolver->resolve(RESULT.value()); + else + resolver->reject(RESULT.error()); + }); +} +std::expected<CPlugin*, std::string> CPluginSystem::loadPluginInternal(const std::string& path) { if (getPluginByPath(path)) { - m_szLastError = "Cannot load a plugin twice!"; Debug::log(ERR, " [PluginSystem] Cannot load a plugin twice!"); - return nullptr; + return std::unexpected("Cannot load a plugin twice!"); } auto* const PLUGIN = m_vLoadedPlugins.emplace_back(makeUnique<CPlugin>()).get(); @@ -29,10 +82,9 @@ CPlugin* CPluginSystem::loadPlugin(const std::string& path) { if (!MODULE) { std::string strerr = dlerror(); - m_szLastError = std::format("Plugin {} could not be loaded: {}", path, strerr); Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded: {}", path, strerr); m_vLoadedPlugins.pop_back(); - return nullptr; + return std::unexpected(std::format("Plugin {} could not be loaded: {}", path, strerr)); } PLUGIN->m_pHandle = MODULE; @@ -41,21 +93,19 @@ CPlugin* CPluginSystem::loadPlugin(const std::string& path) { PPLUGIN_INIT_FUNC initFunc = (PPLUGIN_INIT_FUNC)dlsym(MODULE, PLUGIN_INIT_FUNC_STR); if (!apiVerFunc || !initFunc) { - m_szLastError = std::format("Plugin {} could not be loaded: {}", path, "missing apiver/init func"); Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded. (No apiver/init func)", path); dlclose(MODULE); m_vLoadedPlugins.pop_back(); - return nullptr; + return std::unexpected(std::format("Plugin {} could not be loaded: {}", path, "missing apiver/init func")); } const std::string PLUGINAPIVER = apiVerFunc(); if (PLUGINAPIVER != HYPRLAND_API_VERSION) { - m_szLastError = std::format("Plugin {} could not be loaded: {}", path, "API version mismatch"); Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded. (API version mismatch)", path); dlclose(MODULE); m_vLoadedPlugins.pop_back(); - return nullptr; + return std::unexpected(std::format("Plugin {} could not be loaded: {}", path, "API version mismatch")); } PLUGIN_DESCRIPTION_INFO PLUGINDATA; @@ -70,10 +120,9 @@ CPlugin* CPluginSystem::loadPlugin(const std::string& path) { } } catch (std::exception& e) { m_bAllowConfigVars = false; - m_szLastError = std::format("Plugin {} could not be loaded: plugin crashed/threw in main: {}", path, e.what()); Debug::log(ERR, " [PluginSystem] Plugin {} (Handle {:x}) crashed in init. Unloading.", path, (uintptr_t)MODULE); unloadPlugin(PLUGIN, true); // Plugin could've already hooked/done something - return nullptr; + return std::unexpected(std::format("Plugin {} could not be loaded: plugin crashed/threw in main: {}", path, e.what())); } m_bAllowConfigVars = false; @@ -146,33 +195,39 @@ void CPluginSystem::unloadAllPlugins() { unloadPlugin(p.get(), false); // Unload remaining plugins gracefully } -std::vector<std::string> CPluginSystem::updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed) { - std::vector<std::string> failures; - +void CPluginSystem::updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed) { // unload all plugins that are no longer present for (auto const& p : m_vLoadedPlugins | std::views::reverse) { - if (p->m_bLoadedWithConfig && std::find(plugins.begin(), plugins.end(), p->path) == plugins.end()) { - Debug::log(LOG, "Unloading plugin {} which is no longer present in config", p->path); - unloadPlugin(p.get(), false); - changed = true; - } + if (!p->m_bLoadedWithConfig || std::ranges::find(plugins, p->path) != plugins.end()) + continue; + + Debug::log(LOG, "Unloading plugin {} which is no longer present in config", p->path); + unloadPlugin(p.get(), false); + changed = true; } // load all new plugins for (auto const& path : plugins) { - if (std::find_if(m_vLoadedPlugins.begin(), m_vLoadedPlugins.end(), [&](const auto& other) { return other->path == path; }) == m_vLoadedPlugins.end()) { - Debug::log(LOG, "Loading plugin {} which is now present in config", path); - const auto plugin = loadPlugin(path); - - if (plugin) { - plugin->m_bLoadedWithConfig = true; - changed = true; - } else - failures.push_back(path); - } - } + if (std::ranges::find_if(m_vLoadedPlugins, [&](const auto& other) { return other->path == path; }) != m_vLoadedPlugins.end()) + continue; + + Debug::log(LOG, "Loading plugin {} which is now present in config", path); + + changed = true; - return failures; + loadPlugin(path)->then([path](SP<CPromiseResult<CPlugin*>> result) { + if (result->hasError()) { + const auto NAME = path.contains('/') ? path.substr(path.find_last_of('/') + 1) : path; + Debug::log(ERR, "CPluginSystem::updateConfigPlugins: failed to load plugin {}: {}", NAME, result->error()); + g_pHyprNotificationOverlay->addNotification(std::format("Failed to load plugin {}: {}", NAME, result->error()), CHyprColor{0, 0, 0, 0}, 5000, ICON_ERROR); + return; + } + + result->result()->m_bLoadedWithConfig = true; + + Debug::log(LOG, "CPluginSystem::updateConfigPlugins: loaded {}", path); + }); + } } CPlugin* CPluginSystem::getPluginByPath(const std::string& path) { diff --git a/src/plugins/PluginSystem.hpp b/src/plugins/PluginSystem.hpp index 95841bef..ba1dafc0 100644 --- a/src/plugins/PluginSystem.hpp +++ b/src/plugins/PluginSystem.hpp @@ -1,8 +1,10 @@ #pragma once #include "../defines.hpp" +#include "../helpers/defer/Promise.hpp" #include "PluginAPI.hpp" #include <csetjmp> +#include <expected> class IHyprWindowDecoration; @@ -30,23 +32,24 @@ class CPluginSystem { public: CPluginSystem(); - CPlugin* loadPlugin(const std::string& path); - void unloadPlugin(const CPlugin* plugin, bool eject = false); - void unloadAllPlugins(); - std::vector<std::string> updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed); - CPlugin* getPluginByPath(const std::string& path); - CPlugin* getPluginByHandle(HANDLE handle); - std::vector<CPlugin*> getAllPlugins(); - size_t pluginCount(); - void sigGetPlugins(CPlugin** data, size_t len); + SP<CPromise<CPlugin*>> loadPlugin(const std::string& path); + void unloadPlugin(const CPlugin* plugin, bool eject = false); + void unloadAllPlugins(); + void updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed); + CPlugin* getPluginByPath(const std::string& path); + CPlugin* getPluginByHandle(HANDLE handle); + std::vector<CPlugin*> getAllPlugins(); + size_t pluginCount(); + void sigGetPlugins(CPlugin** data, size_t len); - bool m_bAllowConfigVars = false; - std::string m_szLastError = ""; + bool m_bAllowConfigVars = false; private: - std::vector<UP<CPlugin>> m_vLoadedPlugins; + std::vector<UP<CPlugin>> m_vLoadedPlugins; - jmp_buf m_jbPluginFaultJumpBuf; + jmp_buf m_jbPluginFaultJumpBuf; + + std::expected<CPlugin*, std::string> loadPluginInternal(const std::string& path); }; inline UP<CPluginSystem> g_pPluginSystem; |
