]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
hostnamed: expose FirmwareDate as timestamp in dbus
authorJelle van der Waa <jvanderwaa@redhat.com>
Fri, 13 Jan 2023 16:33:12 +0000 (17:33 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 19 Jan 2023 20:57:06 +0000 (21:57 +0100)
Offer the firmware date as an epoch instead of the literal DMI string.

Closes #25679

man/org.freedesktop.hostname1.xml
src/hostname/hostnamectl.c
src/hostname/hostnamed.c
test/units/testsuite-71.sh

index 02d7063e582e0d3f3bd680fdfd9fef5c5d7aa1e4..be36d8fc7e3772b154eab47243300e095bebab03 100644 (file)
@@ -92,7 +92,7 @@ node /org/freedesktop/hostname1 {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s FirmwareVendor = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
-      readonly s FirmwareDate = '...';
+      readonly t FirmwareDate = ...;
   };
   interface org.freedesktop.DBus.Peer { ... };
   interface org.freedesktop.DBus.Introspectable { ... };
index 52606d2f4046094ba374147c14cc4be4c3283067..51b2bde1f46fad45569cca2638e2050c1ef207f0 100644 (file)
@@ -3,6 +3,7 @@
 #include <getopt.h>
 #include <locale.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -52,6 +53,7 @@ typedef struct StatusInfo {
         const char *hardware_vendor;
         const char *hardware_model;
         const char *firmware_version;
+        usec_t firmware_date;
 } StatusInfo;
 
 static const char* chassis_string_to_glyph(const char *chassis) {
@@ -239,6 +241,14 @@ static int print_status_info(StatusInfo *i) {
                         return table_log_add_error(r);
         }
 
+        if (timestamp_is_set(i->firmware_date)) {
+                r = table_add_many(table,
+                                   TABLE_FIELD, "Firmware Date",
+                                   TABLE_TIMESTAMP, i->firmware_date);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
         r = table_print(table, NULL);
         if (r < 0)
                 return table_log_print_error(r);
@@ -304,6 +314,7 @@ static int show_all_names(sd_bus *bus) {
                 { "HardwareVendor",            "s", NULL, offsetof(StatusInfo, hardware_vendor)  },
                 { "HardwareModel",             "s", NULL, offsetof(StatusInfo, hardware_model)   },
                 { "FirmwareVersion",           "s", NULL, offsetof(StatusInfo, firmware_version) },
+                { "FirmwareDate",              "t", NULL, offsetof(StatusInfo, firmware_date)    },
                 {}
         };
 
index ea19dfbb0412b397e8158ac133b87424b7ce19ba..a9782de4ff9a5396966e3eb7295535f69776d6a6 100644 (file)
@@ -250,8 +250,64 @@ static int get_firmware_vendor(char **ret) {
          return get_hardware_firmware_data("bios_vendor", ret);
 }
 
-static int get_firmware_date(char **ret) {
-         return get_hardware_firmware_data("bios_date", ret);
+static int get_firmware_date(usec_t *ret) {
+         _cleanup_free_ char *bios_date = NULL, *month = NULL, *day = NULL, *year = NULL;
+         int r;
+
+         assert(ret);
+
+         r = get_hardware_firmware_data("bios_date", &bios_date);
+         if (r < 0)
+                return r;
+         if (r == 0) {
+                *ret = USEC_INFINITY;
+                return 0;
+         }
+
+         const char *p = bios_date;
+         r = extract_many_words(&p, "/", EXTRACT_DONT_COALESCE_SEPARATORS, &month, &day, &year, NULL);
+         if (r < 0)
+                return r;
+         if (r != 3) /* less than three args read? */
+                return -EINVAL;
+         if (!isempty(p)) /* more left in the string? */
+                return -EINVAL;
+
+         unsigned m, d, y;
+         r = safe_atou(month, &m);
+         if (r < 0)
+                return r;
+         if (m < 1 || m > 12)
+                return -EINVAL;
+         m -= 1;
+
+         r = safe_atou(day, &d);
+         if (r < 0)
+                return r;
+         if (d < 1 || d > 31)
+                return -EINVAL;
+
+         r = safe_atou(year, &y);
+         if (r < 0)
+                return r;
+         if (y < 1970 || y > (unsigned) INT_MAX)
+                return -EINVAL;
+         y -= 1900;
+
+         struct tm tm = {
+                .tm_mday = d,
+                .tm_mon = m,
+                .tm_year = y,
+         };
+         time_t v = timegm(&tm);
+         if (v == (time_t) -1)
+                return -errno;
+         if (tm.tm_mday != (int) d || tm.tm_mon != (int) m || tm.tm_year != (int) y)
+                return -EINVAL; /* date was not normalized? (e.g. "30th of feb") */
+
+         *ret = (usec_t) v * USEC_PER_SEC;
+
+         return 0;
 }
 
 static const char* valid_chassis(const char *chassis) {
@@ -661,11 +717,11 @@ static int property_get_firmware_date(
                 void *userdata,
                 sd_bus_error *error) {
 
-        _cleanup_free_ char *firmware_date = NULL;
+        usec_t firmware_date = USEC_INFINITY;
 
         (void) get_firmware_date(&firmware_date);
 
-        return sd_bus_message_append(reply, "s", firmware_date);
+        return sd_bus_message_append(reply, "t", firmware_date);
 }
 static int property_get_hostname(
                 sd_bus *bus,
@@ -1189,7 +1245,8 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
 static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
                 *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
-                *firmware_vendor = NULL, *firmware_date = NULL;
+                *firmware_vendor = NULL;
+        usec_t firmware_date = USEC_INFINITY;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         sd_id128_t product_uuid = SD_ID128_NULL;
@@ -1277,7 +1334,7 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                                        JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)),
                                        JSON_BUILD_PAIR("FirmwareVersion", JSON_BUILD_STRING(firmware_version)),
                                        JSON_BUILD_PAIR("FirmwareVendor", JSON_BUILD_STRING(firmware_vendor)),
-                                       JSON_BUILD_PAIR("FirmwareDate", JSON_BUILD_STRING(firmware_date)),
+                                       JSON_BUILD_PAIR_FINITE_USEC("FirmwareDate", firmware_date),
                                        JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
                                        JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
 
@@ -1320,7 +1377,7 @@ static const sd_bus_vtable hostname_vtable[] = {
         SD_BUS_PROPERTY("HardwareModel", "s", property_get_hardware_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("FirmwareVersion", "s", property_get_firmware_version, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("FirmwareVendor", "s", property_get_firmware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("FirmwareDate", "s", property_get_firmware_date, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("FirmwareDate", "t", property_get_firmware_date, 0, SD_BUS_VTABLE_PROPERTY_CONST),
 
         SD_BUS_METHOD_WITH_ARGS("SetHostname",
                                 SD_BUS_ARGS("s", hostname, "b", interactive),
index 2382cccb3a03fa3e53dfdfc718d9d86672d44030..221068f284f7adbbe99fe1e83c46ccb6273143df 100755 (executable)
@@ -89,10 +89,51 @@ test_chassis() {
     fi
 }
 
+restore_sysfs_dmi() {
+    umount /sys/class/dmi/id
+    rm -rf /run/systemd/system/systemd-hostnamed.service.d
+    systemctl daemon-reload
+    systemctl stop systemd-hostnamed
+}
+
+test_firmware_date() {
+    # No DMI on s390x or ppc
+    if [[ ! -d /sys/class/dmi/id ]]; then
+        echo "/sys/class/dmi/id not found, skipping firmware date tests."
+        return 0
+    fi
+
+    trap restore_sysfs_dmi RETURN
+
+    # Ignore /sys being mounted as tmpfs
+    mkdir -p /run/systemd/system/systemd-hostnamed.service.d/
+    cat >/run/systemd/system/systemd-hostnamed.service.d/override.conf <<EOF
+[Service]
+Environment="SYSTEMD_DEVICE_VERIFY_SYSFS=0"
+EOF
+    systemctl daemon-reload
+
+    mount -t tmpfs none /sys/class/dmi/id
+    echo '1' > /sys/class/dmi/id/uevent
+
+    echo '01/01/2000' > /sys/class/dmi/id/bios_date
+    systemctl stop systemd-hostnamed
+    assert_in '2000-01-01' "$(hostnamectl)"
+
+    echo '2022' > /sys/class/dmi/id/bios_date
+    systemctl stop systemd-hostnamed
+    assert_not_in 'Firmware Date' "$(hostnamectl)"
+
+    echo 'garbage' > /sys/class/dmi/id/bios_date
+    systemctl stop systemd-hostnamed
+    assert_not_in 'Firmware Date' "$(hostnamectl)"
+}
+
 : >/failed
 
 test_hostname
 test_chassis
+test_firmware_date
 
 touch /testok
 rm /failed