mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-05-12 23:00:36 +01:00

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
On BSDs "root" is in "wheel" group. Instead of enumerating platforms or probing "wheel" explicitly use numeric value for the superuser. $ truss hyprpm add <url> [...] read(5,"install: unknown group root\n",1023) = 28 (0x1c) [...] [ERR] ✖ Failed to write plugin state
168 lines
4.4 KiB
C++
168 lines
4.4 KiB
C++
#include "Sys.hpp"
|
|
#include "Die.hpp"
|
|
#include "StringUtils.hpp"
|
|
|
|
#include <pwd.h>
|
|
#include <unistd.h>
|
|
#include <sstream>
|
|
#include <print>
|
|
#include <filesystem>
|
|
#include <algorithm>
|
|
|
|
#include <hyprutils/os/Process.hpp>
|
|
#include <hyprutils/string/VarList.hpp>
|
|
|
|
using namespace Hyprutils::OS;
|
|
using namespace Hyprutils::String;
|
|
|
|
inline constexpr std::array<std::string_view, 3> SUPERUSER_BINARIES = {
|
|
"sudo",
|
|
"doas",
|
|
"run0",
|
|
};
|
|
|
|
static std::string validSubinsAsStr() {
|
|
std::ostringstream oss;
|
|
auto it = SUPERUSER_BINARIES.begin();
|
|
if (it != SUPERUSER_BINARIES.end()) {
|
|
oss << *it++;
|
|
for (; it != SUPERUSER_BINARIES.end(); ++it)
|
|
oss << ", " << *it;
|
|
}
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
static bool executableExistsInPath(const std::string& exe) {
|
|
const char* PATHENV = std::getenv("PATH");
|
|
if (!PATHENV)
|
|
return false;
|
|
|
|
CVarList paths(PATHENV, 0, ':', true);
|
|
std::error_code ec;
|
|
|
|
for (const auto& PATH : paths) {
|
|
std::filesystem::path candidate = std::filesystem::path(PATH) / exe;
|
|
if (!std::filesystem::exists(candidate, ec) || ec)
|
|
continue;
|
|
if (!std::filesystem::is_regular_file(candidate, ec) || ec)
|
|
continue;
|
|
auto perms = std::filesystem::status(candidate, ec).permissions();
|
|
if (ec)
|
|
continue;
|
|
if ((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static std::string subin() {
|
|
static std::string bin;
|
|
static bool once = true;
|
|
if (!once)
|
|
return bin;
|
|
|
|
for (const auto& BIN : SUPERUSER_BINARIES) {
|
|
if (!executableExistsInPath(std::string{BIN}))
|
|
continue;
|
|
|
|
bin = BIN;
|
|
break;
|
|
}
|
|
|
|
once = false;
|
|
|
|
if (bin.empty())
|
|
Debug::die("{}", failureString("No valid superuser binary present. Supported: {}", validSubinsAsStr()));
|
|
|
|
return bin;
|
|
}
|
|
|
|
static bool verifyStringValid(const std::string& s) {
|
|
return std::ranges::none_of(s, [](const char& c) { return c == '`' || c == '$' || c == '(' || c == ')' || c == '\'' || c == '"'; });
|
|
}
|
|
|
|
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() == 0;
|
|
}
|
|
|
|
void NSys::root::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
|
|
CProcess proc(subin(), {"echo", "hyprland"});
|
|
proc.runSync();
|
|
}
|
|
|
|
void NSys::root::dropSudo() {
|
|
if (subin() != "sudo") {
|
|
std::println("{}", infoString("Don't know how to drop timestamp for '{}', ignoring.", subin()));
|
|
return;
|
|
}
|
|
|
|
CProcess proc(subin(), {"-k"});
|
|
proc.runSync();
|
|
}
|
|
|
|
bool NSys::root::createDirectory(const std::string& path, const std::string& mode) {
|
|
if (!verifyStringValid(path))
|
|
return false;
|
|
|
|
if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
|
|
return false;
|
|
|
|
CProcess proc(subin(), {"mkdir", "-p", "-m", mode, path});
|
|
|
|
return proc.runSync() && proc.exitCode() == 0;
|
|
}
|
|
|
|
bool NSys::root::removeRecursive(const std::string& path) {
|
|
if (!verifyStringValid(path))
|
|
return false;
|
|
|
|
std::error_code ec;
|
|
const std::string PATH_ABSOLUTE = std::filesystem::canonical(path, ec);
|
|
|
|
if (ec)
|
|
return false;
|
|
|
|
if (!PATH_ABSOLUTE.starts_with("/var/cache/hyprpm"))
|
|
return false;
|
|
|
|
CProcess proc(subin(), {"rm", "-fr", PATH_ABSOLUTE});
|
|
|
|
return proc.runSync() && proc.exitCode() == 0;
|
|
}
|
|
|
|
bool NSys::root::install(const std::string& what, const std::string& where, const std::string& mode) {
|
|
if (!verifyStringValid(what) || !verifyStringValid(where))
|
|
return false;
|
|
|
|
if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
|
|
return false;
|
|
|
|
CProcess proc(subin(), {"install", "-m" + mode, "-o", "0", "-g", "0", what, where});
|
|
|
|
return proc.runSync() && proc.exitCode() == 0;
|
|
}
|
|
|
|
std::string NSys::root::runAsSuperuserUnsafe(const std::string& cmd) {
|
|
CProcess proc(subin(), {"/bin/sh", "-c", cmd});
|
|
|
|
if (!proc.runSync())
|
|
return "";
|
|
|
|
return proc.stdOut();
|
|
}
|