master
c 1,281 lines 45.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "status-file-dmi.h"
4
5 static void dmi_clean_field_placeholder(char *buf, size_t buf_size) {
6 if(!buf || !buf_size) return;
7
8 struct {
9 const char *found;
10 const char *replace;
11 } placeholders[] = {
12 {"$(DEFAULT_STRING)", ""},
13 {"Chassis Manufacture", ""},
14 {"Chassis Manufacturer", ""},
15 {"Chassis Version", ""},
16 {"Default string", ""},
17 {"N/A", ""},
18 {"NA", ""},
19 {"NOT SPECIFIED", ""},
20 {"No Enclosure", ""},
21 {"None Provided", ""},
22 {"None", ""},
23 {"OEM Chassis Manufacturer", ""},
24 {"OEM Default string000", ""},
25 {"OEM", ""},
26 {"OEM_MB", ""},
27 {"SYSTEM_MANUFACTURER", ""},
28 {"SmbiosType1_SystemManufacturer", ""},
29 {"SmbiosType2_BoardManufacturer", ""},
30 {"Standard", ""},
31 {"System Product Name", ""},
32 {"System UUID", ""},
33 {"System Version", ""},
34 {"System manufacturer", ""},
35 {"TBD by OEM", ""},
36 {"TBD", ""},
37 {"To be filled by O.E.M.", ""},
38 {"Type2 - Board Manufacturer", ""},
39 {"Type2 - Board Vendor Name1", ""},
40 {"Unknow", ""},
41 {"Unknown", ""},
42 {"XXXXX", ""},
43 {"default", ""},
44 {"empty", ""},
45 {"unspecified", ""},
46 {"x.x", ""},
47 {"(null)", ""},
48 {"0123456789", ""},
49 {"SKU", ""},
50 };
51
52 for (size_t i = 0; i < _countof(placeholders); i++) {
53 if (strcasecmp(buf, placeholders[i].found) == 0) {
54 strcatz(buf, 0, placeholders[i].replace, buf_size);
55 break;
56 }
57 }
58 }
59
60 static void dmi_clean_field(char *buf, size_t buf_size) {
61 if(!buf || !buf_size) return;
62
63 // replace non-ascii characters and control characters with spaces
64 for (size_t i = 0; i < buf_size && buf[i]; i++) {
65 if (!isascii((uint8_t)buf[i]) || iscntrl((uint8_t)buf[i]))
66 buf[i] = ' ';
67 }
68
69 // detect if all characters are symbols
70 bool contains_alnum = false;
71 for (size_t i = 0; i < buf_size && buf[i]; i++) {
72 if (isalnum((uint8_t)buf[i])) {
73 contains_alnum = true;
74 break;
75 }
76 }
77
78 if (!contains_alnum || !buf[0]) {
79 buf[0] = '\0';
80 return;
81 }
82
83 // remove leading, trailing and duplicate spaces
84 trim_all(buf);
85 if (!buf[0])
86 return;
87
88 dmi_clean_field_placeholder(buf, buf_size);
89 }
90
91 // --------------------------------------------------------------------------------------------------------------------
92
93 #if defined(OS_LINUX)
94 static void linux_get_dmi_field(const char *field, const char *alt, char *dst, size_t dst_size) {
95 char filename[FILENAME_MAX];
96 dst[0] = '\0';
97
98 if (netdata_configured_host_prefix && *netdata_configured_host_prefix) {
99 snprintfz(filename, sizeof(filename), "%s/sys/class/dmi/id/%s", netdata_configured_host_prefix, field);
100 if (access(filename, R_OK) != 0) {
101 snprintfz(
102 filename, sizeof(filename), "%s/sys/devices/virtual/dmi/id/%s", netdata_configured_host_prefix, field);
103 if (access(filename, R_OK) != 0)
104 filename[0] = '\0';
105 }
106 } else
107 filename[0] = '\0';
108
109 if (!filename[0]) {
110 snprintfz(filename, sizeof(filename), "/sys/class/dmi/id/%s", field);
111 if (access(filename, R_OK) != 0) {
112 snprintfz(filename, sizeof(filename), "/sys/devices/virtual/dmi/id/%s", field);
113 if (access(filename, R_OK) != 0) {
114 if (alt && *alt) {
115 safecpy(filename, alt);
116 if (access(filename, R_OK) != 0)
117 filename[0] = '\0';
118 } else
119 filename[0] = '\0';
120 }
121 }
122 }
123
124 if (!filename[0])
125 return;
126
127 size_t buf_size = MAX(256, dst_size);
128 CLEAN_CHAR_P *buf = mallocz(buf_size);
129 if (read_txt_file(filename, buf, buf_size) != 0)
130 return;
131
132 if (!buf[0])
133 return;
134
135 dmi_clean_field(buf, buf_size);
136
137 if (!buf[0])
138 return;
139
140 // copy it to its final location
141 strcatz(dst, 0, buf, dst_size);
142 }
143
144 void os_dmi_info_get(DMI_INFO *dmi) {
145 if (!dmi) return;
146
147 linux_get_dmi_field("sys_vendor", NULL, dmi->sys.vendor, sizeof(dmi->sys.vendor));
148 linux_get_dmi_field("system_serial", NULL, dmi->sys.serial, sizeof(dmi->sys.serial));
149 linux_get_dmi_field("product_uuid", NULL, dmi->sys.uuid, sizeof(dmi->sys.uuid));
150 linux_get_dmi_field("chassis_asset_tag", NULL, dmi->sys.asset_tag, sizeof(dmi->sys.asset_tag));
151
152 linux_get_dmi_field("product_name", "/proc/device-tree/model", dmi->product.name, sizeof(dmi->product.name));
153 linux_get_dmi_field("product_version", NULL, dmi->product.version, sizeof(dmi->product.version));
154 linux_get_dmi_field("product_sku", NULL, dmi->product.sku, sizeof(dmi->product.sku));
155 linux_get_dmi_field("product_family", NULL, dmi->product.family, sizeof(dmi->product.family));
156
157 linux_get_dmi_field("chassis_vendor", NULL, dmi->chassis.vendor, sizeof(dmi->chassis.vendor));
158 linux_get_dmi_field("chassis_version", NULL, dmi->chassis.version, sizeof(dmi->chassis.version));
159 linux_get_dmi_field("chassis_serial", NULL, dmi->chassis.serial, sizeof(dmi->chassis.serial));
160 linux_get_dmi_field("chassis_asset_tag", NULL, dmi->chassis.asset_tag, sizeof(dmi->chassis.asset_tag));
161
162 linux_get_dmi_field("board_vendor", NULL, dmi->board.vendor, sizeof(dmi->board.vendor));
163 linux_get_dmi_field("board_name", NULL, dmi->board.name, sizeof(dmi->board.name));
164 linux_get_dmi_field("board_version", NULL, dmi->board.version, sizeof(dmi->board.version));
165 linux_get_dmi_field("board_serial", NULL, dmi->board.serial, sizeof(dmi->board.serial));
166 linux_get_dmi_field("board_asset_tag", NULL, dmi->board.asset_tag, sizeof(dmi->board.asset_tag));
167
168 linux_get_dmi_field("bios_vendor", NULL, dmi->bios.vendor, sizeof(dmi->bios.vendor));
169 linux_get_dmi_field("bios_version", NULL, dmi->bios.version, sizeof(dmi->bios.version));
170 linux_get_dmi_field("bios_date", NULL, dmi->bios.date, sizeof(dmi->bios.date));
171 linux_get_dmi_field("bios_release", NULL, dmi->bios.release, sizeof(dmi->bios.release));
172
173 linux_get_dmi_field("chassis_type", NULL, dmi->chassis.type, sizeof(dmi->chassis.type));
174
175 }
176 #elif defined(OS_MACOS)
177
178 #include <IOKit/IOKitLib.h>
179 #include <CoreFoundation/CoreFoundation.h>
180 #include <sys/sysctl.h>
181
182 // Helper function to safely convert CF types to C strings
183 static void cf_string_to_cstr(CFTypeRef cf_val, char *buffer, size_t buffer_size) {
184 if (!buffer || buffer_size == 0)
185 return;
186
187 buffer[0] = '\0';
188
189 // Safety check for null CF reference
190 if (!cf_val)
191 return;
192
193 // Pre-set the last byte to ensure termination even if conversion fails
194 buffer[buffer_size - 1] = '\0';
195
196 if (CFGetTypeID(cf_val) == CFStringGetTypeID()) {
197 CFStringRef str_ref = (CFStringRef)cf_val;
198 // Use CFStringGetCString with len-1 to ensure space for null terminator
199 if (!CFStringGetCString(str_ref, buffer, buffer_size - 1, kCFStringEncodingUTF8))
200 buffer[0] = '\0'; // Reset on failure
201 }
202 else if (CFGetTypeID(cf_val) == CFDataGetTypeID()) {
203 CFDataRef data_ref = (CFDataRef)cf_val;
204 CFIndex length = CFDataGetLength(data_ref);
205 if (length > 0 && length < buffer_size - 1) {
206 const UInt8 *bytes = CFDataGetBytePtr(data_ref);
207 memcpy(buffer, bytes, length);
208 buffer[length] = '\0';
209 }
210 }
211
212 dmi_clean_field(buffer, buffer_size);
213 }
214
215 // Get a string property from an IOKit registry entry safely
216 static void get_iokit_string_property(io_registry_entry_t entry, CFStringRef key, char *buffer, size_t buffer_size) {
217 // Initialize buffer to empty
218 if (!buffer || buffer_size == 0 || !entry || !key) {
219 if (buffer && buffer_size > 0)
220 buffer[0] = '\0';
221
222 return;
223 }
224
225 buffer[0] = '\0';
226
227 // Get the property
228 CFTypeRef property = IORegistryEntryCreateCFProperty(entry, key, kCFAllocatorDefault, 0);
229 if (property) {
230 cf_string_to_cstr(property, buffer, buffer_size);
231 CFRelease(property); // Always release the CF object
232 }
233 }
234
235 // Get a string property from an IOKit registry entry's parent safely
236 static void get_parent_iokit_string_property(io_registry_entry_t entry, CFStringRef key,
237 char *buffer, size_t buffer_size,
238 const io_name_t plane) {
239 // Initialize buffer to empty
240 if (!buffer || buffer_size == 0 || !entry || !key || !plane) {
241 if (buffer && buffer_size > 0)
242 buffer[0] = '\0';
243
244 return;
245 }
246
247 buffer[0] = '\0';
248
249 io_registry_entry_t parent;
250
251 // Get parent entry
252 kern_return_t result = IORegistryEntryGetParentEntry(entry, plane, &parent);
253 if (result == KERN_SUCCESS) {
254 get_iokit_string_property(parent, key, buffer, buffer_size);
255 IOObjectRelease(parent); // Always release the IO object
256 }
257 }
258
259 // Get hardware info from IODeviceTree
260 static void get_devicetree_info(DMI_INFO *dmi) {
261 // Initialize all relevant fields to empty
262 if (!dmi)
263 return;
264
265 dmi->product.name[0] = '\0';
266 dmi->board.name[0] = '\0';
267 dmi->sys.vendor[0] = '\0';
268 dmi->product.family[0] = '\0';
269
270 // Get the device tree
271 io_registry_entry_t device_tree = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/");
272 if (!device_tree)
273 return;
274
275 // Get model information - only operate if device_tree is valid
276 get_iokit_string_property(device_tree, CFSTR("model"), dmi->product.name, sizeof(dmi->product.name));
277
278 // Get board ID if available
279 get_iokit_string_property(device_tree, CFSTR("board-id"), dmi->board.name, sizeof(dmi->board.name));
280
281 // Look for platform information
282 io_registry_entry_t platform = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/platform");
283 if (platform) {
284 // Platform information can sometimes have manufacturer info
285 get_iokit_string_property(platform, CFSTR("manufacturer"), dmi->sys.vendor, sizeof(dmi->sys.vendor));
286
287 // Check compatible property for additional info
288 char compatible[256] = {0};
289 get_iokit_string_property(platform, CFSTR("compatible"), compatible, sizeof(compatible));
290
291 // Parse for product family
292 if (compatible[0]) {
293 char *family = strstr(compatible, ",");
294 if (family && family < compatible + sizeof(compatible) - 1) {
295 family++; // Skip the comma
296 safecpy(dmi->product.family, family);
297 dmi_clean_field(dmi->product.family, sizeof(dmi->product.family));
298 }
299 }
300
301 IOObjectRelease(platform);
302 }
303
304 IOObjectRelease(device_tree);
305 }
306
307 // Get hardware info from IOPlatformExpertDevice
308 static void get_platform_expert_info(DMI_INFO *dmi) {
309 if (!dmi)
310 return;
311
312 io_registry_entry_t platform_expert = IORegistryEntryFromPath(
313 kIOMasterPortDefault, "IOService:/IOResources/IOPlatformExpertDevice");
314
315 if (!platform_expert)
316 return;
317
318 // System vendor - almost always "Apple Inc." for Macs
319 get_iokit_string_property(platform_expert, CFSTR("manufacturer"),
320 dmi->sys.vendor, sizeof(dmi->sys.vendor));
321
322 // Product name
323 get_iokit_string_property(platform_expert, CFSTR("model"),
324 dmi->product.name, sizeof(dmi->product.name));
325
326 // Model number - can be used as product version
327 get_iokit_string_property(platform_expert, CFSTR("model-number"),
328 dmi->product.version, sizeof(dmi->product.version));
329
330 // Board name sometimes available
331 get_iokit_string_property(platform_expert, CFSTR("board-id"),
332 dmi->board.name, sizeof(dmi->board.name));
333
334 // System serial number - might be available as IOPlatformSerialNumber
335 get_iokit_string_property(platform_expert, CFSTR("IOPlatformSerialNumber"),
336 dmi->sys.serial, sizeof(dmi->sys.serial));
337
338 // Hardware UUID can be useful to include
339 char uuid_str[64] = {0};
340 get_iokit_string_property(platform_expert, CFSTR("IOPlatformUUID"), uuid_str, sizeof(uuid_str));
341
342 if (uuid_str[0])
343 safecpy(dmi->sys.uuid, uuid_str);
344
345 // For asset tag, try alternative properties since Apple doesn't provide a specific asset tag property
346 // First try chassis tag property if available
347 char asset_tag[64] = {0};
348 get_iokit_string_property(platform_expert, CFSTR("IOPlatformChassisTag"), asset_tag, sizeof(asset_tag));
349
350 // If not available, try using the serial number as an asset tag if not already set
351 if (!asset_tag[0]) {
352 // Model identifier can be a reasonable alternative for asset tag
353 get_iokit_string_property(platform_expert, CFSTR("model"), asset_tag, sizeof(asset_tag));
354 }
355
356 if (asset_tag[0] && (!dmi->sys.serial[0] || strcmp(asset_tag, dmi->sys.serial) != 0)) {
357 safecpy(dmi->sys.asset_tag, asset_tag);
358 }
359
360 // Get device type to determine chassis type
361 char device_type[64] = {0};
362 get_iokit_string_property(platform_expert, CFSTR("device_type"), device_type, sizeof(device_type));
363
364 // Set chassis type based on device_type safely
365 if (device_type[0]) {
366 if (strcasestr(device_type, "laptop") || strcasestr(device_type, "book"))
367 safecpy(dmi->chassis.type, "9");
368 else if (strcasestr(device_type, "server"))
369 safecpy(dmi->chassis.type, "17");
370 else if (strcasestr(device_type, "imac"))
371 safecpy(dmi->chassis.type, "13");
372 else if (strcasestr(device_type, "mac"))
373 safecpy(dmi->chassis.type, "3");
374 }
375
376 // If chassis type not set, guess based on product name
377 if (!dmi->chassis.type[0] && dmi->product.name[0]) {
378 if (strcasestr(dmi->product.name, "book"))
379 safecpy(dmi->chassis.type, "9");
380 else if (strcasestr(dmi->product.name, "imac"))
381 safecpy(dmi->chassis.type, "13");
382 else if (strcasestr(dmi->product.name, "mac") && strcasestr(dmi->product.name, "pro"))
383 safecpy(dmi->chassis.type, "3");
384 else if (strcasestr(dmi->product.name, "mac") && strcasestr(dmi->product.name, "mini"))
385 safecpy(dmi->chassis.type, "35");
386 }
387
388 IOObjectRelease(platform_expert);
389 }
390
391 // Get SMC revision and system firmware info
392 static void get_firmware_info(DMI_INFO *dmi) {
393 if (!dmi)
394 return;
395
396 io_registry_entry_t smc = IOServiceGetMatchingService(
397 kIOMasterPortDefault, IOServiceMatching("AppleSMC"));
398
399 if (smc) {
400 // SMC revision - can be useful for firmware info
401 char smc_version[64] = {0};
402 get_iokit_string_property(smc, CFSTR("smc-version"), smc_version, sizeof(smc_version));
403
404 if (smc_version[0]) {
405 safecpy(dmi->bios.version, smc_version);
406 dmi_clean_field(dmi->bios.version, sizeof(dmi->bios.version));
407 }
408
409 IOObjectRelease(smc);
410 }
411
412 // Check for BIOS information in IODeviceTree:/rom
413 io_registry_entry_t rom = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/rom");
414 if (rom) {
415 // "version" contains firmware version
416 get_iokit_string_property(rom, CFSTR("version"), dmi->bios.version, sizeof(dmi->bios.version));
417
418 // Apple is the vendor for all Mac firmware
419 safecpy(dmi->bios.vendor, "Apple");
420
421 // "release-date" can sometimes be found
422 get_iokit_string_property(rom, CFSTR("release-date"), dmi->bios.date, sizeof(dmi->bios.date));
423
424 IOObjectRelease(rom);
425 }
426
427 // If we still don't have BIOS version, check system version from sysctl
428 if (!dmi->bios.version[0]) {
429 char firmware_version[256] = {0};
430 size_t len = sizeof(firmware_version) - 1;
431
432 if (sysctlbyname("machdep.cpu.brand_string", firmware_version, &len, NULL, 0) == 0) {
433 firmware_version[len] = '\0'; // Ensure null termination
434
435 // Extract firmware info if present
436 char *firmware_info = strstr(firmware_version, "SMC:");
437 if (firmware_info && firmware_info < firmware_version + sizeof(firmware_version) - 1) {
438 safecpy(dmi->bios.version, firmware_info);
439 dmi_clean_field(dmi->bios.version, sizeof(dmi->bios.version));
440 }
441 }
442 }
443 }
444
445 // Get system hardware information using sysctl
446 static void get_sysctl_info(DMI_INFO *dmi) {
447 if (!dmi)
448 return;
449
450 // Get model identifier using sysctl if not already set
451 if (!dmi->product.name[0]) {
452 char model[256] = { 0 };
453 size_t len = sizeof(model) - 1;
454
455 if (sysctlbyname("hw.model", model, &len, NULL, 0) == 0) {
456 model[len] = '\0';
457 safecpy(dmi->product.name, model);
458 dmi_clean_field(dmi->product.name, sizeof(dmi->product.name));
459
460 // If chassis type is still not set, guess from model
461 if (!dmi->chassis.type[0]) {
462 if (strncasecmp(model, "MacBook", 7) == 0)
463 safecpy(dmi->chassis.type, "9");
464 else if (strncasecmp(model, "iMac", 4) == 0)
465 safecpy(dmi->chassis.type, "13");
466 else if (strncasecmp(model, "Mac", 3) == 0 && strcasestr(model, "Pro") != NULL)
467 safecpy(dmi->chassis.type, "3");
468 else if (strncasecmp(model, "Mac", 3) == 0 && strcasestr(model, "mini") != NULL)
469 safecpy(dmi->chassis.type, "35");
470 else
471 safecpy(dmi->chassis.type, "3"); // Default to desktop
472 }
473 }
474 }
475
476 // Get CPU information if board name not set
477 if (!dmi->board.name[0]) {
478 char cpu_brand[256] = {0};
479 size_t len = sizeof(cpu_brand) - 1;
480
481 if (sysctlbyname("machdep.cpu.brand_string", cpu_brand, &len, NULL, 0) == 0) {
482 cpu_brand[len] = '\0'; // Ensure null termination
483 // Use CPU information as part of board info if not available
484 safecpy(dmi->board.name, cpu_brand);
485 dmi_clean_field(dmi->board.name, sizeof(dmi->board.name));
486 }
487 }
488 }
489
490 // Main function to get hardware info
491 void os_dmi_info_get(DMI_INFO *dmi) {
492 if (!dmi) return;
493
494 // Always set Apple as default vendor
495 safecpy(dmi->sys.vendor, "Apple");
496
497 // Get info from IOPlatformExpertDevice
498 get_platform_expert_info(dmi);
499
500 // Get info from IODeviceTree
501 get_devicetree_info(dmi);
502
503 // Get firmware information
504 get_firmware_info(dmi);
505
506 // Get additional info from sysctl
507 get_sysctl_info(dmi);
508
509 // Set board vendor to match system vendor if not set
510 if (!dmi->board.vendor[0] && dmi->sys.vendor[0])
511 safecpy(dmi->board.vendor, dmi->sys.vendor);
512
513 // Set chassis vendor to match system vendor if not set
514 if (!dmi->chassis.vendor[0] && dmi->sys.vendor[0])
515 safecpy(dmi->chassis.vendor, dmi->sys.vendor);
516
517 // Set bios vendor to match system vendor if not set
518 if (!dmi->bios.vendor[0] && dmi->sys.vendor[0])
519 safecpy(dmi->bios.vendor, dmi->sys.vendor);
520
521 // Default product name if all methods failed
522 if (!dmi->product.name[0])
523 safecpy(dmi->product.name, "Mac");
524
525 // Default chassis type if we couldn't determine it
526 if (!dmi->chassis.type[0])
527 safecpy(dmi->chassis.type, "3"); // Desktop
528
529 }
530
531 #elif defined(OS_FREEBSD)
532
533 #include <sys/types.h>
534 #include <sys/sysctl.h>
535 #include <kenv.h>
536
537 static void freebsd_get_sysctl_str(const char *name, char *dst, size_t dst_size) {
538 size_t len = dst_size - 1; // Reserve space for null terminator
539 if (sysctlbyname(name, dst, &len, NULL, 0) == 0 && len < dst_size) {
540 dst[len] = '\0'; // Ensure null termination
541 dmi_clean_field(dst, dst_size);
542 }
543 else
544 dst[0] = '\0';
545 }
546
547 static void freebsd_get_kenv_str(const char *name, char *dst, size_t dst_size) {
548 dst[0] = '\0';
549 if (kenv(KENV_GET, name, dst, dst_size - 1) == -1)
550 dst[0] = '\0';
551 else {
552 dst[dst_size - 1] = '\0';
553 dmi_clean_field(dst, dst_size);
554 }
555 }
556
557 void os_dmi_info_get(DMI_INFO *dmi) {
558 if (!dmi) return;
559
560 // System information from SMBIOS
561 freebsd_get_sysctl_str("hw.vendor", dmi->sys.vendor, sizeof(dmi->sys.vendor));
562 freebsd_get_sysctl_str("hw.product", dmi->product.name, sizeof(dmi->product.name));
563 freebsd_get_sysctl_str("hw.version", dmi->product.version, sizeof(dmi->product.version));
564 freebsd_get_sysctl_str("hw.serial", dmi->sys.serial, sizeof(dmi->sys.serial));
565
566 // Try using kenv for additional information
567 freebsd_get_kenv_str("smbios.system.maker", dmi->sys.vendor, sizeof(dmi->sys.vendor));
568 freebsd_get_kenv_str("smbios.system.product", dmi->product.name, sizeof(dmi->product.name));
569 freebsd_get_kenv_str("smbios.system.version", dmi->product.version, sizeof(dmi->product.version));
570 freebsd_get_kenv_str("smbios.system.sku", dmi->product.sku, sizeof(dmi->product.sku));
571 freebsd_get_kenv_str("smbios.system.family", dmi->product.family, sizeof(dmi->product.family));
572 freebsd_get_kenv_str("smbios.system.serial", dmi->sys.serial, sizeof(dmi->sys.serial));
573
574 // Board information
575 freebsd_get_kenv_str("smbios.planar.maker", dmi->board.vendor, sizeof(dmi->board.vendor));
576 freebsd_get_kenv_str("smbios.planar.product", dmi->board.name, sizeof(dmi->board.name));
577 freebsd_get_kenv_str("smbios.planar.version", dmi->board.version, sizeof(dmi->board.version));
578 freebsd_get_kenv_str("smbios.planar.serial", dmi->board.serial, sizeof(dmi->board.serial));
579
580 // BIOS information
581 freebsd_get_kenv_str("smbios.bios.vendor", dmi->bios.vendor, sizeof(dmi->bios.vendor));
582 freebsd_get_kenv_str("smbios.bios.version", dmi->bios.version, sizeof(dmi->bios.version));
583 freebsd_get_kenv_str("smbios.bios.reldate", dmi->bios.date, sizeof(dmi->bios.date));
584 freebsd_get_kenv_str("smbios.bios.release", dmi->bios.release, sizeof(dmi->bios.release));
585
586 // Chassis information
587 freebsd_get_kenv_str("smbios.chassis.maker", dmi->chassis.vendor, sizeof(dmi->chassis.vendor));
588 freebsd_get_kenv_str("smbios.chassis.version", dmi->chassis.version, sizeof(dmi->chassis.version));
589 freebsd_get_kenv_str("smbios.chassis.serial", dmi->chassis.serial, sizeof(dmi->chassis.serial));
590
591 // Chassis type
592 freebsd_get_kenv_str("smbios.chassis.type", dmi->chassis.type, sizeof(dmi->chassis.type));
593
594 // If we couldn't get system information from SMBIOS, try to use model
595 if (!dmi->product.name[0])
596 freebsd_get_sysctl_str("hw.model", dmi->product.name, sizeof(dmi->product.name));
597
598 // Try to get asset tags
599 freebsd_get_kenv_str("smbios.system.asset_tag", dmi->sys.asset_tag, sizeof(dmi->sys.asset_tag));
600 freebsd_get_kenv_str("smbios.chassis.asset_tag", dmi->chassis.asset_tag, sizeof(dmi->chassis.asset_tag));
601 freebsd_get_kenv_str("smbios.planar.asset_tag", dmi->board.asset_tag, sizeof(dmi->board.asset_tag));
602
603 // Get UUID
604 freebsd_get_kenv_str("smbios.system.uuid", dmi->sys.uuid, sizeof(dmi->sys.uuid));
605
606 }
607
608 #elif defined(OS_WINDOWS)
609
610 #include <windows.h>
611
612 // Helper function to safely read a registry string value
613 static void windows_read_registry_string(HKEY key_base, const char *subkey_path,
614 const char *value_name, char *dst, size_t dst_size) {
615 HKEY key;
616 DWORD type;
617 DWORD size = dst_size;
618
619 // Initialize output buffer to empty string
620 dst[0] = '\0';
621
622 // Open the registry key
623 if (RegOpenKeyExA(key_base, subkey_path, 0, KEY_READ, &key) != ERROR_SUCCESS)
624 return;
625
626 // Read the registry value
627 if (RegQueryValueExA(key, value_name, NULL, &type, (LPBYTE)dst, &size) == ERROR_SUCCESS) {
628 if ((type == REG_SZ || type == REG_EXPAND_SZ || type == REG_MULTI_SZ) && size > 0) {
629
630 if (size >= dst_size)
631 size = dst_size - 1;
632
633 dst[size] = '\0';
634 dmi_clean_field(dst, dst_size);
635 }
636 }
637
638 RegCloseKey(key);
639 }
640
641 // SMBIOS structure header definition
642 typedef struct {
643 uint8_t type;
644 uint8_t length;
645 uint16_t handle;
646 // Remaining fields are variable
647 } smbios_header_t;
648
649 // SMBIOS data container
650 typedef struct {
651 BYTE *data; // The raw SMBIOS data buffer
652 DWORD size; // Size of the data buffer
653 DWORD entries_count; // Number of valid structure entries found
654 bool valid; // Whether the data is valid
655 } smbios_data_t;
656
657 // Helper to safely parse SMBIOS strings with improved bounds checking
658 static char *get_smbios_string(const BYTE *table_start, DWORD table_size,
659 const char *string_start, uint8_t index,
660 char *buffer, size_t buffer_size) {
661 if (!string_start || !buffer || buffer_size == 0 || index == 0) {
662 if (buffer && buffer_size > 0)
663 buffer[0] = '\0';
664 return NULL;
665 }
666
667 buffer[0] = '\0';
668
669 // Check if string_start is within bounds of the table
670 if (string_start < (const char*)table_start ||
671 string_start >= (const char*)(table_start + table_size)) {
672 return NULL;
673 }
674
675 // Navigate to the indexed string
676 const char *s = string_start;
677 const char *end = (const char*)(table_start + table_size);
678 uint8_t current_index = 1;
679
680 // Set a reasonable limit to prevent infinite loops with corrupted data
681 const int MAX_ITERATIONS = 100;
682 int iterations = 0;
683
684 while (current_index < index && s < end && iterations < MAX_ITERATIONS) {
685 // Skip to end of current string
686 while (s < end && *s != '\0')
687 s++;
688
689 // Skip past terminating null
690 if (s < end)
691 s++;
692
693 // If we hit the end of strings section (double null) or table boundary
694 if (s >= end || *s == '\0')
695 return NULL;
696
697 current_index++;
698 iterations++;
699 }
700
701 // If we found our string
702 if (current_index == index && s < end && *s != '\0') {
703 // Copy safely with explicit bounds checking
704 size_t i = 0;
705 while (s < end && *s != '\0' && i < buffer_size - 1) {
706 buffer[i++] = *s++;
707 }
708 buffer[i] = '\0';
709
710 dmi_clean_field(buffer, buffer_size);
711 return buffer;
712 }
713
714 return NULL;
715 }
716
717 // Get SMBIOS data with proper memory management
718 static smbios_data_t get_smbios_data(void) {
719 smbios_data_t result = {NULL, 0, 0, false};
720
721 // Request SMBIOS data size
722 DWORD size = GetSystemFirmwareTable('RSMB', 0, NULL, 0);
723 if (size == 0 || size > 1024*1024) // Sanity check on size
724 return result;
725
726 // Allocate memory with zero initialization
727 result.data = (BYTE *)mallocz(size);
728 if (!result.data)
729 return result;
730
731 result.size = size;
732
733 // Get the SMBIOS data
734 DWORD bytes_read = GetSystemFirmwareTable('RSMB', 0, result.data, result.size);
735 if (bytes_read == 0 || bytes_read > result.size) {
736 freez(result.data);
737 result.data = NULL;
738 result.size = 0;
739 return result;
740 }
741
742 result.valid = true;
743 return result;
744 }
745
746 // Process BIOS Information (Type 0)
747 static void process_smbios_bios_info(const smbios_header_t *header,
748 DMI_INFO *dmi,
749 const char *string_table,
750 const BYTE *smbios_data,
751 DWORD smbios_size) {
752 if (header->length < 18 || !dmi) // Minimum size for BIOS info
753 return;
754
755 const BYTE *data = (const BYTE *)header;
756 char temp_str[256];
757
758 // BIOS Vendor (string index at offset 4)
759 if (data[4] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
760 data[4], temp_str, sizeof(temp_str))) {
761 safecpy(dmi->bios.vendor, temp_str);
762 }
763
764 // BIOS Version (string index at offset 5)
765 if (data[5] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
766 data[5], temp_str, sizeof(temp_str))) {
767 safecpy(dmi->bios.version, temp_str);
768 }
769
770 // BIOS Release Date (string index at offset 8)
771 if (data[8] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
772 data[8], temp_str, sizeof(temp_str))) {
773 safecpy(dmi->bios.date, temp_str);
774 }
775 }
776
777 // Process System Information (Type 1)
778 static void process_smbios_system_info(const smbios_header_t *header,
779 DMI_INFO *dmi,
780 const char *string_table,
781 const BYTE *smbios_data,
782 DWORD smbios_size) {
783 if (header->length < 8 || !dmi) // Minimum size for System info
784 return;
785
786 const BYTE *data = (const BYTE *)header;
787 char temp_str[256];
788
789 // Manufacturer (string index at offset 4)
790 if (data[4] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
791 data[4], temp_str, sizeof(temp_str))) {
792 safecpy(dmi->sys.vendor, temp_str);
793 }
794
795 // Product Name (string index at offset 5)
796 if (data[5] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
797 data[5], temp_str, sizeof(temp_str))) {
798 safecpy(dmi->product.name, temp_str);
799 }
800
801 // Version (string index at offset 6)
802 if (data[6] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
803 data[6], temp_str, sizeof(temp_str))) {
804 safecpy(dmi->product.version, temp_str);
805 }
806
807 // Serial Number (string index at offset 7)
808 if (data[7] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
809 data[7], temp_str, sizeof(temp_str))) {
810 safecpy(dmi->sys.serial, temp_str);
811 }
812
813 // If structure is long enough for family (SMBIOS 2.1+)
814 if (header->length >= 25 && data[21] > 0 &&
815 get_smbios_string(smbios_data, smbios_size, string_table,
816 data[21], temp_str, sizeof(temp_str))) {
817 safecpy(dmi->product.family, temp_str);
818 }
819
820 // Extract system UUID (16 bytes starting at offset 8)
821 if (header->length >= 24) {
822 // Verify we have enough data to access all bytes safely
823 if ((uintptr_t)data + 23 < (uintptr_t)smbios_data + smbios_size) {
824 // UUID is stored as 16 bytes, we'll format it as a standard UUID string
825 char uuid_str[40] = {0}; // 36 chars for UUID + null terminator
826
827 // Note: SMBIOS UUID needs byte swapping for first three fields
828 int ret = snprintf(uuid_str, sizeof(uuid_str),
829 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
830 data[11], data[10], data[9], data[8], // field 1 (byte swapped)
831 data[13], data[12], // field 2 (byte swapped)
832 data[15], data[14], // field 3 (byte swapped)
833 data[16], data[17], // field 4 (not swapped)
834 data[18], data[19], data[20], data[21], data[22], data[23]); // field 5 (not swapped)
835
836 // Verify the formatting worked and buffer wasn't truncated
837 if (ret > 0 && ret < (int)sizeof(uuid_str)) {
838 // Only set if not all zeros (some systems report all zeros)
839 if (uuid_str[0] != '0' || uuid_str[1] != '0' || uuid_str[2] != '0' || uuid_str[3] != '0') {
840 safecpy(dmi->sys.uuid, uuid_str);
841 }
842 }
843 }
844 }
845
846 // Asset Tag is sometimes available in newer SMBIOS versions
847 if (header->length >= 27 && data[26] > 0 &&
848 get_smbios_string(smbios_data, smbios_size, string_table,
849 data[26], temp_str, sizeof(temp_str))) {
850 safecpy(dmi->sys.asset_tag, temp_str);
851 }
852 }
853
854 // Process Baseboard Information (Type 2)
855 static void process_smbios_baseboard_info(const smbios_header_t *header,
856 DMI_INFO *dmi,
857 const char *string_table,
858 const BYTE *smbios_data,
859 DWORD smbios_size) {
860 if (header->length < 8 || !dmi) // Minimum size for Baseboard info
861 return;
862
863 const BYTE *data = (const BYTE *)header;
864 char temp_str[256];
865
866 // Manufacturer (string index at offset 4)
867 if (data[4] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
868 data[4], temp_str, sizeof(temp_str))) {
869 safecpy(dmi->board.vendor, temp_str);
870 }
871
872 // Product (string index at offset 5)
873 if (data[5] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
874 data[5], temp_str, sizeof(temp_str))) {
875 safecpy(dmi->board.name, temp_str);
876 }
877
878 // Version (string index at offset 6)
879 if (data[6] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
880 data[6], temp_str, sizeof(temp_str))) {
881 safecpy(dmi->board.version, temp_str);
882 }
883
884 // Serial Number (string index at offset 7)
885 if (data[7] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
886 data[7], temp_str, sizeof(temp_str))) {
887 safecpy(dmi->board.serial, temp_str);
888 }
889
890 // Asset Tag (string index at offset 8)
891 if (header->length >= 9 && data[8] > 0 &&
892 get_smbios_string(smbios_data, smbios_size, string_table,
893 data[8], temp_str, sizeof(temp_str))) {
894 safecpy(dmi->board.asset_tag, temp_str);
895 }
896 }
897
898 // Process Chassis Information (Type 3)
899 static void process_smbios_chassis_info(const smbios_header_t *header,
900 DMI_INFO *dmi,
901 const char *string_table,
902 const BYTE *smbios_data,
903 DWORD smbios_size) {
904 if (header->length < 9 || !dmi) // Minimum size for Chassis info
905 return;
906
907 const BYTE *data = (const BYTE *)header;
908 char temp_str[256];
909
910 // Manufacturer (string index at offset 4)
911 if (data[4] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
912 data[4], temp_str, sizeof(temp_str))) {
913 safecpy(dmi->chassis.vendor, temp_str);
914 }
915
916 // Type (numerical value at offset 5)
917 BYTE chassis_type = data[5] & 0x7F; // Mask out MSB
918 snprintf(dmi->chassis.type, sizeof(dmi->chassis.type), "%d", chassis_type);
919
920 // Version (string index at offset 6)
921 if (data[6] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
922 data[6], temp_str, sizeof(temp_str))) {
923 safecpy(dmi->chassis.version, temp_str);
924 }
925
926 // Serial Number (string index at offset 7)
927 if (data[7] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
928 data[7], temp_str, sizeof(temp_str))) {
929 safecpy(dmi->chassis.serial, temp_str);
930 }
931
932 // Asset Tag (string index at offset 8)
933 if (header->length >= 9 && data[8] > 0 &&
934 get_smbios_string(smbios_data, smbios_size, string_table,
935 data[8], temp_str, sizeof(temp_str))) {
936 safecpy(dmi->chassis.asset_tag, temp_str);
937 }
938 }
939
940 // Process a complete SMBIOS structure
941 static void process_smbios_structure(const smbios_header_t *header,
942 DMI_INFO *dmi,
943 const char *string_table,
944 const BYTE *smbios_data,
945 DWORD smbios_size) {
946 if (!dmi) return;
947
948 switch (header->type) {
949 case 0: // BIOS Information
950 process_smbios_bios_info(header, dmi, string_table, smbios_data, smbios_size);
951 break;
952
953 case 1: // System Information
954 process_smbios_system_info(header, dmi, string_table, smbios_data, smbios_size);
955 break;
956
957 case 2: // Baseboard Information
958 process_smbios_baseboard_info(header, dmi, string_table, smbios_data, smbios_size);
959 break;
960
961 case 3: // Chassis Information
962 process_smbios_chassis_info(header, dmi, string_table, smbios_data, smbios_size);
963 break;
964 }
965 }
966
967 // Parse all SMBIOS structures with robust error handling
968 static void parse_smbios_structures(const smbios_data_t smbios, DMI_INFO *dmi) {
969 if (!smbios.valid || !smbios.data || smbios.size < 8 || !dmi)
970 return;
971
972 // The SMBIOS header is at offset 8
973 const BYTE *current = smbios.data + 8;
974 const BYTE *end = smbios.data + smbios.size;
975
976 // Track already visited types to avoid duplicates in corrupted tables
977 uint8_t visited[256] = {0};
978 int structures_parsed = 0;
979
980 // Temporary buffer for string parsing
981 char temp_str[256];
982
983 while (current + sizeof(smbios_header_t) <= end) {
984 const smbios_header_t *header = (const smbios_header_t *)current;
985
986 // Basic sanity checks
987 if (header->length < sizeof(smbios_header_t) || current + header->length > end) {
988 break; // Invalid structure
989 }
990
991 // If we've already seen this type and want to skip duplicates
992 if (visited[header->type]) {
993 // Find the string terminator (double NULL)
994 const char *strings = (const char *)(current + header->length);
995 const char *str_end = strings;
996 bool found_terminator = false;
997
998 // Set a reasonable limit to prevent infinite loops
999 const int MAX_STRING_SEARCH = 1000;
1000 int search_count = 0;
1001
1002 // Search with explicit bounds check for string terminator
1003 while (str_end + 1 < (const char *)end && !found_terminator && search_count < MAX_STRING_SEARCH) {
1004 if (str_end[0] == 0 && str_end[1] == 0) {
1005 found_terminator = true;
1006 break;
1007 }
1008 str_end++;
1009 search_count++;
1010 }
1011
1012 if (found_terminator) {
1013 // Advance to next structure
1014 current = (const BYTE *)(str_end + 1);
1015 continue;
1016 } else {
1017 break; // Corrupt data
1018 }
1019 }
1020
1021 // Mark this type as processed
1022 visited[header->type] = 1;
1023 structures_parsed++;
1024
1025 // Process the structure based on type
1026 const char *string_table = (const char *)(current + header->length);
1027 if (string_table < (const char *)end) {
1028 process_smbios_structure(header, dmi, string_table, smbios.data, smbios.size);
1029 }
1030
1031 // Find string table end (double null terminator)
1032 const char *str_end = string_table;
1033 bool found_terminator = false;
1034
1035 // Set a reasonable limit to prevent infinite loops
1036 const int MAX_STRING_SEARCH = 1000;
1037 int search_count = 0;
1038
1039 // Search for end of strings with explicit bounds check
1040 const char *end_char = (const char *)end;
1041 while (str_end + 1 < end_char && !found_terminator && search_count < MAX_STRING_SEARCH) {
1042 if (str_end[0] == 0 && str_end[1] == 0) {
1043 found_terminator = true;
1044 break;
1045 }
1046 str_end++;
1047 search_count++;
1048 }
1049
1050 if (!found_terminator) {
1051 break; // Corrupt data
1052 }
1053
1054 // Move to the next structure
1055 current = (const BYTE *)(str_end + 1);
1056
1057 // Check for end marker or out of bounds
1058 if (current >= end || *current == 127)
1059 break;
1060 }
1061 }
1062 static void windows_get_smbios_info(DMI_INFO *dmi) {
1063 if (!dmi) return;
1064
1065 // Get SMBIOS data using our improved container structure
1066 smbios_data_t smbios = get_smbios_data();
1067 if (!smbios.valid)
1068 return;
1069
1070 // Process the SMBIOS data with our improved parser
1071 parse_smbios_structures(smbios, dmi);
1072
1073 // Clean up
1074 freez(smbios.data);
1075 }
1076
1077 // Fallback method using registry
1078 static void windows_get_registry_info(DMI_INFO *dmi) {
1079 if (!dmi) return;
1080
1081 // System manufacturer and model from registry
1082 windows_read_registry_string(
1083 HKEY_LOCAL_MACHINE,
1084 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1085 "SystemManufacturer",
1086 dmi->sys.vendor,
1087 sizeof(dmi->sys.vendor)
1088 );
1089
1090 windows_read_registry_string(
1091 HKEY_LOCAL_MACHINE,
1092 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1093 "SystemProductName",
1094 dmi->product.name,
1095 sizeof(dmi->product.name)
1096 );
1097
1098 // System Serial Number
1099 windows_read_registry_string(
1100 HKEY_LOCAL_MACHINE,
1101 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1102 "SystemSerialNumber",
1103 dmi->sys.serial,
1104 sizeof(dmi->sys.serial)
1105 );
1106
1107 // BIOS information
1108 windows_read_registry_string(
1109 HKEY_LOCAL_MACHINE,
1110 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1111 "BIOSVendor",
1112 dmi->bios.vendor,
1113 sizeof(dmi->bios.vendor)
1114 );
1115
1116 windows_read_registry_string(
1117 HKEY_LOCAL_MACHINE,
1118 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1119 "BIOSVersion",
1120 dmi->bios.version,
1121 sizeof(dmi->bios.version)
1122 );
1123
1124 windows_read_registry_string(
1125 HKEY_LOCAL_MACHINE,
1126 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1127 "BIOSReleaseDate",
1128 dmi->bios.date,
1129 sizeof(dmi->bios.date)
1130 );
1131
1132 // Board information
1133 windows_read_registry_string(
1134 HKEY_LOCAL_MACHINE,
1135 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1136 "BaseBoardManufacturer",
1137 dmi->board.vendor,
1138 sizeof(dmi->board.vendor)
1139 );
1140
1141 windows_read_registry_string(
1142 HKEY_LOCAL_MACHINE,
1143 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1144 "BaseBoardProduct",
1145 dmi->board.name,
1146 sizeof(dmi->board.name)
1147 );
1148
1149 windows_read_registry_string(
1150 HKEY_LOCAL_MACHINE,
1151 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1152 "BaseBoardVersion",
1153 dmi->board.version,
1154 sizeof(dmi->board.version)
1155 );
1156
1157 // Base Board Serial
1158 windows_read_registry_string(
1159 HKEY_LOCAL_MACHINE,
1160 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1161 "BaseBoardSerialNumber",
1162 dmi->board.serial,
1163 sizeof(dmi->board.serial)
1164 );
1165
1166 // Chassis Serial (might not be available in registry, but try anyway)
1167 windows_read_registry_string(
1168 HKEY_LOCAL_MACHINE,
1169 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1170 "ChassisSerialNumber",
1171 dmi->chassis.serial,
1172 sizeof(dmi->chassis.serial)
1173 );
1174
1175 }
1176
1177 // Main function to get hardware information
1178 void os_dmi_info_get(DMI_INFO *dmi) {
1179 if (!dmi) return;
1180
1181 // First try SMBIOS data through firmware table API
1182 windows_get_smbios_info(dmi);
1183
1184 // Try registry as a fallback or to fill missing values
1185 windows_get_registry_info(dmi);
1186
1187 // Get asset tags if not set by SMBIOS
1188 if (!dmi->sys.asset_tag[0]) {
1189 windows_read_registry_string(
1190 HKEY_LOCAL_MACHINE,
1191 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1192 "SystemAssetTag",
1193 dmi->sys.asset_tag,
1194 sizeof(dmi->sys.asset_tag)
1195 );
1196 }
1197
1198 if (!dmi->board.asset_tag[0]) {
1199 windows_read_registry_string(
1200 HKEY_LOCAL_MACHINE,
1201 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1202 "BaseBoardAssetTag",
1203 dmi->board.asset_tag,
1204 sizeof(dmi->board.asset_tag)
1205 );
1206 }
1207
1208 if (!dmi->chassis.asset_tag[0]) {
1209 windows_read_registry_string(
1210 HKEY_LOCAL_MACHINE,
1211 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1212 "ChassisAssetTag",
1213 dmi->chassis.asset_tag,
1214 sizeof(dmi->chassis.asset_tag)
1215 );
1216 }
1217
1218
1219 // If chassis type is not set or not a valid number, set a default
1220 if (!dmi->chassis.type[0] || atoi(dmi->chassis.type) <= 0) {
1221 // Check common system names for laptops
1222 if (strcasestr(dmi->product.name, "notebook") != NULL ||
1223 strcasestr(dmi->product.name, "laptop") != NULL ||
1224 strcasestr(dmi->product.name, "book") != NULL) {
1225 safecpy(dmi->chassis.type, "9"); // Laptop
1226 }
1227 // Check for servers
1228 else if (strcasestr(dmi->product.name, "server") != NULL) {
1229 safecpy(dmi->chassis.type, "17"); // Server
1230 }
1231 // Default to desktop
1232 else {
1233 safecpy(dmi->chassis.type, "3"); // Desktop
1234 }
1235 }
1236 }
1237
1238 #else
1239 void os_dmi_info_get(DMI_INFO *dmi) {
1240 // No implementation for this platform
1241 }
1242 #endif
1243
1244 // --------------------------------------------------------------------------------------------------------------------
1245 // public API
1246
1247 void dmi_info_init(DMI_INFO *dmi) {
1248 if (!dmi) return;
1249
1250 // System information
1251 dmi->sys.vendor[0] = '\0';
1252 dmi->sys.serial[0] = '\0';
1253 dmi->sys.uuid[0] = '\0';
1254 dmi->sys.asset_tag[0] = '\0';
1255
1256 // Product information
1257 dmi->product.name[0] = '\0';
1258 dmi->product.version[0] = '\0';
1259 dmi->product.sku[0] = '\0';
1260 dmi->product.family[0] = '\0';
1261
1262 // Board information
1263 dmi->board.vendor[0] = '\0';
1264 dmi->board.name[0] = '\0';
1265 dmi->board.version[0] = '\0';
1266 dmi->board.serial[0] = '\0';
1267 dmi->board.asset_tag[0] = '\0';
1268
1269 // BIOS information
1270 dmi->bios.vendor[0] = '\0';
1271 dmi->bios.version[0] = '\0';
1272 dmi->bios.date[0] = '\0';
1273 dmi->bios.release[0] = '\0';
1274
1275 // Chassis information
1276 dmi->chassis.vendor[0] = '\0';
1277 dmi->chassis.version[0] = '\0';
1278 dmi->chassis.type[0] = '\0';
1279 dmi->chassis.serial[0] = '\0';
1280 dmi->chassis.asset_tag[0] = '\0';
1281 }