diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index 8b8a435d95..2375865cea 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -42,6 +42,7 @@ typedef struct FFPackagesResult uint32_t rpm; uint32_t scoopUser; uint32_t scoopGlobal; + uint32_t sdkman; uint32_t snap; uint32_t soar; uint32_t sorcery; diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 8d9c9b0beb..f597f99c23 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -272,6 +272,35 @@ static uint32_t getAMUser(void) return packagesPath.length > 0 ? getAMPackages(&packagesPath) : 0; } +static uint32_t getSDKMAN(void) +{ + const char* candidatesDir = getenv("SDKMAN_CANDIDATES_DIR"); + if (!ffStrSet(candidatesDir)) + return 0; + + FF_AUTO_CLOSE_DIR DIR* dir = opendir(candidatesDir); + if (!dir) + return 0; + + uint32_t count = 0; + char path[PATH_MAX]; + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_name[0] == '.') + continue; + + if (entry->d_type == DT_DIR || entry->d_type == DT_UNKNOWN) + { + snprintf(path, sizeof(path), "%s/%s/current", candidatesDir, entry->d_name); + if (ffPathExists(path, FF_PATHTYPE_DIRECTORY)) + ++count; + } + } + + return count; +} + static int compareHash(const void* a, const void* b) { return memcmp(a, b, 32); @@ -597,6 +626,9 @@ void ffDetectPackagesImpl(FFPackagesResult* result, FFPackagesOptions* options) if (!(options->disabled & FF_PACKAGES_FLAG_AM_BIT)) result->amUser = getAMUser(); + if (!(options->disabled & FF_PACKAGES_FLAG_SDKMAN_BIT)) + result->sdkman = getSDKMAN(); + if (!(options->disabled & FF_PACKAGES_FLAG_SOAR_BIT)) result->soar += getSQLite3Int(&baseDir, ".local/share/soar/db/soar.db", "SELECT COUNT(DISTINCT pkg_id || pkg_name) FROM packages WHERE is_installed = true", "soar"); } diff --git a/src/modules/packages/option.h b/src/modules/packages/option.h index 45c049710e..8c97ad69c5 100644 --- a/src/modules/packages/option.h +++ b/src/modules/packages/option.h @@ -38,6 +38,7 @@ typedef enum __attribute__((__packed__)) FFPackagesFlags FF_PACKAGES_FLAG_SOAR_BIT = 1ULL << 30, FF_PACKAGES_FLAG_KISS_BIT = 1ULL << 31, FF_PACKAGES_FLAG_MOSS_BIT = 1ULL << 32, + FF_PACKAGES_FLAG_SDKMAN_BIT = 1ULL << 33, FF_PACKAGES_FLAG_FORCE_UNSIGNED = UINT64_MAX, } FFPackagesFlags; static_assert(sizeof(FFPackagesFlags) == sizeof(uint64_t), ""); diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index f94337182c..cc50d3bc92 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -148,6 +148,7 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_PRINT_PACKAGE(pisi) FF_PRINT_PACKAGE(soar) FF_PRINT_PACKAGE(moss) + FF_PRINT_PACKAGE_NAME(sdkman, "sdk") putchar('\n'); } @@ -198,6 +199,7 @@ bool ffPrintPackages(FFPackagesOptions* options) FF_FORMAT_ARG(counts.soar, "soar"), FF_FORMAT_ARG(counts.kiss, "kiss"), FF_FORMAT_ARG(counts.moss, "moss"), + FF_FORMAT_ARG(counts.sdkman, "sdk"), FF_FORMAT_ARG(nixAll, "nix-all"), FF_FORMAT_ARG(flatpakAll, "flatpak-all"), FF_FORMAT_ARG(brewAll, "brew-all"), @@ -305,6 +307,7 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) break; case 'S': if (false); FF_TEST_PACKAGE_NAME(SCOOP) + FF_TEST_PACKAGE_NAME(SDKMAN) FF_TEST_PACKAGE_NAME(SNAP) FF_TEST_PACKAGE_NAME(SOAR) FF_TEST_PACKAGE_NAME(SORCERY) @@ -372,6 +375,7 @@ void ffGeneratePackagesJsonConfig(FFPackagesOptions* options, yyjson_mut_doc* do FF_TEST_PACKAGE_NAME(PKGSRC) FF_TEST_PACKAGE_NAME(RPM) FF_TEST_PACKAGE_NAME(SCOOP) + FF_TEST_PACKAGE_NAME(SDKMAN) FF_TEST_PACKAGE_NAME(SNAP) FF_TEST_PACKAGE_NAME(SOAR) FF_TEST_PACKAGE_NAME(SORCERY) @@ -435,6 +439,7 @@ bool ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy FF_APPEND_PACKAGE_COUNT(rpm) FF_APPEND_PACKAGE_COUNT(scoopUser) FF_APPEND_PACKAGE_COUNT(scoopGlobal) + FF_APPEND_PACKAGE_COUNT(sdkman) FF_APPEND_PACKAGE_COUNT(snap) FF_APPEND_PACKAGE_COUNT(soar) FF_APPEND_PACKAGE_COUNT(sorcery) @@ -512,6 +517,7 @@ FFModuleBaseInfo ffPackagesModuleInfo = { {"Number of soar packages", "soar"}, {"Number of kiss packages", "kiss"}, {"Number of moss packages", "moss"}, + {"Number of SDKMAN! packages", "sdk"}, {"Total number of all nix packages", "nix-all"}, {"Total number of all flatpak app packages", "flatpak-all"}, {"Total number of all brew packages", "brew-all"},