]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
report-basic: expose os-release fields as metrics
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Fri, 8 May 2026 07:23:09 +0000 (09:23 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Tue, 12 May 2026 20:56:06 +0000 (22:56 +0200)
Add io.systemd.Basic.OSRelease metric families that reports select fields
from os-release.

$ cat /etc/os-release
NAME="Fedora Linux"
VERSION="45 (Cloud Edition Prerelease)"
RELEASE_TYPE=development
ID=fedora
VERSION_ID=45
VERSION_CODENAME=""
PRETTY_NAME="Fedora Linux 45 (Cloud Edition Prerelease)"
ANSI_COLOR="0;38;2;60;110;180"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:45"
HOME_URL="https://fedoraproject.org/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/rawhide/"
SUPPORT_URL="https://ask.fedoraproject.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=rawhide
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=rawhide
SUPPORT_END=2027-11-24
VARIANT="Cloud Edition"
VARIANT_ID=cloud

$ varlinkctl call --more ./build/systemd-report-basic io.systemd.Metrics.List {} | jq --seq -c
...
{"name":"io.systemd.Basic.OSRelease.NAME","value":"Fedora Linux 44 (Workstation Edition)"}
{"name":"io.systemd.Basic.OSRelease.ID","value":"fedora"}
{"name":"io.systemd.Basic.OSRelease.CPE_NAME","value":"cpe:/o:fedoraproject:fedora:44"}
{"name":"io.systemd.Basic.OSRelease.VARIANT_ID","value":"workstation"}
{"name":"io.systemd.Basic.OSRelease.VERSION_ID","value":"44"}
{"name":"io.systemd.Basic.OSRelease.SUPPORT_END","value":"2027-05-19"}

I picked the fields that contain useful information about the specific
version/image/variant/experiment/flavour of the system. Also, either
NAME or PRETTY_NAME is included. This one is intended for human readers
to be able to identify the OS version easily.

src/report/report-basic.c
test/units/TEST-74-AUX-UTILS.report.sh

index 11ecb7b45c678067bc09058376d2e2bc4bd63d3a..772a5d9f74222dbb976cf1ef1cba32879197e9c1 100644 (file)
@@ -9,7 +9,9 @@
 #include "alloc-util.h"
 #include "architecture.h"
 #include "hostname-setup.h"
+#include "log.h"
 #include "metrics.h"
+#include "os-util.h"
 #include "report-basic.h"
 #include "virt.h"
 
@@ -98,6 +100,71 @@ static int machine_id_generate(const MetricFamily *mf, sd_varlink *link, void *u
                         /* fields= */ NULL);
 }
 
+enum {
+        FIELD_PRETTY_NAME,
+        FIELD_NAME,
+        FIELD_ID,
+        FIELD_CPE_NAME,
+        FIELD_VARIANT_ID,
+        FIELD_VERSION_ID,
+        FIELD_BUILD_ID,
+        FIELD_IMAGE_VERSION,
+        FIELD_IMAGE_ID,
+        FIELD_SUPPORT_END,
+        FIELD_EXPERIMENT,
+        FIELD_SYSEXT_LEVEL,
+        FIELD_CONFEXT_LEVEL,
+        _FIELD_MAX,
+};
+
+static int os_release_generate(const MetricFamily mf[static _FIELD_MAX - 1], sd_varlink *link, void *userdata) {
+        char* values[_FIELD_MAX] = {};
+        CLEANUP_ELEMENTS(values, free_many_charp);
+        int r;
+
+        assert(mf && mf->name);
+        assert(link);
+
+        r = parse_os_release(NULL,
+                             "PRETTY_NAME",   &values[FIELD_PRETTY_NAME],
+                             "NAME",          &values[FIELD_NAME],
+                             "ID",            &values[FIELD_ID],
+                             "CPE_NAME",      &values[FIELD_CPE_NAME],
+                             "VARIANT_ID",    &values[FIELD_VARIANT_ID],
+                             "VERSION_ID",    &values[FIELD_VERSION_ID],
+                             "BUILD_ID",      &values[FIELD_BUILD_ID],
+                             "IMAGE_VERSION", &values[FIELD_IMAGE_VERSION],
+                             "IMAGE_ID",      &values[FIELD_IMAGE_ID],
+                             "SUPPORT_END",   &values[FIELD_SUPPORT_END],
+                             "EXPERIMENT",    &values[FIELD_EXPERIMENT],
+                             "SYSEXT_LEVEL",  &values[FIELD_SYSEXT_LEVEL],
+                             "CONFEXT_LEVEL", &values[FIELD_CONFEXT_LEVEL]);
+        if (r < 0) {
+                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+                               "Failed to read os-release file, ignoring: %m");
+                return 0;
+        }
+
+        for (size_t i = 1; i < _FIELD_MAX; i++) {
+                const char *v = values[i];
+                if (i == FIELD_NAME && values[FIELD_PRETTY_NAME])
+                        v = values[FIELD_PRETTY_NAME];  /* Prefer PRETTY_NAME to NAME */
+
+                if (v) {
+                        r = metric_build_send_string(
+                                        mf + i - 1,
+                                        link,
+                                        /* object= */ NULL,
+                                        v,
+                                        /* fields= */ NULL);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
 static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
         assert(mf && mf->name);
         assert(link);
@@ -114,6 +181,14 @@ static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, voi
                         /* fields= */ NULL);
 }
 
