This commit is contained in:
Mars 2025-04-28 04:14:13 -04:00
parent 24b6a72614
commit 1a2fba7fb8
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
29 changed files with 1676 additions and 1401 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View 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