hostname: add hardware Stock-Keeping Unit

Add support for the hardware Stock-Keeping Unit (SKU). The SKU describes
a distinct type of hardware for sale, purchase or inventory management.
The value is read from the file /etc/machine-info or DMI as fallback.

The integration provides an unified interface to collect detail hardware
information. The /etc/machine-info entry enables embedded devices
without UEFI support to read the information from a custom store.
This commit is contained in:
Stefan Herbrechtsmeier
2025-04-30 09:31:42 +02:00
parent 9daa1dbcce
commit 79e9fb68a3
7 changed files with 90 additions and 9 deletions

View File

@@ -23,6 +23,16 @@ dmi:*:pnTobefilledbyO.E.M.:*
dmi:*:pnToBeFilledByO.E.M.:*
ID_PRODUCT_NAME_IS_RUBBISH=1
dmi:*:skuDefaultstring:*
dmi:*:skuDefault string:*
dmi:*:skuN/A:*
dmi:*:skuO.E.M.:*
dmi:*:skuOEM:*
dmi:*:skuTobefilledbyO.E.M.:*
dmi:*:skuToBeFilledByO.E.M.:*
dmi:*:skuTo Be Filled By O.E.M.:*
ID_PRODUCT_SKU_IS_RUBBISH=1
dmi:*:catDefaultstring:*
dmi:*:catDefault string:*
dmi:*:catN/A:*

View File

