@cryptotaxi247 / netdata-1 / commits / 1de029eba

daemon status 27 (#20058)

* dmi polishing * more code cleanup * keep original dmi information and add product labels separately * isolate dmi from the status file * dmi module is now standalone * DMI polishing * remove serial numbers and asset tags from the status file * prefer product family over product name * combine product family, name and board name to build the final product label * use more space for product name * fix(agent-events): prevent field leakage in server.go JSON processing Fix cross-contamination issue in agent-events server by using explicit map allocation when unmarshaling JSON. Previously, fields could leak between requests when using an empty interface due to memory reuse, causing mixed fields from different providers to appear in the same log entry.

Costa Tsaousis committed Apr 4, 2025 at 21:58 UTC 1de029ebacafda2797ef0a0c734b203480d44c92
12 files changed +1148 -614
CMakeLists.txt
+2
@@ -1288,6 +1288,8 @@ set(DAEMON_FILES
1288 src/daemon/status-file-io.h
1289 src/daemon/status-file-dmi.c
1290 src/daemon/status-file-dmi.h
1291 + src/daemon/status-file-product.c
1292 + src/daemon/status-file-product.h
1293 )
1294
1295 set(DAEMON_SYSTEMD_WATCHER_FILES
packaging/tools/agent-events/server.go
+7 -2
@@ -170,7 +170,9 @@ func handler(w http.ResponseWriter, r *http.Request) {
170 }
171
172 if shouldProcess {
173 - var fullData interface{}
173 + // Always create a fresh map to prevent field reuse between requests
174 + fullData := make(map[string]interface{})
175 +
176 if err := json.Unmarshal(body, &fullData); err != nil {
177 http.Error(w, "Invalid JSON for full parsing", http.StatusBadRequest)
178 bodyDetail := ""
@@ -178,12 +180,15 @@ func handler(w http.ResponseWriter, r *http.Request) {
180 log.Printf("Discarded: Failed to fully parse JSON (post-dedup): %v%s", err, bodyDetail)
181 return
182 }
183 +
184 + // Marshal the map back to JSON
185 outputBytes, err := json.Marshal(fullData)
186 if err != nil {
187 http.Error(w, "Internal Server Error during output marshal", http.StatusInternalServerError)
188 log.Printf("Discarded: Failed to marshal JSON for output: %v", err)
189 return
190 }
191 +
192 fmt.Println(string(outputBytes))
193 if _, err := w.Write([]byte("OK")); err != nil { log.Printf("Error writing OK response: %v", err) }
194 }
@@ -244,4 +249,4 @@ func main() {
249 func limitString(s string, maxLen int) string {
250 if len(s) <= maxLen { return s }
251 return s[:maxLen] + "..."
247 -}
252 +}
\ No newline at end of file
src/daemon/sentry-native/sentry-native.c
+4
@@ -161,6 +161,10 @@ void nd_sentry_init(void) {
161 nd_sentry_set_tag("cloud_type", daemon_status_file_get_cloud_instance_type());
162 nd_sentry_set_tag("cloud_region", daemon_status_file_get_cloud_instance_region());
163 nd_sentry_set_tag("timezone", daemon_status_file_get_timezone());
164 +
165 + nd_sentry_set_tag("hw_sys_vendor", daemon_status_file_get_sys_vendor());
166 + nd_sentry_set_tag("hw_product_name", daemon_status_file_get_product_name());
167 + nd_sentry_set_tag("hw_product_type", daemon_status_file_get_product_type());
168
169 // profile
170 CLEAN_BUFFER *profile = buffer_create(0, NULL);
src/daemon/status-file-dedup.c
+22 -21
@@ -2,6 +2,7 @@
2
3 #include "status-file-dedup.h"
4 #include "status-file-io.h"
5 +#include "status-file-dmi.h"
6
7 #define DEDUP_FILENAME "dedup-netdata.dat"
8 #define DEDUP_VERSION 1
@@ -67,31 +68,31 @@ uint64_t daemon_status_file_hash(DAEMON_STATUS_FILE *ds, const char *msg, const
68
69 dsf_acquire(*ds);
70
70 - to_hash.v = ds->v,
71 - to_hash.status = ds->status,
72 - to_hash.signal_code = ds->fatal.signal_code,
73 - to_hash.profile = ds->profile,
74 - to_hash.exit_reason = ds->exit_reason,
75 - to_hash.db_mode = ds->db_mode,
76 - to_hash.db_tiers = ds->db_tiers,
77 - to_hash.kubernetes = ds->kubernetes,
78 - to_hash.sentry_available = ds->sentry_available,
79 - to_hash.sentry_fatal = ds->fatal.sentry,
80 - to_hash.host_id = ds->host_id,
81 - to_hash.machine_id = ds->machine_id,
82 - to_hash.worker_job_id = ds->fatal.worker_job_id,
83 -
84 - strncpyz(to_hash.version, ds->version, sizeof(to_hash.version) - 1);
85 - strncpyz(to_hash.filename, ds->fatal.filename, sizeof(to_hash.filename) - 1);
86 - strncpyz(to_hash.filename, ds->fatal.function, sizeof(to_hash.function) - 1);
87 - strncpyz(to_hash.stack_trace, ds->fatal.stack_trace, sizeof(to_hash.stack_trace) - 1);
88 - strncpyz(to_hash.thread, ds->fatal.thread, sizeof(to_hash.thread) - 1);
71 + to_hash.v = ds->v;
72 + to_hash.status = ds->status;
73 + to_hash.signal_code = ds->fatal.signal_code;
74 + to_hash.profile = ds->profile;
75 + to_hash.exit_reason = ds->exit_reason;
76 + to_hash.db_mode = ds->db_mode;
77 + to_hash.db_tiers = ds->db_tiers;
78 + to_hash.kubernetes = ds->kubernetes;
79 + to_hash.sentry_available = ds->sentry_available;
80 + to_hash.sentry_fatal = ds->fatal.sentry;
81 + to_hash.host_id = ds->host_id;
82 + to_hash.machine_id = ds->machine_id;
83 + to_hash.worker_job_id = ds->fatal.worker_job_id;
84 +
85 + safecpy(to_hash.version, ds->version);
86 + safecpy(to_hash.filename, ds->fatal.filename);
87 + safecpy(to_hash.filename, ds->fatal.function);
88 + safecpy(to_hash.stack_trace, ds->fatal.stack_trace);
89 + safecpy(to_hash.thread, ds->fatal.thread);
90
91 if(msg)
91 - strncpyz(to_hash.msg, msg, sizeof(to_hash.msg) - 1);
92 + safecpy(to_hash.msg, msg);
93
94 if(cause)
94 - strncpyz(to_hash.cause, cause, sizeof(to_hash.cause) - 1);
95 + safecpy(to_hash.cause, cause);
96
97 stack_trace_anonymize(to_hash.stack_trace);
98
src/daemon/status-file-dmi.c
+538 -504
@@ -2,12 +2,6 @@
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 -
5 static void dmi_clean_field_placeholder(char *buf, size_t buf_size) {
6 if(!buf || !buf_size) return;
7
@@ -63,258 +57,6 @@ static void dmi_clean_field_placeholder(char *buf, size_t buf_size) {
57 }
58 }
59
66 -static void dmi_normalize_vendor_field(char *buf, size_t buf_size) {
67 - if(!buf || !buf_size) return;
68 -
69 - struct {
70 - const char *found;
71 - const char *replace;
72 - } vendors[] = {
73 - {"QEMU", "KVM"},
74 -
75 - // Major vendors with multiple variations
76 - {"AMD Corporation", "AMD"},
77 - {"Advanced Micro Devices, Inc.", "AMD"},
78 -
79 - {"AMI Corp.", "AMI"},
80 - {"AMI Corporation", "AMI"},
81 - {"American Megatrends", "AMI"},
82 - {"American Megatrends Inc.", "AMI"},
83 - {"American Megatrends International", "AMI"},
84 - {"American Megatrends International, LLC.", "AMI"},
85 -
86 - {"AOPEN", "AOpen"},
87 - {"AOPEN Inc.", "AOpen"},
88 -
89 - {"Apache Software Foundation", "Apache"},
90 -
91 - {"Apple Inc.", "Apple"},
92 -
93 - {"ASRock Industrial", "ASRock"},
94 - {"ASRockRack", "ASRock"},
95 - {"AsrockRack", "ASRock"},
96 -
97 - {"ASUS", "ASUSTeK"},
98 - {"ASUSTeK COMPUTER INC.", "ASUSTeK"},
99 - {"ASUSTeK COMPUTER INC. (Licensed from AMI)", "ASUSTeK"},
100 - {"ASUSTeK Computer INC.", "ASUSTeK"},
101 - {"ASUSTeK Computer Inc.", "ASUSTeK"},
102 - {"ASUSTek Computer INC.", "ASUSTeK"},
103 -
104 - {"Apache Software Foundation", "Apache"},
105 -
106 - {"BESSTAR (HK) LIMITED", "Besstar"},
107 - {"BESSTAR TECH", "Besstar"},
108 - {"BESSTAR TECH LIMITED", "Besstar"},
109 - {"BESSTAR Tech", "Besstar"},
110 -
111 - {"CHUWI", "Chuwi"},
112 - {"CHUWI Innovation And Technology(ShenZhen)co.,Ltd", "Chuwi"},
113 -
114 - {"Cisco Systems Inc", "Cisco"},
115 - {"Cisco Systems, Inc.", "Cisco"},
116 -
117 - {"DELL", "Dell"},
118 - {"Dell Computer Corporation", "Dell"},
119 - {"Dell Inc.", "Dell"},
120 -
121 - {"FUJITSU", "Fujitsu"},
122 - {"FUJITSU CLIENT COMPUTING LIMITED", "Fujitsu"},
123 - {"FUJITSU SIEMENS", "Fujitsu"},
124 - {"FUJITSU SIEMENS // Phoenix Technologies Ltd.", "Fujitsu"},
125 - {"FUJITSU // American Megatrends Inc.", "Fujitsu"},
126 - {"FUJITSU // American Megatrends International, LLC.", "Fujitsu"},
127 - {"FUJITSU // Insyde Software Corp.", "Fujitsu"},
128 - {"FUJITSU // Phoenix Technologies Ltd.", "Fujitsu"},
129 -
130 - {"GIGABYTE", "Gigabyte"},
131 - {"Giga Computing", "Gigabyte"},
132 - {"Gigabyte Technology Co., Ltd.", "Gigabyte"},
133 - {"Gigabyte Tecohnology Co., Ltd.", "Gigabyte"},
134 -
135 - {"GOOGLE", "Google"},
136 - {"Google Inc", "Google"},
137 -
138 - {"HC Technology.,Ltd.", "HC Tech"},
139 -
140 - {"HP-Pavilion", "HP"},
141 - {"HPE", "HP"},
142 - {"Hewlett Packard Enterprise", "HP"},
143 - {"Hewlett-Packard", "HP"},
144 -
145 - {"HUAWEI", "Huawei"},
146 - {"Huawei Technologies Co., Ltd.", "Huawei"},
147 -
148 - {"IBM Corp.", "IBM"},
149 -
150 - {"INSYDE", "Insyde"},
151 - {"INSYDE Corp.", "Insyde"},
152 - {"Insyde Corp.", "Insyde"},
153 -
154 - {"INTEL", "Intel"},
155 - {"INTEL Corporation", "Intel"},
156 - {"Intel Corp.", "Intel"},
157 - {"Intel Corporation", "Intel"},
158 - {"Intel corporation", "Intel"},
159 - {"Intel(R) Client Systems", "Intel"},
160 - {"Intel(R) Corporation", "Intel"},
161 -
162 - {"LENOVO", "Lenovo"},
163 - {"LNVO", "Lenovo"},
164 -
165 - {"MICRO-STAR INTERNATIONAL CO., LTD", "MSI"},
166 - {"MICRO-STAR INTERNATIONAL CO.,LTD", "MSI"},
167 - {"MSI", "MSI"},
168 - {"Micro-Star International Co., Ltd", "MSI"},
169 - {"Micro-Star International Co., Ltd.", "MSI"},
170 -
171 - {"MICROSOFT", "Microsoft"},
172 - {"Microsoft Corporation", "Microsoft"},
173 -
174 - {"nVIDIA", "NVIDIA"},
175 -
176 - {"OPENSTACK", "OpenStack"},
177 - {"OpenStack Foundation", "OpenStack"},
178 -
179 - {"ORACLE CORPORATI", "Oracle"},
180 - {"Oracle Corporation", "Oracle"},
181 - {"innotek GmbH", "Oracle"},
182 -
183 - {"Phoenix Technologies LTD", "Phoenix"},
184 - {"Phoenix Technologies Ltd", "Phoenix"},
185 - {"Phoenix Technologies Ltd.", "Phoenix"},
186 - {"Phoenix Technologies, LTD", "Phoenix"},
187 -
188 - {"QNAP Systems, Inc.", "QNAP"},
189 -
190 - {"QUANTA", "Quanta"},
191 - {"Quanta Cloud Technology Inc.", "Quanta"},
192 - {"Quanta Computer Inc", "Quanta"},
193 - {"Quanta Computer Inc.", "Quanta"},
194 -
195 - {"RED HAT", "Red Hat"},
196 -
197 - {"SAMSUNG ELECTRONICS CO., LTD.", "Samsung"},
198 -
199 - {"SUN MICROSYSTEMS", "Sun"},
200 -
201 - {"SuperMicro", "Supermicro"},
202 - {"Supermicro Corporation", "Supermicro"},
203 -
204 - {"SYNOLOGY", "Synology"},
205 - {"Synology Inc.", "Synology"},
206 -
207 - {"TYAN", "Tyan"},
208 - {"TYAN Computer Corporation", "Tyan"},
209 - {"Tyan Computer Corporation", "Tyan"},
210 - {"$(TYAN_SYSTEM_MANUFACTURER)", "Tyan"},
211 -
212 - {"VMware", "VMware"},
213 - {"VMware, Inc.", "VMware"},
214 -
215 - {"XIAOMI", "Xiaomi"},
216 -
217 - {"ZOTAC", "Zotac"},
218 - {"Motherboard by ZOTAC", "Zotac"}
219 - };
220 -
221 - for (size_t i = 0; i < _countof(vendors); i++) {
222 - if (strcasecmp(buf, vendors[i].found) == 0) {
223 - strcatz(buf, 0, vendors[i].replace, buf_size);
224 - break;
225 - }
226 - }
227 -}
228 -
229 -static bool dmi_is_virtual_machine(const DAEMON_STATUS_FILE *ds) {
230 - if(!ds) return false;
231 -
232 - const char *vm_indicators[] = {
233 - "Virt", "KVM", "vServer", "Cloud", "Hyper", "Droplet", "Compute",
234 - "HVM domU", "Parallels", "(i440FX", "(q35", "OpenStack", "QEMU",
235 - "VMWare", "DigitalOcean", "Oracle", "Linode", "Amazon EC2"
236 - };
237 -
238 - const char *strs_to_check[] = {
239 - ds->hw.product.name,
240 - ds->hw.product.family,
241 - ds->hw.sys.vendor,
242 - ds->hw.board.name,
243 - };
244 -
245 - for (size_t i = 0; i < _countof(strs_to_check); i++) {
246 - if (!strs_to_check[i] || !strs_to_check[i][0])
247 - continue;
248 -
249 - for (size_t j = 0; j < _countof(vm_indicators); j++) {
250 - if (strcasestr(strs_to_check[i], vm_indicators[j]) != NULL)
251 - return true;
252 - }
253 - }
254 -
255 - return false;
256 -}
257 -
258 -static const char *dmi_chassis_type_to_string(int chassis_type) {
259 - // Original info from SMBIOS
260 - // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf
261 - // selected values aligned with inxi: https://github.com/smxi/inxi/blob/master/inxi
262 - switch(chassis_type) {
263 - case 1: return "other";
264 - case 2: return "unknown";
265 - case 3: return "desktop";
266 - case 4: return "desktop"; /* "low-profile-desktop" */
267 - case 5: return "pizza-box"; // was a 1 U desktop enclosure, but some old laptops also id this way
268 - case 6: return "desktop"; /* "mini-tower-desktop" */
269 - case 7: return "desktop"; /* "tower-desktop" */
270 - case 8: return "portable";
271 - case 9: return "laptop";
272 - case 10: return "laptop"; /* "notebook" */
273 - case 11: return "portable"; /* "hand-held" */
274 - case 12: return "docking-station";
275 - case 13: return "desktop"; /* "all-in-one" */
276 - case 14: return "notebook"; /* "sub-notebook" */
277 - case 15: return "desktop"; /* "space-saving-desktop" */
278 - case 16: return "laptop"; /* "lunch-box" */
279 - case 17: return "server"; /* "main-server-chassis" */
280 - case 18: return "expansion-chassis";
281 - case 19: return "sub-chassis";
282 - case 20: return "bus-expansion";
283 - case 21: return "peripheral";
284 - case 22: return "raid";
285 - case 23: return "server"; /* "rack-mount-server" */
286 - case 24: return "desktop"; /* "sealed-desktop" */
287 - case 25: return "server"; /* "multimount-chassis" */
288 - case 26: return "compact-pci";
289 - case 27: return "blade"; /* "advanced-tca" */
290 - case 28: return "blade";
291 - case 29: return "blade-enclosure";
292 - case 30: return "tablet";
293 - case 31: return "convertible";
294 - case 32: return "detachable";
295 - case 33: return "iot-gateway";
296 - case 34: return "embedded-pc";
297 - case 35: return "mini-pc";
298 - case 36: return "stick-pc";
299 - default: return NULL; // let it be numeric
300 - }
301 -}
302 -
303 -static void dmi_map_chassis_type(DAEMON_STATUS_FILE *ds, int chassis_type) {
304 - if(!ds) return;
305 -
306 - const char *str = NULL;
307 -
308 - if(dmi_is_virtual_machine(ds))
309 - str = "vm";
310 -
311 - if(!str)
312 - str = dmi_chassis_type_to_string(chassis_type);
313 -
314 - if(str)
315 - safecpy(ds->hw.chassis.type, str);
316 -}
317 -
60 static void dmi_clean_field(char *buf, size_t buf_size) {
61 if(!buf || !buf_size) return;
62
@@ -398,27 +140,73 @@ static void linux_get_dmi_field(const char *field, const char *alt, char *dst, s
140 strcatz(dst, 0, buf, dst_size);
141 }
142
401 -void os_dmi_info(DAEMON_STATUS_FILE *ds) {
402 - linux_get_dmi_field("sys_vendor", NULL, ds->hw.sys.vendor, sizeof(ds->hw.sys.vendor));
403 -
404 - linux_get_dmi_field("product_name", "/proc/device-tree/model", ds->hw.product.name, sizeof(ds->hw.product.name));
405 - linux_get_dmi_field("product_version", NULL, ds->hw.product.version, sizeof(ds->hw.product.version));
406 - linux_get_dmi_field("product_sku", NULL, ds->hw.product.sku, sizeof(ds->hw.product.sku));
407 - linux_get_dmi_field("product_family", NULL, ds->hw.product.family, sizeof(ds->hw.product.family));
408 -
409 - linux_get_dmi_field("chassis_vendor", NULL, ds->hw.chassis.vendor, sizeof(ds->hw.chassis.vendor));
410 - linux_get_dmi_field("chassis_version", NULL, ds->hw.chassis.version, sizeof(ds->hw.chassis.version));
411 -
412 - linux_get_dmi_field("board_vendor", NULL, ds->hw.board.vendor, sizeof(ds->hw.board.vendor));
413 - linux_get_dmi_field("board_name", NULL, ds->hw.board.name, sizeof(ds->hw.board.name));
414 - linux_get_dmi_field("board_version", NULL, ds->hw.board.version, sizeof(ds->hw.board.version));
415 -
416 - linux_get_dmi_field("bios_vendor", NULL, ds->hw.bios.vendor, sizeof(ds->hw.bios.vendor));
417 - linux_get_dmi_field("bios_version", NULL, ds->hw.bios.version, sizeof(ds->hw.bios.version));
418 - linux_get_dmi_field("bios_date", NULL, ds->hw.bios.date, sizeof(ds->hw.bios.date));
419 - linux_get_dmi_field("bios_release", NULL, ds->hw.bios.release, sizeof(ds->hw.bios.release));
420 -
421 - linux_get_dmi_field("chassis_type", NULL, ds->hw.chassis.type, sizeof(ds->hw.chassis.type));
143 +void os_dmi_info_get(DMI_INFO *dmi) {
144 + if (!dmi) return;
145 +
146 + linux_get_dmi_field("sys_vendor", NULL, dmi->sys.vendor, sizeof(dmi->sys.vendor));
147 + linux_get_dmi_field("system_serial", NULL, dmi->sys.serial, sizeof(dmi->sys.serial));
148 + linux_get_dmi_field("product_uuid", NULL, dmi->sys.uuid, sizeof(dmi->sys.uuid));
149 + linux_get_dmi_field("chassis_asset_tag", NULL, dmi->sys.asset_tag, sizeof(dmi->sys.asset_tag));
150 +
151 + linux_get_dmi_field("product_name", "/proc/device-tree/model", dmi->product.name, sizeof(dmi->product.name));
152 + linux_get_dmi_field("product_version", NULL, dmi->product.version, sizeof(dmi->product.version));
153 + linux_get_dmi_field("product_sku", NULL, dmi->product.sku, sizeof(dmi->product.sku));
154 + linux_get_dmi_field("product_family", NULL, dmi->product.family, sizeof(dmi->product.family));
155 +
156 + linux_get_dmi_field("chassis_vendor", NULL, dmi->chassis.vendor, sizeof(dmi->chassis.vendor));
157 + linux_get_dmi_field("chassis_version", NULL, dmi->chassis.version, sizeof(dmi->chassis.version));
158 + linux_get_dmi_field("chassis_serial", NULL, dmi->chassis.serial, sizeof(dmi->chassis.serial));
159 + linux_get_dmi_field("chassis_asset_tag", NULL, dmi->chassis.asset_tag, sizeof(dmi->chassis.asset_tag));
160 +
161 + linux_get_dmi_field("board_vendor", NULL, dmi->board.vendor, sizeof(dmi->board.vendor));
162 + linux_get_dmi_field("board_name", NULL, dmi->board.name, sizeof(dmi->board.name));
163 + linux_get_dmi_field("board_version", NULL, dmi->board.version, sizeof(dmi->board.version));
164 + linux_get_dmi_field("board_serial", NULL, dmi->board.serial, sizeof(dmi->board.serial));
165 + linux_get_dmi_field("board_asset_tag", NULL, dmi->board.asset_tag, sizeof(dmi->board.asset_tag));
166 +
167 + linux_get_dmi_field("bios_vendor", NULL, dmi->bios.vendor, sizeof(dmi->bios.vendor));
168 + linux_get_dmi_field("bios_version", NULL, dmi->bios.version, sizeof(dmi->bios.version));
169 + linux_get_dmi_field("bios_date", NULL, dmi->bios.date, sizeof(dmi->bios.date));
170 + linux_get_dmi_field("bios_release", NULL, dmi->bios.release, sizeof(dmi->bios.release));
171 +
172 + linux_get_dmi_field("chassis_type", NULL, dmi->chassis.type, sizeof(dmi->chassis.type));
173 +
174 + // Check if running in UEFI mode - just file existence, no external command
175 + bool is_uefi = access("/sys/firmware/efi", F_OK) == 0;
176 + if (is_uefi) {
177 + safecpy(dmi->bios.mode, "UEFI");
178 +
179 + // Check EFI variable for secure boot - direct file access, no external command
180 + int fd = open("/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c", O_RDONLY);
181 + if (fd != -1) {
182 + // Skip the first 4 bytes which contain the EFI variable attributes
183 + unsigned char value[5] = {0};
184 + ssize_t bytes_read = read(fd, value, sizeof(value));
185 + if (bytes_read == sizeof(value)) {
186 + // The 5th byte (index 4) contains the secure boot status
187 + dmi->bios.secure_boot = (value[4] == 1);
188 + }
189 + close(fd);
190 + }
191 +
192 + // Alternative check through securelevel - direct file access, no external command
193 + if (!dmi->bios.secure_boot) {
194 + fd = open("/sys/kernel/security/securelevel", O_RDONLY);
195 + if (fd != -1) {
196 + char level[10] = {0};
197 + ssize_t bytes_read = read(fd, level, sizeof(level) - 1);
198 + if (bytes_read > 0) {
199 + level[bytes_read] = '\0'; // Ensure null termination
200 + // If securelevel is > 0, usually means secure boot is enabled
201 + int securelevel = atoi(level);
202 + dmi->bios.secure_boot = (securelevel > 0);
203 + }
204 + close(fd);
205 + }
206 + }
207 + } else {
208 + safecpy(dmi->bios.mode, "Legacy");
209 + }
210 }
211 #elif defined(OS_MACOS)
212
@@ -504,15 +292,15 @@ static void get_parent_iokit_string_property(io_registry_entry_t entry, CFString
292 }
293
294 // Get hardware info from IODeviceTree
507 -static void get_devicetree_info(DAEMON_STATUS_FILE *ds) {
295 +static void get_devicetree_info(DMI_INFO *dmi) {
296 // Initialize all relevant fields to empty
509 - if (!ds)
297 + if (!dmi)
298 return;
299
512 - ds->hw.product.name[0] = '\0';
513 - ds->hw.board.name[0] = '\0';
514 - ds->hw.sys.vendor[0] = '\0';
515 - ds->hw.product.family[0] = '\0';
300 + dmi->product.name[0] = '\0';
301 + dmi->board.name[0] = '\0';
302 + dmi->sys.vendor[0] = '\0';
303 + dmi->product.family[0] = '\0';
304
305 // Get the device tree
306 io_registry_entry_t device_tree = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/");
@@ -520,16 +308,16 @@ static void get_devicetree_info(DAEMON_STATUS_FILE *ds) {
308 return;
309
310 // Get model information - only operate if device_tree is valid
523 - get_iokit_string_property(device_tree, CFSTR("model"), ds->hw.product.name, sizeof(ds->hw.product.name));
311 + get_iokit_string_property(device_tree, CFSTR("model"), dmi->product.name, sizeof(dmi->product.name));
312
313 // Get board ID if available
526 - get_iokit_string_property(device_tree, CFSTR("board-id"), ds->hw.board.name, sizeof(ds->hw.board.name));
314 + get_iokit_string_property(device_tree, CFSTR("board-id"), dmi->board.name, sizeof(dmi->board.name));
315
316 // Look for platform information
317 io_registry_entry_t platform = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/platform");
318 if (platform) {
319 // Platform information can sometimes have manufacturer info
532 - get_iokit_string_property(platform, CFSTR("manufacturer"), ds->hw.sys.vendor, sizeof(ds->hw.sys.vendor));
320 + get_iokit_string_property(platform, CFSTR("manufacturer"), dmi->sys.vendor, sizeof(dmi->sys.vendor));
321
322 // Check compatible property for additional info
323 char compatible[256] = {0};
@@ -540,8 +328,8 @@ static void get_devicetree_info(DAEMON_STATUS_FILE *ds) {
328 char *family = strstr(compatible, ",");
329 if (family && family < compatible + sizeof(compatible) - 1) {
330 family++; // Skip the comma
543 - safecpy(ds->hw.product.family, family);
544 - dmi_clean_field(ds->hw.product.family, sizeof(ds->hw.product.family));
331 + safecpy(dmi->product.family, family);
332 + dmi_clean_field(dmi->product.family, sizeof(dmi->product.family));
333 }
334 }
335
@@ -552,8 +340,8 @@ static void get_devicetree_info(DAEMON_STATUS_FILE *ds) {
340 }
341
342 // Get hardware info from IOPlatformExpertDevice
555 -static void get_platform_expert_info(DAEMON_STATUS_FILE *ds) {
556 - if (!ds)
343 +static void get_platform_expert_info(DMI_INFO *dmi) {
344 + if (!dmi)
345 return;
346
347 io_registry_entry_t platform_expert = IORegistryEntryFromPath(
@@ -564,23 +352,45 @@ static void get_platform_expert_info(DAEMON_STATUS_FILE *ds) {
352
353 // System vendor - almost always "Apple Inc." for Macs
354 get_iokit_string_property(platform_expert, CFSTR("manufacturer"),
567 - ds->hw.sys.vendor, sizeof(ds->hw.sys.vendor));
355 + dmi->sys.vendor, sizeof(dmi->sys.vendor));
356
357 // Product name
358 get_iokit_string_property(platform_expert, CFSTR("model"),
571 - ds->hw.product.name, sizeof(ds->hw.product.name));
359 + dmi->product.name, sizeof(dmi->product.name));
360
361 // Model number - can be used as product version
362 get_iokit_string_property(platform_expert, CFSTR("model-number"),
575 - ds->hw.product.version, sizeof(ds->hw.product.version));
363 + dmi->product.version, sizeof(dmi->product.version));
364
365 // Board name sometimes available
366 get_iokit_string_property(platform_expert, CFSTR("board-id"),
579 - ds->hw.board.name, sizeof(ds->hw.board.name));
367 + dmi->board.name, sizeof(dmi->board.name));
368 +
369 + // System serial number - might be available as IOPlatformSerialNumber
370 + get_iokit_string_property(platform_expert, CFSTR("IOPlatformSerialNumber"),
371 + dmi->sys.serial, sizeof(dmi->sys.serial));
372
373 // Hardware UUID can be useful to include
374 char uuid_str[64] = {0};
375 get_iokit_string_property(platform_expert, CFSTR("IOPlatformUUID"), uuid_str, sizeof(uuid_str));
376 +
377 + if (uuid_str[0])
378 + safecpy(dmi->sys.uuid, uuid_str);
379 +
380 + // For asset tag, try alternative properties since Apple doesn't provide a specific asset tag property
381 + // First try chassis tag property if available
382 + char asset_tag[64] = {0};
383 + get_iokit_string_property(platform_expert, CFSTR("IOPlatformChassisTag"), asset_tag, sizeof(asset_tag));
384 +
385 + // If not available, try using the serial number as an asset tag if not already set
386 + if (!asset_tag[0]) {
387 + // Model identifier can be a reasonable alternative for asset tag
388 + get_iokit_string_property(platform_expert, CFSTR("model"), asset_tag, sizeof(asset_tag));
389 + }
390 +
391 + if (asset_tag[0] && (!dmi->sys.serial[0] || strcmp(asset_tag, dmi->sys.serial) != 0)) {
392 + safecpy(dmi->sys.asset_tag, asset_tag);
393 + }
394
395 // Get device type to determine chassis type
396 char device_type[64] = {0};
@@ -589,33 +399,33 @@ static void get_platform_expert_info(DAEMON_STATUS_FILE *ds) {
399 // Set chassis type based on device_type safely
400 if (device_type[0]) {
401 if (strcasestr(device_type, "laptop") || strcasestr(device_type, "book"))
592 - safecpy(ds->hw.chassis.type, "9");
402 + safecpy(dmi->chassis.type, "9");
403 else if (strcasestr(device_type, "server"))
594 - safecpy(ds->hw.chassis.type, "17");
404 + safecpy(dmi->chassis.type, "17");
405 else if (strcasestr(device_type, "imac"))
596 - safecpy(ds->hw.chassis.type, "13");
406 + safecpy(dmi->chassis.type, "13");
407 else if (strcasestr(device_type, "mac"))
598 - safecpy(ds->hw.chassis.type, "3");
408 + safecpy(dmi->chassis.type, "3");
409 }
410
411 // If chassis type not set, guess based on product name
602 - if (!ds->hw.chassis.type[0] && ds->hw.product.name[0]) {
603 - if (strcasestr(ds->hw.product.name, "book"))
604 - safecpy(ds->hw.chassis.type, "9");
605 - else if (strcasestr(ds->hw.product.name, "imac"))
606 - safecpy(ds->hw.chassis.type, "13");
607 - else if (strcasestr(ds->hw.product.name, "mac") && strcasestr(ds->hw.product.name, "pro"))
608 - safecpy(ds->hw.chassis.type, "3");
609 - else if (strcasestr(ds->hw.product.name, "mac") && strcasestr(ds->hw.product.name, "mini"))
610 - safecpy(ds->hw.chassis.type, "35");
412 + if (!dmi->chassis.type[0] && dmi->product.name[0]) {
413 + if (strcasestr(dmi->product.name, "book"))
414 + safecpy(dmi->chassis.type, "9");
415 + else if (strcasestr(dmi->product.name, "imac"))
416 + safecpy(dmi->chassis.type, "13");
417 + else if (strcasestr(dmi->product.name, "mac") && strcasestr(dmi->product.name, "pro"))
418 + safecpy(dmi->chassis.type, "3");
419 + else if (strcasestr(dmi->product.name, "mac") && strcasestr(dmi->product.name, "mini"))
420 + safecpy(dmi->chassis.type, "35");
421 }
422
423 IOObjectRelease(platform_expert);
424 }
425
426 // Get SMC revision and system firmware info
617 -static void get_firmware_info(DAEMON_STATUS_FILE *ds) {
618 - if (!ds)
427 +static void get_firmware_info(DMI_INFO *dmi) {
428 + if (!dmi)
429 return;
430
431 io_registry_entry_t smc = IOServiceGetMatchingService(
@@ -627,8 +437,8 @@ static void get_firmware_info(DAEMON_STATUS_FILE *ds) {
437 get_iokit_string_property(smc, CFSTR("smc-version"), smc_version, sizeof(smc_version));
438
439 if (smc_version[0]) {
630 - safecpy(ds->hw.bios.version, smc_version);
631 - dmi_clean_field(ds->hw.bios.version, sizeof(ds->hw.bios.version));
440 + safecpy(dmi->bios.version, smc_version);
441 + dmi_clean_field(dmi->bios.version, sizeof(dmi->bios.version));
442 }
443
444 IOObjectRelease(smc);
@@ -638,19 +448,19 @@ static void get_firmware_info(DAEMON_STATUS_FILE *ds) {
448 io_registry_entry_t rom = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/rom");
449 if (rom) {
450 // "version" contains firmware version
641 - get_iokit_string_property(rom, CFSTR("version"), ds->hw.bios.version, sizeof(ds->hw.bios.version));
451 + get_iokit_string_property(rom, CFSTR("version"), dmi->bios.version, sizeof(dmi->bios.version));
452
453 // Apple is the vendor for all Mac firmware
644 - safecpy(ds->hw.bios.vendor, "Apple");
454 + safecpy(dmi->bios.vendor, "Apple");
455
456 // "release-date" can sometimes be found
647 - get_iokit_string_property(rom, CFSTR("release-date"), ds->hw.bios.date, sizeof(ds->hw.bios.date));
457 + get_iokit_string_property(rom, CFSTR("release-date"), dmi->bios.date, sizeof(dmi->bios.date));
458
459 IOObjectRelease(rom);
460 }
461
462 // If we still don't have BIOS version, check system version from sysctl
653 - if (!ds->hw.bios.version[0]) {
463 + if (!dmi->bios.version[0]) {
464 char firmware_version[256] = {0};
465 size_t len = sizeof(firmware_version) - 1;
466
@@ -660,96 +470,141 @@ static void get_firmware_info(DAEMON_STATUS_FILE *ds) {
470 // Extract firmware info if present
471 char *firmware_info = strstr(firmware_version, "SMC:");
472 if (firmware_info && firmware_info < firmware_version + sizeof(firmware_version) - 1) {
663 - safecpy(ds->hw.bios.version, firmware_info);
664 - dmi_clean_field(ds->hw.bios.version, sizeof(ds->hw.bios.version));
473 + safecpy(dmi->bios.version, firmware_info);
474 + dmi_clean_field(dmi->bios.version, sizeof(dmi->bios.version));
475 }
476 }
477 }
478 }
479
480 // Get system hardware information using sysctl
671 -static void get_sysctl_info(DAEMON_STATUS_FILE *ds) {
672 - if (!ds)
481 +static void get_sysctl_info(DMI_INFO *dmi) {
482 + if (!dmi)
483 return;
484
485 // Get model identifier using sysctl if not already set
676 - if (!ds->hw.product.name[0]) {
486 + if (!dmi->product.name[0]) {
487 char model[256] = { 0 };
488 size_t len = sizeof(model) - 1;
489
490 if (sysctlbyname("hw.model", model, &len, NULL, 0) == 0) {
491 model[len] = '\0';
682 - safecpy(ds->hw.product.name, model);
683 - dmi_clean_field(ds->hw.product.name, sizeof(ds->hw.product.name));
492 + safecpy(dmi->product.name, model);
493 + dmi_clean_field(dmi->product.name, sizeof(dmi->product.name));
494
495 // If chassis type is still not set, guess from model
686 - if (!ds->hw.chassis.type[0]) {
496 + if (!dmi->chassis.type[0]) {
497 if (strncasecmp(model, "MacBook", 7) == 0)
688 - safecpy(ds->hw.chassis.type, "9");
498 + safecpy(dmi->chassis.type, "9");
499 else if (strncasecmp(model, "iMac", 4) == 0)
690 - safecpy(ds->hw.chassis.type, "13");
500 + safecpy(dmi->chassis.type, "13");
501 else if (strncasecmp(model, "Mac", 3) == 0 && strcasestr(model, "Pro") != NULL)
692 - safecpy(ds->hw.chassis.type, "3");
502 + safecpy(dmi->chassis.type, "3");
503 else if (strncasecmp(model, "Mac", 3) == 0 && strcasestr(model, "mini") != NULL)
694 - safecpy(ds->hw.chassis.type, "35");
504 + safecpy(dmi->chassis.type, "35");
505 else
696 - safecpy(ds->hw.chassis.type, "3"); // Default to desktop
506 + safecpy(dmi->chassis.type, "3"); // Default to desktop
507 }
508 }
509 }
510
511 // Get CPU information if board name not set
702 - if (!ds->hw.board.name[0]) {
512 + if (!dmi->board.name[0]) {
513 char cpu_brand[256] = {0};
514 size_t len = sizeof(cpu_brand) - 1;
515
516 if (sysctlbyname("machdep.cpu.brand_string", cpu_brand, &len, NULL, 0) == 0) {
517 cpu_brand[len] = '\0'; // Ensure null termination
518 // Use CPU information as part of board info if not available
709 - safecpy(ds->hw.board.name, cpu_brand);
710 - dmi_clean_field(ds->hw.board.name, sizeof(ds->hw.board.name));
519 + safecpy(dmi->board.name, cpu_brand);
520 + dmi_clean_field(dmi->board.name, sizeof(dmi->board.name));
521 }
522 }
523 }
524
525 // Main function to get hardware info
716 -void os_dmi_info(DAEMON_STATUS_FILE *ds) {
717 - if (!ds) return;
526 +void os_dmi_info_get(DMI_INFO *dmi) {
527 + if (!dmi) return;
528
529 // Always set Apple as default vendor
720 - safecpy(ds->hw.sys.vendor, "Apple");
530 + safecpy(dmi->sys.vendor, "Apple");
531
532 // Get info from IOPlatformExpertDevice
723 - get_platform_expert_info(ds);
533 + get_platform_expert_info(dmi);
534
535 // Get info from IODeviceTree
726 - get_devicetree_info(ds);
536 + get_devicetree_info(dmi);
537
538 // Get firmware information
729 - get_firmware_info(ds);
539 + get_firmware_info(dmi);
540
541 // Get additional info from sysctl
732 - get_sysctl_info(ds);
542 + get_sysctl_info(dmi);
543
544 // Set board vendor to match system vendor if not set
735 - if (!ds->hw.board.vendor[0] && ds->hw.sys.vendor[0])
736 - safecpy(ds->hw.board.vendor, ds->hw.sys.vendor);
545 + if (!dmi->board.vendor[0] && dmi->sys.vendor[0])
546 + safecpy(dmi->board.vendor, dmi->sys.vendor);
547
548 // Set chassis vendor to match system vendor if not set
739 - if (!ds->hw.chassis.vendor[0] && ds->hw.sys.vendor[0])
740 - safecpy(ds->hw.chassis.vendor, ds->hw.sys.vendor);
549 + if (!dmi->chassis.vendor[0] && dmi->sys.vendor[0])
550 + safecpy(dmi->chassis.vendor, dmi->sys.vendor);
551
552 // Set bios vendor to match system vendor if not set
743 - if (!ds->hw.bios.vendor[0] && ds->hw.sys.vendor[0])
744 - safecpy(ds->hw.bios.vendor, ds->hw.sys.vendor);
553 + if (!dmi->bios.vendor[0] && dmi->sys.vendor[0])
554 + safecpy(dmi->bios.vendor, dmi->sys.vendor);
555
556 // Default product name if all methods failed
747 - if (!ds->hw.product.name[0])
748 - safecpy(ds->hw.product.name, "Mac");
557 + if (!dmi->product.name[0])
558 + safecpy(dmi->product.name, "Mac");
559
560 // Default chassis type if we couldn't determine it
751 - if (!ds->hw.chassis.type[0])
752 - safecpy(ds->hw.chassis.type, "3"); // Desktop
561 + if (!dmi->chassis.type[0])
562 + safecpy(dmi->chassis.type, "3"); // Desktop
563 +
564 + // Check boot mode (UEFI vs Legacy) on macOS
565 + io_registry_entry_t options = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options");
566 + if (options) {
567 + bool found_efi = false;
568 + CFTypeRef property = IORegistryEntryCreateCFProperty(options, CFSTR("efi-boot-device"), kCFAllocatorDefault, 0);
569 + if (property) {
570 + found_efi = true;
571 + CFRelease(property);
572 + }
573 +
574 + if (found_efi) {
575 + safecpy(dmi->bios.mode, "UEFI");
576 +
577 + // Check secure boot status
578 + CFTypeRef secure_boot_prop = IORegistryEntryCreateCFProperty(options,
579 + CFSTR("SecureBootLevel"),
580 + kCFAllocatorDefault, 0);
581 + if (secure_boot_prop) {
582 + if (CFGetTypeID(secure_boot_prop) == CFNumberGetTypeID()) {
583 + int level;
584 + if (CFNumberGetValue((CFNumberRef)secure_boot_prop, kCFNumberIntType, &level)) {
585 + // Any positive value indicates secure boot is enabled
586 + dmi->bios.secure_boot = (level > 0);
587 + }
588 + }
589 + CFRelease(secure_boot_prop);
590 + }
591 + } else {
592 + safecpy(dmi->bios.mode, "Legacy");
593 + }
594 +
595 + IOObjectRelease(options);
596 + } else {
597 + safecpy(dmi->bios.mode, "Unknown");
598 + }
599 +
600 + // Check for Apple T2 security chip (similar to TPM but without using external commands)
601 + // This uses only IOKit APIs which are safe and built-in
602 + io_registry_entry_t chip = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/AppleACPIPlatformExpert/SMC/AppleT2:");
603 + if (chip) {
604 + // If T2 chip is present, secure boot is likely enabled
605 + dmi->bios.secure_boot = true;
606 + IOObjectRelease(chip);
607 + }
608 }
609
610 #elif defined(OS_FREEBSD)
@@ -778,46 +633,83 @@ static void freebsd_get_kenv_str(const char *name, char *dst, size_t dst_size) {
633 }
634 }
635
781 -void os_dmi_info(DAEMON_STATUS_FILE *ds) {
636 +void os_dmi_info_get(DMI_INFO *dmi) {
637 + if (!dmi) return;
638 +
639 // System information from SMBIOS
783 - freebsd_get_sysctl_str("hw.vendor", ds->hw.sys.vendor, sizeof(ds->hw.sys.vendor));
784 - freebsd_get_sysctl_str("hw.product", ds->hw.product.name, sizeof(ds->hw.product.name));
785 - freebsd_get_sysctl_str("hw.version", ds->hw.product.version, sizeof(ds->hw.product.version));
640 + freebsd_get_sysctl_str("hw.vendor", dmi->sys.vendor, sizeof(dmi->sys.vendor));
641 + freebsd_get_sysctl_str("hw.product", dmi->product.name, sizeof(dmi->product.name));
642 + freebsd_get_sysctl_str("hw.version", dmi->product.version, sizeof(dmi->product.version));
643 + freebsd_get_sysctl_str("hw.serial", dmi->sys.serial, sizeof(dmi->sys.serial));
644
645 // Try using kenv for additional information
788 - freebsd_get_kenv_str("smbios.system.maker", ds->hw.sys.vendor, sizeof(ds->hw.sys.vendor));
789 - freebsd_get_kenv_str("smbios.system.product", ds->hw.product.name, sizeof(ds->hw.product.name));
790 - freebsd_get_kenv_str("smbios.system.version", ds->hw.product.version, sizeof(ds->hw.product.version));
791 - freebsd_get_kenv_str("smbios.system.sku", ds->hw.product.sku, sizeof(ds->hw.product.sku));
792 - freebsd_get_kenv_str("smbios.system.family", ds->hw.product.family, sizeof(ds->hw.product.family));
646 + freebsd_get_kenv_str("smbios.system.maker", dmi->sys.vendor, sizeof(dmi->sys.vendor));
647 + freebsd_get_kenv_str("smbios.system.product", dmi->product.name, sizeof(dmi->product.name));
648 + freebsd_get_kenv_str("smbios.system.version", dmi->product.version, sizeof(dmi->product.version));
649 + freebsd_get_kenv_str("smbios.system.sku", dmi->product.sku, sizeof(dmi->product.sku));
650 + freebsd_get_kenv_str("smbios.system.family", dmi->product.family, sizeof(dmi->product.family));
651 + freebsd_get_kenv_str("smbios.system.serial", dmi->sys.serial, sizeof(dmi->sys.serial));
652
653 // Board information
795 - freebsd_get_kenv_str("smbios.planar.maker", ds->hw.board.vendor, sizeof(ds->hw.board.vendor));
796 - freebsd_get_kenv_str("smbios.planar.product", ds->hw.board.name, sizeof(ds->hw.board.name));
797 - freebsd_get_kenv_str("smbios.planar.version", ds->hw.board.version, sizeof(ds->hw.board.version));
654 + freebsd_get_kenv_str("smbios.planar.maker", dmi->board.vendor, sizeof(dmi->board.vendor));
655 + freebsd_get_kenv_str("smbios.planar.product", dmi->board.name, sizeof(dmi->board.name));
656 + freebsd_get_kenv_str("smbios.planar.version", dmi->board.version, sizeof(dmi->board.version));
657 + freebsd_get_kenv_str("smbios.planar.serial", dmi->board.serial, sizeof(dmi->board.serial));
658
659 // BIOS information
800 - freebsd_get_kenv_str("smbios.bios.vendor", ds->hw.bios.vendor, sizeof(ds->hw.bios.vendor));
801 - freebsd_get_kenv_str("smbios.bios.version", ds->hw.bios.version, sizeof(ds->hw.bios.version));
802 - freebsd_get_kenv_str("smbios.bios.reldate", ds->hw.bios.date, sizeof(ds->hw.bios.date));
803 - freebsd_get_kenv_str("smbios.bios.release", ds->hw.bios.release, sizeof(ds->hw.bios.release));
660 + freebsd_get_kenv_str("smbios.bios.vendor", dmi->bios.vendor, sizeof(dmi->bios.vendor));
661 + freebsd_get_kenv_str("smbios.bios.version", dmi->bios.version, sizeof(dmi->bios.version));
662 + freebsd_get_kenv_str("smbios.bios.reldate", dmi->bios.date, sizeof(dmi->bios.date));
663 + freebsd_get_kenv_str("smbios.bios.release", dmi->bios.release, sizeof(dmi->bios.release));
664
665 // Chassis information
806 - freebsd_get_kenv_str("smbios.chassis.maker", ds->hw.chassis.vendor, sizeof(ds->hw.chassis.vendor));
807 - freebsd_get_kenv_str("smbios.chassis.version", ds->hw.chassis.version, sizeof(ds->hw.chassis.version));
666 + freebsd_get_kenv_str("smbios.chassis.maker", dmi->chassis.vendor, sizeof(dmi->chassis.vendor));
667 + freebsd_get_kenv_str("smbios.chassis.version", dmi->chassis.version, sizeof(dmi->chassis.version));
668 + freebsd_get_kenv_str("smbios.chassis.serial", dmi->chassis.serial, sizeof(dmi->chassis.serial));
669
670 // Chassis type
810 - char chassis_type[16] = "";
811 - freebsd_get_kenv_str("smbios.chassis.type", chassis_type, sizeof(chassis_type));
812 - if (chassis_type[0]) {
813 - int type = atoi(chassis_type);
814 - if (type > 0)
815 - snprintf(ds->hw.chassis.type, sizeof(ds->hw.chassis.type), "%d", type);
816 - }
671 + freebsd_get_kenv_str("smbios.chassis.type", dmi->chassis.type, sizeof(dmi->chassis.type));
672
673 // If we couldn't get system information from SMBIOS, try to use model
819 - if (!ds->hw.product.name[0])
820 - freebsd_get_sysctl_str("hw.model", ds->hw.product.name, sizeof(ds->hw.product.name));
674 + if (!dmi->product.name[0])
675 + freebsd_get_sysctl_str("hw.model", dmi->product.name, sizeof(dmi->product.name));
676 +
677 + // Try to get asset tags
678 + freebsd_get_kenv_str("smbios.system.asset_tag", dmi->sys.asset_tag, sizeof(dmi->sys.asset_tag));
679 + freebsd_get_kenv_str("smbios.chassis.asset_tag", dmi->chassis.asset_tag, sizeof(dmi->chassis.asset_tag));
680 + freebsd_get_kenv_str("smbios.planar.asset_tag", dmi->board.asset_tag, sizeof(dmi->board.asset_tag));
681 +
682 + // Get UUID
683 + freebsd_get_kenv_str("smbios.system.uuid", dmi->sys.uuid, sizeof(dmi->sys.uuid));
684 +
685 + // Check for UEFI boot mode using sysctl (no external commands)
686 + char bootmethod[64] = {0};
687 + size_t bootmethod_size = sizeof(bootmethod) - 1;
688 + if (sysctlbyname("kern.bootmethod", bootmethod, &bootmethod_size, NULL, 0) == 0) {
689 + if (strncmp(bootmethod, "UEFI", 4) == 0) {
690 + safecpy(dmi->bios.mode, "UEFI");
691 +
692 + // Check secure boot status - FreeBSD stores this in sysctl (no external commands)
693 + int secure_boot_enabled = 0;
694 + size_t secboot_size = sizeof(secure_boot_enabled);
695 + if (sysctlbyname("kern.secureboot.enable", &secure_boot_enabled, &secboot_size, NULL, 0) == 0) {
696 + dmi->bios.secure_boot = (secure_boot_enabled != 0);
697 + }
698 + } else {
699 + safecpy(dmi->bios.mode, "Legacy");
700 + }
701 + } else {
702 + // Fallback: check for EFI presence (older FreeBSD versions) - no external commands
703 + int efi_present = 0;
704 + size_t efi_size = sizeof(efi_present);
705 + if (sysctlbyname("kern.efi.runtime", &efi_present, &efi_size, NULL, 0) == 0) {
706 + if (efi_present) {
707 + safecpy(dmi->bios.mode, "UEFI");
708 + } else {
709 + safecpy(dmi->bios.mode, "Legacy");
710 + }
711 + }
712 + }
713 }
714
715 #elif defined(OS_WINDOWS)
@@ -960,11 +852,11 @@ static smbios_data_t get_smbios_data(void) {
852
853 // Process BIOS Information (Type 0)
854 static void process_smbios_bios_info(const smbios_header_t *header,
963 - DAEMON_STATUS_FILE *ds,
855 + DMI_INFO *dmi,
856 const char *string_table,
857 const BYTE *smbios_data,
858 DWORD smbios_size) {
967 - if (header->length < 18) // Minimum size for BIOS info
859 + if (header->length < 18 || !dmi) // Minimum size for BIOS info
860 return;
861
862 const BYTE *data = (const BYTE *)header;
@@ -973,29 +865,29 @@ static void process_smbios_bios_info(const smbios_header_t *header,
865 // BIOS Vendor (string index at offset 4)
866 if (data[4] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
867 data[4], temp_str, sizeof(temp_str))) {
976 - safecpy(ds->hw.bios.vendor, temp_str);
868 + safecpy(dmi->bios.vendor, temp_str);
869 }
870
871 // BIOS Version (string index at offset 5)
872 if (data[5] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
873 data[5], temp_str, sizeof(temp_str))) {
982 - safecpy(ds->hw.bios.version, temp_str);
874 + safecpy(dmi->bios.version, temp_str);
875 }
876
877 // BIOS Release Date (string index at offset 8)
878 if (data[8] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
879 data[8], temp_str, sizeof(temp_str))) {
988 - safecpy(ds->hw.bios.date, temp_str);
880 + safecpy(dmi->bios.date, temp_str);
881 }
882 }
883
884 // Process System Information (Type 1)
885 static void process_smbios_system_info(const smbios_header_t *header,
994 - DAEMON_STATUS_FILE *ds,
886 + DMI_INFO *dmi,
887 const char *string_table,
888 const BYTE *smbios_data,
889 DWORD smbios_size) {
998 - if (header->length < 8) // Minimum size for System info
890 + if (header->length < 8 || !dmi) // Minimum size for System info
891 return;
892
893 const BYTE *data = (const BYTE *)header;
@@ -1004,36 +896,75 @@ static void process_smbios_system_info(const smbios_header_t *header,
896 // Manufacturer (string index at offset 4)
897 if (data[4] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
898 data[4], temp_str, sizeof(temp_str))) {
1007 - safecpy(ds->hw.sys.vendor, temp_str);
899 + safecpy(dmi->sys.vendor, temp_str);
900 }
901
902 // Product Name (string index at offset 5)
903 if (data[5] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
904 data[5], temp_str, sizeof(temp_str))) {
1013 - safecpy(ds->hw.product.name, temp_str);
905 + safecpy(dmi->product.name, temp_str);
906 }
907
908 // Version (string index at offset 6)
909 if (data[6] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
910 data[6], temp_str, sizeof(temp_str))) {
1019 - safecpy(ds->hw.product.version, temp_str);
911 + safecpy(dmi->product.version, temp_str);
912 + }
913 +
914 + // Serial Number (string index at offset 7)
915 + if (data[7] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
916 + data[7], temp_str, sizeof(temp_str))) {
917 + safecpy(dmi->sys.serial, temp_str);
918 }
919
920 // If structure is long enough for family (SMBIOS 2.1+)
921 if (header->length >= 25 && data[21] > 0 &&
922 get_smbios_string(smbios_data, smbios_size, string_table,
923 data[21], temp_str, sizeof(temp_str))) {
1026 - safecpy(ds->hw.product.family, temp_str);
924 + safecpy(dmi->product.family, temp_str);
925 + }
926 +
927 + // Extract system UUID (16 bytes starting at offset 8)
928 + if (header->length >= 24) {
929 + // Verify we have enough data to access all bytes safely
930 + if ((uintptr_t)data + 23 < (uintptr_t)smbios_data + smbios_size) {
931 + // UUID is stored as 16 bytes, we'll format it as a standard UUID string
932 + char uuid_str[40] = {0}; // 36 chars for UUID + null terminator
933 +
934 + // Note: SMBIOS UUID needs byte swapping for first three fields
935 + int ret = snprintf(uuid_str, sizeof(uuid_str),
936 + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
937 + data[11], data[10], data[9], data[8], // field 1 (byte swapped)
938 + data[13], data[12], // field 2 (byte swapped)
939 + data[15], data[14], // field 3 (byte swapped)
940 + data[16], data[17], // field 4 (not swapped)
941 + data[18], data[19], data[20], data[21], data[22], data[23]); // field 5 (not swapped)
942 +
943 + // Verify the formatting worked and buffer wasn't truncated
944 + if (ret > 0 && ret < (int)sizeof(uuid_str)) {
945 + // Only set if not all zeros (some systems report all zeros)
946 + if (uuid_str[0] != '0' || uuid_str[1] != '0' || uuid_str[2] != '0' || uuid_str[3] != '0') {
947 + safecpy(dmi->sys.uuid, uuid_str);
948 + }
949 + }
950 + }
951 + }
952 +
953 + // Asset Tag is sometimes available in newer SMBIOS versions
954 + if (header->length >= 27 && data[26] > 0 &&
955 + get_smbios_string(smbios_data, smbios_size, string_table,
956 + data[26], temp_str, sizeof(temp_str))) {
957 + safecpy(dmi->sys.asset_tag, temp_str);
958 }
959 }
960
961 // Process Baseboard Information (Type 2)
962 static void process_smbios_baseboard_info(const smbios_header_t *header,
1032 - DAEMON_STATUS_FILE *ds,
963 + DMI_INFO *dmi,
964 const char *string_table,
965 const BYTE *smbios_data,
966 DWORD smbios_size) {
1036 - if (header->length < 8) // Minimum size for Baseboard info
967 + if (header->length < 8 || !dmi) // Minimum size for Baseboard info
968 return;
969
970 const BYTE *data = (const BYTE *)header;
@@ -1042,29 +973,42 @@ static void process_smbios_baseboard_info(const smbios_header_t *header,
973 // Manufacturer (string index at offset 4)
974 if (data[4] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
975 data[4], temp_str, sizeof(temp_str))) {
1045 - safecpy(ds->hw.board.vendor, temp_str);
976 + safecpy(dmi->board.vendor, temp_str);
977 }
978
979 // Product (string index at offset 5)
980 if (data[5] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
981 data[5], temp_str, sizeof(temp_str))) {
1051 - safecpy(ds->hw.board.name, temp_str);
982 + safecpy(dmi->board.name, temp_str);
983 }
984
985 // Version (string index at offset 6)
986 if (data[6] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
987 data[6], temp_str, sizeof(temp_str))) {
1057 - safecpy(ds->hw.board.version, temp_str);
988 + safecpy(dmi->board.version, temp_str);
989 + }
990 +
991 + // Serial Number (string index at offset 7)
992 + if (data[7] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
993 + data[7], temp_str, sizeof(temp_str))) {
994 + safecpy(dmi->board.serial, temp_str);
995 + }
996 +
997 + // Asset Tag (string index at offset 8)
998 + if (header->length >= 9 && data[8] > 0 &&
999 + get_smbios_string(smbios_data, smbios_size, string_table,
1000 + data[8], temp_str, sizeof(temp_str))) {
1001 + safecpy(dmi->board.asset_tag, temp_str);
1002 }
1003 }
1004
1005 // Process Chassis Information (Type 3)
1006 static void process_smbios_chassis_info(const smbios_header_t *header,
1063 - DAEMON_STATUS_FILE *ds,
1007 + DMI_INFO *dmi,
1008 const char *string_table,
1009 const BYTE *smbios_data,
1010 DWORD smbios_size) {
1067 - if (header->length < 9) // Minimum size for Chassis info
1011 + if (header->length < 9 || !dmi) // Minimum size for Chassis info
1012 return;
1013
1014 const BYTE *data = (const BYTE *)header;
@@ -1073,49 +1017,63 @@ static void process_smbios_chassis_info(const smbios_header_t *header,
1017 // Manufacturer (string index at offset 4)
1018 if (data[4] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
1019 data[4], temp_str, sizeof(temp_str))) {
1076 - safecpy(ds->hw.chassis.vendor, temp_str);
1020 + safecpy(dmi->chassis.vendor, temp_str);
1021 }
1022
1023 // Type (numerical value at offset 5)
1024 BYTE chassis_type = data[5] & 0x7F; // Mask out MSB
1081 - if (chassis_type > 0 && chassis_type < 36) // Valid range check
1082 - snprintf(ds->hw.chassis.type, sizeof(ds->hw.chassis.type), "%d", chassis_type);
1025 + snprintf(dmi->chassis.type, sizeof(dmi->chassis.type), "%d", chassis_type);
1026
1027 // Version (string index at offset 6)
1028 if (data[6] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
1029 data[6], temp_str, sizeof(temp_str))) {
1087 - safecpy(ds->hw.chassis.version, temp_str);
1030 + safecpy(dmi->chassis.version, temp_str);
1031 + }
1032 +
1033 + // Serial Number (string index at offset 7)
1034 + if (data[7] > 0 && get_smbios_string(smbios_data, smbios_size, string_table,
1035 + data[7], temp_str, sizeof(temp_str))) {
1036 + safecpy(dmi->chassis.serial, temp_str);
1037 + }
1038 +
1039 + // Asset Tag (string index at offset 8)
1040 + if (header->length >= 9 && data[8] > 0 &&
1041 + get_smbios_string(smbios_data, smbios_size, string_table,
1042 + data[8], temp_str, sizeof(temp_str))) {
1043 + safecpy(dmi->chassis.asset_tag, temp_str);
1044 }
1045 }
1046
1047 // Process a complete SMBIOS structure
1048 static void process_smbios_structure(const smbios_header_t *header,
1093 - DAEMON_STATUS_FILE *ds,
1049 + DMI_INFO *dmi,
1050 const char *string_table,
1051 const BYTE *smbios_data,
1052 DWORD smbios_size) {
1053 + if (!dmi) return;
1054 +
1055 switch (header->type) {
1056 case 0: // BIOS Information
1099 - process_smbios_bios_info(header, ds, string_table, smbios_data, smbios_size);
1057 + process_smbios_bios_info(header, dmi, string_table, smbios_data, smbios_size);
1058 break;
1059
1060 case 1: // System Information
1103 - process_smbios_system_info(header, ds, string_table, smbios_data, smbios_size);
1061 + process_smbios_system_info(header, dmi, string_table, smbios_data, smbios_size);
1062 break;
1063
1064 case 2: // Baseboard Information
1107 - process_smbios_baseboard_info(header, ds, string_table, smbios_data, smbios_size);
1065 + process_smbios_baseboard_info(header, dmi, string_table, smbios_data, smbios_size);
1066 break;
1067
1068 case 3: // Chassis Information
1111 - process_smbios_chassis_info(header, ds, string_table, smbios_data, smbios_size);
1069 + process_smbios_chassis_info(header, dmi, string_table, smbios_data, smbios_size);
1070 break;
1071 }
1072 }
1073
1074 // Parse all SMBIOS structures with robust error handling
1117 -static void parse_smbios_structures(const smbios_data_t smbios, DAEMON_STATUS_FILE *ds) {
1118 - if (!smbios.valid || !smbios.data || smbios.size < 8)
1075 +static void parse_smbios_structures(const smbios_data_t smbios, DMI_INFO *dmi) {
1076 + if (!smbios.valid || !smbios.data || smbios.size < 8 || !dmi)
1077 return;
1078
1079 // The SMBIOS header is at offset 8
@@ -1174,7 +1132,7 @@ static void parse_smbios_structures(const smbios_data_t smbios, DAEMON_STATUS_FI
1132 // Process the structure based on type
1133 const char *string_table = (const char *)(current + header->length);
1134 if (string_table < (const char *)end) {
1177 - process_smbios_structure(header, ds, string_table, smbios.data, smbios.size);
1135 + process_smbios_structure(header, dmi, string_table, smbios.data, smbios.size);
1136 }
1137
1138 // Find string table end (double null terminator)
@@ -1208,36 +1166,49 @@ static void parse_smbios_structures(const smbios_data_t smbios, DAEMON_STATUS_FI
1166 break;
1167 }
1168 }
1211 -static void windows_get_smbios_info(DAEMON_STATUS_FILE *ds) {
1169 +static void windows_get_smbios_info(DMI_INFO *dmi) {
1170 + if (!dmi) return;
1171 +
1172 // Get SMBIOS data using our improved container structure
1173 smbios_data_t smbios = get_smbios_data();
1174 if (!smbios.valid)
1175 return;
1176
1177 // Process the SMBIOS data with our improved parser
1218 - parse_smbios_structures(smbios, ds);
1178 + parse_smbios_structures(smbios, dmi);
1179
1180 // Clean up
1181 freez(smbios.data);
1182 }
1183
1184 // Fallback method using registry
1225 -static void windows_get_registry_info(DAEMON_STATUS_FILE *ds) {
1185 +static void windows_get_registry_info(DMI_INFO *dmi) {
1186 + if (!dmi) return;
1187 +
1188 // System manufacturer and model from registry
1189 windows_read_registry_string(
1190 HKEY_LOCAL_MACHINE,
1191 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1192 "SystemManufacturer",
1231 - ds->hw.sys.vendor,
1232 - sizeof(ds->hw.sys.vendor)
1193 + dmi->sys.vendor,
1194 + sizeof(dmi->sys.vendor)
1195 );
1196
1197 windows_read_registry_string(
1198 HKEY_LOCAL_MACHINE,
1199 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1200 "SystemProductName",
1239 - ds->hw.product.name,
1240 - sizeof(ds->hw.product.name)
1201 + dmi->product.name,
1202 + sizeof(dmi->product.name)
1203 + );
1204 +
1205 + // System Serial Number
1206 + windows_read_registry_string(
1207 + HKEY_LOCAL_MACHINE,
1208 + "HARDWARE\\DESCRIPTION\\System\\BIOS",
1209 + "SystemSerialNumber",
1210 + dmi->sys.serial,
1211 + sizeof(dmi->sys.serial)
1212 );
1213
1214 // BIOS information
@@ -1245,24 +1216,24 @@ static void windows_get_registry_info(DAEMON_STATUS_FILE *ds) {
1216 HKEY_LOCAL_MACHINE,
1217 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1218 "BIOSVendor",
1248 - ds->hw.bios.vendor,
1249 - sizeof(ds->hw.bios.vendor)
1219 + dmi->bios.vendor,
1220 + sizeof(dmi->bios.vendor)
1221 );
1222
1223 windows_read_registry_string(
1224 HKEY_LOCAL_MACHINE,
1225 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1226 "BIOSVersion",
1256 - ds->hw.bios.version,
1257 - sizeof(ds->hw.bios.version)
1227 + dmi->bios.version,
1228 + sizeof(dmi->bios.version)
1229 );
1230
1231 windows_read_registry_string(
1232 HKEY_LOCAL_MACHINE,
1233 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1234 "BIOSReleaseDate",
1264 - ds->hw.bios.date,
1265 - sizeof(ds->hw.bios.date)
1235 + dmi->bios.date,
1236 + sizeof(dmi->bios.date)
1237 );
1238
1239 // Board information
@@ -1270,126 +1241,189 @@ static void windows_get_registry_info(DAEMON_STATUS_FILE *ds) {
1241 HKEY_LOCAL_MACHINE,
1242 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1243 "BaseBoardManufacturer",
1273 - ds->hw.board.vendor,
1274 - sizeof(ds->hw.board.vendor)
1244 + dmi->board.vendor,
1245 + sizeof(dmi->board.vendor)
1246 );
1247
1248 windows_read_registry_string(
1249 HKEY_LOCAL_MACHINE,
1250 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1251 "BaseBoardProduct",
1281 - ds->hw.board.name,
1282 - sizeof(ds->hw.board.name)
1252 + dmi->board.name,
1253 + sizeof(dmi->board.name)
1254 );
1255
1256 windows_read_registry_string(
1257 HKEY_LOCAL_MACHINE,
1258 "HARDWARE\\DESCRIPTION\\System\\BIOS",
1259 "BaseBoardVersion",
1289 - ds->hw.board.version,
1290 - sizeof(ds->hw.board.version)
1260 + dmi->board.version,
1261 + sizeof(dmi->board.version)
1262 + );
1263 +
1264 + // Base Board Serial
1265 + windows_read_registry_string(
1266 + HKEY_LOCAL_MACHINE,
1267 + "HARDWARE\\DESCRIPTION\\System\\BIOS",
1268 + "BaseBoardSerialNumber",
1269 + dmi->board.serial,
1270 + sizeof(dmi->board.serial)
1271 );
1272 +
1273 + // Chassis Serial (might not be available in registry, but try anyway)
1274 + windows_read_registry_string(
1275 + HKEY_LOCAL_MACHINE,
1276 + "HARDWARE\\DESCRIPTION\\System\\BIOS",
1277 + "ChassisSerialNumber",
1278 + dmi->chassis.serial,
1279 + sizeof(dmi->chassis.serial)
1280 + );
1281 +
1282 + // Get BIOS boot mode (UEFI or Legacy)
1283 + DWORD firmware_type = 0;
1284 + if (GetFirmwareType(&firmware_type)) {
1285 + switch (firmware_type) {
1286 + case 1: // FirmwareTypeBios
1287 + safecpy(dmi->bios.mode, "Legacy");
1288 + break;
1289 + case 2: // FirmwareTypeUefi
1290 + safecpy(dmi->bios.mode, "UEFI");
1291 + break;
1292 + default:
1293 + safecpy(dmi->bios.mode, "Unknown");
1294 + }
1295 + }
1296 +
1297 + // Check if secure boot is enabled
1298 + HKEY key;
1299 + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SecureBoot\\State",
1300 + 0, KEY_READ, &key) == ERROR_SUCCESS) {
1301 + DWORD secure_boot_value = 0;
1302 + DWORD size = sizeof(secure_boot_value);
1303 + if (RegQueryValueExA(key, "UEFISecureBootEnabled", NULL, NULL,
1304 + (LPBYTE)&secure_boot_value, &size) == ERROR_SUCCESS) {
1305 + dmi->bios.secure_boot = (secure_boot_value != 0);
1306 + }
1307 + RegCloseKey(key);
1308 + }
1309 }
1310
1311 // Main function to get hardware information
1295 -void os_dmi_info(DAEMON_STATUS_FILE *ds) {
1312 +void os_dmi_info_get(DMI_INFO *dmi) {
1313 + if (!dmi) return;
1314 +
1315 // First try SMBIOS data through firmware table API
1297 - windows_get_smbios_info(ds);
1316 + windows_get_smbios_info(dmi);
1317
1318 // Try registry as a fallback or to fill missing values
1300 - windows_get_registry_info(ds);
1319 + windows_get_registry_info(dmi);
1320 +
1321 + // Get asset tags if not set by SMBIOS
1322 + if (!dmi->sys.asset_tag[0]) {
1323 + windows_read_registry_string(
1324 + HKEY_LOCAL_MACHINE,
1325 + "HARDWARE\\DESCRIPTION\\System\\BIOS",
1326 + "SystemAssetTag",
1327 + dmi->sys.asset_tag,
1328 + sizeof(dmi->sys.asset_tag)
1329 + );
1330 + }
1331 +
1332 + if (!dmi->board.asset_tag[0]) {
1333 + windows_read_registry_string(
1334 + HKEY_LOCAL_MACHINE,
1335 + "HARDWARE\\DESCRIPTION\\System\\BIOS",
1336 + "BaseBoardAssetTag",
1337 + dmi->board.asset_tag,
1338 + sizeof(dmi->board.asset_tag)
1339 + );
1340 + }
1341 +
1342 + if (!dmi->chassis.asset_tag[0]) {
1343 + windows_read_registry_string(
1344 + HKEY_LOCAL_MACHINE,
1345 + "HARDWARE\\DESCRIPTION\\System\\BIOS",
1346 + "ChassisAssetTag",
1347 + dmi->chassis.asset_tag,
1348 + sizeof(dmi->chassis.asset_tag)
1349 + );
1350 + }
1351 +
1352 + // Check for Secure Boot - Read directly from registry, no external commands
1353 + HKEY secureBootKey;
1354 + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\SecureBoot\\State",
1355 + 0, KEY_READ, &secureBootKey) == ERROR_SUCCESS) {
1356 + DWORD secure_boot = 0;
1357 + DWORD size = sizeof(secure_boot);
1358 + if (RegQueryValueExA(secureBootKey, "UEFISecureBootEnabled", NULL, NULL,
1359 + (LPBYTE)&secure_boot, &size) == ERROR_SUCCESS) {
1360 + dmi->bios.secure_boot = (secure_boot != 0);
1361 + }
1362 + RegCloseKey(secureBootKey);
1363 + }
1364
1365 // If chassis type is not set or not a valid number, set a default
1303 - if (!ds->hw.chassis.type[0] || atoi(ds->hw.chassis.type) <= 0) {
1366 + if (!dmi->chassis.type[0] || atoi(dmi->chassis.type) <= 0) {
1367 // Check common system names for laptops
1305 - if (strcasestr(ds->hw.product.name, "notebook") != NULL ||
1306 - strcasestr(ds->hw.product.name, "laptop") != NULL ||
1307 - strcasestr(ds->hw.product.name, "book") != NULL) {
1308 - safecpy(ds->hw.chassis.type, "9"); // Laptop
1368 + if (strcasestr(dmi->product.name, "notebook") != NULL ||
1369 + strcasestr(dmi->product.name, "laptop") != NULL ||
1370 + strcasestr(dmi->product.name, "book") != NULL) {
1371 + safecpy(dmi->chassis.type, "9"); // Laptop
1372 }
1373 // Check for servers
1311 - else if (strcasestr(ds->hw.product.name, "server") != NULL) {
1312 - safecpy(ds->hw.chassis.type, "17"); // Server
1374 + else if (strcasestr(dmi->product.name, "server") != NULL) {
1375 + safecpy(dmi->chassis.type, "17"); // Server
1376 }
1377 // Default to desktop
1378 else {
1316 - safecpy(ds->hw.chassis.type, "3"); // Desktop
1379 + safecpy(dmi->chassis.type, "3"); // Desktop
1380 }
1381 }
1382 }
1383
1384 #else
1322 -void os_dmi_info(DAEMON_STATUS_FILE *ds) {
1323 - ;
1385 +void os_dmi_info_get(DMI_INFO *dmi) {
1386 + // No implementation for this platform
1387 }
1388 #endif
1389
1390 // --------------------------------------------------------------------------------------------------------------------
1391 // public API
1392
1330 -void finalize_vendor_product_vm(DAEMON_STATUS_FILE *ds) {
1331 - if(ds->cloud_provider_type[0] && strcmp(ds->cloud_provider_type, "unknown") != 0)
1332 - safecpy(ds->hw.sys.vendor, ds->cloud_provider_type);
1333 -
1334 - if(ds->cloud_instance_type[0] && strcmp(ds->cloud_instance_type, "unknown") != 0)
1335 - safecpy(ds->hw.product.name, ds->cloud_instance_type);
1336 -
1337 - if(ds->virtualization[0] && strcmp(ds->virtualization, "none") != 0 && strcmp(ds->virtualization, "unknown") != 0)
1338 - safecpy(ds->hw.chassis.type, "vm");
1339 -}
1340 -
1341 -void fill_dmi_info(DAEMON_STATUS_FILE *ds) {
1342 - ds->hw.sys.vendor[0] = '\0';
1343 - ds->hw.product.name[0] = '\0';
1344 - ds->hw.product.version[0] = '\0';
1345 - ds->hw.product.sku[0] = '\0';
1346 - ds->hw.product.family[0] = '\0';
1347 - ds->hw.board.vendor[0] = '\0';
1348 - ds->hw.board.name[0] = '\0';
1349 - ds->hw.board.version[0] = '\0';
1350 - ds->hw.bios.vendor[0] = '\0';
1351 - ds->hw.bios.version[0] = '\0';
1352 - ds->hw.bios.date[0] = '\0';
1353 - ds->hw.bios.release[0] = '\0';
1354 - ds->hw.chassis.vendor[0] = '\0';
1355 - ds->hw.chassis.version[0] = '\0';
1356 - ds->hw.chassis.type[0] = '\0';
1357 -
1358 - os_dmi_info(ds);
1359 -
1360 - dmi_normalize_vendor_field(ds->hw.sys.vendor, sizeof(ds->hw.sys.vendor));
1361 - dmi_normalize_vendor_field(ds->hw.board.vendor, sizeof(ds->hw.board.vendor));
1362 - dmi_normalize_vendor_field(ds->hw.chassis.vendor, sizeof(ds->hw.chassis.vendor));
1363 - dmi_normalize_vendor_field(ds->hw.bios.vendor, sizeof(ds->hw.bios.vendor));
1364 -
1365 - dmi_map_chassis_type(ds, atoi(ds->hw.chassis.type));
1366 -
1367 - // make sure we have a system vendor
1368 - if(!ds->hw.sys.vendor[0])
1369 - safecpy(ds->hw.sys.vendor, ds->hw.board.vendor);
1370 - if(!ds->hw.sys.vendor[0])
1371 - safecpy(ds->hw.sys.vendor, ds->hw.chassis.vendor);
1372 - if(!ds->hw.sys.vendor[0])
1373 - safecpy(ds->hw.sys.vendor, ds->hw.bios.vendor);
1374 - if(!ds->hw.sys.vendor[0] && strstr(ds->hw.product.name, "Raspberry") != NULL) {
1375 - safecpy(ds->hw.sys.vendor, "Raspberry");
1376 - safecpy(ds->hw.chassis.type, "mini-pc");
1377 - }
1378 - if(!ds->hw.sys.vendor[0] && (strstr(ds->hw.product.name, "VirtualMac") != NULL || strstr(ds->hw.board.name, "Apple") != NULL))
1379 - safecpy(ds->hw.sys.vendor, "Apple");
1380 - if(!ds->hw.sys.vendor[0])
1381 - safecpy(ds->hw.sys.vendor, "Unknown");
1382 -
1383 - // make sure we have a product name
1384 - if(!ds->hw.product.name[0])
1385 - safecpy(ds->hw.product.name, ds->hw.board.name);
1386 - if(!ds->hw.product.name[0])
1387 - safecpy(ds->hw.product.name, "Unknown");
1388 -
1389 - // make sure we have a chassis type
1390 - if(!ds->hw.chassis.type[0])
1391 - safecpy(ds->hw.chassis.type, "Unknown");
1392 -
1393 - // make sure the cloud provider and cloud instance loaded from system-info.sh are preferred
1394 - finalize_vendor_product_vm(ds);
1393 +void dmi_info_init(DMI_INFO *dmi) {
1394 + if (!dmi) return;
1395 +
1396 + // System information
1397 + dmi->sys.vendor[0] = '\0';
1398 + dmi->sys.serial[0] = '\0';
1399 + dmi->sys.uuid[0] = '\0';
1400 + dmi->sys.asset_tag[0] = '\0';
1401 +
1402 + // Product information
1403 + dmi->product.name[0] = '\0';
1404 + dmi->product.version[0] = '\0';
1405 + dmi->product.sku[0] = '\0';
1406 + dmi->product.family[0] = '\0';
1407 +
1408 + // Board information
1409 + dmi->board.vendor[0] = '\0';
1410 + dmi->board.name[0] = '\0';
1411 + dmi->board.version[0] = '\0';
1412 + dmi->board.serial[0] = '\0';
1413 + dmi->board.asset_tag[0] = '\0';
1414 +
1415 + // BIOS information
1416 + dmi->bios.vendor[0] = '\0';
1417 + dmi->bios.version[0] = '\0';
1418 + dmi->bios.date[0] = '\0';
1419 + dmi->bios.release[0] = '\0';
1420 + dmi->bios.mode[0] = '\0';
1421 + dmi->bios.secure_boot = false;
1422 +
1423 + // Chassis information
1424 + dmi->chassis.vendor[0] = '\0';
1425 + dmi->chassis.version[0] = '\0';
1426 + dmi->chassis.type[0] = '\0';
1427 + dmi->chassis.serial[0] = '\0';
1428 + dmi->chassis.asset_tag[0] = '\0';
1429 }
src/daemon/status-file-dmi.h
+55 -2
@@ -3,9 +3,62 @@
3 #ifndef NETDATA_STATUS_FILE_DMI_H
4 #define NETDATA_STATUS_FILE_DMI_H
5
6 +#include "libnetdata/libnetdata.h"
7 +
8 +#define safecpy(dst, src) do { \
9 + _Static_assert(sizeof(dst) != sizeof(char *), \
10 + "safecpy: dst must not be a pointer, but a buffer (e.g., char dst[SIZE])"); \
11 + \
12 + strcatz(dst, 0, src, sizeof(dst)); \
13 +} while (0)
14 +
15 +typedef struct dmi_info {
16 + struct {
17 + char vendor[64];
18 + char serial[64]; // System serial number
19 + char uuid[64]; // System UUID (hardware identifier)
20 + char asset_tag[64]; // System asset tag
21 + } sys;
22 +
23 + struct {
24 + char name[64];
25 + char version[64];
26 + char sku[64];
27 + char family[64];
28 + } product;
29 +
30 + struct {
31 + char name[64];
32 + char version[64];
33 + char vendor[64];
34 + char serial[64]; // Board serial number
35 + char asset_tag[64]; // Board asset tag
36 + } board;
37 +
38 + struct {
39 + char type[16];
40 + char vendor[64];
41 + char version[64];
42 + char serial[64]; // Chassis serial number
43 + char asset_tag[64]; // Chassis asset tag
44 + } chassis;
45 +
46 + struct {
47 + char date[16];
48 + char release[64];
49 + char version[64];
50 + char vendor[64];
51 + char mode[16]; // Boot mode (UEFI/Legacy)
52 + bool secure_boot; // Secure boot status (true/false)
53 + } bios;
54 +} DMI_INFO;
55 +
56 #include "status-file.h"
57
8 -void finalize_vendor_product_vm(DAEMON_STATUS_FILE *ds);
9 -void fill_dmi_info(DAEMON_STATUS_FILE *ds);
58 +// Initialize DMI_INFO structure
59 +void dmi_info_init(DMI_INFO *dmi);
60 +
61 +// Platform-specific function to get DMI info
62 +void os_dmi_info_get(DMI_INFO *dmi);
63
64 #endif //NETDATA_STATUS_FILE_DMI_H
src/daemon/status-file-product.c new
+385
@@ -0,0 +1,385 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "status-file-dmi.h"
4 +#include "status-file-product.h"
5 +
6 +static void dmi_normalize_vendor_field(char *buf, size_t buf_size) {
7 + if(!buf || !buf_size) return;
8 +
9 + struct {
10 + const char *found;
11 + const char *replace;
12 + } vendors[] = {
13 + {"QEMU", "KVM"},
14 +
15 + // Major vendors with multiple variations
16 + {"AMD Corporation", "AMD"},
17 + {"Advanced Micro Devices, Inc.", "AMD"},
18 +
19 + {"AMI Corp.", "AMI"},
20 + {"AMI Corporation", "AMI"},
21 + {"American Megatrends", "AMI"},
22 + {"American Megatrends Inc.", "AMI"},
23 + {"American Megatrends International", "AMI"},
24 + {"American Megatrends International, LLC.", "AMI"},
25 +
26 + {"AOPEN", "AOpen"},
27 + {"AOPEN Inc.", "AOpen"},
28 +
29 + {"Apache Software Foundation", "Apache"},
30 +
31 + {"Apple Inc.", "Apple"},
32 +
33 + {"ASRock Industrial", "ASRock"},
34 + {"ASRockRack", "ASRock"},
35 + {"AsrockRack", "ASRock"},
36 +
37 + {"ASUS", "ASUSTeK"},
38 + {"ASUSTeK COMPUTER INC.", "ASUSTeK"},
39 + {"ASUSTeK COMPUTER INC. (Licensed from AMI)", "ASUSTeK"},
40 + {"ASUSTeK Computer INC.", "ASUSTeK"},
41 + {"ASUSTeK Computer Inc.", "ASUSTeK"},
42 + {"ASUSTek Computer INC.", "ASUSTeK"},
43 +
44 + {"Apache Software Foundation", "Apache"},
45 +
46 + {"BESSTAR (HK) LIMITED", "Besstar"},
47 + {"BESSTAR TECH", "Besstar"},
48 + {"BESSTAR TECH LIMITED", "Besstar"},
49 + {"BESSTAR Tech", "Besstar"},
50 +
51 + {"CHUWI", "Chuwi"},
52 + {"CHUWI Innovation And Technology(ShenZhen)co.,Ltd", "Chuwi"},
53 +
54 + {"Cisco Systems Inc", "Cisco"},
55 + {"Cisco Systems, Inc.", "Cisco"},
56 +
57 + {"DELL", "Dell"},
58 + {"Dell Computer Corporation", "Dell"},
59 + {"Dell Inc.", "Dell"},
60 +
61 + {"FUJITSU", "Fujitsu"},
62 + {"FUJITSU CLIENT COMPUTING LIMITED", "Fujitsu"},
63 + {"FUJITSU SIEMENS", "Fujitsu"},
64 + {"FUJITSU SIEMENS // Phoenix Technologies Ltd.", "Fujitsu"},
65 + {"FUJITSU // American Megatrends Inc.", "Fujitsu"},
66 + {"FUJITSU // American Megatrends International, LLC.", "Fujitsu"},
67 + {"FUJITSU // Insyde Software Corp.", "Fujitsu"},
68 + {"FUJITSU // Phoenix Technologies Ltd.", "Fujitsu"},
69 +
70 + {"GIGABYTE", "Gigabyte"},
71 + {"Giga Computing", "Gigabyte"},
72 + {"Gigabyte Technology Co., Ltd.", "Gigabyte"},
73 + {"Gigabyte Tecohnology Co., Ltd.", "Gigabyte"},
74 +
75 + {"GOOGLE", "Google"},
76 + {"Google Inc", "Google"},
77 +
78 + {"HC Technology.,Ltd.", "HC Tech"},
79 +
80 + {"HP-Pavilion", "HP"},
81 + {"HPE", "HP"},
82 + {"Hewlett Packard Enterprise", "HP"},
83 + {"Hewlett-Packard", "HP"},
84 +
85 + {"HUAWEI", "Huawei"},
86 + {"Huawei Technologies Co., Ltd.", "Huawei"},
87 +
88 + {"IBM Corp.", "IBM"},
89 +
90 + {"INSYDE", "Insyde"},
91 + {"INSYDE Corp.", "Insyde"},
92 + {"Insyde Corp.", "Insyde"},
93 +
94 + {"INTEL", "Intel"},
95 + {"INTEL Corporation", "Intel"},
96 + {"Intel Corp.", "Intel"},
97 + {"Intel Corporation", "Intel"},
98 + {"Intel corporation", "Intel"},
99 + {"Intel(R) Client Systems", "Intel"},
100 + {"Intel(R) Corporation", "Intel"},
101 +
102 + {"LENOVO", "Lenovo"},
103 + {"LNVO", "Lenovo"},
104 +
105 + {"Shenzhen Meigao Electronic Equipment Co.,Ltd", "Meigao"},
106 + {"Micro Computer (HK) Tech Limited", "Micro Computer"},
107 + {"Micro Computer(HK) Tech Limited", "Micro Computer"},
108 +
109 + {"MICRO-STAR INTERNATIONAL CO., LTD", "MSI"},
110 + {"MICRO-STAR INTERNATIONAL CO.,LTD", "MSI"},
111 + {"MSI", "MSI"},
112 + {"Micro-Star International Co., Ltd", "MSI"},
113 + {"Micro-Star International Co., Ltd.", "MSI"},
114 +
115 + {"MICROSOFT", "Microsoft"},
116 + {"Microsoft Corporation", "Microsoft"},
117 +
118 + {"nVIDIA", "NVIDIA"},
119 +
120 + {"OPENSTACK", "OpenStack"},
121 + {"OpenStack Foundation", "OpenStack"},
122 +
123 + {"ORACLE CORPORATI", "Oracle"},
124 + {"Oracle Corporation", "Oracle"},
125 + {"innotek GmbH", "Oracle"},
126 +
127 + {"Phoenix Technologies LTD", "Phoenix"},
128 + {"Phoenix Technologies Ltd", "Phoenix"},
129 + {"Phoenix Technologies Ltd.", "Phoenix"},
130 + {"Phoenix Technologies, LTD", "Phoenix"},
131 +
132 + {"QNAP Systems, Inc.", "QNAP"},
133 +
134 + {"QUANTA", "Quanta"},
135 + {"Quanta Cloud Technology Inc.", "Quanta"},
136 + {"Quanta Computer Inc", "Quanta"},
137 + {"Quanta Computer Inc.", "Quanta"},
138 +
139 + {"RED HAT", "Red Hat"},
140 +
141 + {"SAMSUNG ELECTRONICS CO., LTD.", "Samsung"},
142 +
143 + {"SUN MICROSYSTEMS", "Sun"},
144 +
145 + {"SuperMicro", "Supermicro"},
146 + {"Supermicro Corporation", "Supermicro"},
147 +
148 + {"SYNOLOGY", "Synology"},
149 + {"Synology Inc.", "Synology"},
150 +
151 + {"TYAN", "Tyan"},
152 + {"TYAN Computer Corporation", "Tyan"},
153 + {"Tyan Computer Corporation", "Tyan"},
154 + {"$(TYAN_SYSTEM_MANUFACTURER)", "Tyan"},
155 +
156 + {"VMware", "VMware"},
157 + {"VMware, Inc.", "VMware"},
158 +
159 + {"XIAOMI", "Xiaomi"},
160 +
161 + {"ZOTAC", "Zotac"},
162 + {"Motherboard by ZOTAC", "Zotac"}
163 + };
164 +
165 + for (size_t i = 0; i < _countof(vendors); i++) {
166 + if (strcasecmp(buf, vendors[i].found) == 0) {
167 + strcatz(buf, 0, vendors[i].replace, buf_size);
168 + break;
169 + }
170 + }
171 +}
172 +
173 +static bool dmi_is_virtual_machine(const DMI_INFO *dmi) {
174 + if(!dmi) return false;
175 +
176 + const char *vm_indicators[] = {
177 + "Virt", "KVM", "vServer", "Cloud", "Hyper", "Droplet", "Compute",
178 + "HVM domU", "Parallels", "(i440FX", "(q35", "OpenStack", "QEMU",
179 + "VMWare", "DigitalOcean", "Oracle", "Linode", "Amazon EC2"
180 + };
181 +
182 + const char *strs_to_check[] = {
183 + dmi->product.name,
184 + dmi->product.family,
185 + dmi->sys.vendor,
186 + dmi->board.name,
187 + };
188 +
189 + for (size_t i = 0; i < _countof(strs_to_check); i++) {
190 + if (!strs_to_check[i] || !strs_to_check[i][0])
191 + continue;
192 +
193 + for (size_t j = 0; j < _countof(vm_indicators); j++) {
194 + if (strcasestr(strs_to_check[i], vm_indicators[j]) != NULL)
195 + return true;
196 + }
197 + }
198 +
199 + return false;
200 +}
201 +
202 +static const char *dmi_chassis_type_to_string(int chassis_type) {
203 + // Original info from SMBIOS
204 + // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf
205 + // see also inxi: https://github.com/smxi/inxi/blob/master/inxi
206 +
207 + // we categorize chassis types as:
208 + // 1. desktop
209 + // 2. laptop
210 + // 3. server
211 + // 4. mini-pc
212 + // 5. unknown
213 + // elsewhere we mark them also as "vm" (which is preferred over this)
214 +
215 + switch(chassis_type) {
216 + case 3: /* "desktop" */
217 + case 4: /* "low-profile-desktop" */
218 + case 6: /* "mini-tower-desktop" */
219 + case 7: /* "tower-desktop" */
220 + case 13: /* "all-in-one" */
221 + case 15: /* "space-saving-desktop" */
222 + case 24: /* "sealed-desktop" */
223 + case 26: /* "compact-pci" */
224 + return "desktop";
225 +
226 + case 5: /* "pizza-box" - was 1U desktops and some laptops */
227 + case 8: /* "portable" */
228 + case 9: /* "laptop" */
229 + case 10: /* "notebook" */
230 + case 11: /* "hand-held" */
231 + case 12: /* "docking-station" */
232 + case 14: /* "sub-notebook" */
233 + case 16: /* "lunch-box" */
234 + case 30: /* "tablet" */
235 + case 31: /* "convertible" */
236 + case 32: /* "detachable" */
237 + return "laptop";
238 +
239 + case 17: /* "main-server-chassis" */
240 + case 23: /* "rack-mount-server" */
241 + case 25: /* "multimount-chassis" */
242 + case 27: /* "advanced-tca" */
243 + case 28: /* "blade" */
244 + case 29: /* "blade-enclosure" */
245 + return "server";
246 +
247 + case 33: /* "iot-gateway" */
248 + case 34: /* "embedded-pc" */
249 + case 35: /* "mini-pc" */
250 + case 36: /* "stick-pc" */
251 + return "mini-pc";
252 +
253 + case 1: /* "other" */
254 + case 2: /* "unknown" */
255 + case 18: /* "expansion-chassis" */
256 + case 19: /* "sub-chassis" */
257 + case 20: /* "bus-expansion" */
258 + case 21: /* "peripheral" */
259 + case 22: /* "raid" */
260 + default:
261 + return "unknown";
262 + }
263 +}
264 +
265 +void product_name_vendor_type(DAEMON_STATUS_FILE *ds) {
266 + char *force_type = NULL;
267 +
268 + if(ds->cloud_provider_type[0] && strcasecmp(ds->cloud_provider_type, "unknown") != 0)
269 + safecpy(ds->product.vendor, ds->cloud_provider_type);
270 + else {
271 + // copy one of the names found in DMI
272 + if(ds->hw.sys.vendor[0])
273 + safecpy(ds->product.vendor, ds->hw.sys.vendor);
274 + else if(ds->hw.board.vendor[0])
275 + safecpy(ds->product.vendor, ds->hw.board.vendor);
276 + else if(ds->hw.chassis.vendor[0])
277 + safecpy(ds->product.vendor, ds->hw.chassis.vendor);
278 + else if(ds->hw.bios.vendor[0])
279 + safecpy(ds->product.vendor, ds->hw.bios.vendor);
280 +
281 + // derive the vendor from other DMI fields
282 + if(!ds->product.vendor[0]) {
283 + if(strcasestr(ds->hw.product.name, "VirtualMac") != NULL ||
284 + (strcasestr(ds->hw.board.name, "Apple") != NULL &&
285 + strcasestr(ds->hw.board.name, "Virtual") != NULL)) {
286 + safecpy(ds->product.vendor, "Apple");
287 + force_type = "vm";
288 + }
289 + else if(strcasestr(ds->hw.product.name, "NVIDIA") != NULL &&
290 + strcasestr(ds->hw.product.name, "Kit") != NULL) {
291 + safecpy(ds->product.vendor, "NVIDIA");
292 + force_type = "vm";
293 + }
294 + else if(strcasestr(ds->hw.product.name, "Raspberry") != NULL) {
295 + safecpy(ds->product.vendor, "Raspberry");
296 + force_type = "mini-pc";
297 + }
298 + else if(strcasestr(ds->hw.product.name, "ODROID") != NULL) {
299 + safecpy(ds->product.vendor, "Odroid");
300 + force_type = "mini-pc";
301 + }
302 + else if(strcasestr(ds->hw.product.name, "BananaPi") != NULL ||
303 + strcasestr(ds->hw.product.name, "Banana Pi") != NULL) {
304 + safecpy(ds->product.vendor, "BananaPi");
305 + force_type = "mini-pc";
306 + }
307 + else if(strcasestr(ds->hw.product.name, "OrangePi") != NULL ||
308 + strcasestr(ds->hw.product.name, "Orange Pi") != NULL) {
309 + safecpy(ds->product.vendor, "OrangePi");
310 + force_type = "mini-pc";
311 + }
312 + }
313 +
314 + if(!ds->product.vendor[0])
315 + safecpy(ds->product.vendor, "unknown");
316 + else
317 + dmi_normalize_vendor_field(ds->product.vendor, sizeof(ds->product.vendor));
318 + }
319 +
320 + if(ds->cloud_instance_type[0] && strcasecmp(ds->cloud_instance_type, "unknown") != 0)
321 + safecpy(ds->product.name, ds->cloud_instance_type);
322 + else {
323 + // copy the product family
324 + size_t len = strcatz(ds->product.name, 0, ds->hw.product.family, sizeof(ds->product.name));
325 +
326 + // append the product name, if it is not included already
327 + if(ds->hw.product.name[0] && !strcasestr(ds->product.name, ds->hw.product.name)) {
328 + if(ds->product.name[0]) {
329 + bool includes_family = strcasestr(ds->hw.product.name, ds->hw.product.family) != NULL;
330 +
331 + if(includes_family) {
332 + // the product name includes the product family we have in buf
333 + // we can clear the family and add only the product name
334 + len = 0;
335 + }
336 + else {
337 + // the product name is not included in the product family
338 + // we append a separator, and later the name
339 + len = strcatz(ds->product.name, len, " / ", sizeof(ds->product.name));
340 + }
341 + }
342 +
343 + len = strcatz(ds->product.name, len, ds->hw.product.name, sizeof(ds->product.name));
344 + }
345 +
346 + // append the board name, if it is not included already
347 + if(ds->hw.board.name[0] && !strcasestr(ds->product.name, ds->hw.board.name)) {
348 + if(ds->product.name[0]) {
349 + bool includes_family = !ds->hw.product.family[0] || strcasestr(ds->hw.board.name, ds->hw.product.family) != NULL;
350 + bool includes_product = !ds->hw.product.name[0] || strcasestr(ds->hw.board.name, ds->hw.product.name) != NULL;
351 +
352 + if(includes_family && includes_product) {
353 + // the board name includes both the product and the family
354 + // we can clear buf and add only the board name
355 + len = 0;
356 + }
357 + else {
358 + // the board name is not included in buf
359 + // we append a separator, and later the name
360 + len = strcatz(ds->product.name, len, " / ", sizeof(ds->product.name));
361 + }
362 + }
363 +
364 + len = strcatz(ds->product.name, len, ds->hw.board.name, sizeof(ds->product.name));
365 + }
366 +
367 + if(!ds->product.name[0])
368 + safecpy(ds->product.name, "unknown");
369 + }
370 +
371 + if(ds->virtualization[0] && strcasecmp(ds->virtualization, "none") != 0 && strcasecmp(ds->virtualization, "unknown") != 0)
372 + safecpy(ds->product.type, "vm");
373 + else if(force_type)
374 + safecpy(ds->product.type, force_type);
375 + else if(dmi_is_virtual_machine(&ds->hw))
376 + safecpy(ds->product.type, "vm");
377 + else {
378 + char *end = NULL;
379 + int type = (int)strtol(ds->hw.chassis.type, &end, 10);
380 + if(type && (!end || !*end))
381 + safecpy(ds->product.type, dmi_chassis_type_to_string(type));
382 + else
383 + safecpy(ds->product.type, "unknown");
384 + }
385 +}
src/daemon/status-file-product.h new
+12
@@ -0,0 +1,12 @@
1 +//
2 +// Created by costa on 4/4/25.
3 +//
4 +
5 +#ifndef NETDATA_STATUS_FILE_PRODUCT_H
6 +#define NETDATA_STATUS_FILE_PRODUCT_H
7 +
8 +#include "status-file.h"
9 +
10 +void product_name_vendor_type(DAEMON_STATUS_FILE *ds);
11 +
12 +#endif //NETDATA_STATUS_FILE_PRODUCT_H
src/daemon/status-file.c
+94 -43
@@ -9,6 +9,7 @@
9 #include <openssl/pem.h>
10 #include <openssl/err.h>
11 #include "status-file-dmi.h"
12 +#include "status-file-product.h"
13
14 #ifdef ENABLE_SENTRY
15 #include "sentry-native/sentry-native.h"
@@ -67,7 +68,7 @@ static void copy_and_clean_thread_name_if_empty(DAEMON_STATUS_FILE *ds, const ch
68
69 if(!name || !*name) name = "NO_NAME";
70
70 - strncpyz(ds->fatal.thread, name, sizeof(ds->fatal.thread) - 1);
71 + safecpy(ds->fatal.thread, name);
72
73 // remove the variable part from the thread by removing [XXX] from it
74 unsigned char *p = (unsigned char *)strchr(ds->fatal.thread, '[');
@@ -82,7 +83,15 @@ static bool stack_trace_is_empty(DAEMON_STATUS_FILE *ds) {
83
84 static void set_stack_trace_message_if_empty(DAEMON_STATUS_FILE *ds, const char *msg) {
85 if(stack_trace_is_empty(ds))
85 - strncpyz(ds->fatal.stack_trace, msg, sizeof(ds->fatal.stack_trace) - 1);
86 + safecpy(ds->fatal.stack_trace, msg);
87 +}
88 +
89 +static void fill_dmi_info(DAEMON_STATUS_FILE *ds) {
90 + if(!ds) return;
91 +
92 + dmi_info_init(&ds->hw);
93 + os_dmi_info_get(&ds->hw);
94 + product_name_vendor_type(ds);
95 }
96
97 // --------------------------------------------------------------------------------------------------------------------
@@ -115,6 +124,10 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
124
125 if(ds->v >= 24)
126 buffer_json_member_add_uint64(wb, "crashes", ds->crashes);
127 +
128 + // Only include PID if we're at version 27 or later
129 + if(ds->v >= 27)
130 + buffer_json_member_add_uint64(wb, "pid", (uint64_t)ds->pid);
131
132 if(ds->v >= 22) {
133 buffer_json_member_add_uint64(wb, "posts", ds->posts);
@@ -215,6 +228,9 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
228 buffer_json_member_add_object(wb, "sys");
229 {
230 buffer_json_member_add_string(wb, "vendor", ds->hw.sys.vendor);
231 + buffer_json_member_add_string(wb, "uuid", ds->hw.sys.uuid);
232 + // buffer_json_member_add_string(wb, "serial", ds->hw.sys.serial);
233 + // buffer_json_member_add_string(wb, "asset_tag", ds->hw.sys.asset_tag);
234 }
235 buffer_json_object_close(wb);
236
@@ -232,6 +248,8 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
248 buffer_json_member_add_string(wb, "name", ds->hw.board.name);
249 buffer_json_member_add_string(wb, "version", ds->hw.board.version);
250 buffer_json_member_add_string(wb, "vendor", ds->hw.board.vendor);
251 + // buffer_json_member_add_string(wb, "serial", ds->hw.board.serial);
252 + // buffer_json_member_add_string(wb, "asset_tag", ds->hw.board.asset_tag);
253 }
254 buffer_json_object_close(wb);
255
@@ -240,6 +258,8 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
258 buffer_json_member_add_string(wb, "type", ds->hw.chassis.type);
259 buffer_json_member_add_string(wb, "vendor", ds->hw.chassis.vendor);
260 buffer_json_member_add_string(wb, "version", ds->hw.chassis.version);
261 + // buffer_json_member_add_string(wb, "serial", ds->hw.chassis.serial);
262 + // buffer_json_member_add_string(wb, "asset_tag", ds->hw.chassis.asset_tag);
263 }
264 buffer_json_object_close(wb);
265
@@ -249,11 +269,21 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
269 buffer_json_member_add_string(wb, "release", ds->hw.bios.release);
270 buffer_json_member_add_string(wb, "version", ds->hw.bios.version);
271 buffer_json_member_add_string(wb, "vendor", ds->hw.bios.vendor);
272 + buffer_json_member_add_string(wb, "mode", ds->hw.bios.mode);
273 + buffer_json_member_add_boolean(wb, "secure_boot", ds->hw.bios.secure_boot);
274 }
275 buffer_json_object_close(wb);
276 }
277 buffer_json_object_close(wb);
278
279 + buffer_json_member_add_object(wb, "product");
280 + {
281 + buffer_json_member_add_string(wb, "vendor", ds->product.vendor);
282 + buffer_json_member_add_string(wb, "name", ds->product.name);
283 + buffer_json_member_add_string(wb, "type", ds->product.type);
284 + }
285 + buffer_json_object_close(wb);
286 +
287 buffer_json_member_add_object(wb, "fatal");
288 {
289 buffer_json_member_add_uint64(wb, "line", ds->fatal.line);
@@ -322,6 +352,8 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
352 bool required_v23 = version >= 23 ? strict : false;
353 bool required_v24 = version >= 24 ? strict : false;
354 bool required_v25 = version >= 25 ? strict : false;
355 + bool required_v26 = version >= 26 ? strict : false;
356 + bool required_v27 = version >= 27 ? strict : false;
357
358 // Parse timestamp
359 JSONC_PARSE_TXT2RFC3339_USEC_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", ds->timestamp_ut, error, required_v1);
@@ -367,6 +399,10 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
399
400 if(version >= 24)
401 JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "crashes", ds->crashes, error, required_v24);
402 +
403 + // Only try to parse PID if we're at version 27 or later
404 + if(version >= 27)
405 + JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "pid", ds->pid, error, false);
406
407 if(version >= 22) {
408 JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "posts", ds->posts, error, required_v22);
@@ -452,6 +488,9 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
488 JSONC_PARSE_SUBOBJECT(jobj, path, "hw", error, required_v25, {
489 JSONC_PARSE_SUBOBJECT(jobj, path, "sys", error, required_v25, {
490 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "vendor", ds->hw.sys.vendor, error, required_v25);
491 + JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "uuid", ds->hw.sys.uuid, error, required_v27);
492 + // JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "serial", ds->hw.sys.serial, error, required_v26);
493 + // JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "asset_tag", ds->hw.sys.asset_tag, error, required_v27);
494 });
495
496 JSONC_PARSE_SUBOBJECT(jobj, path, "product", error, required_v25, {
@@ -465,21 +504,34 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
504 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "name", ds->hw.board.name, error, required_v25);
505 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "version", ds->hw.board.version, error, required_v25);
506 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "vendor", ds->hw.board.vendor, error, required_v25);
507 + // JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "serial", ds->hw.board.serial, error, required_v26);
508 + // JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "asset_tag", ds->hw.board.asset_tag, error, required_v27);
509 });
510
511 JSONC_PARSE_SUBOBJECT(jobj, path, "chassis", error, required_v25, {
471 - JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path,"type", ds->hw.chassis.type ,error ,required_v25);
472 - JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj,path,"vendor" ,ds->hw.chassis.vendor ,error ,required_v25);
473 - JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj,path,"version" ,ds->hw.chassis.version ,error ,required_v25);
512 + JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "type", ds->hw.chassis.type, error, required_v25);
513 + JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "vendor", ds->hw.chassis.vendor, error, required_v25);
514 + JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "version", ds->hw.chassis.version, error, required_v25);
515 + // JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "serial", ds->hw.chassis.serial, error, required_v26);
516 + // JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "asset_tag", ds->hw.chassis.asset_tag, error, required_v27);
517 });
518
476 - JSONC_PARSE_SUBOBJECT(jobj,path,"bios" ,error ,required_v25,{
519 + JSONC_PARSE_SUBOBJECT(jobj, path, "bios", error, required_v25, {
520 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "date", ds->hw.bios.date, error, required_v25);
521 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "release", ds->hw.bios.release, error, required_v25);
522 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "version", ds->hw.bios.version, error, required_v25);
523 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "vendor", ds->hw.bios.vendor, error, required_v25);
524 + JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "mode", ds->hw.bios.mode, error, required_v27);
525 + JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, "secure_boot", ds->hw.bios.secure_boot, error, required_v27);
526 });
527 });
528 +
529 + // Parse product object
530 + JSONC_PARSE_SUBOBJECT(jobj, path, "product", error, required_v26, {
531 + JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "vendor", ds->product.vendor, error, required_v26);
532 + JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "name", ds->product.name, error, required_v26);
533 + JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "type", ds->product.type, error, required_v26);
534 + });
535
536 // Parse fatal object
537 JSONC_PARSE_SUBOBJECT(jobj, path, "fatal", error, required_v1, {
@@ -524,7 +576,7 @@ static void daemon_status_file_migrate_once(void) {
576 dsf_acquire(last_session_status);
577 dsf_acquire(session_status);
578
527 - strncpyz(session_status.version, NETDATA_VERSION, sizeof(session_status.version) - 1);
579 + safecpy(session_status.version, NETDATA_VERSION);
580 session_status.machine_id = os_machine_id();
581
582 {
@@ -532,7 +584,7 @@ static void daemon_status_file_migrate_once(void) {
584 get_install_type_internal(&install_type, &prebuilt_arch, &prebuilt_dist);
585
586 if(install_type)
535 - strncpyz(session_status.install_type, install_type, sizeof(session_status.install_type) - 1);
587 + safecpy(session_status.install_type, install_type);
588
589 freez(prebuilt_arch);
590 freez(prebuilt_dist);
@@ -556,18 +608,18 @@ static void daemon_status_file_migrate_once(void) {
608 session_status.node_id = last_session_status.node_id;
609 session_status.host_id = *machine_guid_get();
610
559 - strncpyz(session_status.architecture, last_session_status.architecture, sizeof(session_status.architecture) - 1);
560 - strncpyz(session_status.virtualization, last_session_status.virtualization, sizeof(session_status.virtualization) - 1);
561 - strncpyz(session_status.container, last_session_status.container, sizeof(session_status.container) - 1);
562 - strncpyz(session_status.kernel_version, last_session_status.kernel_version, sizeof(session_status.kernel_version) - 1);
563 - strncpyz(session_status.os_name, last_session_status.os_name, sizeof(session_status.os_name) - 1);
564 - strncpyz(session_status.os_version, last_session_status.os_version, sizeof(session_status.os_version) - 1);
565 - strncpyz(session_status.os_id, last_session_status.os_id, sizeof(session_status.os_id) - 1);
566 - strncpyz(session_status.os_id_like, last_session_status.os_id_like, sizeof(session_status.os_id_like) - 1);
567 - strncpyz(session_status.timezone, last_session_status.timezone, sizeof(session_status.timezone) - 1);
568 - strncpyz(session_status.cloud_provider_type, last_session_status.cloud_provider_type, sizeof(session_status.cloud_provider_type) - 1);
569 - strncpyz(session_status.cloud_instance_type, last_session_status.cloud_instance_type, sizeof(session_status.cloud_instance_type) - 1);
570 - strncpyz(session_status.cloud_instance_region, last_session_status.cloud_instance_region, sizeof(session_status.cloud_instance_region) - 1);
611 + safecpy(session_status.architecture, last_session_status.architecture);
612 + safecpy(session_status.virtualization, last_session_status.virtualization);
613 + safecpy(session_status.container, last_session_status.container);
614 + safecpy(session_status.kernel_version, last_session_status.kernel_version);
615 + safecpy(session_status.os_name, last_session_status.os_name);
616 + safecpy(session_status.os_version, last_session_status.os_version);
617 + safecpy(session_status.os_id, last_session_status.os_id);
618 + safecpy(session_status.os_id_like, last_session_status.os_id_like);
619 + safecpy(session_status.timezone, last_session_status.timezone);
620 + safecpy(session_status.cloud_provider_type, last_session_status.cloud_provider_type);
621 + safecpy(session_status.cloud_instance_type, last_session_status.cloud_instance_type);
622 + safecpy(session_status.cloud_instance_region, last_session_status.cloud_instance_region);
623
624 session_status.posts = last_session_status.posts;
625 session_status.restarts = last_session_status.restarts + 1;
@@ -584,7 +636,7 @@ static void daemon_status_file_migrate_once(void) {
636 session_status.reliability++;
637 }
638
587 - strncpyz(session_status.stack_traces, capture_stack_trace_backend(), sizeof(session_status.stack_traces) - 1);
639 + safecpy(session_status.stack_traces, capture_stack_trace_backend());
640
641 fill_dmi_info(&session_status);
642
@@ -627,6 +679,7 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
679 session_status.invocation = nd_log_get_invocation_id();
680 session_status.db_mode = default_rrd_memory_mode;
681 session_status.db_tiers = nd_profile.storage_tiers;
682 + session_status.pid = getpid();
683
684 // we keep the highest cloud status, to know how the agent gets connected to netdata.cloud
685 CLOUD_STATUS cs = cloud_status();
@@ -652,10 +705,10 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
705 }
706
707 if(get_daemon_status_fields_from_system_info(&session_status))
655 - finalize_vendor_product_vm(&session_status);
708 + product_name_vendor_type(&session_status);
709
710 if(netdata_configured_timezone)
658 - strncpyz(session_status.timezone, netdata_configured_timezone, sizeof(session_status.timezone) - 1);
711 + safecpy(session_status.timezone, netdata_configured_timezone);
712
713 session_status.exit_reason = exit_initiated_get();
714 session_status.profile = nd_profile_detect_and_configure(false);
@@ -758,6 +811,7 @@ static void post_status_file(struct post_status_file_thread_data *d) {
811 buffer_json_member_add_uint64(wb, "priority", d->priority);
812 buffer_json_member_add_uint64(wb, "version_saved", d->status->v);
813 buffer_json_member_add_string(wb, "agent_version_now", NETDATA_VERSION);
814 + buffer_json_member_add_uint64(wb, "agent_pid_now", getpid());
815 buffer_json_member_add_boolean(wb, "host_memory_critical",
816 OS_SYSTEM_MEMORY_OK(d->status->memory) && d->status->memory.ram_available_bytes <= d->status->oom_protection);
817 buffer_json_member_add_uint64(wb, "host_memory_free_percent", (uint64_t)round(os_system_memory_available_percent(d->status->memory)));
@@ -1123,7 +1177,7 @@ void daemon_status_file_check_crash(void) {
1177 last_session_status = session_status;
1178 last_session_status.status = DAEMON_STATUS_NONE;
1179 last_session_status.exit_reason = 0;
1126 - strncpyz(last_session_status.fatal.function, "no_status", sizeof(last_session_status.fatal.function) - 1);
1180 + safecpy(last_session_status.fatal.function, "no_status");
1181 }
1182
1183 struct post_status_file_thread_data d = {
@@ -1163,13 +1217,10 @@ static void daemon_status_file_save_twice_if_we_can_get_stack_trace(BUFFER *wb,
1217 // Store the first netdata function from the stack trace if available
1218 const char *first_nd_fn = capture_stack_trace_root_cause_function();
1219 if (first_nd_fn && *first_nd_fn && !ds->fatal.function[0])
1166 - strncpyz(ds->fatal.function, first_nd_fn, sizeof(ds->fatal.function) - 1);
1220 + safecpy(ds->fatal.function, first_nd_fn);
1221
1222 if(buffer_strlen(wb) > 0) {
1169 - strncpyz(
1170 - ds->fatal.stack_trace,
1171 - buffer_tostring(wb),
1172 - sizeof(ds->fatal.stack_trace) - 1);
1223 + safecpy(ds->fatal.stack_trace, buffer_tostring(wb));
1224
1225 daemon_status_file_save(wb, ds, false);
1226 }
@@ -1196,19 +1247,19 @@ void daemon_status_file_register_fatal(const char *filename, const char *functio
1247 copy_and_clean_thread_name_if_empty(&session_status, nd_thread_tag());
1248
1249 if(filename && *filename)
1199 - strncpyz(session_status.fatal.filename, filename, sizeof(session_status.fatal.filename) - 1);
1250 + safecpy(session_status.fatal.filename, filename);
1251
1252 if(function && *function)
1202 - strncpyz(session_status.fatal.function, function, sizeof(session_status.fatal.function) - 1);
1253 + safecpy(session_status.fatal.function, function);
1254
1255 if(message && *message)
1205 - strncpyz(session_status.fatal.message, message, sizeof(session_status.fatal.message) - 1);
1256 + safecpy(session_status.fatal.message, message);
1257
1258 if(errno_str && *errno_str)
1208 - strncpyz(session_status.fatal.errno_str, errno_str, sizeof(session_status.fatal.errno_str) - 1);
1259 + safecpy(session_status.fatal.errno_str, errno_str);
1260
1261 if(stack_trace && *stack_trace && stack_trace_is_empty(&session_status))
1211 - strncpyz(session_status.fatal.stack_trace, stack_trace, sizeof(session_status.fatal.stack_trace) - 1);
1262 + safecpy(session_status.fatal.stack_trace, stack_trace);
1263
1264 if(!session_status.fatal.worker_job_id)
1265 session_status.fatal.worker_job_id = workers_get_last_job_id();
@@ -1335,10 +1386,10 @@ void daemon_status_file_shutdown_timeout(BUFFER *trace) {
1386 exit_initiated_add(EXIT_REASON_SHUTDOWN_TIMEOUT);
1387 session_status.exit_reason |= EXIT_REASON_SHUTDOWN_TIMEOUT;
1388 if(trace && buffer_strlen(trace) && stack_trace_is_empty(&session_status))
1338 - strncpyz(session_status.fatal.stack_trace, buffer_tostring(trace), sizeof(session_status.fatal.stack_trace) - 1);
1389 + safecpy(session_status.fatal.stack_trace, buffer_tostring(trace));
1390 dsf_release(session_status);
1391
1341 - strncpyz(session_status.fatal.function, "shutdown_timeout", sizeof(session_status.fatal.function) - 1);
1392 + safecpy(session_status.fatal.function, "shutdown_timeout");
1393
1394 CLEAN_BUFFER *wb = buffer_create(0, NULL);
1395 daemon_status_file_save(wb, &session_status, false);
@@ -1383,7 +1434,7 @@ void daemon_status_file_startup_step(const char *step) {
1434 return;
1435
1436 if(step != NULL)
1386 - strncpyz(session_status.fatal.function, step, sizeof(session_status.fatal.function) - 1);
1437 + safecpy(session_status.fatal.function, step);
1438 else
1439 session_status.fatal.function[0] = '\0';
1440
@@ -1469,16 +1520,16 @@ const char *daemon_status_file_get_fatal_thread(void) {
1520 return session_status.fatal.thread;
1521 }
1522
1472 -const char *daemon_status_file_get_hw_sys_vendor(void) {
1473 - return session_status.hw.sys.vendor;
1523 +const char *daemon_status_file_get_sys_vendor(void) {
1524 + return session_status.product.vendor;
1525 }
1526
1476 -const char *daemon_status_file_get_hw_product_name(void) {
1477 - return session_status.hw.product.name;
1527 +const char *daemon_status_file_get_product_name(void) {
1528 + return session_status.product.name;
1529 }
1530
1480 -const char *daemon_status_file_get_hw_chassis_type(void) {
1481 - return session_status.hw.chassis.type;
1531 +const char *daemon_status_file_get_product_type(void) {
1532 + return session_status.product.type;
1533 }
1534
1535 pid_t daemon_status_file_get_fatal_thread_id(void) {
src/daemon/status-file.h
+13 -34
@@ -8,8 +8,9 @@
8 #include "database/rrd-database-mode.h"
9 #include "claim/cloud-status.h"
10 #include "machine-guid.h"
11 +#include "status-file-dmi.h"
12
12 -#define STATUS_FILE_VERSION 26
13 +#define STATUS_FILE_VERSION 27
14
15 typedef enum {
16 DAEMON_STATUS_NONE,
@@ -51,6 +52,7 @@ typedef struct daemon_status_file {
52 size_t crashes; // the number of times this agent has crashed (ever)
53 size_t posts; // the number of posts to the backend
54 ssize_t reliability; // consecutive restarts: > 0 reliable, < 0 crashing
55 + pid_t pid; // the process id of the netdata agent
56
57 ND_MACHINE_GUID host_id; // the machine guid of the system
58
@@ -87,37 +89,14 @@ typedef struct daemon_status_file {
89 char cloud_instance_region[32];
90 bool read_system_info;
91
92 + DMI_INFO hw;
93 +
94 struct {
91 - struct {
92 - char vendor[64];
93 - } sys;
94 -
95 - struct {
96 - char name[64];
97 - char version[64];
98 - char sku[64];
99 - char family[64];
100 - } product;
101 -
102 - struct {
103 - char name[64];
104 - char version[64];
105 - char vendor[64];
106 - } board;
107 -
108 - struct {
109 - char type[64];
110 - char vendor[64];
111 - char version[64];
112 - } chassis;
113 -
114 - struct {
115 - char date[16];
116 - char release[64];
117 - char version[64];
118 - char vendor[64];
119 - } bios;
120 - } hw;
95 + // normalized information from cloud provider and h/w information
96 + char vendor[32];
97 + char name[96];
98 + char type[16];
99 + } product;
100
101 char stack_traces[63]; // the backend for capturing stack traces
102
@@ -181,9 +160,9 @@ const char *daemon_status_file_get_fatal_errno(void);
160 const char *daemon_status_file_get_fatal_stack_trace(void);
161 const char *daemon_status_file_get_fatal_thread(void);
162 const char *daemon_status_file_get_stack_trace_backend(void);
184 -const char *daemon_status_file_get_hw_sys_vendor(void);
185 -const char *daemon_status_file_get_hw_product_name(void);
186 -const char *daemon_status_file_get_hw_chassis_type(void);
163 +const char *daemon_status_file_get_sys_vendor(void);
164 +const char *daemon_status_file_get_product_name(void);
165 +const char *daemon_status_file_get_product_type(void);
166
167 pid_t daemon_status_file_get_fatal_thread_id(void);
168 long daemon_status_file_get_fatal_line(void);
src/database/rrdhost-system-info.c
+6 -6
@@ -241,9 +241,9 @@ void rrdhost_system_info_to_rrdlabels(struct rrdhost_system_info *system_info, R
241 if (system_info->prebuilt_dist)
242 rrdlabels_add(labels, "_prebuilt_dist", system_info->prebuilt_dist, RRDLABEL_SRC_AUTO);
243
244 - rrdlabels_add(labels, "_hw_sys_vendor", daemon_status_file_get_hw_sys_vendor(), RRDLABEL_SRC_AUTO);
245 - rrdlabels_add(labels, "_hw_product_name", daemon_status_file_get_hw_product_name(), RRDLABEL_SRC_AUTO);
246 - rrdlabels_add(labels, "_hw_product_type", daemon_status_file_get_hw_chassis_type(), RRDLABEL_SRC_AUTO);
244 + rrdlabels_add(labels, "_hw_sys_vendor", daemon_status_file_get_sys_vendor(), RRDLABEL_SRC_AUTO);
245 + rrdlabels_add(labels, "_hw_product_name", daemon_status_file_get_product_name(), RRDLABEL_SRC_AUTO);
246 + rrdlabels_add(labels, "_hw_product_type", daemon_status_file_get_product_type(), RRDLABEL_SRC_AUTO);
247 }
248
249 int rrdhost_system_info_detect(struct rrdhost_system_info *system_info) {
@@ -664,13 +664,13 @@ bool get_daemon_status_fields_from_system_info(DAEMON_STATUS_FILE *ds) {
664 ds->kubernetes = false;
665 }
666
667 - if(ri->cloud_provider_type && strcmp(ri->cloud_provider_type, "unknown") != 0)
667 + if(ri->cloud_provider_type && strcasecmp(ri->cloud_provider_type, "unknown") != 0)
668 strncpyz(ds->cloud_provider_type, ri->cloud_provider_type, sizeof(ds->cloud_provider_type) - 1);
669
670 - if(ri->cloud_instance_type && strcmp(ri->cloud_instance_type, "unknown") != 0)
670 + if(ri->cloud_instance_type && strcasecmp(ri->cloud_instance_type, "unknown") != 0)
671 strncpyz(ds->cloud_instance_type, ri->cloud_instance_type, sizeof(ds->cloud_instance_type) - 1);
672
673 - if(ri->cloud_instance_region && strcmp(ri->cloud_instance_region, "unknown") != 0)
673 + if(ri->cloud_instance_region && strcasecmp(ri->cloud_instance_region, "unknown") != 0)
674 strncpyz(ds->cloud_instance_region, ri->cloud_instance_region, sizeof(ds->cloud_instance_region) - 1);
675
676 ds->read_system_info = true;
src/libnetdata/inlined.h
+10 -2
@@ -424,8 +424,16 @@ static inline char *strncpyz(char *dst, const char *src, size_t dst_size_minus_1
424 // dst is always null terminated
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';
427 + if (unlikely(len >= size)) {
428 + if(size > 0)
429 + dst[size - 1] = '\0';
430 +
431 + return len;
432 + }
433 +
434 + // If the source is not valid or empty, do nothing.
435 + if(unlikely(!src || !*src)) {
436 + dst[len] = '\0';
437 return len;
438 }
439