diff --git a/meson.build b/meson.build index fccf1cd..8770a76 100644 --- a/meson.build +++ b/meson.build @@ -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'], } diff --git a/src/os/bsd.cpp b/src/os/bsd.cpp index 7ef0c1e..bd1a3d9 100644 --- a/src/os/bsd.cpp +++ b/src/os/bsd.cpp @@ -483,8 +483,6 @@ namespace os { .total_bytes = stat.f_blocks * stat.f_frsize, }; } - - fn GetPackageCount() -> Result { return shared::GetPackageCount(); } } // namespace os #endif // __FreeBSD__ || __DragonFly__ || __NetBSD__ diff --git a/src/os/shared.cpp b/src/os/bsd/pkg_count.cpp similarity index 55% rename from src/os/shared.cpp rename to src/os/bsd/pkg_count.cpp index e6874e1..c30c23b 100644 --- a/src/os/shared.cpp +++ b/src/os/bsd/pkg_count.cpp @@ -1,65 +1,93 @@ -#ifndef __serenity__ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) // clang-format off -#ifndef _WIN32 - #include // SQLite::{Database, OPEN_READONLY} - #include // SQLite::Exception - #include // SQLite::Statement -#endif +#include // SQLite::{Database, OPEN_READONLY} +#include // SQLite::Exception +#include // SQLite::Statement #include // std::chrono #include // std::filesystem #include // std::format -#include // std::{ifstream, ofstream} #include // glz::write_beve #include // glz::object #include // glz::detail::Object -#include // std::istreambuf_iterator #include // 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 { + 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 { const auto& [pmId, dbPath, countQuery] = pmInfo; @@ -122,73 +150,31 @@ namespace { return count; } #endif +} // namespace - #ifndef _WIN32 - fn GetNixPackageCount() -> Result { - 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 { + #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 { - using util::helpers::GetEnv; - - fs::path cargoPath {}; - - if (const Result cargoHome = GetEnv("CARGO_HOME")) - cargoPath = fs::path(*cargoHome) / "bin"; - else if (const Result 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 count = 0; - - #ifndef _WIN32 - if (const Result pkgCount = GetNixPackageCount()) - count += *pkgCount; - else - debug_at(pkgCount.error()); - #endif - - if (const Result pkgCount = GetCargoPackageCount()) - count += *pkgCount; - else - debug_at(pkgCount.error()); - - return count; - } -} // namespace os::shared - -#endif // !__serenity__ +#endif // __FreeBSD__ || __DragonFly__ || __NetBSD__ diff --git a/src/os/bsd/pkg_count.hpp b/src/os/bsd/pkg_count.hpp new file mode 100644 index 0000000..4f7515a --- /dev/null +++ b/src/os/bsd/pkg_count.hpp @@ -0,0 +1,36 @@ +#pragma once + +// clang-format off +#include // std::filesystem::path +#include // glz::object +#include // 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 diff --git a/src/util/cache.hpp b/src/util/cache.hpp index ac8454c..c42e9d7 100644 --- a/src/util/cache.hpp +++ b/src/util/cache.hpp @@ -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() ) )); }