@@ -154,6 +154,15 @@
<xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>HARDWARE_SKU=</varname></term>
<listitem><para>Specifies the hardware SKU (Stock-Keeping Unit). If unspecified, the hardware
SKU set in DMI will be used.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@@ -94,6 +94,8 @@ node /org/freedesktop/hostname1 {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s HardwareModel = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s HardwareSKU = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s FirmwareVersion = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s FirmwareVendor = '...';
@@ -180,6 +182,8 @@ node /org/freedesktop/hostname1 {
<variablelist class="dbus-property" generated="True" extra-ref="HardwareModel"/>
<variablelist class="dbus-property" generated="True" extra-ref="HardwareSKU"/>
<variablelist class="dbus-property" generated="True" extra-ref="FirmwareVersion"/>
<variablelist class="dbus-property" generated="True" extra-ref="FirmwareVendor"/>
@@ -306,9 +310,11 @@ node /org/freedesktop/hostname1 {
to the <varname>IMAGE_ID=</varname> and <varname>IMAGE_VERSION=</varname> fields of the
<filename>os-release</filename> file.</para>
<para><varname>HardwareVendor</varname> and <varname>HardwareModel</varname> expose information about the
vendor of the hardware of the system. If no such information can be determined these properties are set
to empty strings.</para>
<para><varname>HardwareVendor</varname>, <varname>HardwareModel</varname>, and <varname>HardwareSKU</varname>
expose vendor information about the hardware of the system. The stock keeping unit (SKU) describes a
distinct type of hardware for sale, purchase or inventory management. The SKU is only available if it
deviates from the model. If no such information can be determined these properties are set to empty
strings.</para>
<para><varname>FirmwareVersion</varname> and <varname>FirmwareVendor</varname> expose information about
the system's firmware, i.e. a version string and a vendor name. If no such information can be determined
@@ -473,8 +479,9 @@ node /org/freedesktop/hostname1 {
<varname>FirmwareDate</varname> were added in version 253.</para>
<para><varname>MachineID</varname>, <varname>BootID</varname> and
<varname>VSockCID</varname> were added in version 256.</para>
<para><varname>ChassisAssetTag</varname>, <varname>OperatingSystemImageID</varname> and
<varname>OperatingSystemImageVersion</varname> were added in version 258.</para>
<para><varname>ChassisAssetTag</varname>, <varname>OperatingSystemImageID</varname>,
<varname>OperatingSystemImageVersion</varname>, and <varname>HardwareSKU</varname>
were added in version 258.</para>
</refsect2>
</refsect1>

View File

@@ -11,6 +11,9 @@ ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="product_version", ENV{ID_MODEL}="$attr{product_v
ENV{ID_VENDOR}=="", ENV{ID_VENDOR}="$attr{board_vendor}"
ENV{ID_MODEL}=="", ENV{ID_MODEL}="$attr{board_name}"
# stock keeping unit
ENV{ID_PRODUCT_SKU_IS_RUBBISH}!="1", ENV{ID_SKU}="$attr{product_sku}"
# chassis asset tag
ENV{MODALIAS}!="", ATTR{chassis_asset_tag}!="", IMPORT{builtin}="hwdb '$attr{modalias}cat$attr{chassis_asset_tag}:'"
ENV{ID_CHASSIS_ASSET_TAG_IS_RUBBISH}!="1", ENV{ID_CHASSIS_ASSET_TAG}="$attr{chassis_asset_tag}"

View File

@@ -66,6 +66,7 @@ typedef struct StatusInfo {
const char *hardware_serial;
sd_id128_t product_uuid;
uint32_t vsock_cid;
const char *hardware_sku;
} StatusInfo;
static const char* chassis_string_to_glyph(const char *chassis) {
@@ -320,6 +321,14 @@ static int print_status_info(StatusInfo *i) {
return table_log_add_error(r);
}
if (!isempty(i->hardware_sku)) {
r = table_add_many(table,
TABLE_FIELD, "Hardware SKU",
TABLE_STRING, i->hardware_sku);
if (r < 0)
return table_log_add_error(r);
}
if (!isempty(i->firmware_version)) {
r = table_add_many(table,
TABLE_FIELD, "Firmware Version",
@@ -413,6 +422,7 @@ static int show_all_names(sd_bus *bus) {
{ "HomeURL", "s", NULL, offsetof(StatusInfo, home_url) },
{ "HardwareVendor", "s", NULL, offsetof(StatusInfo, hardware_vendor) },
{ "HardwareModel", "s", NULL, offsetof(StatusInfo, hardware_model) },
{ "HardwareSKU", "s", NULL, offsetof(StatusInfo, hardware_sku) },
{ "FirmwareVersion", "s", NULL, offsetof(StatusInfo, firmware_version) },
{ "FirmwareDate", "t", NULL, offsetof(StatusInfo, firmware_date) },
{ "MachineID", "ay", bus_map_id128, offsetof(StatusInfo, machine_id) },

View File

@@ -66,6 +66,7 @@ typedef enum {
PROP_LOCATION,
PROP_HARDWARE_VENDOR,
PROP_HARDWARE_MODEL,
PROP_HARDWARE_SKU,
/* Read from /etc/os-release (or /usr/lib/os-release) */
PROP_OS_PRETTY_NAME,
@@ -170,7 +171,8 @@ static void context_read_machine_info(Context *c) {
(UINT64_C(1) << PROP_DEPLOYMENT) |
(UINT64_C(1) << PROP_LOCATION) |
(UINT64_C(1) << PROP_HARDWARE_VENDOR) |
(UINT64_C(1) << PROP_HARDWARE_MODEL));
(UINT64_C(1) << PROP_HARDWARE_MODEL) |
(UINT64_C(1) << PROP_HARDWARE_SKU));
r = parse_env_file(NULL, "/etc/machine-info",
"PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
@@ -179,7 +181,8 @@ static void context_read_machine_info(Context *c) {
"DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
"LOCATION", &c->data[PROP_LOCATION],
"HARDWARE_VENDOR", &c->data[PROP_HARDWARE_VENDOR],
"HARDWARE_MODEL", &c->data[PROP_HARDWARE_MODEL]);
"HARDWARE_MODEL", &c->data[PROP_HARDWARE_MODEL],
"HARDWARE_SKU", &c->data[PROP_HARDWARE_SKU]);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
@@ -356,6 +359,27 @@ static int get_hardware_model(Context *c, char **ret) {
return get_dmi_properties(c, STRV_MAKE_CONST("ID_MODEL_FROM_DATABASE", "ID_MODEL"), ret);
}
static int get_hardware_sku(Context *c, char **ret) {
_cleanup_free_ char *model = NULL, *sku = NULL;
int r;
r = get_dmi_property(c, "ID_SKU", &sku);
if (r < 0)
return r;
/* Suppress reporting the SKU field, if it's the same string as the
* model field, which it appears to be on various systems */
r = get_hardware_model(c, &model);
if (r < 0) {
if (r != -ENOENT)
return r;
} else if (streq_ptr(sku, model))
return -ENOENT;
*ret = TAKE_PTR(sku);
return 0;
}
static int get_sysattr(sd_device *device, const char *key, char **ret) {
const char *s;
int r;
@@ -824,7 +848,8 @@ static int property_get_hardware_property(
assert(reply);
assert(c);
assert(IN_SET(prop, PROP_HARDWARE_VENDOR, PROP_HARDWARE_MODEL));
assert(IN_SET(prop, PROP_HARDWARE_VENDOR, PROP_HARDWARE_MODEL,
PROP_HARDWARE_SKU));
assert(getter);
context_read_machine_info(c);
@@ -859,6 +884,18 @@ static int property_get_hardware_model(
return property_get_hardware_property(reply, userdata, PROP_HARDWARE_MODEL, get_hardware_model);
}
static int property_get_hardware_sku(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
return property_get_hardware_property(reply, userdata, PROP_HARDWARE_SKU, get_hardware_sku);
}
static int property_get_firmware_version(
sd_bus *bus,
const char *path,
@@ -1539,7 +1576,7 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
static int build_describe_response(Context *c, bool privileged, sd_json_variant **ret) {
_cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL,
*chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
*firmware_vendor = NULL, *chassis_asset_tag = NULL;
*firmware_vendor = NULL, *chassis_asset_tag = NULL, *sku = NULL;
_cleanup_strv_free_ char **os_release_pairs = NULL, **machine_info_pairs = NULL;
usec_t firmware_date = USEC_INFINITY, eol = USEC_INFINITY;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
@@ -1575,6 +1612,8 @@ static int build_describe_response(Context *c, bool privileged, sd_json_variant
(void) get_hardware_vendor(c, &vendor);
if (isempty(c->data[PROP_HARDWARE_MODEL]))
(void) get_hardware_model(c, &model);
if (isempty(c->data[PROP_HARDWARE_SKU]))
(void) get_hardware_sku(c, &sku);
if (privileged) {
/* The product UUID and hardware serial is only available to privileged clients */
@@ -1628,6 +1667,7 @@ static int build_describe_response(Context *c, bool privileged, sd_json_variant
SD_JSON_BUILD_PAIR_STRING("HardwareVendor", vendor ?: c->data[PROP_HARDWARE_VENDOR]),
SD_JSON_BUILD_PAIR_STRING("HardwareModel", model ?: c->data[PROP_HARDWARE_MODEL]),
SD_JSON_BUILD_PAIR_STRING("HardwareSerial", serial),
SD_JSON_BUILD_PAIR_STRING("HardwareSKU", sku ?: c->data[PROP_HARDWARE_SKU]),
SD_JSON_BUILD_PAIR_STRING("FirmwareVersion", firmware_version),
SD_JSON_BUILD_PAIR_STRING("FirmwareVendor", firmware_vendor),
JSON_BUILD_PAIR_FINITE_USEC("FirmwareDate", firmware_date),
@@ -1699,6 +1739,7 @@ static const sd_bus_vtable hostname_vtable[] = {
SD_BUS_PROPERTY("OperatingSystemImageVersion", "s", property_get_os_release_field, offsetof(Context, data[PROP_OS_IMAGE_VERSION]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HardwareVendor", "s", property_get_hardware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HardwareModel", "s", property_get_hardware_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("HardwareSKU", "s", property_get_hardware_sku, 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", "t", property_get_firmware_date, 0, SD_BUS_VTABLE_PROPERTY_CONST),

View File

@@ -27,6 +27,7 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_DEFINE_OUTPUT(HardwareVendor, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_OUTPUT(HardwareModel, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_OUTPUT(HardwareSerial, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_OUTPUT(HardwareSKU, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_OUTPUT(FirmwareVersion, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_OUTPUT(FirmwareVendor, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_OUTPUT(FirmwareDate, SD_VARLINK_INT, SD_VARLINK_NULLABLE),