guhg
This commit is contained in:
parent
24b6a72614
commit
1a2fba7fb8
29 changed files with 1676 additions and 1401 deletions
|
@ -1,70 +0,0 @@
|
|||
#ifdef __linux__
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "display_guards.h"
|
||||
|
||||
#include "src/util/macros.h"
|
||||
|
||||
namespace os::linux {
|
||||
XorgDisplayGuard::XorgDisplayGuard(const CStr name) : m_Connection(xcb_connect(name, nullptr)) {}
|
||||
|
||||
XorgDisplayGuard::~XorgDisplayGuard() {
|
||||
if (m_Connection)
|
||||
xcb_disconnect(m_Connection);
|
||||
}
|
||||
|
||||
XorgDisplayGuard::XorgDisplayGuard(XorgDisplayGuard&& other) noexcept
|
||||
: m_Connection(std::exchange(other.m_Connection, nullptr)) {}
|
||||
|
||||
fn XorgDisplayGuard::operator=(XorgDisplayGuard&& other) noexcept -> XorgDisplayGuard& {
|
||||
if (this != &other) {
|
||||
if (m_Connection)
|
||||
xcb_disconnect(m_Connection);
|
||||
m_Connection = std::exchange(other.m_Connection, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
XorgDisplayGuard::operator bool() const { return m_Connection && !xcb_connection_has_error(m_Connection); }
|
||||
|
||||
fn XorgDisplayGuard::get() const -> xcb_connection_t* { return m_Connection; }
|
||||
|
||||
fn XorgDisplayGuard::setup() const -> const xcb_setup_t* {
|
||||
return m_Connection ? xcb_get_setup(m_Connection) : nullptr;
|
||||
}
|
||||
|
||||
fn XorgDisplayGuard::rootScreen() const -> xcb_screen_t* {
|
||||
const xcb_setup_t* setup = this->setup();
|
||||
return setup ? xcb_setup_roots_iterator(setup).data : nullptr;
|
||||
}
|
||||
|
||||
WaylandDisplayGuard::WaylandDisplayGuard() : m_Display(wl_display_connect(nullptr)) {}
|
||||
|
||||
WaylandDisplayGuard::~WaylandDisplayGuard() {
|
||||
if (m_Display)
|
||||
wl_display_disconnect(m_Display);
|
||||
}
|
||||
|
||||
WaylandDisplayGuard::WaylandDisplayGuard(WaylandDisplayGuard&& other) noexcept
|
||||
: m_Display(std::exchange(other.m_Display, nullptr)) {}
|
||||
|
||||
fn WaylandDisplayGuard::operator=(WaylandDisplayGuard&& other) noexcept -> WaylandDisplayGuard& {
|
||||
if (this != &other) {
|
||||
if (m_Display)
|
||||
wl_display_disconnect(m_Display);
|
||||
|
||||
m_Display = std::exchange(other.m_Display, nullptr);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
WaylandDisplayGuard::operator bool() const { return m_Display != nullptr; }
|
||||
|
||||
fn WaylandDisplayGuard::get() const -> wl_display* { return m_Display; }
|
||||
|
||||
fn WaylandDisplayGuard::fd() const -> i32 { return m_Display ? wl_display_get_fd(m_Display) : -1; }
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,110 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "src/util/macros.h"
|
||||
|
||||
namespace os::linux {
|
||||
/**
|
||||
* RAII wrapper for X11 Display connections
|
||||
* Automatically handles resource acquisition and cleanup
|
||||
*/
|
||||
class XorgDisplayGuard {
|
||||
xcb_connection_t* m_Connection = nullptr;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Opens an XCB connection
|
||||
* @param name Display name (nullptr for default)
|
||||
*/
|
||||
explicit XorgDisplayGuard(CStr name = nullptr);
|
||||
~XorgDisplayGuard();
|
||||
|
||||
// Non-copyable
|
||||
XorgDisplayGuard(const XorgDisplayGuard&) = delete;
|
||||
fn operator=(const XorgDisplayGuard&)->XorgDisplayGuard& = delete;
|
||||
|
||||
// Movable
|
||||
XorgDisplayGuard(XorgDisplayGuard&& other) noexcept;
|
||||
fn operator=(XorgDisplayGuard&& other) noexcept -> XorgDisplayGuard&;
|
||||
|
||||
[[nodiscard]] explicit operator bool() const;
|
||||
|
||||
[[nodiscard]] fn get() const -> xcb_connection_t*;
|
||||
[[nodiscard]] fn setup() const -> const xcb_setup_t*;
|
||||
[[nodiscard]] fn rootScreen() const -> xcb_screen_t*;
|
||||
};
|
||||
|
||||
/**
|
||||
* RAII wrapper for XCB replies
|
||||
* Handles automatic cleanup of various XCB reply objects
|
||||
*/
|
||||
template <typename T>
|
||||
class XcbReplyGuard {
|
||||
T* m_Reply = nullptr;
|
||||
|
||||
public:
|
||||
XcbReplyGuard() = default;
|
||||
explicit XcbReplyGuard(T* reply) : m_Reply(reply) {}
|
||||
|
||||
~XcbReplyGuard() {
|
||||
if (m_Reply)
|
||||
free(m_Reply);
|
||||
}
|
||||
|
||||
// Non-copyable
|
||||
XcbReplyGuard(const XcbReplyGuard&) = delete;
|
||||
fn operator=(const XcbReplyGuard&)->XcbReplyGuard& = delete;
|
||||
|
||||
// Movable
|
||||
XcbReplyGuard(XcbReplyGuard&& other) noexcept : m_Reply(std::exchange(other.m_Reply, nullptr)) {}
|
||||
fn operator=(XcbReplyGuard&& other) noexcept -> XcbReplyGuard& {
|
||||
if (this != &other) {
|
||||
if (m_Reply)
|
||||
free(m_Reply);
|
||||
|
||||
m_Reply = std::exchange(other.m_Reply, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] explicit operator bool() const { return m_Reply != nullptr; }
|
||||
|
||||
[[nodiscard]] fn get() const -> T* { return m_Reply; }
|
||||
[[nodiscard]] fn operator->() const->T* { return m_Reply; }
|
||||
[[nodiscard]] fn operator*() const->T& { return *m_Reply; }
|
||||
};
|
||||
|
||||
/**
|
||||
* RAII wrapper for Wayland display connections
|
||||
* Automatically handles resource acquisition and cleanup
|
||||
*/
|
||||
class WaylandDisplayGuard {
|
||||
wl_display* m_Display;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Opens a Wayland display connection
|
||||
*/
|
||||
WaylandDisplayGuard();
|
||||
~WaylandDisplayGuard();
|
||||
|
||||
// Non-copyable
|
||||
WaylandDisplayGuard(const WaylandDisplayGuard&) = delete;
|
||||
fn operator=(const WaylandDisplayGuard&)->WaylandDisplayGuard& = delete;
|
||||
|
||||
// Movable
|
||||
WaylandDisplayGuard(WaylandDisplayGuard&& other) noexcept;
|
||||
fn operator=(WaylandDisplayGuard&& other) noexcept -> WaylandDisplayGuard&;
|
||||
|
||||
[[nodiscard]] explicit operator bool() const;
|
||||
|
||||
[[nodiscard]] fn get() const -> wl_display*;
|
||||
[[nodiscard]] fn fd() const -> i32;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,3 +1,4 @@
|
|||
#include "src/util/macros.h"
|
||||
#include "src/core/util/defs.hpp"
|
||||
#include "src/core/util/types.hpp"
|
||||
|
||||
extern "C" fn issetugid() -> usize { return 0; } // NOLINT
|
||||
extern "C" fn issetugid() -> util::types::usize { return 0; } // NOLINT
|
||||
|
|
|
@ -1 +1,217 @@
|
|||
#include "src/os/linux/pkg_count.h"
|
||||
#include "src/os/linux/pkg_count.hpp"
|
||||
|
||||
#include <SQLiteCpp/SQLiteCpp.h>
|
||||
#include <fstream>
|
||||
#include <glaze/core/common.hpp>
|
||||
#include <glaze/core/read.hpp>
|
||||
#include <glaze/core/reflect.hpp>
|
||||
#include <glaze/json/write.hpp>
|
||||
|
||||
#include "src/core/util/logging.hpp"
|
||||
#include "src/core/util/types.hpp"
|
||||
|
||||
using util::error::DraconisError, util::error::DraconisErrorCode;
|
||||
using util::types::u64, util::types::i64, util::types::Result, util::types::Err, util::types::String,
|
||||
util::types::Exception;
|
||||
|
||||
namespace {
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::chrono;
|
||||
|
||||
struct NixPkgCacheData {
|
||||
u64 count {};
|
||||
system_clock::time_point timestamp;
|
||||
|
||||
// NOLINTBEGIN(readability-identifier-naming) - Needs to specifically use `glaze`
|
||||
struct [[maybe_unused]] glaze {
|
||||
using T = NixPkgCacheData;
|
||||
static constexpr auto value = glz::object("count", &T::count, "timestamp", [](auto& self) -> auto& {
|
||||
thread_local auto epoch_seconds = duration_cast<seconds>(self.timestamp.time_since_epoch()).count();
|
||||
return epoch_seconds;
|
||||
});
|
||||
};
|
||||
// NOLINTEND(readability-identifier-naming)
|
||||
};
|
||||
|
||||
fn GetPkgCountCachePath() -> Result<fs::path, DraconisError> {
|
||||
std::error_code errc;
|
||||
const fs::path cacheDir = fs::temp_directory_path(errc);
|
||||
if (errc) {
|
||||
return Err(DraconisError(DraconisErrorCode::IoError, "Failed to get temp directory: " + errc.message()));
|
||||
}
|
||||
return cacheDir / "nix_pkg_count_cache.json";
|
||||
}
|
||||
|
||||
fn ReadPkgCountCache() -> Result<NixPkgCacheData, DraconisError> {
|
||||
auto cachePathResult = GetPkgCountCachePath();
|
||||
if (!cachePathResult) {
|
||||
return Err(cachePathResult.error());
|
||||
}
|
||||
const fs::path& cachePath = *cachePathResult;
|
||||
|
||||
if (!fs::exists(cachePath)) {
|
||||
return Err(DraconisError(DraconisErrorCode::NotFound, "Cache file not found: " + cachePath.string()));
|
||||
}
|
||||
|
||||
std::ifstream ifs(cachePath, std::ios::binary);
|
||||
if (!ifs.is_open()) {
|
||||
return Err(
|
||||
DraconisError(DraconisErrorCode::IoError, "Failed to open cache file for reading: " + cachePath.string())
|
||||
);
|
||||
}
|
||||
|
||||
debug_log("Reading Nix package count from cache file: {}", cachePath.string());
|
||||
|
||||
try {
|
||||
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
NixPkgCacheData result;
|
||||
glz::context ctx {};
|
||||
|
||||
if (auto glazeResult = glz::read<glz::opts { .error_on_unknown_keys = false }>(result, content, ctx);
|
||||
glazeResult.ec != glz::error_code::none) {
|
||||
return Err(DraconisError(
|
||||
DraconisErrorCode::ParseError,
|
||||
std::format("JSON parse error reading cache: {}", glz::format_error(glazeResult, content))
|
||||
));
|
||||
}
|
||||
|
||||
if (size_t tsPos = content.find("\"timestamp\""); tsPos != String::npos) {
|
||||
size_t colonPos = content.find(':', tsPos);
|
||||
if (size_t valueStart = content.find_first_of("0123456789", colonPos); valueStart != String::npos) {
|
||||
long long timestampSeconds = 0;
|
||||
char* endPtr = nullptr;
|
||||
timestampSeconds = std::strtoll(content.c_str() + valueStart, &endPtr, 10);
|
||||
result.timestamp = system_clock::time_point(seconds(timestampSeconds));
|
||||
} else {
|
||||
return Err(DraconisError(DraconisErrorCode::ParseError, "Could not parse timestamp value from cache JSON."));
|
||||
}
|
||||
} else {
|
||||
return Err(DraconisError(DraconisErrorCode::ParseError, "Timestamp field not found in cache JSON."));
|
||||
}
|
||||
|
||||
debug_log("Successfully read package count from cache file.");
|
||||
return result;
|
||||
} catch (const Exception& e) {
|
||||
return Err(
|
||||
DraconisError(DraconisErrorCode::InternalError, std::format("Error reading package count cache: {}", e.what()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn WritePkgCountCache(const NixPkgCacheData& data) -> Result<void, DraconisError> {
|
||||
auto cachePathResult = GetPkgCountCachePath();
|
||||
if (!cachePathResult) {
|
||||
return Err(cachePathResult.error());
|
||||
}
|
||||
const fs::path& cachePath = *cachePathResult;
|
||||
fs::path tempPath = cachePath;
|
||||
tempPath += ".tmp";
|
||||
|
||||
debug_log("Writing Nix package count to cache file: {}", cachePath.string());
|
||||
|
||||
try {
|
||||
{
|
||||
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
|
||||
if (!ofs.is_open()) {
|
||||
return Err(DraconisError(DraconisErrorCode::IoError, "Failed to open temp cache file: " + tempPath.string()));
|
||||
}
|
||||
|
||||
String jsonStr;
|
||||
|
||||
NixPkgCacheData mutableData = data;
|
||||
|
||||
if (auto glazeErr = glz::write_json(mutableData, jsonStr); glazeErr.ec != glz::error_code::none) {
|
||||
return Err(DraconisError(
|
||||
DraconisErrorCode::ParseError,
|
||||
std::format("JSON serialization error writing cache: {}", glz::format_error(glazeErr, jsonStr))
|
||||
));
|
||||
}
|
||||
|
||||
ofs << jsonStr;
|
||||
if (!ofs) {
|
||||
return Err(DraconisError(DraconisErrorCode::IoError, "Failed to write to temp cache file"));
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code errc;
|
||||
fs::rename(tempPath, cachePath, errc);
|
||||
if (errc) {
|
||||
fs::remove(tempPath);
|
||||
return Err(DraconisError(
|
||||
DraconisErrorCode::IoError,
|
||||
std::format("Failed to replace cache file '{}': {}", cachePath.string(), errc.message())
|
||||
));
|
||||
}
|
||||
|
||||
debug_log("Successfully wrote package count to cache file.");
|
||||
return {};
|
||||
} catch (const Exception& e) {
|
||||
fs::remove(tempPath);
|
||||
return Err(DraconisError(
|
||||
DraconisErrorCode::InternalError, std::format("File operation error writing package count cache: {}", e.what())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
fn os::linux::GetNixPackageCount() -> Result<u64, DraconisError> {
|
||||
const fs::path nixDbPath = "/nix/var/nix/db/db.sqlite";
|
||||
|
||||
if (Result<NixPkgCacheData, DraconisError> cachedDataResult = ReadPkgCountCache()) {
|
||||
const auto& [count, timestamp] = *cachedDataResult;
|
||||
|
||||
std::error_code errc;
|
||||
std::filesystem::file_time_type dbModTime = fs::last_write_time(nixDbPath, errc);
|
||||
|
||||
if (errc) {
|
||||
warn_log("Could not get modification time for '{}': {}. Invalidating cache.", nixDbPath.string(), errc.message());
|
||||
} else {
|
||||
if (timestamp.time_since_epoch() >= dbModTime.time_since_epoch()) {
|
||||
debug_log(
|
||||
"Using valid Nix package count cache (DB file unchanged since {}). Count: {}",
|
||||
std::format("{:%F %T %Z}", floor<seconds>(timestamp)),
|
||||
count
|
||||
);
|
||||
return count;
|
||||
}
|
||||
debug_log("Nix package count cache stale (DB file modified).");
|
||||
}
|
||||
} else {
|
||||
if (cachedDataResult.error().code != DraconisErrorCode::NotFound)
|
||||
debug_at(cachedDataResult.error());
|
||||
debug_log("Nix package count cache not found or unreadable.");
|
||||
}
|
||||
|
||||
debug_log("Fetching fresh Nix package count from database: {}", nixDbPath.string());
|
||||
u64 count = 0;
|
||||
|
||||
try {
|
||||
const SQLite::Database database("/nix/var/nix/db/db.sqlite", SQLite::OPEN_READONLY);
|
||||
|
||||
if (SQLite::Statement query(database, "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL");
|
||||
query.executeStep()) {
|
||||
const i64 countInt64 = query.getColumn(0).getInt64();
|
||||
if (countInt64 < 0)
|
||||
return Err(DraconisError(DraconisErrorCode::ParseError, "Negative count returned by Nix DB COUNT(*) query."));
|
||||
count = static_cast<u64>(countInt64);
|
||||
} else {
|
||||
return Err(DraconisError(DraconisErrorCode::ParseError, "No rows returned by Nix DB COUNT(*) query."));
|
||||
}
|
||||
} catch (const SQLite::Exception& e) {
|
||||
return Err(DraconisError(
|
||||
DraconisErrorCode::ApiUnavailable, std::format("SQLite error occurred accessing Nix DB: {}", e.what())
|
||||
));
|
||||
} catch (const Exception& e) { return Err(DraconisError(DraconisErrorCode::InternalError, e.what())); } catch (...) {
|
||||
return Err(DraconisError(DraconisErrorCode::Other, "Unknown error occurred accessing Nix DB"));
|
||||
}
|
||||
|
||||
const NixPkgCacheData dataToCache = { .count = count, .timestamp = system_clock::now() };
|
||||
if (Result<void, DraconisError> writeResult = WritePkgCountCache(dataToCache); !writeResult) {
|
||||
warn_at(writeResult.error());
|
||||
warn_log("Failed to write Nix package count to cache.");
|
||||
}
|
||||
|
||||
debug_log("Fetched fresh Nix package count: {}", count);
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "src/util/macros.h"
|
||||
|
||||
// Get package count from dpkg (Debian/Ubuntu)
|
||||
fn GetDpkgPackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from RPM (Red Hat/Fedora/CentOS)
|
||||
fn GetRpmPackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from pacman (Arch Linux)
|
||||
fn GetPacmanPackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from Portage (Gentoo)
|
||||
fn GetPortagePackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from zypper (openSUSE)
|
||||
fn GetZypperPackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from apk (Alpine)
|
||||
fn GetApkPackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from nix
|
||||
fn GetNixPackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from flatpak
|
||||
fn GetFlatpakPackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from snap
|
||||
fn GetSnapPackageCount() -> Option<usize>;
|
||||
|
||||
// Get package count from AppImage
|
||||
fn GetAppimagePackageCount() -> Option<usize>;
|
||||
|
||||
// Get total package count from all available package managers
|
||||
fn GetTotalPackageCount() -> Option<usize>;
|
43
src/os/linux/pkg_count.hpp
Normal file
43
src/os/linux/pkg_count.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "src/core/util/defs.hpp"
|
||||
#include "src/core/util/error.hpp"
|
||||
#include "src/core/util/types.hpp"
|
||||
|
||||
namespace os::linux {
|
||||
using util::error::DraconisError;
|
||||
using util::types::Result, util::types::u64;
|
||||
|
||||
// Get package count from dpkg (Debian/Ubuntu)
|
||||
fn GetDpkgPackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from RPM (Red Hat/Fedora/CentOS)
|
||||
fn GetRpmPackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from pacman (Arch Linux)
|
||||
fn GetPacmanPackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from Portage (Gentoo)
|
||||
fn GetPortagePackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from zypper (openSUSE)
|
||||
fn GetZypperPackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from apk (Alpine)
|
||||
fn GetApkPackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from nix
|
||||
fn GetNixPackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from flatpak
|
||||
fn GetFlatpakPackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from snap
|
||||
fn GetSnapPackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get package count from AppImage
|
||||
fn GetAppimagePackageCount() -> Result<u64, DraconisError>;
|
||||
|
||||
// Get total package count from all available package managers
|
||||
fn GetTotalPackageCount() -> Result<u64, DraconisError>;
|
||||
} // namespace os::linux
|
Loading…
Add table
Add a link
Reference in a new issue