master
c 501 lines 16.8 KB
Raw
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 {"Dell EMC", "Dell"},
61
62 {"FUJITSU", "Fujitsu"},
63 {"FUJITSU CLIENT COMPUTING LIMITED", "Fujitsu"},
64 {"FUJITSU SIEMENS", "Fujitsu"},
65 {"FUJITSU SIEMENS // Phoenix Technologies Ltd.", "Fujitsu"},
66 {"FUJITSU // American Megatrends Inc.", "Fujitsu"},
67 {"FUJITSU // American Megatrends International, LLC.", "Fujitsu"},
68 {"FUJITSU // Insyde Software Corp.", "Fujitsu"},
69 {"FUJITSU // Phoenix Technologies Ltd.", "Fujitsu"},
70
71 {"GIGABYTE", "Gigabyte"},
72 {"Giga Computing", "Gigabyte"},
73 {"Gigabyte Technology Co., Ltd.", "Gigabyte"},
74 {"Gigabyte Tecohnology Co., Ltd.", "Gigabyte"},
75
76 {"GOOGLE", "Google"},
77 {"Google Inc", "Google"},
78
79 {"HC Technology.,Ltd.", "HC Tech"},
80
81 {"HP-Pavilion", "HP"},
82 {"HPE", "HP"},
83 {"Hewlett Packard Enterprise", "HP"},
84 {"Hewlett-Packard", "HP"},
85
86 {"HUAWEI", "Huawei"},
87 {"Huawei Technologies Co., Ltd.", "Huawei"},
88
89 {"IBM Corp.", "IBM"},
90
91 {"IceWhale Technology Co.,Ltd.", "IceWhale"},
92
93 {"INSYDE", "Insyde"},
94 {"INSYDE Corp.", "Insyde"},
95 {"Insyde Corp.", "Insyde"},
96
97 {"INTEL", "Intel"},
98 {"INTEL Corporation", "Intel"},
99 {"Intel Corp.", "Intel"},
100 {"Intel Corporation", "Intel"},
101 {"Intel corporation", "Intel"},
102 {"Intel(R) Client Systems", "Intel"},
103 {"Intel(R) Corporation", "Intel"},
104
105 {"LENOVO", "Lenovo"},
106 {"LNVO", "Lenovo"},
107
108 {"Shenzhen Meigao Electronic Equipment Co.,Ltd", "Meigao"},
109 {"Micro Computer (HK) Tech Limited", "Micro Computer"},
110 {"Micro Computer(HK) Tech Limited", "Micro Computer"},
111
112 {"MICRO-STAR INTERNATIONAL CO., LTD", "MSI"},
113 {"MICRO-STAR INTERNATIONAL CO.,LTD", "MSI"},
114 {"MSI", "MSI"},
115 {"Micro-Star International Co., Ltd", "MSI"},
116 {"Micro-Star International Co., Ltd.", "MSI"},
117
118 {"MICROSOFT", "Microsoft"},
119 {"Microsoft Corporation", "Microsoft"},
120
121 {"nVIDIA", "NVIDIA"},
122
123 {"OPENSTACK", "OpenStack"},
124 {"OpenStack Foundation", "OpenStack"},
125
126 {"ORACLE CORPORATI", "Oracle"},
127 {"Oracle Corporation", "Oracle"},
128 {"innotek GmbH", "Oracle"},
129
130 {"Phoenix Technologies LTD", "Phoenix"},
131 {"Phoenix Technologies Ltd", "Phoenix"},
132 {"Phoenix Technologies Ltd.", "Phoenix"},
133 {"Phoenix Technologies, LTD", "Phoenix"},
134
135 {"QNAP Systems, Inc.", "QNAP"},
136
137 {"QUANTA", "Quanta"},
138 {"Quanta Cloud Technology Inc.", "Quanta"},
139 {"Quanta Computer Inc", "Quanta"},
140 {"Quanta Computer Inc.", "Quanta"},
141
142 {"RED HAT", "Red Hat"},
143
144 {"SAMSUNG ELECTRONICS CO., LTD.", "Samsung"},
145
146 {"SUN MICROSYSTEMS", "Sun"},
147
148 {"SuperMicro", "Supermicro"},
149 {"Supermicro Corporation", "Supermicro"},
150
151 {"SYNOLOGY", "Synology"},
152 {"Synology Inc.", "Synology"},
153
154 {"TYAN", "Tyan"},
155 {"TYAN Computer Corporation", "Tyan"},
156 {"Tyan Computer Corporation", "Tyan"},
157 {"$(TYAN_SYSTEM_MANUFACTURER)", "Tyan"},
158
159 {"VMware", "VMware"},
160 {"VMware, Inc.", "VMware"},
161
162 {"XIAOMI", "Xiaomi"},
163
164 {"ZOTAC", "Zotac"},
165 {"Motherboard by ZOTAC", "Zotac"}
166 };
167
168 for (size_t i = 0; i < _countof(vendors); i++) {
169 if (strcasecmp(buf, vendors[i].found) == 0) {
170 strcatz(buf, 0, vendors[i].replace, buf_size);
171 break;
172 }
173 }
174 }
175
176 static bool dmi_is_virtual_machine(const DMI_INFO *dmi) {
177 if(!dmi) return false;
178
179 const char *vm_indicators[] = {
180 "Virt", "KVM", "vServer", "Cloud", "Hyper", "Droplet",
181 "Compute ", // with a space to not match "Computer"
182 "HVM domU", "Parallels", "(i440FX", "(q35", "OpenStack", "QEMU",
183 "VMWare", "DigitalOcean", "Oracle", "Linode", "Amazon EC2"
184 };
185
186 const char *strs_to_check[] = {
187 dmi->product.name,
188 dmi->product.family,
189 dmi->sys.vendor,
190 dmi->board.name,
191 };
192
193 for (size_t i = 0; i < _countof(strs_to_check); i++) {
194 if (!strs_to_check[i] || !strs_to_check[i][0])
195 continue;
196
197 for (size_t j = 0; j < _countof(vm_indicators); j++) {
198 if (strcasestr(strs_to_check[i], vm_indicators[j]) != NULL)
199 return true;
200 }
201 }
202
203 return false;
204 }
205
206 static const char *dmi_chassis_type_to_string(int chassis_type) {
207 // Original info from SMBIOS
208 // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf
209 // see also inxi: https://github.com/smxi/inxi/blob/master/inxi
210
211 // we categorize chassis types as:
212 // 1. desktop
213 // 2. laptop
214 // 3. server
215 // 4. mini-pc
216 // 5. unknown
217 // elsewhere we mark them also as "vm" (which is preferred over this)
218
219 switch(chassis_type) {
220 case 3: /* "desktop" */
221 case 4: /* "low-profile-desktop" */
222 case 6: /* "mini-tower-desktop" */
223 case 7: /* "tower-desktop" */
224 case 13: /* "all-in-one" */
225 case 15: /* "space-saving-desktop" */
226 case 24: /* "sealed-desktop" */
227 case 26: /* "compact-pci" */
228 return "desktop";
229
230 case 5: /* "pizza-box" - was 1U desktops and some laptops */
231 case 8: /* "portable" */
232 case 9: /* "laptop" */
233 case 10: /* "notebook" */
234 case 11: /* "hand-held" */
235 case 12: /* "docking-station" */
236 case 14: /* "sub-notebook" */
237 case 16: /* "lunch-box" */
238 case 30: /* "tablet" */
239 case 31: /* "convertible" */
240 case 32: /* "detachable" */
241 return "laptop";
242
243 case 17: /* "main-server-chassis" */
244 case 23: /* "rack-mount-server" */
245 case 25: /* "multimount-chassis" */
246 case 27: /* "advanced-tca" */
247 case 28: /* "blade" */
248 case 29: /* "blade-enclosure" */
249 return "server";
250
251 case 33: /* "iot-gateway" */
252 case 34: /* "embedded-pc" */
253 case 35: /* "mini-pc" */
254 case 36: /* "stick-pc" */
255 return "mini-pc";
256
257 case 1: /* "other" */
258 case 2: /* "unknown" */
259 case 18: /* "expansion-chassis" */
260 case 19: /* "sub-chassis" */
261 case 20: /* "bus-expansion" */
262 case 21: /* "peripheral" */
263 case 22: /* "raid" */
264 default:
265 return "unknown";
266 }
267 }
268
269 // Check for active ECC memory controllers
270 static bool has_ecc_memory(void) {
271 #if defined(OS_LINUX)
272 char edac_path[FILENAME_MAX + 1];
273 snprintfz(edac_path, FILENAME_MAX, "%s/sys/devices/system/edac/mc", netdata_configured_host_prefix ? netdata_configured_host_prefix : "");
274
275 DIR *dir = opendir(edac_path);
276 if (!dir)
277 return false;
278
279 struct dirent *entry;
280 bool found_mc = false;
281
282 while ((entry = readdir(dir))) {
283 // Look for "mc0", "mc1", etc. directories
284 if (strncmp(entry->d_name, "mc", 2) == 0 && isdigit(entry->d_name[2])) {
285 // Check for presence of ECC-related files in this mc directory
286 char mc_path[FILENAME_MAX + 1];
287 snprintfz(mc_path, FILENAME_MAX, "%s/%s/ce_count", edac_path, entry->d_name);
288
289 struct stat st;
290 if (stat(mc_path, &st) == 0) {
291 found_mc = true;
292 break;
293 }
294 }
295 }
296
297 closedir(dir);
298 return found_mc;
299 #else
300 return false; // Not implemented for this OS
301 #endif
302 }
303
304 // Check for IPMI device
305 static bool has_ipmi(void) {
306 #if defined(OS_LINUX)
307 char ipmi_dev_path[FILENAME_MAX + 1];
308 snprintfz(ipmi_dev_path, FILENAME_MAX, "%s/dev/ipmi0", netdata_configured_host_prefix ? netdata_configured_host_prefix : "");
309 struct stat ipmi_stat;
310 return (stat(ipmi_dev_path, &ipmi_stat) == 0);
311 #else
312 return false; // Not implemented for this OS
313 #endif
314 }
315
316 // Check for multiple CPU sockets
317 static bool has_multiple_cpu_sockets(void) {
318 #if defined(OS_LINUX)
319 char cpu_path[FILENAME_MAX + 1];
320 snprintfz(cpu_path, FILENAME_MAX, "%s/sys/devices/system/cpu", netdata_configured_host_prefix ? netdata_configured_host_prefix : "");
321
322 DIR *dir = opendir(cpu_path);
323 if (!dir)
324 return false;
325
326 DICTIONARY *physical_ids = dictionary_create(DICT_OPTION_SINGLE_THREADED);
327 struct dirent *entry;
328
329 while ((entry = readdir(dir))) {
330 // Check for cpu directories (cpu0, cpu1, etc.)
331 if (strncmp(entry->d_name, "cpu", 3) == 0 && isdigit(entry->d_name[3])) {
332 char topology_path[FILENAME_MAX + 1];
333 char physical_id[64];
334
335 // Read the physical_package_id file for this CPU
336 snprintfz(topology_path, FILENAME_MAX, "%s/%s/topology/physical_package_id",
337 cpu_path, entry->d_name);
338
339 if (read_txt_file(topology_path, physical_id, sizeof(physical_id)) == 0) {
340 dictionary_set(physical_ids, physical_id, NULL, 0);
341 }
342 }
343 }
344
345 closedir(dir);
346
347 unsigned int socket_count = dictionary_entries(physical_ids);
348 dictionary_destroy(physical_ids);
349
350 return (socket_count > 1);
351 #else
352 return false; // Not implemented for this OS
353 #endif
354 }
355
356 // Main function to check for server hardware indicators
357 static bool is_server_hardware(void) {
358 // Check each server indicator
359 if (has_ecc_memory())
360 return true;
361
362 if (has_ipmi())
363 return true;
364
365 if (has_multiple_cpu_sockets())
366 return true;
367
368 return false; // No server indicators found
369 }
370
371 void product_name_vendor_type(DAEMON_STATUS_FILE *ds) {
372 char *force_type = NULL;
373
374 if(ds->cloud_provider_type[0] && strcasecmp(ds->cloud_provider_type, "unknown") != 0)
375 safecpy(ds->product.vendor, ds->cloud_provider_type);
376 else {
377 // copy one of the names found in DMI
378 if(ds->hw.sys.vendor[0])
379 safecpy(ds->product.vendor, ds->hw.sys.vendor);
380 else if(ds->hw.board.vendor[0])
381 safecpy(ds->product.vendor, ds->hw.board.vendor);
382 else if(ds->hw.chassis.vendor[0])
383 safecpy(ds->product.vendor, ds->hw.chassis.vendor);
384 else if(ds->hw.bios.vendor[0])
385 safecpy(ds->product.vendor, ds->hw.bios.vendor);
386
387 // derive the vendor from other DMI fields
388 if(!ds->product.vendor[0]) {
389 if(strcasestr(ds->hw.product.name, "VirtualMac") != NULL ||
390 (strcasestr(ds->hw.board.name, "Apple") != NULL &&
391 strcasestr(ds->hw.board.name, "Virtual") != NULL)) {
392 safecpy(ds->product.vendor, "Apple");
393 force_type = "vm";
394 }
395 else if(strcasestr(ds->hw.product.name, "NVIDIA") != NULL &&
396 strcasestr(ds->hw.product.name, "Kit") != NULL) {
397 safecpy(ds->product.vendor, "NVIDIA");
398 force_type = "mini-pc";
399 }
400 else if(strcasestr(ds->hw.product.name, "Raspberry") != NULL) {
401 safecpy(ds->product.vendor, "Raspberry");
402 force_type = "mini-pc";
403 }
404 else if(strcasestr(ds->hw.product.name, "ODROID") != NULL) {
405 safecpy(ds->product.vendor, "Odroid");
406 force_type = "mini-pc";
407 }
408 else if(strcasestr(ds->hw.product.name, "BananaPi") != NULL ||
409 strcasestr(ds->hw.product.name, "Banana Pi") != NULL) {
410 safecpy(ds->product.vendor, "BananaPi");
411 force_type = "mini-pc";
412 }
413 else if(strcasestr(ds->hw.product.name, "OrangePi") != NULL ||
414 strcasestr(ds->hw.product.name, "Orange Pi") != NULL) {
415 safecpy(ds->product.vendor, "OrangePi");
416 force_type = "mini-pc";
417 }
418 }
419
420 if(!ds->product.vendor[0])
421 safecpy(ds->product.vendor, "unknown");
422 else
423 dmi_normalize_vendor_field(ds->product.vendor, sizeof(ds->product.vendor));
424 }
425
426 if (!force_type &&
427 (strcasestr(ds->product.vendor, "Raspberry") != NULL ||
428 strcasestr(ds->product.vendor, "BananaPi") != NULL ||
429 strcasestr(ds->product.vendor, "OrangePi") != NULL ||
430 strcasestr(ds->product.vendor, "ODROID") != NULL)) {
431 force_type = "mini-pc";
432 }
433
434 if(ds->cloud_instance_type[0] && strcasecmp(ds->cloud_instance_type, "unknown") != 0)
435 safecpy(ds->product.name, ds->cloud_instance_type);
436 else {
437 // copy the product family
438 size_t len = strcatz(ds->product.name, 0, ds->hw.product.family, sizeof(ds->product.name));
439
440 // append the product name, if it is not included already
441 if(ds->hw.product.name[0] && !strcasestr(ds->product.name, ds->hw.product.name)) {
442 if(ds->product.name[0]) {
443 bool includes_family = strcasestr(ds->hw.product.name, ds->hw.product.family) != NULL;
444
445 if(includes_family) {
446 // the product name includes the product family we have in buf
447 // we can clear the family and add only the product name
448 len = 0;
449 }
450 else {
451 // the product name is not included in the product family
452 // we append a separator, and later the name
453 len = strcatz(ds->product.name, len, " / ", sizeof(ds->product.name));
454 }
455 }
456
457 len = strcatz(ds->product.name, len, ds->hw.product.name, sizeof(ds->product.name));
458 }
459
460 // append the board name, if it is not included already
461 if(ds->hw.board.name[0] && !strcasestr(ds->product.name, ds->hw.board.name)) {
462 if(ds->product.name[0]) {
463 bool includes_family = !ds->hw.product.family[0] || strcasestr(ds->hw.board.name, ds->hw.product.family) != NULL;
464 bool includes_product = !ds->hw.product.name[0] || strcasestr(ds->hw.board.name, ds->hw.product.name) != NULL;
465
466 if(includes_family && includes_product) {
467 // the board name includes both the product and the family
468 // we can clear buf and add only the board name
469 len = 0;
470 }
471 else {
472 // the board name is not included in buf
473 // we append a separator, and later the name
474 len = strcatz(ds->product.name, len, " / ", sizeof(ds->product.name));
475 }
476 }
477
478 len = strcatz(ds->product.name, len, ds->hw.board.name, sizeof(ds->product.name));
479 }
480
481 if(!ds->product.name[0])
482 safecpy(ds->product.name, "unknown");
483 }
484
485 if(ds->virtualization[0] && strcasecmp(ds->virtualization, "none") != 0 && strcasecmp(ds->virtualization, "unknown") != 0)
486 safecpy(ds->product.type, "vm");
487 else if(force_type)
488 safecpy(ds->product.type, force_type);
489 else if(dmi_is_virtual_machine(&ds->hw))
490 safecpy(ds->product.type, "vm");
491 else if(is_server_hardware())
492 safecpy(ds->product.type, "server");
493 else {
494 char *end = NULL;
495 int type = (int)strtol(ds->hw.chassis.type, &end, 10);
496 if(type && (!end || !*end))
497 safecpy(ds->product.type, dmi_chassis_type_to_string(type));
498 else
499 safecpy(ds->product.type, "unknown");
500 }
501 }