From 374dc49f8983b1712497d46fde1e38f7564352c5 Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Fri, 22 Aug 2025 10:17:58 +0200 Subject: [PATCH] Added os_version_minor sys variable Ticket: ENT-8118 Signed-off-by: Victor Moene --- libenv/sysinfo.c | 85 +++++++++++++++++++ .../01_vars/01_basic/os_version_minor.cf | 70 +++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 tests/acceptance/01_vars/01_basic/os_version_minor.cf diff --git a/libenv/sysinfo.c b/libenv/sysinfo.c index d9315f4bec..8df12a6691 100644 --- a/libenv/sysinfo.c +++ b/libenv/sysinfo.c @@ -3790,6 +3790,90 @@ static void SysOsVersionMajor(EvalContext *ctx) /*****************************************************************************/ + +#define SUPPORTED_PLATFORMS "debian|ubuntu|redhat|rhel|centos|fedora|aix|hpux|suse|opensuse|opensuse_leap|sles|solaris|sunos|windows|freebsd|macos" + +static bool SetOsVersionMinorFromOSRelease(EvalContext *ctx) +{ + + DataType type_out; + const JsonElement *os_release = EvalContextVariableGetSpecial(ctx, SPECIAL_SCOPE_SYS, "os_release", &type_out); + + if (os_release == NULL) + { + return false; + } + const JsonElement *version_id = JsonObjectGet(os_release, "VERSION_ID"); + if (version_id == NULL) + { + return false; + } + const char *version_id_string = JsonPrimitiveGetAsString(version_id); + Item *version_tuple = SplitString(version_id_string, '.'); + if (version_tuple->next == NULL) + { + DeleteItemList(version_tuple); + return false; + } + bool ret = EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "os_version_minor", version_tuple->next->name, CF_DATA_TYPE_STRING, "source=agent"); + DeleteItemList(version_tuple); + return ret; +} + +static bool SetOsVersionMinorFromHardClass(EvalContext *ctx) +{ + const char *regex_string = "^(" SUPPORTED_PLATFORMS ")+_[0-9]+_[0-9]+$"; + ClassTableIterator *iter = EvalContextClassTableIteratorNewGlobal(ctx, NULL, true, false); + StringSet *matches = ClassesMatching(ctx, iter, regex_string, NULL, false); + + StringSetIterator match_iter = StringSetIteratorInit(matches); + const char *os_class = StringSetIteratorNext(&match_iter); + if (os_class == NULL) // no match + { + ClassTableIteratorDestroy(iter); + StringSetDestroy(matches); + return false; + } + Item *os_class_tuple_start = SplitString(os_class, '_'); + + ClassTableIteratorDestroy(iter); + StringSetDestroy(matches); + + Item *os_class_tuple = os_class_tuple_start; + if (os_class_tuple->next == NULL) + { + DeleteItemList(os_class_tuple_start); + return false; + } +#ifndef __sun + os_class_tuple = os_class_tuple->next; + if (os_class_tuple->next == NULL) + { + DeleteItemList(os_class_tuple_start); + return false; + } +#endif + bool ret = EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "os_version_minor", os_class_tuple->next->name, CF_DATA_TYPE_STRING, "source=agent"); + DeleteItemList(os_class_tuple_start); + return ret; +} + +static void SysOsVersionMinor(EvalContext *ctx) +{ + // 1. parse the os-release file + if (!SetOsVersionMinorFromOSRelease(ctx)) + { + // 2. if version minor not found, find "_major_minor" hard class with classesmatching + if (!SetOsVersionMinorFromHardClass(ctx)) + { + // 3. if version minor not found, set to "Unknown" + EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "os_version_minor", "Unknown", CF_DATA_TYPE_STRING, "source=agent"); + } + } +} + +/*****************************************************************************/ + void DetectEnvironment(EvalContext *ctx) { GetNameInfo3(ctx); @@ -3802,4 +3886,5 @@ void DetectEnvironment(EvalContext *ctx) GetDefVars(ctx); SysOSNameHuman(ctx); SysOsVersionMajor(ctx); + SysOsVersionMinor(ctx); } diff --git a/tests/acceptance/01_vars/01_basic/os_version_minor.cf b/tests/acceptance/01_vars/01_basic/os_version_minor.cf new file mode 100644 index 0000000000..035eb9fc28 --- /dev/null +++ b/tests/acceptance/01_vars/01_basic/os_version_minor.cf @@ -0,0 +1,70 @@ +body common control +{ + bundlesequence => { "test", "check" }; +} + +bundle agent test +{ + + vars: + # Platforms to test + + "platforms" + slist => { "debian", "ubuntu", "redhat", "rhel", "centos", "fedora", + "aix", "hpux", "suse", "opensuse", "opensuse_leap", "sles", + "solaris", "sunos", "windows", "freebsd", "macos" }; + + + # Regex matching current platforms OS-class with version numbers + "class_regex" + string => format("^(%s)_[0-9]+_[0-9]+$", join("|", "platforms")); + # Find OS-class with version numbers using regex + "os_classes" + slist => classesmatching("$(class_regex)"); + + # Regex to extract minor version number from OS-class + # Edge cases: + # - On Solaris/SunOS minor version comes second + # E.g. Solaris 11 has class "solaris_5_11" + + "extract_regex" + string => ifelse("solaris|sunos", "^[a-z]+_([0-9]+)_[0-9]+$", + "opensuse_leap", "^[a-z_]+_[0-9]+_([0-9]+$)", + "^[a-z]+_[0-9]+_([0-9]+$)"); + + # Get extracted minor version number + "version_number" + data => data_regextract("$(extract_regex)", "$(os_classes)"); + + "expected" + string => nth("@(version_number)", "1"); +} + +bundle agent check +{ + vars: + "defined_classes" + slist => classesmatching(".*"); + + classes: + "exists" + expression => isvariable("test.expected"); + + exists:: + "passed" + expression => strcmp("$(test.expected)", "$(sys.os_version_minor)"); + !exists:: + "passed" + expression => strcmp("Unknown", "$(sys.os_version_minor)"); + + reports: + DEBUG:: + "Version number extracted from class: $(test.os_classes)"; + "Defined classes: $(defined_classes)"; + "$(this.promise_filename) Expected: $(test.expected)"; + "$(this.promise_filename) Found: $(sys.os_version_minor)"; + passed:: + "$(this.promise_filename) Pass"; + !passed:: + "$(this.promise_filename) FAIL"; +}