+#define OS_RELEASE_STANDARD_FIELD(name)                                 \
+        {                                                               \
+                METRIC_IO_SYSTEMD_BASIC_PREFIX "OSRelease." name,       \
+                "Operating system identification (" name "= field from os-release)", \
+                METRIC_FAMILY_TYPE_STRING,                              \
+                .generate = NULL,                                       \
+        }
+
 static const MetricFamily metric_family_table[] = {
         /* Keep entries ordered alphabetically */
         {
@@ -146,6 +221,24 @@ static const MetricFamily metric_family_table[] = {
                 METRIC_FAMILY_TYPE_STRING,
                 .generate = machine_id_generate,
         },
+        {
+                METRIC_IO_SYSTEMD_BASIC_PREFIX "OSRelease.NAME",
+                "Operating system human-readable name (PRETTY_NAME= or NAME= field from os-release)",
+                METRIC_FAMILY_TYPE_STRING,
+                .generate = os_release_generate,
+        },
+        OS_RELEASE_STANDARD_FIELD("ID"),
+        OS_RELEASE_STANDARD_FIELD("CPE_NAME"),
+        OS_RELEASE_STANDARD_FIELD("VARIANT_ID"),
+        OS_RELEASE_STANDARD_FIELD("VERSION_ID"),
+        OS_RELEASE_STANDARD_FIELD("BUILD_ID"),
+        OS_RELEASE_STANDARD_FIELD("IMAGE_VERSION"),
+        OS_RELEASE_STANDARD_FIELD("IMAGE_ID"),
+        OS_RELEASE_STANDARD_FIELD("SUPPORT_END"),
+        OS_RELEASE_STANDARD_FIELD("EXPERIMENT"),
+        OS_RELEASE_STANDARD_FIELD("SYSEXT_LEVEL"),
+        OS_RELEASE_STANDARD_FIELD("CONFEXT_LEVEL"),
+        /* Keep those ↑ in sync with os_release_generate(). */
         {
                 METRIC_IO_SYSTEMD_BASIC_PREFIX "Virtualization",
                 "Virtualization type",
index 61d2b10d0b7c69875935fb2cd89c261f61e851c2..0669f5587f0804d25a36f7999880efb36e7b0742 100755 (executable)
@@ -57,6 +57,10 @@ varlinkctl list-methods /run/systemd/report/io.systemd.Basic
 varlinkctl --more call /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {}
 varlinkctl --more call /run/systemd/report/io.systemd.Basic io.systemd.Metrics.Describe {}
 
+id1="$(varlinkctl call --more /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {} | jq --seq -r 'select(.name == "io.systemd.Basic.OSRelease.ID") | .value')"
+id2="$(. /etc/os-release; echo "$ID")"
+[ "$id1" = "$id2" ]
+
 # Test HTTP upload (plain http)
 FAKE_SERVER=/usr/lib/systemd/tests/integration-tests/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/fake-report-server.py
 CERTDIR=$(mktemp -d)