mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-05-13 07:10:35 +01:00
hyprpm: move to system directories for storing plugins (#10211)
Some checks are pending
Build Hyprland / Build Hyprland (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland with Meson (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland without precompiled headers (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland in pure Wayland (Arch) (push) Waiting to run
Build Hyprland / Code Style (Arch) (push) Waiting to run
Nix (CI) / update-inputs (push) Waiting to run
Nix (CI) / build (push) Waiting to run
Security Checks / Flawfinder Checks (push) Waiting to run
Some checks are pending
Build Hyprland / Build Hyprland (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland with Meson (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland without precompiled headers (Arch) (push) Waiting to run
Build Hyprland / Build Hyprland in pure Wayland (Arch) (push) Waiting to run
Build Hyprland / Code Style (Arch) (push) Waiting to run
Nix (CI) / update-inputs (push) Waiting to run
Nix (CI) / build (push) Waiting to run
Security Checks / Flawfinder Checks (push) Waiting to run
This commit is contained in:
parent
b5ef049ea1
commit
858c0e26d1
11 changed files with 278 additions and 74 deletions
|
@ -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(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0)
|
||||||
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
|
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(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)
|
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1)
|
||||||
|
|
||||||
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
|
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
|
||||||
|
|
|
@ -9,7 +9,7 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
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)
|
find_package(glaze QUIET)
|
||||||
if (NOT glaze_FOUND)
|
if (NOT glaze_FOUND)
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
#include "DataState.hpp"
|
#include "DataState.hpp"
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <toml++/toml.hpp>
|
#include <toml++/toml.hpp>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include "PluginManager.hpp"
|
#include "PluginManager.hpp"
|
||||||
|
#include "../helpers/Die.hpp"
|
||||||
|
#include "../helpers/Sys.hpp"
|
||||||
|
#include "../helpers/StringUtils.hpp"
|
||||||
|
|
||||||
std::filesystem::path DataState::getDataStatePath() {
|
std::filesystem::path DataState::getDataStatePath() {
|
||||||
const auto HOME = getenv("HOME");
|
return std::filesystem::path("/var/cache/hyprpm/" + g_pPluginManager->m_szUsername);
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DataState::getHeadersPath() {
|
std::string DataState::getHeadersPath() {
|
||||||
|
@ -41,13 +34,15 @@ std::vector<std::filesystem::path> DataState::getPluginStates() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataState::ensureStateStoreExists() {
|
void DataState::ensureStateStoreExists() {
|
||||||
const auto PATH = getDataStatePath();
|
std::error_code ec;
|
||||||
|
if (!std::filesystem::exists(getHeadersPath(), ec) || ec) {
|
||||||
if (!std::filesystem::exists(PATH))
|
std::println("{}", infoString("The hyprpm state store doesn't exist. Creating now..."));
|
||||||
std::filesystem::create_directories(PATH);
|
if (!std::filesystem::exists("/var/cache/hyprpm/", ec) || ec)
|
||||||
|
NSys::runAsSuperuser("mkdir -p -m 755 '/var/cache/hyprpm/'");
|
||||||
if (!std::filesystem::exists(getHeadersPath()))
|
if (!std::filesystem::exists(getDataStatePath(), ec) || ec)
|
||||||
std::filesystem::create_directories(getHeadersPath());
|
NSys::runAsSuperuser("mkdir -p -m 755 '" + getDataStatePath().string() + "'");
|
||||||
|
NSys::runAsSuperuser("mkdir -p -m 755 '" + getHeadersPath() + "'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||||
|
@ -55,7 +50,9 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||||
|
|
||||||
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
|
// clang-format off
|
||||||
auto DATA = toml::table{
|
auto DATA = toml::table{
|
||||||
{"repository", toml::table{
|
{"repository", toml::table{
|
||||||
|
@ -68,9 +65,9 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||||
for (auto const& p : repo.plugins) {
|
for (auto const& p : repo.plugins) {
|
||||||
const auto filename = p.name + ".so";
|
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))
|
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{
|
DATA.emplace(p.name, toml::table{
|
||||||
{"filename", filename},
|
{"filename", filename},
|
||||||
|
@ -80,16 +77,16 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
std::ofstream ofs(PATH / "state.toml", std::ios::trunc);
|
std::stringstream ss;
|
||||||
ofs << DATA;
|
ss << DATA;
|
||||||
ofs.close();
|
|
||||||
|
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) {
|
bool DataState::pluginRepoExists(const std::string& urlOrName) {
|
||||||
ensureStateStoreExists();
|
ensureStateStoreExists();
|
||||||
|
|
||||||
const auto PATH = getDataStatePath();
|
|
||||||
|
|
||||||
for (const auto& stateFile : getPluginStates()) {
|
for (const auto& stateFile : getPluginStates()) {
|
||||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
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) {
|
void DataState::removePluginRepo(const std::string& urlOrName) {
|
||||||
ensureStateStoreExists();
|
ensureStateStoreExists();
|
||||||
|
|
||||||
const auto PATH = getDataStatePath();
|
|
||||||
|
|
||||||
for (const auto& stateFile : getPluginStates()) {
|
for (const auto& stateFile : getPluginStates()) {
|
||||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
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);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +134,9 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
||||||
|
|
||||||
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
|
// clang-format off
|
||||||
auto DATA = toml::table{
|
auto DATA = toml::table{
|
||||||
{"state", toml::table{
|
{"state", toml::table{
|
||||||
|
@ -143,9 +146,11 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
std::ofstream ofs(PATH / "state.toml", std::ios::trunc);
|
std::stringstream ss;
|
||||||
ofs << DATA;
|
ss << DATA;
|
||||||
ofs.close();
|
|
||||||
|
NSys::runAsSuperuser("cat << EOF > " + (PATH / "state.toml").string() + "\n" + ss.str() + "\nEOF");
|
||||||
|
NSys::runAsSuperuser("chmod 644 '" + (PATH / "state.toml").string() + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
SGlobalState DataState::getGlobalState() {
|
SGlobalState DataState::getGlobalState() {
|
||||||
|
@ -153,7 +158,8 @@ SGlobalState DataState::getGlobalState() {
|
||||||
|
|
||||||
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{};
|
return SGlobalState{};
|
||||||
|
|
||||||
auto DATA = toml::parse_file(stateFile.c_str());
|
auto DATA = toml::parse_file(stateFile.c_str());
|
||||||
|
@ -168,8 +174,6 @@ SGlobalState DataState::getGlobalState() {
|
||||||
std::vector<SPluginRepository> DataState::getAllRepositories() {
|
std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||||
ensureStateStoreExists();
|
ensureStateStoreExists();
|
||||||
|
|
||||||
const auto PATH = getDataStatePath();
|
|
||||||
|
|
||||||
std::vector<SPluginRepository> repos;
|
std::vector<SPluginRepository> repos;
|
||||||
for (const auto& stateFile : getPluginStates()) {
|
for (const auto& stateFile : getPluginStates()) {
|
||||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
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) {
|
bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||||
ensureStateStoreExists();
|
ensureStateStoreExists();
|
||||||
|
|
||||||
const auto PATH = getDataStatePath();
|
|
||||||
|
|
||||||
for (const auto& stateFile : getPluginStates()) {
|
for (const auto& stateFile : getPluginStates()) {
|
||||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||||
for (const auto& [key, val] : STATE) {
|
for (const auto& [key, val] : STATE) {
|
||||||
|
@ -224,9 +226,11 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||||
auto modifiedState = STATE;
|
auto modifiedState = STATE;
|
||||||
(*modifiedState[key].as_table()).insert_or_assign("enabled", enabled);
|
(*modifiedState[key].as_table()).insert_or_assign("enabled", enabled);
|
||||||
|
|
||||||
std::ofstream state(stateFile, std::ios::trunc);
|
std::stringstream ss;
|
||||||
state << modifiedState;
|
ss << modifiedState;
|
||||||
state.close();
|
|
||||||
|
NSys::runAsSuperuser("cat << EOF > " + stateFile.string() + "\n" + ss.str() + "\nEOF");
|
||||||
|
NSys::runAsSuperuser("chmod 644 '" + stateFile.string() + "'");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -234,3 +238,17 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||||
|
|
||||||
return false;
|
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 + "'");
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace DataState {
|
||||||
void removePluginRepo(const std::string& urlOrName);
|
void removePluginRepo(const std::string& urlOrName);
|
||||||
bool pluginRepoExists(const std::string& urlOrName);
|
bool pluginRepoExists(const std::string& urlOrName);
|
||||||
void updateGlobalState(const SGlobalState& state);
|
void updateGlobalState(const SGlobalState& state);
|
||||||
|
void purgeAllCache();
|
||||||
SGlobalState getGlobalState();
|
SGlobalState getGlobalState();
|
||||||
bool setPluginEnabled(const std::string& name, bool enabled);
|
bool setPluginEnabled(const std::string& name, bool enabled);
|
||||||
std::vector<SPluginRepository> getAllRepositories();
|
std::vector<SPluginRepository> getAllRepositories();
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include "Manifest.hpp"
|
#include "Manifest.hpp"
|
||||||
#include "DataState.hpp"
|
#include "DataState.hpp"
|
||||||
#include "HyprlandSocket.hpp"
|
#include "HyprlandSocket.hpp"
|
||||||
|
#include "../helpers/Sys.hpp"
|
||||||
|
#include "../helpers/Die.hpp"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -50,6 +52,13 @@ static std::string getTempRoot() {
|
||||||
return STR;
|
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) {
|
SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
|
||||||
static bool onceRunning = false;
|
static bool onceRunning = false;
|
||||||
static bool onceInstalled = false;
|
static bool onceInstalled = false;
|
||||||
|
@ -275,6 +284,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||||
|
|
||||||
if (HEADERSSTATUS != HEADERS_OK) {
|
if (HEADERSSTATUS != HEADERS_OK) {
|
||||||
std::println("\n{}", headerError(HEADERSSTATUS));
|
std::println("\n{}", headerError(HEADERSSTATUS));
|
||||||
|
std::println("\n{}", infoString("if the problem persists, try running hyprpm purge-cache."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,13 +560,20 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||||
progress.m_szCurrentMessage = "Installing sources";
|
progress.m_szCurrentMessage = "Installing sources";
|
||||||
progress.print();
|
progress.print();
|
||||||
|
|
||||||
const std::string& cmd =
|
std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile", DataState::getHeadersPath(), WORKINGDIR);
|
||||||
std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR);
|
|
||||||
if (m_bVerbose)
|
if (m_bVerbose)
|
||||||
progress.printMessageAbove(verboseString("installation will run: {}", cmd));
|
progress.printMessageAbove(verboseString("prepare install will run: {}", cmd));
|
||||||
|
|
||||||
ret = execAndGet(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)
|
if (m_bVerbose)
|
||||||
std::println("{}", verboseString("installer returned: {}", ret));
|
std::println("{}", verboseString("installer returned: {}", ret));
|
||||||
|
|
||||||
|
@ -570,9 +587,14 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||||
progress.m_szCurrentMessage = "Done!";
|
progress.m_szCurrentMessage = "Done!";
|
||||||
progress.print();
|
progress.print();
|
||||||
|
|
||||||
|
auto GLOBALSTATE = DataState::getGlobalState();
|
||||||
|
GLOBALSTATE.headersHashCompiled = HLVER.hash;
|
||||||
|
DataState::updateGlobalState(GLOBALSTATE);
|
||||||
|
|
||||||
std::print("\n");
|
std::print("\n");
|
||||||
} else {
|
} else {
|
||||||
progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID)));
|
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_iSteps = 5;
|
||||||
progress.m_szCurrentMessage = "Failed";
|
progress.m_szCurrentMessage = "Failed";
|
||||||
progress.print();
|
progress.print();
|
||||||
|
@ -884,7 +906,8 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
|
||||||
auto HLVER = getHyprlandVersion(true);
|
auto HLVER = getHyprlandVersion(true);
|
||||||
|
|
||||||
if (state.headersHashCompiled != HLVER.hash) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@ struct SHyprlandVersion {
|
||||||
|
|
||||||
class CPluginManager {
|
class CPluginManager {
|
||||||
public:
|
public:
|
||||||
|
CPluginManager();
|
||||||
|
|
||||||
bool addNewPluginRepo(const std::string& url, const std::string& rev);
|
bool addNewPluginRepo(const std::string& url, const std::string& rev);
|
||||||
bool removePluginRepo(const std::string& urlOrName);
|
bool removePluginRepo(const std::string& urlOrName);
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ class CPluginManager {
|
||||||
|
|
||||||
bool m_bVerbose = false;
|
bool m_bVerbose = false;
|
||||||
bool m_bNoShallow = false;
|
bool m_bNoShallow = false;
|
||||||
std::string m_szCustomHlUrl;
|
std::string m_szCustomHlUrl, m_szUsername;
|
||||||
|
|
||||||
// will delete recursively if exists!!
|
// will delete recursively if exists!!
|
||||||
bool createSafeDirectory(const std::string& path);
|
bool createSafeDirectory(const std::string& path);
|
||||||
|
|
15
hyprpm/src/helpers/Die.hpp
Normal file
15
hyprpm/src/helpers/Die.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE
|
||||||
|
namespace Debug {
|
||||||
|
template <typename... Args>
|
||||||
|
void die(std::format_string<Args...> fmt, Args&&... args) {
|
||||||
|
const std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||||
|
|
||||||
|
std::cout << "\n[ERR] " << logMsg << "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
110
hyprpm/src/helpers/Sys.cpp
Normal file
110
hyprpm/src/helpers/Sys.cpp
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#include "Sys.hpp"
|
||||||
|
#include "Die.hpp"
|
||||||
|
#include "StringUtils.hpp"
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <print>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <hyprutils/os/Process.hpp>
|
||||||
|
#include <hyprutils/string/VarList.hpp>
|
||||||
|
using namespace Hyprutils::OS;
|
||||||
|
using namespace Hyprutils::String;
|
||||||
|
|
||||||
|
static const std::vector<const char*> 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<std::string, int> 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;
|
||||||
|
}
|
||||||
|
}
|
12
hyprpm/src/helpers/Sys.hpp
Normal file
12
hyprpm/src/helpers/Sys.hpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace NSys {
|
||||||
|
bool isSuperuser();
|
||||||
|
int getUID();
|
||||||
|
int getEUID();
|
||||||
|
std::string runAsSuperuser(const std::string& cmd);
|
||||||
|
void cacheSudo();
|
||||||
|
void dropSudo();
|
||||||
|
};
|
|
@ -2,34 +2,37 @@
|
||||||
#include "helpers/StringUtils.hpp"
|
#include "helpers/StringUtils.hpp"
|
||||||
#include "core/PluginManager.hpp"
|
#include "core/PluginManager.hpp"
|
||||||
#include "core/DataState.hpp"
|
#include "core/DataState.hpp"
|
||||||
|
#include "helpers/Sys.hpp"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||||
|
using namespace Hyprutils::Utils;
|
||||||
|
|
||||||
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
|
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.
|
┃ is optional, when set, commit locks are ignored.
|
||||||
┣ remove [url/name] → Remove an installed plugin repository
|
┣ remove [url/name] → Remove an installed plugin repository.
|
||||||
┣ enable [name] → Enable a plugin
|
┣ enable [name] → Enable a plugin.
|
||||||
┣ disable [name] → Disable a plugin
|
┣ disable [name] → Disable a plugin.
|
||||||
┣ update → Check and update all plugins if needed
|
┣ update → Check and update all plugins if needed.
|
||||||
┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded.
|
┣ 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:
|
┣ Flags:
|
||||||
┃
|
┃
|
||||||
┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events)
|
┣ --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
|
┣ --notify-fail | -nn → Send a hyprland notification for fail events only.
|
||||||
┣ --help | -h → Show this menu
|
┣ --help | -h → Show this menu.
|
||||||
┣ --verbose | -v → Enable too much logging
|
┣ --verbose | -v → Enable too much logging.
|
||||||
┣ --force | -f → Force an operation ignoring checks (e.g. update -f)
|
┣ --force | -f → Force an operation ignoring checks (e.g. update -f).
|
||||||
┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources
|
┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources.
|
||||||
┣ --hl-url | → Pass a custom hyprland source url
|
┣ --hl-url | → Pass a custom hyprland source url.
|
||||||
┗
|
┗
|
||||||
)#";
|
)#";
|
||||||
|
|
||||||
|
@ -96,9 +99,11 @@ int main(int argc, char** argv, char** envp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string rev = "";
|
std::string rev = "";
|
||||||
if (command.size() >= 3) {
|
if (command.size() >= 3)
|
||||||
rev = command[2];
|
rev = command[2];
|
||||||
}
|
|
||||||
|
NSys::cacheSudo();
|
||||||
|
CScopeGuard x([] { NSys::dropSudo(); });
|
||||||
|
|
||||||
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
|
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
|
||||||
} else if (command[0] == "remove") {
|
} else if (command[0] == "remove") {
|
||||||
|
@ -107,10 +112,17 @@ int main(int argc, char** argv, char** envp) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSys::cacheSudo();
|
||||||
|
CScopeGuard x([] { NSys::dropSudo(); });
|
||||||
|
|
||||||
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
|
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
|
||||||
} else if (command[0] == "update") {
|
} else if (command[0] == "update") {
|
||||||
|
NSys::cacheSudo();
|
||||||
|
CScopeGuard x([] { NSys::dropSudo(); });
|
||||||
|
|
||||||
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
|
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
|
||||||
bool headers = g_pPluginManager->updateHeaders(force);
|
bool headers = g_pPluginManager->updateHeaders(force);
|
||||||
|
|
||||||
if (headers) {
|
if (headers) {
|
||||||
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
|
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
|
||||||
auto GLOBALSTATE = DataState::getGlobalState();
|
auto GLOBALSTATE = DataState::getGlobalState();
|
||||||
|
@ -141,6 +153,9 @@ int main(int argc, char** argv, char** envp) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSys::cacheSudo();
|
||||||
|
CScopeGuard x([] { NSys::dropSudo(); });
|
||||||
|
|
||||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||||
|
|
||||||
if (ret == LOADSTATE_HYPRLAND_UPDATED)
|
if (ret == LOADSTATE_HYPRLAND_UPDATED)
|
||||||
|
@ -159,7 +174,11 @@ int main(int argc, char** argv, char** envp) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSys::cacheSudo();
|
||||||
|
CScopeGuard x([] { NSys::dropSudo(); });
|
||||||
|
|
||||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||||
|
|
||||||
if (ret != LOADSTATE_OK)
|
if (ret != LOADSTATE_OK)
|
||||||
return 1;
|
return 1;
|
||||||
} else if (command[0] == "reload") {
|
} else if (command[0] == "reload") {
|
||||||
|
@ -181,6 +200,10 @@ int main(int argc, char** argv, char** envp) {
|
||||||
} else if (notify && !notifyFail) {
|
} else if (notify && !notifyFail) {
|
||||||
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
|
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") {
|
} else if (command[0] == "list") {
|
||||||
g_pPluginManager->listAllPlugins();
|
g_pPluginManager->listAllPlugins();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -35,7 +35,7 @@ aquamarine = dependency('aquamarine', version: '>=0.8.0')
|
||||||
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
|
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
|
||||||
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
|
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
|
||||||
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
|
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('.')
|
aquamarine_version_list = aquamarine.version().split('.')
|
||||||
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
|
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')
|
add_project_arguments(['-DAQUAMARINE_VERSION_MAJOR=@0@'.format(aquamarine_version_list.get(0))], language: 'cpp')
|
||||||
|
|
Loading…
Reference in a new issue