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 # # 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 = { 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'], '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'], 'serenity' : ['src/os/serenity.cpp'],
'windows' : ['src/os/windows.cpp'], 'windows' : ['src/os/windows.cpp'],
} }

View file

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

View file

@ -1,65 +1,93 @@
#ifndef __serenity__ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
// clang-format off // clang-format off
#ifndef _WIN32
#include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY} #include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY}
#include <SQLiteCpp/Exception.h> // SQLite::Exception #include <SQLiteCpp/Exception.h> // SQLite::Exception
#include <SQLiteCpp/Statement.h> // SQLite::Statement #include <SQLiteCpp/Statement.h> // SQLite::Statement
#endif
#include <chrono> // std::chrono #include <chrono> // std::chrono
#include <filesystem> // std::filesystem #include <filesystem> // std::filesystem
#include <format> // std::format #include <format> // std::format
#include <fstream> // std::{ifstream, ofstream}
#include <glaze/beve/write.hpp> // glz::write_beve #include <glaze/beve/write.hpp> // glz::write_beve
#include <glaze/core/common.hpp> // glz::object #include <glaze/core/common.hpp> // glz::object
#include <glaze/core/meta.hpp> // glz::detail::Object #include <glaze/core/meta.hpp> // glz::detail::Object
#include <iterator> // std::istreambuf_iterator
#include <system_error> // std::error_code #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/cache.hpp"
#include "src/util/defs.hpp" #include "src/util/defs.hpp"
#include "src/util/error.hpp" #include "src/util/error.hpp"
#include "src/util/helpers.hpp"
#include "src/util/logging.hpp" #include "src/util/logging.hpp"
#include "src/util/types.hpp" #include "src/util/types.hpp"
#include "os.hpp"
// clang-format on // clang-format on
using util::error::DracError, util::error::DracErrorCode; using util::error::DracError, util::error::DracErrorCode;
using util::types::u64, util::types::i64, util::types::String, util::types::StringView, util::types::Result, using util::types::u64, util::types::i64, util::types::Result, util::types::Err, util::types::String,
util::types::Err, util::types::Exception; util::types::StringView, util::types::Exception;
namespace fs = std::filesystem;
namespace { namespace {
using namespace std::chrono; using namespace std::chrono;
using namespace util::cache; using namespace util::cache;
#ifndef _WIN32 using os::bsd::PackageManagerInfo, os::bsd::PkgCountCacheData;
struct PackageManagerInfo {
String id;
fs::path dbPath;
String countQuery;
};
#endif
struct PkgCountCacheData { #ifdef __NetBSD__
u64 count {}; fn GetPackageCountInternalDir(const String& pmId, const fs::path& dirPath) -> Result<u64, DracError> {
i64 timestampEpochSeconds {}; debug_log("Attempting to get {} package count.", pmId);
// NOLINTBEGIN(readability-identifier-naming) std::error_code errc;
struct [[maybe_unused]] glaze { if (!fs::exists(dirPath, errc)) {
using T = PkgCountCacheData; if (errc)
return Err(DracError(
DracErrorCode::IoError, std::format("Filesystem error checking {} directory: {}", pmId, errc.message())
));
static constexpr glz::detail::Object value = return Err(
glz::object("count", &T::count, "timestamp", &T::timestampEpochSeconds); DracError(DracErrorCode::ApiUnavailable, std::format("{} directory not found: {}", pmId, dirPath.string()))
}; );
// NOLINTEND(readability-identifier-naming) }
};
#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> { fn GetPackageCountInternalDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError> {
const auto& [pmId, dbPath, countQuery] = pmInfo; const auto& [pmId, dbPath, countQuery] = pmInfo;
@ -122,73 +150,31 @@ namespace {
return count; return count;
} }
#endif #endif
} // namespace
#ifndef _WIN32 namespace os {
fn GetNixPackageCount() -> Result<u64, DracError> { fn GetPackageCount() -> Result<u64, DracError> {
debug_log("Attempting to get Nix package count."); #ifdef __NetBSD__
return GetPackageCountInternalDir("PkgSrc", fs::current_path().root_path() / "usr" / "pkg" / "pkgdb");
const PackageManagerInfo nixInfo = { #else
.id = "nix", const PackageManagerInfo pkgInfo = {
.dbPath = "/nix/var/nix/db/db.sqlite", .id = "pkg_count",
.countQuery = "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL", .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) { 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::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 #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 { #endif // __FreeBSD__ || __DragonFly__ || __NetBSD__
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__

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