diff options
author | Lennart Poettering <lennart@poettering.net> | 2024-11-15 11:38:30 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2024-12-18 17:38:42 +0100 |
commit | 8f114904fc0ee5b3d4b12cfb0231d90d5142f9c0 (patch) | |
tree | b39f20f86e85a131ceccee59ea68b09eb18155c6 | |
parent | man: Document generator sandbox environment (diff) | |
download | systemd-8f114904fc0ee5b3d4b12cfb0231d90d5142f9c0.tar.xz systemd-8f114904fc0ee5b3d4b12cfb0231d90d5142f9c0.zip |
analyze: add verb for showing system's CHIDs
We have the code already, expose it in systemd-analyze too.
This should make it easier to debug the CHID use in the UKIs with
onboard tooling.
-rw-r--r-- | man/systemd-analyze.xml | 36 | ||||
-rw-r--r-- | shell-completion/bash/systemd-analyze | 2 | ||||
-rw-r--r-- | src/analyze/analyze-chid.c | 224 | ||||
-rw-r--r-- | src/analyze/analyze-chid.h | 4 | ||||
-rw-r--r-- | src/analyze/analyze.c | 3 | ||||
-rw-r--r-- | src/analyze/meson.build | 1 | ||||
-rw-r--r-- | src/fundamental/chid-fundamental.c | 2 | ||||
-rw-r--r-- | src/fundamental/chid-fundamental.h | 2 | ||||
-rwxr-xr-x | test/units/TEST-65-ANALYZE.sh | 5 |
9 files changed, 277 insertions, 2 deletions
diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 68d006a1ce..cf005db70b 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -205,6 +205,11 @@ <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="plain">smbios11</arg> </cmdsynopsis> + <cmdsynopsis> + <command>systemd-analyze</command> + <arg choice="opt" rep="repeat">OPTIONS</arg> + <arg choice="plain">chid</arg> + </cmdsynopsis> </refsynopsisdiv> <refsect1> @@ -1084,6 +1089,37 @@ io.systemd.credential:vmm.notify_socket=vsock-stream:2:254570042 <xi:include href="version-info.xml" xpointer="v257"/> </refsect2> + <refsect2> + <title><command>systemd-analyze chid</command></title> + + <para>Shows a list of Computer Hardware IDs (CHIDs) of the local system. These IDs identify the + system's computer hardware, based on SMBIOS data. See <ulink + url="https://learn.microsoft.com/en-us/windows-hardware/drivers/dashboard/using-chids">Using Computer + Hardware IDs (CHIDs)</ulink> for details about CHIDs.</para> + + <example> + <title>Example output</title> + <programlisting>$ systemd-analyze chid +TYPE INPUT CHID + 3 MFPSmp 520537c0-3b59-504f-b062-9682ea236b21 + 4 MFPS-- edf05dc8-a53d-5b2c-8023-630bca2a2463 + 5 MFP--- ebc6a4d9-ec48-537a-916b-c69fa4fdd814 + 6 M--Smp 5ebe4bba-f598-5e90-9ff2-9fd0d3211465 + 7 M--S-- 1a3fb835-b42a-5f9c-a38c-eff5bfd5c41d + 8 M-P-mp 2a831dce-8163-5bad-8406-435b8c752dd8 + 9 M-P--- 7c21c878-4a75-50f7-9816-21e811588da0 + 10 MF--mp 9a003537-bcc5-500e-b10a-8d8892e4fc64 + 11 MF---- bb9122bb-8a5c-50d2-a742-a85beb719909 + 13 M---mp bfc36935-5032-5987-a0a3-6311f01de33a + +LEGEND: M → sys_vendor (LENOVO) ┄ F → product_family (ThinkPad X1 Carbon Gen 9) ┄ P → product_name (20XW0055GE) + S → product_sku (LENOVO_MT_20XW_BU_Think_FM_ThinkPad X1 Carbon Gen 9) ┄ m → board_vendor (LENOVO) + p → board_name (20XW0055GE)</programlisting> + </example> + + <xi:include href="version-info.xml" xpointer="v258"/> + </refsect2> + </refsect1> <refsect1> diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze index caec77e718..41a2151e12 100644 --- a/shell-completion/bash/systemd-analyze +++ b/shell-completion/bash/systemd-analyze @@ -67,7 +67,7 @@ _systemd_analyze() { ) local -A VERBS=( - [STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk has-tpm2 smbios11' + [STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk has-tpm2 smbios11 chid' [CRITICAL_CHAIN]='critical-chain' [DOT]='dot' [DUMP]='dump' diff --git a/src/analyze/analyze-chid.c b/src/analyze/analyze-chid.c new file mode 100644 index 0000000000..5bb50f54d8 --- /dev/null +++ b/src/analyze/analyze-chid.c @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-chid.h" +#include "chid-fundamental.h" +#include "efi-api.h" +#include "fd-util.h" +#include "fileio.h" +#include "format-table.h" +#include "parse-util.h" +#include "strv.h" +#include "utf8.h" +#include "virt.h" + +static int parse_chid_type(const char *s, size_t *ret) { + unsigned u; + int r; + + assert(s); + + r = safe_atou(s, &u); + if (r < 0) + return r; + if (u >= CHID_TYPES_MAX) + return -ERANGE; + + if (ret) + *ret = u; + + return 0; +} + +static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = { + [CHID_SMBIOS_MANUFACTURER] = 'M', + [CHID_SMBIOS_FAMILY] = 'F', + [CHID_SMBIOS_PRODUCT_NAME] = 'P', + [CHID_SMBIOS_PRODUCT_SKU] = 'S', + [CHID_SMBIOS_BASEBOARD_MANUFACTURER] = 'm', + [CHID_SMBIOS_BASEBOARD_PRODUCT] = 'p', +}; + +static char *chid_smbios_fields_string(uint32_t combination) { + _cleanup_free_ char *s = NULL; + + for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) { + char c; + + c = (combination & (UINT32_C(1) << f)) ? chid_smbios_fields_char[f] : '-'; + + if (!strextend(&s, CHAR_TO_STR(c))) + return NULL; + } + + return TAKE_PTR(s); +} + +static int add_chid(Table *table, const EFI_GUID guids[static CHID_TYPES_MAX], size_t t) { + int r; + + assert(table); + assert(guids); + assert(t < CHID_TYPES_MAX); + + sd_id128_t id = efi_guid_to_id128(guids + t); + + if (sd_id128_is_null(id)) + return 0; + + _cleanup_free_ char *flags = chid_smbios_fields_string(chid_smbios_table[t]); + if (!flags) + return log_oom(); + + r = table_add_many(table, + TABLE_UINT, (unsigned) t, + TABLE_STRING, flags, + TABLE_UUID, id); + if (r < 0) + return table_log_add_error(r); + + return 0; +} + +static void smbios_fields_free(char16_t *(*fields)[_CHID_SMBIOS_FIELDS_MAX]) { + assert(fields); + + FOREACH_ARRAY(i, *fields, _CHID_SMBIOS_FIELDS_MAX) + free(*i); +} + +int verb_chid(int argc, char *argv[], void *userdata) { + + static const char *const smbios_files[_CHID_SMBIOS_FIELDS_MAX] = { + [CHID_SMBIOS_MANUFACTURER] = "sys_vendor", + [CHID_SMBIOS_FAMILY] = "product_family", + [CHID_SMBIOS_PRODUCT_NAME] = "product_name", + [CHID_SMBIOS_PRODUCT_SKU] = "product_sku", + [CHID_SMBIOS_BASEBOARD_MANUFACTURER] = "board_vendor", + [CHID_SMBIOS_BASEBOARD_PRODUCT] = "board_name", + }; + + _cleanup_(table_unrefp) Table *table = NULL; + int r; + + if (detect_container() > 0) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Container environments do not have SMBIOS."); + + table = table_new("type", "input", "chid"); + if (!table) + return log_oom(); + + (void) table_set_align_percent(table, table_get_cell(table, 0, 0), 100); + (void) table_set_align_percent(table, table_get_cell(table, 0, 1), 50); + + _cleanup_close_ int smbios_fd = open("/sys/class/dmi/id", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (smbios_fd < 0) + return log_error_errno(errno, "Failed to open SMBIOS sysfs object: %m"); + + _cleanup_(smbios_fields_free) char16_t* smbios_fields[_CHID_SMBIOS_FIELDS_MAX] = {}; + for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) { + _cleanup_free_ char *buf = NULL; + size_t size; + + r = read_virtual_file_at(smbios_fd, smbios_files[f], SIZE_MAX, &buf, &size); + if (r < 0) + return log_error_errno(r, "Failed to read SMBIOS field '%s': %m", smbios_files[f]); + + if (size < 1 || buf[size-1] != '\n') + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected SMBIOS field '%s' to end in newline, but it doesn't, refusing.", smbios_files[f]); + + size--; + + smbios_fields[f] = utf8_to_utf16(buf, size); + if (!smbios_fields[f]) + return log_oom(); + } + + EFI_GUID chids[CHID_TYPES_MAX] = {}; + chid_calculate((const char16_t* const*) smbios_fields, chids); + + if (strv_isempty(strv_skip(argv, 1))) + for (size_t t = 0; t < CHID_TYPES_MAX; t++) { + r = add_chid(table, chids, t); + if (r < 0) + return r; + } + else { + STRV_FOREACH(as, strv_skip(argv, 1)) { + size_t t; + r = parse_chid_type(*as, &t); + if (r < 0) + return log_error_errno(r, "Failed to pare CHID type: %s", *as); + + r = add_chid(table, chids, t); + if (r < 0) + return r; + } + + (void) table_set_sort(table, (size_t) 0); + } + + r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); + if (r < 0) + return log_error_errno(r, "Failed to output table: %m"); + + if (!sd_json_format_enabled(arg_json_format_flags)) { + _cleanup_free_ char *legend = NULL; + bool separator = false; + size_t w = 0; + + legend = strjoin(ansi_grey(), "LEGEND: ", ansi_normal()); + if (!legend) + return log_oom(); + + for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) { + _cleanup_free_ char *c = utf16_to_utf8(smbios_fields[f], SIZE_MAX); + if (!c) + return log_oom(); + + if (!strextend(&legend, + ansi_grey(), + separator ? " " : "", + separator ? special_glyph(SPECIAL_GLYPH_HORIZONTAL_DOTTED) : "", + separator ? " " : "", + ansi_normal(), + CHAR_TO_STR(chid_smbios_fields_char[f]), + ansi_grey(), + " ", + special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), + " ", + ansi_normal(), + smbios_files[f], + ansi_grey(), + " (", + ansi_highlight(), + c, + ansi_grey(), + ")", + ansi_normal())) + return log_oom(); + + w += separator * 3 + + 4 + + utf8_console_width(smbios_files[f]) + + 2 + + utf8_console_width(c) + + 1; + + if (w > 79) { + if (!strextend(&legend, "\n ")) + return log_oom(); + + separator = false; + w = 8; + } else + separator = true; + + } + + putchar('\n'); + puts(legend); + } + + return EXIT_SUCCESS; +} diff --git a/src/analyze/analyze-chid.h b/src/analyze/analyze-chid.h new file mode 100644 index 0000000000..a3f40c6013 --- /dev/null +++ b/src/analyze/analyze-chid.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_chid(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index e21f12c65e..ad3dd446ab 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -18,6 +18,7 @@ #include "analyze-calendar.h" #include "analyze-capability.h" #include "analyze-cat-config.h" +#include "analyze-chid.h" #include "analyze-compare-versions.h" #include "analyze-condition.h" #include "analyze-critical-chain.h" @@ -219,6 +220,7 @@ static int help(int argc, char *argv[], void *userdata) { " filesystems [NAME...] List known filesystems\n" " architectures [NAME...] List known architectures\n" " smbios11 List strings passed via SMBIOS Type #11\n" + " chid List local CHIDs\n" "\n%3$sExpression Evaluation:%4$s\n" " condition CONDITION... Evaluate conditions and asserts\n" " compare-versions VERSION1 [OP] VERSION2\n" @@ -691,6 +693,7 @@ static int run(int argc, char *argv[]) { { "srk", VERB_ANY, 1, 0, verb_srk }, { "architectures", VERB_ANY, VERB_ANY, 0, verb_architectures }, { "smbios11", VERB_ANY, 1, 0, verb_smbios11 }, + { "chid", VERB_ANY, VERB_ANY, 0, verb_chid }, {} }; diff --git a/src/analyze/meson.build b/src/analyze/meson.build index c42db1a633..5a7c6c9285 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -6,6 +6,7 @@ systemd_analyze_sources = files( 'analyze-calendar.c', 'analyze-capability.c', 'analyze-cat-config.c', + 'analyze-chid.c', 'analyze-compare-versions.c', 'analyze-condition.c', 'analyze-critical-chain.c', diff --git a/src/fundamental/chid-fundamental.c b/src/fundamental/chid-fundamental.c index 01045176f5..9c719a334d 100644 --- a/src/fundamental/chid-fundamental.c +++ b/src/fundamental/chid-fundamental.c @@ -61,7 +61,7 @@ static void get_chid(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIE ret_chid->Data4[0] = (ret_chid->Data4[0] & UINT8_C(0x3f)) | UINT8_C(0x80); } -static const uint32_t chid_smbios_table[CHID_TYPES_MAX] = { +const uint32_t chid_smbios_table[CHID_TYPES_MAX] = { [3] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) | (UINT32_C(1) << CHID_SMBIOS_FAMILY) | (UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) | diff --git a/src/fundamental/chid-fundamental.h b/src/fundamental/chid-fundamental.h index 1e582932fd..41f7be337d 100644 --- a/src/fundamental/chid-fundamental.h +++ b/src/fundamental/chid-fundamental.h @@ -23,5 +23,7 @@ typedef enum ChidSmbiosFields { _CHID_SMBIOS_FIELDS_MAX, } ChidSmbiosFields; +extern const uint32_t chid_smbios_table[CHID_TYPES_MAX]; + /* CHID (also called HWID by fwupd) is described at https://github.com/fwupd/fwupd/blob/main/docs/hwids.md */ void chid_calculate(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], EFI_GUID ret_chids[static CHID_TYPES_MAX]); diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh index 2fa368a678..ac39c42da4 100755 --- a/test/units/TEST-65-ANALYZE.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -990,6 +990,11 @@ systemd-analyze architectures uname systemd-analyze smbios11 systemd-analyze smbios11 -q +if test -f /sys/class/dmi/id/board_vendor && ! systemd-detect-virt --container ; then + systemd-analyze chid + systemd-analyze chid --json=pretty +fi + systemd-analyze condition --instance=tmp --unit=systemd-growfs@.service systemd-analyze verify --instance=tmp --man=no systemd-growfs@.service systemd-analyze security --instance=tmp systemd-growfs@.service |