permissions: add perms for plugin loading (#10184)
Adds permission management for loading plugins --------- Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
This commit is contained in:
parent
2118440488
commit
5bd7ff884d
12 changed files with 416 additions and 91 deletions
|
|
@ -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) {
|
||||
|
||||
m_szLastError = "";
|
||||
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;
|
||||
}
|
||||
|
||||
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 (std::ranges::find_if(m_vLoadedPlugins, [&](const auto& other) { return other->path == path; }) != m_vLoadedPlugins.end())
|
||||
continue;
|
||||
|
||||
if (plugin) {
|
||||
plugin->m_bLoadedWithConfig = true;
|
||||
changed = true;
|
||||
} else
|
||||
failures.push_back(path);
|
||||
}
|
||||
Debug::log(LOG, "Loading plugin {} which is now present in config", path);
|
||||
|
||||
changed = true;
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
CPlugin* CPluginSystem::getPluginByPath(const std::string& path) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue