]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
analyze: add verb for showing system's CHIDs
authorLennart Poettering <lennart@poettering.net>
Fri, 15 Nov 2024 10:38:30 +0000 (11:38 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 18 Dec 2024 16:38:42 +0000 (17:38 +0100)
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.

man/systemd-analyze.xml
shell-completion/bash/systemd-analyze
src/analyze/analyze-chid.c [new file with mode: 0644]
src/analyze/analyze-chid.h [new file with mode: 0644]
src/analyze/analyze.c
src/analyze/meson.build
src/fundamental/chid-fundamental.c
src/fundamental/chid-fundamental.h
test/units/TEST-65-ANALYZE.sh

index 68d006a1cefcbf9253f5d85bd551a5386f7fcb37..cf005db70b46e8a5f6af3152762f3de72071b12e 100644 (file)
       <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>
index caec77e7186019cef30a8623f61b815aedd79993..41a2151e12757851592107ba1317e621a9de914c 100644 (file)
@@ -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 (file)
index 0000000..5bb50f5
--- /dev/null
@@ -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 (file)
index 0000000..a3f40c6
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_chid(int argc, char *argv[], void *userdata);
index e21f12c65e703faf37567e7332c829f6f0b9905f..ad3dd446abbe5b21d57503322f9bcd2d48e7f1d0 100644 (file)
@@ -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              },
                 {}
         };
 
index c42db1a63360d4f69cd4457b01bb570d58208c8b..5a7c6c9285a8691acd21add07cd2e6f3a5908958 100644 (file)
@@ -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',
index 01045176f5227913d6cca10c2af48bd0a95b0e62..9c719a334dd426e8f266f78f9b6e1ec70940644a 100644 (file)
@@ -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) |
index 1e582932fd29335a0a71d0a8f1433c857dc3c366..41f7be337dda2caf5b7c366aa5278abd2a2b721a 100644 (file)
@@ -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]);
index 2fa368a67886f0d055f30ab42712e97fc238356e..ac39c42da4847fc02a9ea00120cb2db99f5c78d4 100755 (executable)
@@ -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