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