This commit is contained in:
Mars 2025-05-04 00:09:07 -04:00
parent 6ce7ab2b83
commit 7d5d7b488a
5 changed files with 127 additions and 107 deletions

View file

@ -88,15 +88,15 @@ add_project_arguments(common_cpp_args, language : 'cpp')
# ------- #
# Files #
# ------- #
base_sources = files('src/core/system_data.cpp', 'src/os/shared.cpp', 'src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp')
base_sources = files('src/core/system_data.cpp', 'src/config/config.cpp', 'src/config/weather.cpp', 'src/main.cpp')
platform_sources = {
'linux' : ['src/os/linux.cpp', 'src/os/linux/pkg_count.cpp'],
'freebsd' : ['src/os/bsd.cpp'],
'netbsd' : ['src/os/bsd.cpp'],
'dragonfly' : ['src/os/bsd.cpp'],
'haiku' : ['src/os/haiku.cpp'],
'darwin' : ['src/os/macos.cpp', 'src/os/macos/bridge.mm'],
'dragonfly' : ['src/os/bsd.cpp', 'src/os/bsd/pkg_count.cpp'],
'freebsd' : ['src/os/bsd.cpp', 'src/os/bsd/pkg_count.cpp'],
'haiku' : ['src/os/haiku.cpp'],
'linux' : ['src/os/linux.cpp', 'src/os/linux/pkg_count.cpp'],
'netbsd' : ['src/os/bsd.cpp', 'src/os/bsd/pkg_count.cpp'],
'serenity' : ['src/os/serenity.cpp'],
'windows' : ['src/os/windows.cpp'],
}

View file

@ -483,8 +483,6 @@ namespace os {
.total_bytes = stat.f_blocks * stat.f_frsize,
};
}
fn GetPackageCount() -> Result<u64, DracError> { return shared::GetPackageCount(); }
} // namespace os
#endif // __FreeBSD__ || __DragonFly__ || __NetBSD__

View file

