From 858c0e26d19391d56c1e7a6ac8574da956068d38 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Thu, 1 May 2025 18:00:26 +0200 Subject: [PATCH] hyprpm: move to system directories for storing plugins (#10211) --- CMakeLists.txt | 2 +- hyprpm/CMakeLists.txt | 2 +- hyprpm/src/core/DataState.cpp | 108 +++++++++++++++++------------ hyprpm/src/core/DataState.hpp | 1 + hyprpm/src/core/PluginManager.cpp | 31 +++++++-- hyprpm/src/core/PluginManager.hpp | 4 +- hyprpm/src/helpers/Die.hpp | 15 ++++ hyprpm/src/helpers/Sys.cpp | 110 ++++++++++++++++++++++++++++++ hyprpm/src/helpers/Sys.hpp | 12 ++++ hyprpm/src/main.cpp | 65 ++++++++++++------ meson.build | 2 +- 11 files changed, 278 insertions(+), 74 deletions(-) create mode 100644 hyprpm/src/helpers/Die.hpp create mode 100644 hyprpm/src/helpers/Sys.cpp create mode 100644 hyprpm/src/helpers/Sys.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4342d093c..bc016d850 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,7 +109,7 @@ find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0) pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2) pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7) -pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.6.0) +pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.7.0) pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1) string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION}) diff --git a/hyprpm/CMakeLists.txt b/hyprpm/CMakeLists.txt index b3028d0ad..f2e0b2236 100644 --- a/hyprpm/CMakeLists.txt +++ b/hyprpm/CMakeLists.txt @@ -9,7 +9,7 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") set(CMAKE_CXX_STANDARD 23) -pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.2.4) +pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0) find_package(glaze QUIET) if (NOT glaze_FOUND) diff --git a/hyprpm/src/core/DataState.cpp b/hyprpm/src/core/DataState.cpp index 55f148a4d..d8a9477ea 100644 --- a/hyprpm/src/core/DataState.cpp +++ b/hyprpm/src/core/DataState.cpp @@ -1,22 +1,15 @@ #include "DataState.hpp" +#include #include #include #include #include "PluginManager.hpp" +#include "../helpers/Die.hpp" +#include "../helpers/Sys.hpp" +#include "../helpers/StringUtils.hpp" std::filesystem::path DataState::getDataStatePath() { - const auto HOME = getenv("HOME"); - if (!HOME) { - std::println(stderr, "DataState: no $HOME"); - throw std::runtime_error("no $HOME"); - return ""; - } - - const auto XDG_DATA_HOME = getenv("XDG_DATA_HOME"); - - if (XDG_DATA_HOME) - return std::filesystem::path{XDG_DATA_HOME} / "hyprpm"; - return std::filesystem::path{HOME} / ".local/share/hyprpm"; + return std::filesystem::path("/var/cache/hyprpm/" + g_pPluginManager->m_szUsername); } std::string DataState::getHeadersPath() { @@ -41,21 +34,25 @@ std::vector DataState::getPluginStates() { } void DataState::ensureStateStoreExists() { - const auto PATH = getDataStatePath(); - - if (!std::filesystem::exists(PATH)) - std::filesystem::create_directories(PATH); - - if (!std::filesystem::exists(getHeadersPath())) - std::filesystem::create_directories(getHeadersPath()); + std::error_code ec; + if (!std::filesystem::exists(getHeadersPath(), ec) || ec) { + std::println("{}", infoString("The hyprpm state store doesn't exist. Creating now...")); + if (!std::filesystem::exists("/var/cache/hyprpm/", ec) || ec) + NSys::runAsSuperuser("mkdir -p -m 755 '/var/cache/hyprpm/'"); + if (!std::filesystem::exists(getDataStatePath(), ec) || ec) + NSys::runAsSuperuser("mkdir -p -m 755 '" + getDataStatePath().string() + "'"); + NSys::runAsSuperuser("mkdir -p -m 755 '" + getHeadersPath() + "'"); + } } void DataState::addNewPluginRepo(const SPluginRepository& repo) { ensureStateStoreExists(); - const auto PATH = getDataStatePath() / repo.name; + const auto PATH = getDataStatePath() / repo.name; - std::filesystem::create_directories(PATH); + std::error_code ec; + if (!std::filesystem::exists(PATH, ec) || ec) + NSys::runAsSuperuser("mkdir -p -m 755 '" + PATH.string() + "'"); // clang-format off auto DATA = toml::table{ {"repository", toml::table{ @@ -68,9 +65,9 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) { for (auto const& p : repo.plugins) { const auto filename = p.name + ".so"; - // copy .so to the good place + // copy .so to the good place and chmod 755 if (std::filesystem::exists(p.filename)) - std::filesystem::copy_file(p.filename, PATH / filename); + NSys::runAsSuperuser("cp '" + p.filename + "' '" + (PATH / filename).string() + "' && chmod 755 '" + (PATH / filename).string() + "'"); DATA.emplace(p.name, toml::table{ {"filename", filename}, @@ -80,16 +77,16 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) { } // clang-format on - std::ofstream ofs(PATH / "state.toml", std::ios::trunc); - ofs << DATA; - ofs.close(); + std::stringstream ss; + ss << DATA; + + NSys::runAsSuperuser("cat << EOF > " + (PATH / "state.toml").string() + "\n" + ss.str() + "\nEOF"); + NSys::runAsSuperuser("chmod 644 '" + (PATH / "state.toml").string() + "'"); } bool DataState::pluginRepoExists(const std::string& urlOrName) { ensureStateStoreExists(); - const auto PATH = getDataStatePath(); - for (const auto& stateFile : getPluginStates()) { const auto STATE = toml::parse_file(stateFile.c_str()); const auto NAME = STATE["repository"]["name"].value_or(""); @@ -105,8 +102,6 @@ bool DataState::pluginRepoExists(const std::string& urlOrName) { void DataState::removePluginRepo(const std::string& urlOrName) { ensureStateStoreExists(); - const auto PATH = getDataStatePath(); - for (const auto& stateFile : getPluginStates()) { const auto STATE = toml::parse_file(stateFile.c_str()); const auto NAME = STATE["repository"]["name"].value_or(""); @@ -122,7 +117,13 @@ void DataState::removePluginRepo(const std::string& urlOrName) { g_pPluginManager->loadUnloadPlugin(std::filesystem::absolute(file.path()), false); } - std::filesystem::remove_all(stateFile.parent_path()); + const auto PATH = stateFile.parent_path().string(); + + if (!PATH.starts_with("/var/cache/hyprpm") || PATH.contains('\'')) + return; // WTF? + + // scary! + NSys::runAsSuperuser("rm -r '" + PATH + "'"); return; } } @@ -131,9 +132,11 @@ void DataState::removePluginRepo(const std::string& urlOrName) { void DataState::updateGlobalState(const SGlobalState& state) { ensureStateStoreExists(); - const auto PATH = getDataStatePath(); + const auto PATH = getDataStatePath(); - std::filesystem::create_directories(PATH); + std::error_code ec; + if (!std::filesystem::exists(PATH, ec) || ec) + NSys::runAsSuperuser("mkdir -p -m 755 '" + PATH.string() + "'"); // clang-format off auto DATA = toml::table{ {"state", toml::table{ @@ -143,17 +146,20 @@ void DataState::updateGlobalState(const SGlobalState& state) { }; // clang-format on - std::ofstream ofs(PATH / "state.toml", std::ios::trunc); - ofs << DATA; - ofs.close(); + std::stringstream ss; + ss << DATA; + + NSys::runAsSuperuser("cat << EOF > " + (PATH / "state.toml").string() + "\n" + ss.str() + "\nEOF"); + NSys::runAsSuperuser("chmod 644 '" + (PATH / "state.toml").string() + "'"); } SGlobalState DataState::getGlobalState() { ensureStateStoreExists(); - const auto stateFile = getDataStatePath() / "state.toml"; + const auto stateFile = getDataStatePath() / "state.toml"; - if (!std::filesystem::exists(stateFile)) + std::error_code ec; + if (!std::filesystem::exists(stateFile, ec) || ec) return SGlobalState{}; auto DATA = toml::parse_file(stateFile.c_str()); @@ -168,8 +174,6 @@ SGlobalState DataState::getGlobalState() { std::vector DataState::getAllRepositories() { ensureStateStoreExists(); - const auto PATH = getDataStatePath(); - std::vector repos; for (const auto& stateFile : getPluginStates()) { const auto STATE = toml::parse_file(stateFile.c_str()); @@ -205,8 +209,6 @@ std::vector DataState::getAllRepositories() { bool DataState::setPluginEnabled(const std::string& name, bool enabled) { ensureStateStoreExists(); - const auto PATH = getDataStatePath(); - for (const auto& stateFile : getPluginStates()) { const auto STATE = toml::parse_file(stateFile.c_str()); for (const auto& [key, val] : STATE) { @@ -224,9 +226,11 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) { auto modifiedState = STATE; (*modifiedState[key].as_table()).insert_or_assign("enabled", enabled); - std::ofstream state(stateFile, std::ios::trunc); - state << modifiedState; - state.close(); + std::stringstream ss; + ss << modifiedState; + + NSys::runAsSuperuser("cat << EOF > " + stateFile.string() + "\n" + ss.str() + "\nEOF"); + NSys::runAsSuperuser("chmod 644 '" + stateFile.string() + "'"); return true; } @@ -234,3 +238,17 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) { return false; } + +void DataState::purgeAllCache() { + std::error_code ec; + if (!std::filesystem::exists(getDataStatePath()) && !ec) { + std::println("{}", infoString("Nothing to do")); + return; + } + + const auto PATH = getDataStatePath().string(); + if (PATH.contains('\'')) + return; + // scary! + NSys::runAsSuperuser("rm -r '" + PATH + "'"); +} diff --git a/hyprpm/src/core/DataState.hpp b/hyprpm/src/core/DataState.hpp index ebb550ba9..c35ded064 100644 --- a/hyprpm/src/core/DataState.hpp +++ b/hyprpm/src/core/DataState.hpp @@ -18,6 +18,7 @@ namespace DataState { void removePluginRepo(const std::string& urlOrName); bool pluginRepoExists(const std::string& urlOrName); void updateGlobalState(const SGlobalState& state); + void purgeAllCache(); SGlobalState getGlobalState(); bool setPluginEnabled(const std::string& name, bool enabled); std::vector getAllRepositories(); diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index ee7d8e8fd..33e565d35 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -5,6 +5,8 @@ #include "Manifest.hpp" #include "DataState.hpp" #include "HyprlandSocket.hpp" +#include "../helpers/Sys.hpp" +#include "../helpers/Die.hpp" #include #include @@ -50,6 +52,13 @@ static std::string getTempRoot() { return STR; } +CPluginManager::CPluginManager() { + if (NSys::isSuperuser()) + Debug::die("Don't run hyprpm as a superuser."); + + m_szUsername = getpwuid(NSys::getUID())->pw_name; +} + SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) { static bool onceRunning = false; static bool onceInstalled = false; @@ -275,6 +284,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& if (HEADERSSTATUS != HEADERS_OK) { std::println("\n{}", headerError(HEADERSSTATUS)); + std::println("\n{}", infoString("if the problem persists, try running hyprpm purge-cache.")); return false; } @@ -550,13 +560,20 @@ bool CPluginManager::updateHeaders(bool force) { progress.m_szCurrentMessage = "Installing sources"; progress.print(); - const std::string& cmd = - std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR); + std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile", DataState::getHeadersPath(), WORKINGDIR); if (m_bVerbose) - progress.printMessageAbove(verboseString("installation will run: {}", cmd)); + progress.printMessageAbove(verboseString("prepare install will run: {}", cmd)); ret = execAndGet(cmd); + cmd = std::format("cd {} && make installheaders && chmod -R 644 {} && find {} -type d -exec chmod o+x {{}} \\;", WORKINGDIR, DataState::getHeadersPath(), + DataState::getHeadersPath()); + + if (m_bVerbose) + progress.printMessageAbove(verboseString("install will run as sudo: {}", cmd)); + + ret = NSys::runAsSuperuser(cmd); + if (m_bVerbose) std::println("{}", verboseString("installer returned: {}", ret)); @@ -570,9 +587,14 @@ bool CPluginManager::updateHeaders(bool force) { progress.m_szCurrentMessage = "Done!"; progress.print(); + auto GLOBALSTATE = DataState::getGlobalState(); + GLOBALSTATE.headersHashCompiled = HLVER.hash; + DataState::updateGlobalState(GLOBALSTATE); + std::print("\n"); } else { progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID))); + progress.printMessageAbove(infoString("if the problem persists, try running hyprpm purge-cache.")); progress.m_iSteps = 5; progress.m_szCurrentMessage = "Failed"; progress.print(); @@ -884,7 +906,8 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) { auto HLVER = getHyprlandVersion(true); if (state.headersHashCompiled != HLVER.hash) { - std::println("{}", infoString("Running Hyprland version differs from plugin state, please restart Hyprland.")); + if (load) + std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersHashCompiled)); return false; } diff --git a/hyprpm/src/core/PluginManager.hpp b/hyprpm/src/core/PluginManager.hpp index b159b9210..7640d7c89 100644 --- a/hyprpm/src/core/PluginManager.hpp +++ b/hyprpm/src/core/PluginManager.hpp @@ -40,6 +40,8 @@ struct SHyprlandVersion { class CPluginManager { public: + CPluginManager(); + bool addNewPluginRepo(const std::string& url, const std::string& rev); bool removePluginRepo(const std::string& urlOrName); @@ -62,7 +64,7 @@ class CPluginManager { bool m_bVerbose = false; bool m_bNoShallow = false; - std::string m_szCustomHlUrl; + std::string m_szCustomHlUrl, m_szUsername; // will delete recursively if exists!! bool createSafeDirectory(const std::string& path); diff --git a/hyprpm/src/helpers/Die.hpp b/hyprpm/src/helpers/Die.hpp new file mode 100644 index 000000000..0c00bcf35 --- /dev/null +++ b/hyprpm/src/helpers/Die.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +// NOLINTNEXTLINE +namespace Debug { + template + void die(std::format_string fmt, Args&&... args) { + const std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...)); + + std::cout << "\n[ERR] " << logMsg << "\n"; + exit(1); + } +}; \ No newline at end of file diff --git a/hyprpm/src/helpers/Sys.cpp b/hyprpm/src/helpers/Sys.cpp new file mode 100644 index 000000000..3c6501eea --- /dev/null +++ b/hyprpm/src/helpers/Sys.cpp @@ -0,0 +1,110 @@ +#include "Sys.hpp" +#include "Die.hpp" +#include "StringUtils.hpp" +#include +#include +#include +#include + +#include +#include +using namespace Hyprutils::OS; +using namespace Hyprutils::String; + +static const std::vector SUPERUSER_BINARIES = { + "sudo", + "doas", + "run0", +}; + +static bool executableExistsInPath(const std::string& exe) { + if (!getenv("PATH")) + return false; + + static CVarList paths(getenv("PATH"), 0, ':', true); + + for (auto& p : paths) { + std::string path = p + std::string{"/"} + exe; + std::error_code ec; + if (!std::filesystem::exists(path, ec) || ec) + continue; + + if (!std::filesystem::is_regular_file(path, ec) || ec) + continue; + + auto stat = std::filesystem::status(path, ec); + if (ec) + continue; + + auto perms = stat.permissions(); + + return std::filesystem::perms::none != (perms & std::filesystem::perms::others_exec); + } + + return false; +} + +static std::pair execAndGet(std::string cmd, bool noRedirect = false) { + if (!noRedirect) + cmd += " 2>&1"; + + CProcess proc("/bin/sh", {"-c", cmd}); + + if (!proc.runSync()) + return {"error", 1}; + + return {proc.stdOut(), proc.exitCode()}; +} + +int NSys::getUID() { + const auto UID = getuid(); + const auto PWUID = getpwuid(UID); + return PWUID ? PWUID->pw_uid : UID; +} + +int NSys::getEUID() { + const auto UID = geteuid(); + const auto PWUID = getpwuid(UID); + return PWUID ? PWUID->pw_uid : UID; +} + +bool NSys::isSuperuser() { + return getuid() != geteuid() || !geteuid(); +} + +std::string NSys::runAsSuperuser(const std::string& cmd) { + for (const auto& SB : SUPERUSER_BINARIES) { + if (!executableExistsInPath(SB)) + continue; + + const auto RESULT = execAndGet(std::string{SB} + " /bin/sh -c \"" + cmd + "\"", true); + + if (RESULT.second != 0) + Debug::die("Failed to run a command as sudo. This could be due to an invalid password, or a hyprpm bug."); + + return RESULT.first; + } + + Debug::die("Failed to find a superuser binary. Supported: sudo, doas, run0."); + return ""; +} + +void NSys::cacheSudo() { + // "caches" the sudo so that the prompt later doesn't pop up in a weird spot + // sudo will not ask us again for a moment + runAsSuperuser("echo e > /dev/null"); +} + +void NSys::dropSudo() { + for (const auto& SB : SUPERUSER_BINARIES) { + if (!executableExistsInPath(SB)) + continue; + + if (SB == std::string_view{"sudo"}) + execAndGet("sudo -k"); + else + std::println("{}", infoString("Don't know how to drop timestamp for {}, ignoring.", SB)); + + return; + } +} diff --git a/hyprpm/src/helpers/Sys.hpp b/hyprpm/src/helpers/Sys.hpp new file mode 100644 index 000000000..2d643d401 --- /dev/null +++ b/hyprpm/src/helpers/Sys.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace NSys { + bool isSuperuser(); + int getUID(); + int getEUID(); + std::string runAsSuperuser(const std::string& cmd); + void cacheSudo(); + void dropSudo(); +}; \ No newline at end of file diff --git a/hyprpm/src/main.cpp b/hyprpm/src/main.cpp index 0d5efd1d3..6d0cda400 100644 --- a/hyprpm/src/main.cpp +++ b/hyprpm/src/main.cpp @@ -2,34 +2,37 @@ #include "helpers/StringUtils.hpp" #include "core/PluginManager.hpp" #include "core/DataState.hpp" +#include "helpers/Sys.hpp" #include #include #include #include -#include -#include + +#include +using namespace Hyprutils::Utils; constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager ┃ -┣ add [url] [git rev] → Install a new plugin repository from git. Git revision +┣ add [url] [git rev] → Install a new plugin repository from git. Git revision. ┃ is optional, when set, commit locks are ignored. -┣ remove [url/name] → Remove an installed plugin repository -┣ enable [name] → Enable a plugin -┣ disable [name] → Disable a plugin -┣ update → Check and update all plugins if needed +┣ remove [url/name] → Remove an installed plugin repository. +┣ enable [name] → Enable a plugin. +┣ disable [name] → Disable a plugin. +┣ update → Check and update all plugins if needed. ┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded. -┣ list → List all installed plugins +┣ list → List all installed plugins. +┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers. ┃ ┣ Flags: ┃ -┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events) -┣ --notify-fail | -nn → Send a hyprland notification for fail events only -┣ --help | -h → Show this menu -┣ --verbose | -v → Enable too much logging -┣ --force | -f → Force an operation ignoring checks (e.g. update -f) -┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources -┣ --hl-url | → Pass a custom hyprland source url +┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events). +┣ --notify-fail | -nn → Send a hyprland notification for fail events only. +┣ --help | -h → Show this menu. +┣ --verbose | -v → Enable too much logging. +┣ --force | -f → Force an operation ignoring checks (e.g. update -f). +┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources. +┣ --hl-url | → Pass a custom hyprland source url. ┗ )#"; @@ -96,9 +99,11 @@ int main(int argc, char** argv, char** envp) { } std::string rev = ""; - if (command.size() >= 3) { + if (command.size() >= 3) rev = command[2]; - } + + NSys::cacheSudo(); + CScopeGuard x([] { NSys::dropSudo(); }); return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1; } else if (command[0] == "remove") { @@ -107,10 +112,17 @@ int main(int argc, char** argv, char** envp) { return 1; } + NSys::cacheSudo(); + CScopeGuard x([] { NSys::dropSudo(); }); + return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1; } else if (command[0] == "update") { - bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK; - bool headers = g_pPluginManager->updateHeaders(force); + NSys::cacheSudo(); + CScopeGuard x([] { NSys::dropSudo(); }); + + bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK; + bool headers = g_pPluginManager->updateHeaders(force); + if (headers) { const auto HLVER = g_pPluginManager->getHyprlandVersion(false); auto GLOBALSTATE = DataState::getGlobalState(); @@ -141,7 +153,10 @@ int main(int argc, char** argv, char** envp) { return 1; } - auto ret = g_pPluginManager->ensurePluginsLoadState(); + NSys::cacheSudo(); + CScopeGuard x([] { NSys::dropSudo(); }); + + auto ret = g_pPluginManager->ensurePluginsLoadState(); if (ret == LOADSTATE_HYPRLAND_UPDATED) g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Enabled plugin, but Hyprland was updated. Please restart Hyprland."); @@ -159,7 +174,11 @@ int main(int argc, char** argv, char** envp) { return 1; } - auto ret = g_pPluginManager->ensurePluginsLoadState(); + NSys::cacheSudo(); + CScopeGuard x([] { NSys::dropSudo(); }); + + auto ret = g_pPluginManager->ensurePluginsLoadState(); + if (ret != LOADSTATE_OK) return 1; } else if (command[0] == "reload") { @@ -181,6 +200,10 @@ int main(int argc, char** argv, char** envp) { } else if (notify && !notifyFail) { g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins"); } + } else if (command[0] == "purge-cache") { + NSys::cacheSudo(); + CScopeGuard x([] { NSys::dropSudo(); }); + DataState::purgeAllCache(); } else if (command[0] == "list") { g_pPluginManager->listAllPlugins(); } else { diff --git a/meson.build b/meson.build index 66ef15396..a482cbf2d 100644 --- a/meson.build +++ b/meson.build @@ -35,7 +35,7 @@ aquamarine = dependency('aquamarine', version: '>=0.8.0') hyprcursor = dependency('hyprcursor', version: '>=0.1.7') hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1') hyprlang = dependency('hyprlang', version: '>= 0.3.2') -hyprutils = dependency('hyprutils', version: '>= 0.6.0') +hyprutils = dependency('hyprutils', version: '>= 0.7.0') aquamarine_version_list = aquamarine.version().split('.') add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp') add_project_arguments(['-DAQUAMARINE_VERSION_MAJOR=@0@'.format(aquamarine_version_list.get(0))], language: 'cpp')