<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>
<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>
--- /dev/null
+/* 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;
+}
#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"
" 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"
{ "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 },
{}
};