diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index f0d42004de..f99d87581b 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -43,6 +43,7 @@ typedef struct FFPackagesResult { uint32_t rpm; uint32_t scoopGlobal; uint32_t scoopUser; + 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 79de8cb3e3..798956a038 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -287,6 +287,34 @@ 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); } @@ -640,6 +668,10 @@ void ffDetectPackagesImpl(FFPackagesResult* result, FFPackagesOptions* options) result->amUser = getAMUser(); } + if (FF_PACKAGES_IS_ENABLED(options, SDKMAN)) { + result->sdkman = getSDKMAN(); + } + if (FF_PACKAGES_IS_ENABLED(options, SOAR)) { 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 1e895335a3..dabd09337c 100644 --- a/src/modules/packages/option.h +++ b/src/modules/packages/option.h @@ -39,6 +39,7 @@ typedef enum FF_A_PACKED FFPackagesFlags { FF_PACKAGES_FLAG_MOSS_BIT = UINT64_C(1) << 32U, FF_PACKAGES_FLAG_APPIMAGE_BIT = UINT64_C(1) << 33U, FF_PACKAGES_FLAG_CARDS_BIT = UINT64_C(1) << 34U, + FF_PACKAGES_FLAG_SDKMAN_BIT = UINT64_C(1) << 35U, 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 4ef1d049af..06739c4d1e 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -128,6 +128,7 @@ bool ffPrintPackages(FFPackagesOptions* options) { } else { FF_PRINT_PACKAGE_NAME(scoopUser, "scoop") } + FF_PRINT_PACKAGE_NAME(sdkman, "sdk") FF_PRINT_PACKAGE(snap) FF_PRINT_PACKAGE(soar) FF_PRINT_PACKAGE(sorcery) @@ -190,6 +191,7 @@ bool ffPrintPackages(FFPackagesOptions* options) { FF_ARG(counts.rpm, "rpm"), FF_ARG(counts.scoopGlobal, "scoop-global"), FF_ARG(counts.scoopUser, "scoop-user"), + FF_ARG(counts.sdkman, "sdk"), FF_ARG(counts.snap, "snap"), FF_ARG(counts.soar, "soar"), FF_ARG(counts.sorcery, "sorcery"), @@ -328,6 +330,7 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) { 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) @@ -403,6 +406,7 @@ void ffGeneratePackagesJsonConfig(FFPackagesOptions* options, yyjson_mut_doc* do FF_TEST_PACKAGE_NAME(PKGTOOL) 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) @@ -470,6 +474,7 @@ bool ffGeneratePackagesJsonResult(FFPackagesOptions* options, yyjson_mut_doc* do FF_APPEND_PACKAGE_COUNT(rpm) FF_APPEND_PACKAGE_COUNT(scoopGlobal) FF_APPEND_PACKAGE_COUNT(scoopUser) + FF_APPEND_PACKAGE_COUNT(sdkman) FF_APPEND_PACKAGE_COUNT(snap) FF_APPEND_PACKAGE_COUNT(soar) FF_APPEND_PACKAGE_COUNT(sorcery) @@ -551,6 +556,7 @@ FFModuleBaseInfo ffPackagesModuleInfo = { { "Number of rpm packages", "rpm" }, { "Number of scoop-global packages", "scoop-global" }, { "Number of scoop-user packages", "scoop-user" }, + {"Number of SDKMAN! packages", "sdk"}, { "Number of snap packages", "snap" }, { "Number of soar packages", "soar" }, { "Number of sorcery packages", "sorcery" },