hostnamed: expose FirmwareDate as timestamp in dbus

Offer the firmware date as an epoch instead of the literal DMI string.

Closes #25679
This commit is contained in:
Jelle van der Waa 2023-01-13 17:33:12 +01:00 committed by Lennart Poettering
parent 0bdf70f3fa
commit ad8858c1f7
4 changed files with 117 additions and 8 deletions

View 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 { ... };

View 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) },
{}
};

View 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),

View file

@ -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