hyprpm: move to system directories for storing plugins (#10211)

This commit is contained in:
Vaxry 2025-05-01 18:00:26 +02:00 committed by GitHub
parent b5ef049ea1
commit 858c0e26d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 278 additions and 74 deletions

View file

@ -1,22 +1,15 @@
#include "DataState.hpp"
#include <sys/stat.h>
#include <toml++/toml.hpp>
#include <print>
#include <fstream>
#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<std::filesystem::path> 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<SPluginRepository> DataState::getAllRepositories() {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
std::vector<SPluginRepository> repos;
for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str());
@ -205,8 +209,6 @@ std::vector<SPluginRepository> 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 + "'");
}

View file

@ -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<SPluginRepository> getAllRepositories();

View file

@ -5,6 +5,8 @@
#include "Manifest.hpp"
#include "DataState.hpp"
#include "HyprlandSocket.hpp"
#include "../helpers/Sys.hpp"
#include "../helpers/Die.hpp"
#include <cstdio>
#include <iostream>
@ -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;
}

View file

@ -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);