@ -1,65 +1,93 @@
#ifndef __serenity__
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
// clang-format off
#ifndef _WIN32
#include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY}
#include <SQLiteCpp/Exception.h> // SQLite::Exception
#include <SQLiteCpp/Statement.h> // SQLite::Statement
#endif
#include <chrono> // std::chrono
#include <filesystem> // std::filesystem
#include <format> // std::format
#include <fstream> // std::{ifstream, ofstream}
#include <glaze/beve/write.hpp> // glz::write_beve
#include <glaze/core/common.hpp> // glz::object
#include <glaze/core/meta.hpp> // glz::detail::Object
#include <iterator> // std::istreambuf_iterator
#include <system_error> // std::error_code
#include "src/os/bsd/pkg_count.hpp"
#include "src/os/os.hpp"
#include "src/util/cache.hpp"
#include "src/util/defs.hpp"
#include "src/util/error.hpp"
#include "src/util/helpers.hpp"
#include "src/util/logging.hpp"
#include "src/util/types.hpp"
#include "os.hpp"
// clang-format on
using util::error::DracError, util::error::DracErrorCode;
using util::types::u64, util::types::i64, util::types::String, util::types::StringView, util::types::Result,
util::types::Err, util::types::Exception;
namespace fs = std::filesystem;
using util::types::u64, util::types::i64, util::types::Result, util::types::Err, util::types::String,
util::types::StringView, util::types::Exception;
namespace {
using namespace std::chrono;
using namespace util::cache;
#ifndef _WIN32
struct PackageManagerInfo {
String id;
fs::path dbPath;
String countQuery;
};
#endif
using os::bsd::PackageManagerInfo, os::bsd::PkgCountCacheData;
struct PkgCountCacheData {
u64 count {};
i64 timestampEpochSeconds {};
#ifdef __NetBSD__
fn GetPackageCountInternalDir(const String& pmId, const fs::path& dirPath) -> Result<u64, DracError> {
debug_log("Attempting to get {} package count.", pmId);
// NOLINTBEGIN(readability-identifier-naming)
struct [[maybe_unused]] glaze {
using T = PkgCountCacheData;
std::error_code errc;
if (!fs::exists(dirPath, errc)) {
if (errc)
return Err(DracError(
DracErrorCode::IoError, std::format("Filesystem error checking {} directory: {}", pmId, errc.message())
));
static constexpr glz::detail::Object value =
glz::object("count", &T::count, "timestamp", &T::timestampEpochSeconds);
};
// NOLINTEND(readability-identifier-naming)
};
return Err(
DracError(DracErrorCode::ApiUnavailable, std::format("{} directory not found: {}", pmId, dirPath.string()))
);
}
#ifndef _WIN32
if (!fs::is_directory(dirPath, errc)) {
if (errc)
return Err(DracError(
DracErrorCode::IoError, std::format("Filesystem error checking {} path type: {}", pmId, errc.message())
));
warn_log("Expected {} directory at '{}', but it's not a directory.", pmId, dirPath.string());
return Err(
DracError(DracErrorCode::IoError, std::format("{} path is not a directory: {}", pmId, dirPath.string()))
);
}
u64 count = 0;
try {
const fs::directory_iterator dirIter(dirPath, fs::directory_options::skip_permission_denied, errc);
if (errc)
return Err(
DracError(DracErrorCode::IoError, std::format("Failed to iterate {} directory: {}", pmId, errc.message()))
);
for (const fs::directory_entry& entry : dirIter) { count++; }
} catch (const fs::filesystem_error& e) {
return Err(DracError(
DracErrorCode::IoError,
std::format("Filesystem error iterating {} directory '{}': {}", pmId, dirPath.string(), e.what())
));
} catch (...) {
return Err(DracError(
DracErrorCode::Other, std::format("Unknown error iterating {} directory '{}'", pmId, dirPath.string())
));
}
if (count > 0)
count--;
return count;
}
#else
fn GetPackageCountInternalDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError> {
const auto& [pmId, dbPath, countQuery] = pmInfo;
@ -122,73 +150,31 @@ namespace {
return count;
}
#endif
} // namespace
#ifndef _WIN32
fn GetNixPackageCount() -> Result<u64, DracError> {
debug_log("Attempting to get Nix package count.");
const PackageManagerInfo nixInfo = {
.id = "nix",
.dbPath = "/nix/var/nix/db/db.sqlite",
.countQuery = "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL",
namespace os {
fn GetPackageCount() -> Result<u64, DracError> {
#ifdef __NetBSD__
return GetPackageCountInternalDir("PkgSrc", fs::current_path().root_path() / "usr" / "pkg" / "pkgdb");
#else
const PackageManagerInfo pkgInfo = {
.id = "pkg_count",
.dbPath = "/var/db/pkg/local.sqlite",
.countQuery = "SELECT COUNT(*) FROM packages",
};
if (std::error_code errc; !fs::exists(nixInfo.dbPath, errc)) {
if (std::error_code errc; !fs::exists(pkgInfo.dbPath, errc)) {
if (errc) {
warn_log("Filesystem error checking for Nix DB at '{}': {}", nixInfo.dbPath.string(), errc.message());
warn_log("Filesystem error checking for pkg DB at '{}': {}", pkgInfo.dbPath.string(), errc.message());
return Err(DracError(DracErrorCode::IoError, "Filesystem error checking Nix DB: " + errc.message()));
}
return Err(DracError(DracErrorCode::ApiUnavailable, "Nix db not found: " + nixInfo.dbPath.string()));
return Err(DracError(DracErrorCode::ApiUnavailable, "pkg db not found: " + pkgInfo.dbPath.string()));
}
return GetPackageCountInternalDb(nixInfo);
}
return GetPackageCountInternalDb(pkgInfo);
#endif
fn GetCargoPackageCount() -> Result<u64, DracError> {
using util::helpers::GetEnv;
fs::path cargoPath {};
if (const Result<String, DracError> cargoHome = GetEnv("CARGO_HOME"))
cargoPath = fs::path(*cargoHome) / "bin";
else if (const Result<String, DracError> homeDir = GetEnv("HOME"))
cargoPath = fs::path(*homeDir) / ".cargo" / "bin";
if (cargoPath.empty() || !fs::exists(cargoPath))
return Err(DracError(DracErrorCode::NotFound, "Could not find cargo directory"));
u64 count = 0;
for (const fs::directory_entry& entry : fs::directory_iterator(cargoPath))
if (entry.is_regular_file())
++count;
debug_log("Found {} packages in cargo directory: {}", count, cargoPath.string());
return count;
}
} // namespace
} // namespace os
namespace os::shared {
fn GetPackageCount() -> Result<u64, DracError> {
u64 count = 0;
#ifndef _WIN32
if (const Result<u64, DracError> pkgCount = GetNixPackageCount())
count += *pkgCount;
else
debug_at(pkgCount.error());
#endif
if (const Result<u64, DracError> pkgCount = GetCargoPackageCount())
count += *pkgCount;
else
debug_at(pkgCount.error());
return count;
}
} // namespace os::shared
#endif // !__serenity__
#endif // __FreeBSD__ || __DragonFly__ || __NetBSD__

36
src/os/bsd/pkg_count.hpp Normal file
View file

@ -0,0 +1,36 @@
#pragma once
// clang-format off
#include <filesystem> // std::filesystem::path
#include <glaze/core/common.hpp> // glz::object
#include <glaze/core/meta.hpp> // glz::detail::Object
#include "src/util/error.hpp"
#include "src/util/types.hpp"
// clang-format on
namespace os::bsd {
using util::error::DracError;
using util::types::Result, util::types::u64, util::types::i64, util::types::String;
namespace fs = std::filesystem;
struct PackageManagerInfo {
String id;
fs::path dbPath;
String countQuery;
};
struct PkgCountCacheData {
u64 count {};
i64 timestampEpochSeconds {};
// NOLINTBEGIN(readability-identifier-naming)
struct [[maybe_unused]] glaze {
using T = PkgCountCacheData;
static constexpr glz::detail::Object value =
glz::object("count", &T::count, "timestamp", &T::timestampEpochSeconds);
};
// NOLINTEND(readability-identifier-naming)
};
} // namespace os::bsd

View file

@ -61,10 +61,10 @@ namespace util::cache {
const fs::path& cachePath = *cachePathResult;
if (std::error_code exists_ec; !fs::exists(cachePath, exists_ec) || exists_ec) {
if (exists_ec) {
if (std::error_code existsEc; !fs::exists(cachePath, existsEc) || existsEc) {
if (existsEc) {
// Log if there was an error checking existence, but still return NotFound
warn_log("Error checking existence of cache file '{}': {}", cachePath.string(), exists_ec.message());
warn_log("Error checking existence of cache file '{}': {}", cachePath.string(), existsEc.message());
}
return Err(DracError(DracErrorCode::NotFound, "Cache file not found: " + cachePath.string()));
}
@ -174,9 +174,9 @@ namespace util::cache {
}
// Attempt to atomically replace the old cache file
std::error_code rename_ec;
fs::rename(tempPath, cachePath, rename_ec);
if (rename_ec) {
std::error_code renameEc;
fs::rename(tempPath, cachePath, renameEc);
if (renameEc) {
// If rename failed, attempt to clean up the temp file
std::error_code removeEc;
fs::remove(tempPath, removeEc); // Ignore error on cleanup attempt
@ -186,7 +186,7 @@ namespace util::cache {
"Failed to replace cache file '{}' with temporary file '{}': {}",
cachePath.string(),
tempPath.string(),
rename_ec.message()
renameEc.message()
)
));
}