From ef9eddd2020d64c25527feca3c18666c6c3f5444 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 May 2026 05:05:51 +0200 Subject: [PATCH 1/4] report-basic: also report /etc/machine-info fields, just like os-release fields The TAGS= field is really nice to have in reports, hence make this a thing. --- src/report/report-basic.c | 65 ++++++++++++++++++++++++++ test/units/TEST-74-AUX-UTILS.report.sh | 40 ++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/src/report/report-basic.c b/src/report/report-basic.c index 43c9cb9774..f867ab3505 100644 --- a/src/report/report-basic.c +++ b/src/report/report-basic.c @@ -9,7 +9,9 @@ #include "alloc-util.h" #include "architecture.h" #include "cpu-set-util.h" +#include "env-file.h" #include "hostname-setup.h" +#include "hostname-util.h" #include "limits-util.h" #include "log.h" #include "metrics.h" @@ -198,6 +200,51 @@ static int os_release_generate(const MetricFamily mf[static _FIELD_MAX - 1], sd_ return 0; } +static int machine_info_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + enum { + MACHINE_INFO_FIELD_PRETTY_HOSTNAME, + MACHINE_INFO_FIELD_DEPLOYMENT, + MACHINE_INFO_FIELD_LOCATION, + MACHINE_INFO_FIELD_TAGS, + _MACHINE_INFO_FIELD_MAX, + }; + + char* values[_MACHINE_INFO_FIELD_MAX] = {}; + CLEANUP_ELEMENTS(values, free_many_charp); + int r; + + assert(mf && mf->name); + assert(link); + + r = parse_env_file(/* f= */ NULL, etc_machine_info(), + "PRETTY_HOSTNAME", &values[MACHINE_INFO_FIELD_PRETTY_HOSTNAME], + "DEPLOYMENT", &values[MACHINE_INFO_FIELD_DEPLOYMENT], + "LOCATION", &values[MACHINE_INFO_FIELD_LOCATION], + "TAGS", &values[MACHINE_INFO_FIELD_TAGS]); + if (r < 0) { + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read machine-info file, ignoring: %m"); + return 0; + } + + for (size_t i = 0; i < _MACHINE_INFO_FIELD_MAX; i++) { + const char *v = values[i]; + if (!v) + continue; + + r = metric_build_send_string( + mf + i, + 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); @@ -222,6 +269,14 @@ static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, voi .generate = NULL, \ } +#define MACHINE_INFO_STANDARD_FIELD(name) \ + { \ + METRIC_IO_SYSTEMD_BASIC_PREFIX "MachineInfo." name, \ + "Machine identification (" name "= field from machine-info)", \ + METRIC_FAMILY_TYPE_STRING, \ + .generate = NULL, \ + } + static const MetricFamily metric_family_table[] = { /* Keep entries ordered alphabetically */ { @@ -260,6 +315,16 @@ static const MetricFamily metric_family_table[] = { METRIC_FAMILY_TYPE_STRING, .generate = machine_id_generate, }, + { + METRIC_IO_SYSTEMD_BASIC_PREFIX "MachineInfo.PRETTY_HOSTNAME", + "Pretty hostname (PRETTY_HOSTNAME= field from machine-info)", + METRIC_FAMILY_TYPE_STRING, + .generate = machine_info_generate, + }, + MACHINE_INFO_STANDARD_FIELD("DEPLOYMENT"), + MACHINE_INFO_STANDARD_FIELD("LOCATION"), + MACHINE_INFO_STANDARD_FIELD("TAGS"), + /* Keep those ↑ in sync with machine_info_generate(). */ { METRIC_IO_SYSTEMD_BASIC_PREFIX "OSRelease.NAME", "Operating system human-readable name (PRETTY_NAME= or NAME= field from os-release)", diff --git a/test/units/TEST-74-AUX-UTILS.report.sh b/test/units/TEST-74-AUX-UTILS.report.sh index d89a06cd9a..48bbc4841a 100755 --- a/test/units/TEST-74-AUX-UTILS.report.sh +++ b/test/units/TEST-74-AUX-UTILS.report.sh @@ -62,6 +62,46 @@ id1="$(varlinkctl call --more /run/systemd/report/io.systemd.Basic io.systemd.Me id2="$(. /etc/os-release; echo "$ID")" [ "$id1" = "$id2" ] +# test io.systemd.Basic.MachineInfo.* metrics, sourced from /etc/machine-info +if [ -e /etc/machine-info ]; then + MACHINE_INFO_BACKUP="$(mktemp)" + cp /etc/machine-info "$MACHINE_INFO_BACKUP" + MACHINE_INFO_EXISTED=1 +else + MACHINE_INFO_EXISTED=0 +fi + +restore_machine_info() { + set +e + if [ "$MACHINE_INFO_EXISTED" = 1 ]; then + cp "$MACHINE_INFO_BACKUP" /etc/machine-info + rm -f "$MACHINE_INFO_BACKUP" + else + rm -f /etc/machine-info + fi + set -e +} +trap restore_machine_info EXIT + +cat >/etc/machine-info < Date: Fri, 22 May 2026 05:34:28 +0200 Subject: [PATCH 2/4] report-basic: report various SMBIOS fields as metrics, too This are pretty fundamental hw fields, let's make them available. --- src/report/report-basic.c | 142 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/src/report/report-basic.c b/src/report/report-basic.c index f867ab3505..8587bf19e2 100644 --- a/src/report/report-basic.c +++ b/src/report/report-basic.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include #include #include "sd-id128.h" @@ -10,6 +11,9 @@ #include "architecture.h" #include "cpu-set-util.h" #include "env-file.h" +#include "errno-util.h" +#include "fd-util.h" +#include "fileio.h" #include "hostname-setup.h" #include "hostname-util.h" #include "limits-util.h" @@ -17,6 +21,7 @@ #include "metrics.h" #include "os-util.h" #include "report-basic.h" +#include "string-util.h" #include "virt.h" static int architecture_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { @@ -245,6 +250,107 @@ static int machine_info_generate(const MetricFamily *mf, sd_varlink *link, void return 0; } +static int smbios_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + enum { + SMBIOS_FIELD_SYS_VENDOR, + SMBIOS_FIELD_PRODUCT_NAME, + SMBIOS_FIELD_PRODUCT_VERSION, + SMBIOS_FIELD_PRODUCT_SKU, + SMBIOS_FIELD_PRODUCT_FAMILY, + SMBIOS_FIELD_PRODUCT_SERIAL, + SMBIOS_FIELD_PRODUCT_UUID, + SMBIOS_FIELD_BOARD_VENDOR, + SMBIOS_FIELD_BOARD_NAME, + SMBIOS_FIELD_BOARD_VERSION, + SMBIOS_FIELD_BOARD_SERIAL, + SMBIOS_FIELD_BOARD_ASSET_TAG, + SMBIOS_FIELD_BIOS_VENDOR, + SMBIOS_FIELD_BIOS_VERSION, + SMBIOS_FIELD_BIOS_DATE, + SMBIOS_FIELD_CHASSIS_TYPE, + SMBIOS_FIELD_CHASSIS_VENDOR, + SMBIOS_FIELD_CHASSIS_SERIAL, + SMBIOS_FIELD_CHASSIS_ASSET_TAG, + _SMBIOS_FIELD_MAX, + }; + + /* The sysfs attribute names exposed by the kernel below /sys/class/dmi/id/. The order must match the + * SMBIOS_STANDARD_FIELD() entries in the metric family table below. */ + static const char* const smbios_files[_SMBIOS_FIELD_MAX] = { + /* SMBIOS Type 1 */ + [SMBIOS_FIELD_SYS_VENDOR] = "sys_vendor", + [SMBIOS_FIELD_PRODUCT_NAME] = "product_name", + [SMBIOS_FIELD_PRODUCT_VERSION] = "product_version", + [SMBIOS_FIELD_PRODUCT_SKU] = "product_sku", + [SMBIOS_FIELD_PRODUCT_FAMILY] = "product_family", + [SMBIOS_FIELD_PRODUCT_SERIAL] = "product_serial", + [SMBIOS_FIELD_PRODUCT_UUID] = "product_uuid", + /* SMBIOS Type 2 */ + [SMBIOS_FIELD_BOARD_VENDOR] = "board_vendor", + [SMBIOS_FIELD_BOARD_NAME] = "board_name", + [SMBIOS_FIELD_BOARD_VERSION] = "board_version", + [SMBIOS_FIELD_BOARD_SERIAL] = "board_serial", + [SMBIOS_FIELD_BOARD_ASSET_TAG] = "board_asset_tag", + /* SMBIOS Type 0 */ + [SMBIOS_FIELD_BIOS_VENDOR] = "bios_vendor", + [SMBIOS_FIELD_BIOS_VERSION] = "bios_version", + [SMBIOS_FIELD_BIOS_DATE] = "bios_date", + /* SMBIOS Type 3 */ + [SMBIOS_FIELD_CHASSIS_TYPE] = "chassis_type", + [SMBIOS_FIELD_CHASSIS_VENDOR] = "chassis_vendor", + [SMBIOS_FIELD_CHASSIS_SERIAL] = "chassis_serial", + [SMBIOS_FIELD_CHASSIS_ASSET_TAG] = "chassis_asset_tag", + }; + + int r; + + assert(mf && mf->name); + assert(link); + + /* Reports the fundamental SMBIOS/DMI identification fields. Some of these (serial numbers, asset + * tags, the system UUID) are privacy sensitive and only readable by root — if we lack the + * privileges to read them we simply skip them. */ + + _cleanup_close_ int dir_fd = open("/sys/class/dmi/id", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (dir_fd < 0) { + log_full_errno(ERRNO_IS_DEVICE_ABSENT(errno) ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to open /sys/class/dmi/id/, ignoring: %m"); + return 0; + } + + for (size_t i = 0; i < _SMBIOS_FIELD_MAX; i++) { + _cleanup_free_ char *buf = NULL; + + r = read_virtual_file_at(dir_fd, smbios_files[i], /* max_size= */ SIZE_MAX, &buf, /* ret_size= */ NULL); + if (r < 0) { + log_full_errno(r == -ENOENT || ERRNO_IS_NEG_PRIVILEGE(r) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read SMBIOS field '%s', ignoring: %m", smbios_files[i]); + continue; + } + + delete_trailing_chars(buf, NEWLINE); + + if (isempty(buf)) + continue; + + if (!string_is_safe(buf, STRING_ALLOW_BACKSLASHES|STRING_ALLOW_QUOTES|STRING_ALLOW_GLOBS)) { + log_debug("SMBIOS field '%s' contains unsafe characters, ignoring.", smbios_files[i]); + continue; + } + + r = metric_build_send_string( + mf + i, + link, + /* object= */ NULL, + buf, + /* 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); @@ -277,6 +383,14 @@ static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, voi .generate = NULL, \ } +#define SMBIOS_STANDARD_FIELD(name) \ + { \ + METRIC_IO_SYSTEMD_BASIC_PREFIX "SMBIOS." name, \ + "Firmware/hardware identification (" name " field from SMBIOS/DMI)", \ + METRIC_FAMILY_TYPE_STRING, \ + .generate = NULL, \ + } + static const MetricFamily metric_family_table[] = { /* Keep entries ordered alphabetically */ { @@ -349,6 +463,34 @@ static const MetricFamily metric_family_table[] = { METRIC_FAMILY_TYPE_GAUGE, .generate = physical_memory_generate, }, + { + /* NB: Here we use the naming of the field as per SMBIOS specification, i.e. undo the weird + * renaming that Linux did on the fields. When new fields are added here, please make sure to + * check the specification again for naming them. */ + METRIC_IO_SYSTEMD_BASIC_PREFIX "SMBIOS.SystemManufacturer", + "Firmware/hardware identification (SystemManufacturer field from SMBIOS/DMI)", + METRIC_FAMILY_TYPE_STRING, + .generate = smbios_generate, + }, + SMBIOS_STANDARD_FIELD("SystemProductName"), + SMBIOS_STANDARD_FIELD("SystemVersion"), + SMBIOS_STANDARD_FIELD("SystemSKUNumber"), + SMBIOS_STANDARD_FIELD("SystemFamily"), + SMBIOS_STANDARD_FIELD("SystemSerialNumber"), + SMBIOS_STANDARD_FIELD("SystemUUID"), + SMBIOS_STANDARD_FIELD("BaseBoardManufacturer"), + SMBIOS_STANDARD_FIELD("BaseBoardProduct"), + SMBIOS_STANDARD_FIELD("BaseBoardVersion"), + SMBIOS_STANDARD_FIELD("BaseBoardSerial"), + SMBIOS_STANDARD_FIELD("BaseBoardAssetTag"), + SMBIOS_STANDARD_FIELD("FirmwareVendor"), + SMBIOS_STANDARD_FIELD("FirmwareVersion"), + SMBIOS_STANDARD_FIELD("FirmwareReleaseDate"), + SMBIOS_STANDARD_FIELD("ChassisType"), + SMBIOS_STANDARD_FIELD("ChassisManufacturer"), + SMBIOS_STANDARD_FIELD("ChassisSerialNumber"), + SMBIOS_STANDARD_FIELD("ChassisAssetTagNumber"), + /* Keep those ↑ in sync with smbios_generate(). */ { METRIC_IO_SYSTEMD_BASIC_PREFIX "Virtualization", "Virtualization type", From ef9cc58dfa64b77beaa63a887e3a4938d2a4c81f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 May 2026 05:39:57 +0200 Subject: [PATCH 3/4] report-basic: also report confidential computing tech --- src/report/report-basic.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/report/report-basic.c b/src/report/report-basic.c index 8587bf19e2..f2c5e54d5b 100644 --- a/src/report/report-basic.c +++ b/src/report/report-basic.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "architecture.h" +#include "confidential-virt.h" #include "cpu-set-util.h" #include "env-file.h" #include "errno-util.h" @@ -351,6 +352,22 @@ static int smbios_generate(const MetricFamily *mf, sd_varlink *link, void *userd return 0; } +static int confidential_virtualization_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + assert(mf && mf->name); + assert(link); + + ConfidentialVirtualization cv = detect_confidential_virtualization(); + if (cv < 0) + return cv; + + return metric_build_send_string( + mf, + link, + /* object= */ NULL, + confidential_virtualization_to_string(cv), + /* fields= */ NULL); +} + static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { assert(mf && mf->name); assert(link); @@ -405,6 +422,12 @@ static const MetricFamily metric_family_table[] = { METRIC_FAMILY_TYPE_STRING, .generate = boot_id_generate, }, + { + METRIC_IO_SYSTEMD_BASIC_PREFIX "ConfidentialVirtualization", + "Confidential computing technology", + METRIC_FAMILY_TYPE_STRING, + .generate = confidential_virtualization_generate, + }, { METRIC_IO_SYSTEMD_BASIC_PREFIX "CPUsOnline", "Number of CPUs currently online", From e997c015804c43488e258605977daf65b54cd9ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 May 2026 05:49:38 +0200 Subject: [PATCH 4/4] report-basic: report TPM2 vendor strings too This is highly relevant to debug/analyze TPM related behaviour, hence let's report this as part of the metrics. --- src/report/report-basic.c | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/report/report-basic.c b/src/report/report-basic.c index f2c5e54d5b..cf7a4f0b5e 100644 --- a/src/report/report-basic.c +++ b/src/report/report-basic.c @@ -3,6 +3,7 @@ #include #include +#include "sd-device.h" #include "sd-id128.h" #include "sd-json.h" #include "sd-varlink.h" @@ -352,6 +353,59 @@ static int smbios_generate(const MetricFamily *mf, sd_varlink *link, void *userd return 0; } +static int tpm2_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + enum { + TPM2_FIELD_MANUFACTURER, + TPM2_FIELD_VENDOR_STRING, + _TPM2_FIELD_MAX, + }; + + /* The udev properties set by the 'tpm2_id' builtin on the tpmrm device. The order must match the + * metric family table entries below. */ + static const char* const tpm2_properties[_TPM2_FIELD_MAX] = { + [TPM2_FIELD_MANUFACTURER] = "ID_TPM2_MANUFACTURER", + [TPM2_FIELD_VENDOR_STRING] = "ID_TPM2_VENDOR_STRING", + }; + + int r; + + assert(mf && mf->name); + assert(link); + + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + r = sd_device_new_from_subsystem_sysname(&dev, "tpmrm", "tpmrm0"); + if (r < 0) { + log_full_errno(ERRNO_IS_NEG_DEVICE_ABSENT(r) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to open tpmrm0 device, ignoring: %m"); + return 0; + } + + for (size_t i = 0; i < _TPM2_FIELD_MAX; i++) { + const char *v; + + r = sd_device_get_property_value(dev, tpm2_properties[i], &v); + if (r < 0) { + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, + "Failed to read TPM2 property '%s', ignoring: %m", tpm2_properties[i]); + continue; + } + + if (isempty(v)) + continue; + + r = metric_build_send_string( + mf + i, + link, + /* object= */ NULL, + v, + /* fields= */ NULL); + if (r < 0) + return r; + } + + return 0; +} + static int confidential_virtualization_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { assert(mf && mf->name); assert(link); @@ -514,6 +568,19 @@ static const MetricFamily metric_family_table[] = { SMBIOS_STANDARD_FIELD("ChassisSerialNumber"), SMBIOS_STANDARD_FIELD("ChassisAssetTagNumber"), /* Keep those ↑ in sync with smbios_generate(). */ + { + METRIC_IO_SYSTEMD_BASIC_PREFIX "TPM2.Manufacturer", + "TPM2 device manufacturer (ID_TPM2_MANUFACTURER property of the tpmrm0 device)", + METRIC_FAMILY_TYPE_STRING, + .generate = tpm2_generate, + }, + { + METRIC_IO_SYSTEMD_BASIC_PREFIX "TPM2.VendorString", + "TPM2 device vendor string (ID_TPM2_VENDOR_STRING property of the tpmrm0 device)", + METRIC_FAMILY_TYPE_STRING, + .generate = NULL, + }, + /* Keep those ↑ in sync with tpm2_generate(). */ { METRIC_IO_SYSTEMD_BASIC_PREFIX "Virtualization", "Virtualization type",