@cryptotaxi247 / netdata-1 / commits / e01e27b34

daemon status 26 - dmi strings (#20037)

* improved dmi information * prepare it for multi-os support * move the spawn server before the crash check * undo last * added freebsd, macos and windows versions of the dmi strings * new version for windows * new macos version * error checking * fix mac compile * code review and cleanup * refactor(status-file): improve Windows SMBIOS parsing with better memory management and error handling - Create dedicated structure types for SMBIOS header and data - Refactor string parsing with improved boundary checks - Separate structure processing into type-specific functions - Replace malloc/free with mallocz/freez for better integration - Improve safety checks against malformed SMBIOS data - Add reasonable limits to prevent infinite loops - Use const qualifiers for read-only data

Costa Tsaousis committed Apr 2, 2025 at 23:48 UTC e01e27b34ca8bfb17af91681e261fb664726aff1
11 files changed +1414 -163
CMakeLists.txt
+2
@@ -1286,6 +1286,8 @@ set(DAEMON_FILES
1286 src/daemon/status-file-dedup.h
1287 src/daemon/status-file-io.c
1288 src/daemon/status-file-io.h
1289 + src/daemon/status-file-dmi.c
1290 + src/daemon/status-file-dmi.h
1291 )
1292
1293 set(DAEMON_SYSTEMD_WATCHER_FILES
src/daemon/signal-handler.c
+9 -9
@@ -83,20 +83,20 @@ void nd_signal_handler(int signo, siginfo_t *info, void *context __maybe_unused)
83 // log it
84 char b[1024];
85 size_t len = 0;
86 - len = strcatz(b, len, sizeof(b), "SIGNAL HANDLER: received deadly signal: ");
87 - len = strcatz(b, len, sizeof(b), signals_waiting[i].name);
86 + len = strcatz(b, len, "SIGNAL HANDLER: received deadly signal: ", sizeof(b));
87 + len = strcatz(b, len, signals_waiting[i].name, sizeof(b));
88 if(sc) {
89 char buf[128];
90 SIGNAL_CODE_2str_h(sc, buf, sizeof(buf));
91 - len = strcatz(b, len, sizeof(b), " (");
92 - len = strcatz(b, len, sizeof(b), buf);
93 - len = strcatz(b, len, sizeof(b), ")");
91 + len = strcatz(b, len, " (", sizeof(b));
92 + len = strcatz(b, len, buf, sizeof(b));
93 + len = strcatz(b, len, ")", sizeof(b));
94 }
95 - len = strcatz(b, len, sizeof(b), " in thread ");
95 + len = strcatz(b, len, " in thread ", sizeof(b));
96 print_uint64(&b[len], gettid_cached());
97 - len = strcatz(b, len, sizeof(b), " ");
98 - len = strcatz(b, len, sizeof(b), nd_thread_tag_async_safe());
99 - len = strcatz(b, len, sizeof(b), "!\n");
97 + len = strcatz(b, len, " ", sizeof(b));
98 + len = strcatz(b, len, nd_thread_tag_async_safe(), sizeof(b));
99 + len = strcatz(b, len, "!\n", sizeof(b));
100
101 if(write(STDERR_FILENO, b, strlen(b)) == -1) {
102 // nothing to do - we cannot write but there is no way to complain about it
src/daemon/status-file-dmi.c new
+1371
@@ -0,0 +1,1371 @@
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 +}
src/daemon/status-file-dmi.h new
+11
@@ -0,0 +1,11 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_STATUS_FILE_DMI_H
4 +#define NETDATA_STATUS_FILE_DMI_H
5 +
6 +#include "status-file.h"
7 +
8 +void finalize_vendor_product_vm(DAEMON_STATUS_FILE *ds);
9 +void fill_dmi_info(DAEMON_STATUS_FILE *ds);
10 +
11 +#endif //NETDATA_STATUS_FILE_DMI_H
src/daemon/status-file-io.c
+6 -6
@@ -24,10 +24,10 @@ static bool status_file_io_check(const char *directory, const char *filename, ch
24 return false;
25
26 size_t len = 0;
27 - len = strcatz(dst, len, dst_size, directory);
27 + len = strcatz(dst, len, directory, dst_size);
28 if(!len || dst[len - 1] != '/')
29 - len = strcatz(dst, len, dst_size, "/");
30 - len = strcatz(dst, len, dst_size, filename);
29 + len = strcatz(dst, len, "/", dst_size);
30 + len = strcatz(dst, len, filename, dst_size);
31
32 // Get file metadata
33 OS_FILE_METADATA metadata = os_get_file_metadata(dst);
@@ -54,10 +54,10 @@ static void status_file_io_remove_obsolete(const char *protected_dir, const char
54 continue;
55
56 size_t len = 0;
57 - len = strcatz(dst, len, sizeof(dst), status_file_io_fallback_dirs[i]);
57 + len = strcatz(dst, len, status_file_io_fallback_dirs[i], sizeof(dst));
58 if(!len || dst[len - 1] != '/')
59 - len = strcatz(dst, len, sizeof(dst), "/");
60 - len = strcatz(dst, len, sizeof(dst), filename);
59 + len = strcatz(dst, len, "/", sizeof(dst));
60 + len = strcatz(dst, len, filename, sizeof(dst));
61
62 unlink(dst);
63 }
src/daemon/status-file.c
+3 -139
@@ -8,6 +8,7 @@
8 #include <openssl/evp.h>
9 #include <openssl/pem.h>
10 #include <openssl/err.h>
11 +#include "status-file-dmi.h"
12
13 #ifdef ENABLE_SENTRY
14 #include "sentry-native/sentry-native.h"
@@ -84,144 +85,6 @@ static void set_stack_trace_message_if_empty(DAEMON_STATUS_FILE *ds, const char
85 strncpyz(ds->fatal.stack_trace, msg, sizeof(ds->fatal.stack_trace) - 1);
86 }
87
87 -// --------------------------------------------------------------------------------------------------------------------
88 -// DMI data
89 -
90 -static void dmi_info(const char *file, const char *alt, char *dst, size_t dst_size) {
91 - char filename[FILENAME_MAX];
92 -
93 - if(netdata_configured_host_prefix && *netdata_configured_host_prefix) {
94 - snprintfz(filename, sizeof(filename), "%s/sys/class/dmi/id/%s", netdata_configured_host_prefix, file);
95 - if(access(filename, R_OK) != 0) {
96 - snprintfz(filename, sizeof(filename), "%s/sys/devices/virtual/dmi/id/%s", netdata_configured_host_prefix, file);
97 - if (access(filename, R_OK) != 0)
98 - filename[0] = '\0';
99 - }
100 - }
101 - else
102 - filename[0] = '\0';
103 -
104 - if(!filename[0]) {
105 - snprintfz(filename, sizeof(filename), "/sys/class/dmi/id/%s", file);
106 - if (access(filename, R_OK) != 0) {
107 - snprintfz(filename, sizeof(filename), "/sys/devices/virtual/dmi/id/%s", file);
108 - if (access(filename, R_OK) != 0) {
109 - if (alt && *alt) {
110 - strncpyz(filename, alt, sizeof(filename) - 1);
111 - if (access(filename, R_OK) != 0)
112 - filename[0] = '\0';
113 - }
114 - else
115 - filename[0] = '\0';
116 - }
117 - }
118 - }
119 -
120 - if(!filename[0]) {
121 - dst[0] = '\0';
122 - return;
123 - }
124 -
125 - if(read_txt_file(filename, dst, dst_size) != 0) {
126 - dst[0] = '\0';
127 - return;
128 - }
129 -
130 - trim_all(dst);
131 -
132 - struct {
133 - const char *found;
134 - const char *replace;
135 - } replacements[] = {
136 - {"Default string", ""},
137 - {"To be filled by O.E.M.", ""},
138 - {"x.x", ""},
139 - {"System Product Name", ""},
140 - {"System Version", ""},
141 - {"System UUID", ""},
142 - };
143 -
144 - if(dst[0]) {
145 - for (size_t i = 0; i < _countof(replacements); i++) {
146 - if (strcasecmp(dst, replacements[i].found) == 0) {
147 - strncpyz(dst, replacements[i].replace, dst_size - 1);
148 - break;
149 - }
150 - }
151 - }
152 -}
153 -
154 -static void fill_dmi_info(DAEMON_STATUS_FILE *ds) {
155 - dmi_info("sys_vendor", NULL, ds->hw.sys.vendor, sizeof(ds->hw.sys.vendor));
156 -
157 - dmi_info("product_name", "/proc/device-tree/model", ds->hw.product.name, sizeof(ds->hw.product.name));
158 - dmi_info("product_version", NULL, ds->hw.product.version, sizeof(ds->hw.product.version));
159 - dmi_info("product_sku", NULL, ds->hw.product.sku, sizeof(ds->hw.product.sku));
160 - dmi_info("product_family", NULL, ds->hw.product.family, sizeof(ds->hw.product.family));
161 -
162 - dmi_info("chassis_type", NULL, ds->hw.chassis.type, sizeof(ds->hw.chassis.type));
163 - dmi_info("chassis_vendor", NULL, ds->hw.chassis.vendor, sizeof(ds->hw.chassis.vendor));
164 - dmi_info("chassis_version", NULL, ds->hw.chassis.version, sizeof(ds->hw.chassis.version));
165 -
166 - dmi_info("board_vendor", NULL, ds->hw.board.vendor, sizeof(ds->hw.board.vendor));
167 - dmi_info("board_name", NULL, ds->hw.board.name, sizeof(ds->hw.board.name));
168 - dmi_info("board_version", NULL, ds->hw.board.version, sizeof(ds->hw.board.version));
169 -
170 - dmi_info("bios_vendor", NULL, ds->hw.bios.vendor, sizeof(ds->hw.bios.vendor));
171 - dmi_info("bios_version", NULL, ds->hw.bios.version, sizeof(ds->hw.bios.version));
172 - dmi_info("bios_date", NULL, ds->hw.bios.date, sizeof(ds->hw.bios.date));
173 - dmi_info("bios_release", NULL, ds->hw.bios.release, sizeof(ds->hw.bios.release));
174 -
175 - struct {
176 - const char *found;
177 - const char *replace;
178 - } chassis_types[] = {
179 - {"1", "other"},
180 - {"2", "unknown"},
181 - {"3", "desktop"},
182 - {"4", "low-profile-desktop"},
183 - {"5", "pizza-box"},
184 - {"6", "mini-tower-desktop"},
185 - {"7", "tower-desktop"},
186 - {"8", "portable"},
187 - {"9", "laptop"},
188 - {"10", "notebook"},
189 - {"11", "hand-held"},
190 - {"12", "docking-station"},
191 - {"13", "all-in-one"},
192 - {"14", "sub-notebook"},
193 - {"15", "space-saving-desktop"},
194 - {"16", "lunch-box"},
195 - {"17", "main-server-chassis"},
196 - {"18", "expansion-chassis"},
197 - {"19", "sub-chassis"},
198 - {"20", "bus-expansion-chassis"},
199 - {"21", "peripheral-chassis"},
200 - {"22", "raid-chassis"},
201 - {"23", "rack-mount-server"},
202 - {"24", "sealed-desktop"},
203 - {"25", "multi-chassis"},
204 - {"26", "compact-pci"},
205 - {"27", "advanced-tca"},
206 - {"28", "blade"},
207 - {"29", "blade-enclosure"},
208 - {"30", "tablet"},
209 - {"31", "convertible"},
210 - {"32", "detachable"},
211 - {"33", "iot-gateway"},
212 - {"34", "embedded-pc"},
213 - {"35", "mini-pc"},
214 - {"36", "stick-pc"},
215 - };
216 -
217 - for(size_t i = 0; i < _countof(chassis_types) ;i++) {
218 - if(strcasecmp(ds->hw.chassis.type, chassis_types[i].found) == 0) {
219 - strncpyz(ds->hw.chassis.type, chassis_types[i].replace, sizeof(ds->hw.chassis.type) - 1);
220 - break;
221 - }
222 - }
223 -}
224 -
88 // --------------------------------------------------------------------------------------------------------------------
89 // json generation
90
@@ -788,7 +651,8 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
651 session_status.node_id = localhost->node_id;
652 }
653
791 - get_daemon_status_fields_from_system_info(&session_status);
654 + if(get_daemon_status_fields_from_system_info(&session_status))
655 + finalize_vendor_product_vm(&session_status);
656
657 if(netdata_configured_timezone)
658 strncpyz(session_status.timezone, netdata_configured_timezone, sizeof(session_status.timezone) - 1);
src/daemon/status-file.h
+1 -1
@@ -9,7 +9,7 @@
9 #include "claim/cloud-status.h"
10 #include "machine-guid.h"
11
12 -#define STATUS_FILE_VERSION 25
12 +#define STATUS_FILE_VERSION 26
13
14 typedef enum {
15 DAEMON_STATUS_NONE,
src/database/rrdhost-system-info.c
+6 -3
@@ -619,13 +619,14 @@ void rrdhost_system_info_to_streaming_function_array(BUFFER *wb, struct rrdhost_
619 }
620 }
621
622 -void get_daemon_status_fields_from_system_info(DAEMON_STATUS_FILE *ds) {
623 - if(ds->read_system_info) return;
622 +bool get_daemon_status_fields_from_system_info(DAEMON_STATUS_FILE *ds) {
623 + if(ds->read_system_info)
624 + return false;
625
626 struct rrdhost_system_info *ri = (localhost && localhost->system_info) ? localhost->system_info : NULL;
627 if(!ri) {
628 // nothing we can do, let it be
628 - return;
629 + return false;
630 }
631
632 if(ri->architecture)
@@ -669,4 +670,6 @@ void get_daemon_status_fields_from_system_info(DAEMON_STATUS_FILE *ds) {
670 strncpyz(ds->cloud_instance_region, ri->cloud_instance_region, sizeof(ds->cloud_instance_region) - 1);
671
672 ds->read_system_info = true;
673 +
674 + return true;
675 }
src/database/rrdhost-system-info.h
+1 -1
@@ -99,7 +99,7 @@ void rrdhost_system_info_to_node_info(struct rrdhost_system_info *system_info, s
99
100 void rrdhost_system_info_to_streaming_function_array(BUFFER *wb, struct rrdhost_system_info *system_info);
101
102 -void get_daemon_status_fields_from_system_info(DAEMON_STATUS_FILE *ds);
102 +bool get_daemon_status_fields_from_system_info(DAEMON_STATUS_FILE *ds);
103 void rrdhost_system_info_swap(struct rrdhost_system_info *a, struct rrdhost_system_info *b);
104
105 #endif //NETDATA_RRDHOST_SYSTEM_INFO_H
src/libnetdata/inlined.h
+1 -1
@@ -422,7 +422,7 @@ static inline char *strncpyz(char *dst, const char *src, size_t dst_size_minus_1
422
423 // append src to dst, but only if there is space for it
424 // dst is always null terminated
425 -static inline size_t strcatz(char *dst, size_t len, size_t size, const char *src) {
425 +static inline size_t strcatz(char *dst, size_t len, const char *src, size_t size) {
426 // If starting offset is out of bounds, do nothing.
427 if (len >= size) {
428 dst[size - 1] = '\0';
src/libnetdata/log/nd_log-stacktrace.c
+3 -3
@@ -90,14 +90,14 @@ static void bt_error_handler(void *data, const char *msg, int errnum) {
90
91 // Add the error message
92 if (msg)
93 - len = strcatz(error_buf, len, sizeof(error_buf), msg);
93 + len = strcatz(error_buf, len, msg, sizeof(error_buf));
94
95 // Add the error number description if available
96 if (errnum > 0) {
97 if (msg) {
98 - len = strcatz(error_buf, len, sizeof(error_buf), ": ");
98 + len = strcatz(error_buf, len, ": ", sizeof(error_buf));
99 }
100 - len = strcatz(error_buf, len, sizeof(error_buf), strerror(errnum));
100 + len = strcatz(error_buf, len, strerror(errnum), sizeof(error_buf));
101 }
102
103 add_stack_frame(bt_data, 0, function, error_buf, 0);