master
js 1,085 lines 46.8 KB
Raw
1 /*
2 Copyright 2019-2021 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 function trimIdentifiers(val)
18 {
19 for(var v in val)
20 {
21 if(typeof val[v] === 'string') val[v] = val[v].trim();
22 if (!val[v] || val[v] == 'None' || val[v] == '') { delete val[v]; }
23 }
24 }
25 function trimResults(val)
26 {
27 var i, x;
28 for (i = 0; i < val.length; ++i)
29 {
30 for (x in val[i])
31 {
32 if (x.startsWith('_'))
33 {
34 delete val[i][x];
35 }
36 else
37 {
38 if (val[i][x] == null || val[i][x] == 0)
39 {
40 delete val[i][x];
41 }
42 }
43 }
44 }
45 }
46 function brief(headers, obj)
47 {
48 var i, x;
49 for (x = 0; x < obj.length; ++x)
50 {
51 for (i in obj[x])
52 {
53 if (!headers.includes(i))
54 {
55 delete obj[x][i];
56 }
57 }
58 }
59 return (obj);
60 }
61
62 function dataHandler(c)
63 {
64 this.str += c.toString();
65 }
66
67 function linux_identifiers()
68 {
69 var identifiers = {};
70 var ret = {};
71 var values = {};
72 var child = null;
73
74 if (!require('fs').existsSync('/sys/class/dmi/id')) {
75 if (require('fs').existsSync('/sys/firmware/devicetree/base/model')) {
76 if (require('fs').readFileSync('/sys/firmware/devicetree/base/model').toString().trim().startsWith('Raspberry')) {
77 identifiers['board_vendor'] = 'Raspberry Pi';
78 identifiers['board_name'] = require('fs').readFileSync('/sys/firmware/devicetree/base/model').toString().trim();
79 identifiers['board_serial'] = require('fs').readFileSync('/sys/firmware/devicetree/base/serial-number').toString().trim();
80 const memorySlots = [];
81 child = require('child_process').execFile('/bin/sh', ['sh']);
82 child.stdout.str = ''; child.stdout.on('data', dataHandler);
83 child.stdin.write('vcgencmd get_mem arm && vcgencmd get_mem gpu\nexit\n');
84 child.waitExit();
85 try {
86 const lines = child.stdout.str.trim().split('\n');
87 if (lines.length == 2) {
88 memorySlots.push({ Locator: "ARM Memory", Size: lines[0].split('=')[1].trim() })
89 memorySlots.push({ Locator: "GPU Memory", Size: lines[1].split('=')[1].trim() })
90 ret.memory = { Memory_Device: memorySlots };
91 }
92 } catch (xx) { }
93 } else {
94 throw('Unknown board');
95 }
96 } else {
97 throw ('this platform does not have DMI statistics');
98 }
99 } else {
100 var entries = require('fs').readdirSync('/sys/class/dmi/id');
101 for (var i in entries) {
102 if (require('fs').statSync('/sys/class/dmi/id/' + entries[i]).isFile()) {
103 try {
104 ret[entries[i]] = require('fs').readFileSync('/sys/class/dmi/id/' + entries[i]).toString().trim();
105 } catch(z) { }
106 if (ret[entries[i]] == 'None') { delete ret[entries[i]]; }
107 }
108 }
109 entries = null;
110
111 identifiers['bios_date'] = ret['bios_date'];
112 identifiers['bios_vendor'] = ret['bios_vendor'];
113 identifiers['bios_version'] = ret['bios_version'];
114 identifiers['bios_serial'] = ret['product_serial'];
115 identifiers['board_name'] = ret['board_name'];
116 identifiers['board_serial'] = ret['board_serial'];
117 identifiers['board_vendor'] = ret['board_vendor'];
118 identifiers['board_version'] = ret['board_version'];
119 identifiers['product_uuid'] = ret['product_uuid'];
120 identifiers['product_name'] = ret['product_name'];
121 }
122
123 // BIOS Mode
124 try {
125 var uefiExist = false;
126 var assumePi = false;
127
128 try { uefiExist = (require('fs')).existsSync('/sys/firmware/efi'); }
129 catch (ex) { uefiExist = false; }
130
131 try { assumePi = (require('fs')).existsSync('/sys/firmware/devicetree/base/model'); }
132 catch (ex) { assumePi = false; }
133
134 if (uefiExist) {
135 identifiers['bios_mode'] = 'UEFI';
136 } else if (assumePi) {
137 var modelBuffer = (require('fs')).readFileSync('/sys/firmware/devicetree/base/model');
138 var modelString = modelBuffer.toString().trim()
139
140 if (modelString.includes('Raspberry Pi')) {
141 identifiers['bios_mode'] = 'Raspberry Pi Firmware (Proprietary)';
142 }
143 } else {
144 identifiers['bios_mode'] = 'Legacy BIOS (MBR)';
145 }
146 } catch (ex) { identifiers['bios_mode'] = 'Legacy / Unknown'; }
147
148 // CPU Model info
149 child = require('child_process').execFile('/bin/sh', ['sh']);
150 child.stdout.str = ''; child.stdout.on('data', dataHandler);
151 child.stdin.write('cat /proc/cpuinfo | grep -i "model name" | ' + "tr '\\n' ':' | awk -F: '{ print $2 }'\nexit\n");
152 child.waitExit();
153 try {
154 identifiers['cpu_name'] = child.stdout.str.trim();
155 if (identifiers['cpu_name'] == "") { // CPU BLANK, check lscpu instead
156 child = require('child_process').execFile('/bin/sh', ['sh']);
157 child.stdout.str = ''; child.stdout.on('data', dataHandler);
158 child.stdin.write('lscpu | grep -i "model name" | ' + "tr '\\n' ':' | awk -F: '{ print $2 }'\nexit\n");
159 child.waitExit();
160 try { identifiers['cpu_name'] = child.stdout.str.trim(); } catch (xx) { }
161 }
162 } catch (xx) { }
163
164 // Kernel info
165 child = require('child_process').execFile('/bin/sh', ['sh']);
166 child.stdout.str = ''; child.stdout.on('data', dataHandler);
167 child.stdin.write('uname -r\nexit\n');
168 child.waitExit();
169 try { ret['kernel_release'] = child.stdout.str.trim(); } catch (xx) { }
170
171 child = require('child_process').execFile('/bin/sh', ['sh']);
172 child.stdout.str = ''; child.stdout.on('data', dataHandler);
173 child.stdin.write('uname -v\nexit\n');
174 child.waitExit();
175 try { ret['kernel_build'] = child.stdout.str.trim(); } catch (xx) { }
176
177 child = require('child_process').execFile('/bin/sh', ['sh']);
178 child.stdout.str = ''; child.stdout.on('data', dataHandler);
179 child.stdin.write('uname -m\nexit\n');
180 child.waitExit();
181 try { ret['arch'] = child.stdout.str.trim(); } catch (xx) { }
182
183 // Fetch GPU info
184 child = require('child_process').execFile('/bin/sh', ['sh']);
185 child.stdout.str = ''; child.stdout.on('data', dataHandler);
186 child.stdin.write("lspci | grep ' VGA ' | tr '\\n' '`' | awk '{ a=split($0,lines" + ',"`"); printf "["; for(i=1;i<a;++i) { split(lines[i],gpu,"r: "); printf "%s\\"%s\\"", (i==1?"":","),gpu[2]; } printf "]"; }\'\nexit\n');
187 child.waitExit();
188 try { identifiers['gpu_name'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
189
190 // Fetch Storage Info
191 child = require('child_process').execFile('/bin/sh', ['sh']);
192 child.stdout.str = ''; child.stdout.on('data', dataHandler);
193 child.stdin.write("lshw -class disk -disable network | tr '\\n' '`' | awk '" + '{ len=split($0,lines,"*"); printf "["; for(i=2;i<=len;++i) { model=""; caption=""; size=""; clen=split(lines[i],item,"`"); for(j=2;j<clen;++j) { split(item[j],tokens,":"); split(tokens[1],key," "); if(key[1]=="description") { caption=substr(tokens[2],2); } if(key[1]=="product") { model=substr(tokens[2],2); } if(key[1]=="size") { size=substr(tokens[2],2); } } if(model=="") { model=caption; } if(caption!="" || model!="") { printf "%s{\\"Caption\\":\\"%s\\",\\"Model\\":\\"%s\\",\\"Size\\":\\"%s\\"}",(i==2?"":","),caption,model,size; } } printf "]"; }\'\nexit\n');
194 child.waitExit();
195 try { identifiers['storage_devices'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
196
197 // Fetch storage volumes using df
198 child = require('child_process').execFile('/bin/sh', ['sh']);
199 child.stdout.str = ''; child.stdout.on('data', dataHandler);
200 child.stdin.write('df -T -x tmpfs -x devtmpfs -x efivarfs | awk \'NR==1 || $1 ~ ".+"{print $3, $4, $5, $7, $2}\' | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\",\\"type\\":\\"%s\\"},", $1, $2, $3, $4, $5}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
201 child.waitExit();
202 try { ret.volumes = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
203
204 values.identifiers = identifiers;
205 values.linux = ret;
206 trimIdentifiers(values.identifiers);
207
208 var dmidecode = require('lib-finder').findBinary('dmidecode');
209 if (dmidecode != null)
210 {
211 child = require('child_process').execFile('/bin/sh', ['sh']);
212 child.stdout.str = ''; child.stdout.on('data', dataHandler);
213 child.stderr.str = ''; child.stderr.on('data', dataHandler);
214 child.stdin.write(dmidecode + " -t memory | tr '\\n' '`' | ");
215 child.stdin.write(" awk '{ ");
216 child.stdin.write(' printf("[");');
217 child.stdin.write(' comma="";');
218 child.stdin.write(' c=split($0, lines, "``");');
219 child.stdin.write(' for(i=1;i<=c;++i)');
220 child.stdin.write(' {');
221 child.stdin.write(' d=split(lines[i], val, "`");');
222 child.stdin.write(' split(val[1], tokens, ",");');
223 child.stdin.write(' split(tokens[2], dmitype, " ");');
224 child.stdin.write(' dmi = dmitype[3]+0; ');
225 child.stdin.write(' if(dmi == 5 || dmi == 6 || dmi == 16 || dmi == 17)');
226 child.stdin.write(' {');
227 child.stdin.write(' ccx="";');
228 child.stdin.write(' printf("%s{\\"%s\\": {", comma, val[2]);');
229 child.stdin.write(' for(j=3;j<d;++j)');
230 child.stdin.write(' {');
231 child.stdin.write(' sub(/^[ \\t]*/,"",val[j]);');
232 child.stdin.write(' if(split(val[j],tmp,":")>1)');
233 child.stdin.write(' {');
234 child.stdin.write(' sub(/^[ \\t]*/,"",tmp[2]);');
235 child.stdin.write(' gsub(/ /,"",tmp[1]);');
236 child.stdin.write(' printf("%s\\"%s\\": \\"%s\\"", ccx, tmp[1], tmp[2]);');
237 child.stdin.write(' ccx=",";');
238 child.stdin.write(' }');
239 child.stdin.write(' }');
240 child.stdin.write(' printf("}}");');
241 child.stdin.write(' comma=",";');
242 child.stdin.write(' }');
243 child.stdin.write(' }');
244 child.stdin.write(' printf("]");');
245 child.stdin.write("}'\nexit\n");
246 child.waitExit();
247
248 try
249 {
250 var j = JSON.parse(child.stdout.str);
251 var i, key, key2;
252 for (i = 0; i < j.length; ++i)
253 {
254 for (key in j[i])
255 {
256 delete j[i][key]['ArrayHandle'];
257 delete j[i][key]['ErrorInformationHandle'];
258 for (key2 in j[i][key])
259 {
260 if (j[i][key][key2] == 'Unknown' || j[i][key][key2] == 'Not Specified' || j[i][key][key2] == '')
261 {
262 delete j[i][key][key2];
263 }
264 }
265 }
266 }
267
268 if(j.length > 0){
269 var mem = {};
270 for (i = 0; i < j.length; ++i)
271 {
272 for (key in j[i])
273 {
274 if (mem[key] == null) { mem[key] = []; }
275 mem[key].push(j[i][key]);
276 }
277 }
278 values.linux.memory = mem;
279 }
280 }
281 catch (e)
282 { }
283 child = null;
284 }
285
286 var usbdevices = require('lib-finder').findBinary('usb-devices');
287 if (usbdevices != null)
288 {
289 var child = require('child_process').execFile('/bin/sh', ['sh']);
290 child.stdout.str = ''; child.stdout.on('data', dataHandler);
291 child.stderr.str = ''; child.stderr.on('data', dataHandler);
292 child.stdin.write(usbdevices + " | tr '\\n' '`' | ");
293 child.stdin.write(" awk '");
294 child.stdin.write('{');
295 child.stdin.write(' comma="";');
296 child.stdin.write(' printf("[");');
297 child.stdin.write(' len=split($0, group, "``");');
298 child.stdin.write(' for(i=1;i<=len;++i)');
299 child.stdin.write(' {');
300 child.stdin.write(' comma2="";');
301 child.stdin.write(' xlen=split(group[i], line, "`");');
302 child.stdin.write(' scount=0;');
303 child.stdin.write(' for(x=1;x<xlen;++x)');
304 child.stdin.write(' {');
305 child.stdin.write(' if(line[x] ~ "^S:")');
306 child.stdin.write(' {');
307 child.stdin.write(' ++scount;');
308 child.stdin.write(' }');
309 child.stdin.write(' }');
310 child.stdin.write(' if(scount>0)');
311 child.stdin.write(' {');
312 child.stdin.write(' printf("%s{", comma); comma=",";');
313 child.stdin.write(' for(x=1;x<xlen;++x)');
314 child.stdin.write(' {');
315 child.stdin.write(' if(line[x] ~ "^T:")');
316 child.stdin.write(' {');
317 child.stdin.write(' comma3="";');
318 child.stdin.write(' printf("%s\\"hardware\\": {", comma2); comma2=",";');
319 child.stdin.write(' sub(/^T:[ \\t]*/, "", line[x]);');
320 child.stdin.write(' gsub(/= */, "=", line[x]);');
321 child.stdin.write(' blen=split(line[x], tokens, " ");');
322 child.stdin.write(' for(y=1;y<blen;++y)');
323 child.stdin.write(' {');
324 child.stdin.write(' match(tokens[y],/=/);');
325 child.stdin.write(' h=substr(tokens[y],1,RSTART-1);');
326 child.stdin.write(' v=substr(tokens[y],RSTART+1);');
327 child.stdin.write(' sub(/#/, "", h);');
328 child.stdin.write(' printf("%s\\"%s\\": \\"%s\\"", comma3, h, v); comma3=",";');
329 child.stdin.write(' }');
330 child.stdin.write(' printf("}");');
331 child.stdin.write(' }');
332 child.stdin.write(' if(line[x] ~ "^S:")');
333 child.stdin.write(' {');
334 child.stdin.write(' sub(/^S:[ \\t]*/, "", line[x]);');
335 child.stdin.write(' match(line[x], /=/);');
336 child.stdin.write(' h=substr(line[x],1,RSTART-1);');
337 child.stdin.write(' v=substr(line[x],RSTART+1);');
338 child.stdin.write(' printf("%s\\"%s\\": \\"%s\\"", comma2, h,v); comma2=",";');
339 child.stdin.write(' }');
340 child.stdin.write(' }');
341 child.stdin.write(' printf("}");');
342 child.stdin.write(' }');
343 child.stdin.write(' }');
344 child.stdin.write(' printf("]");');
345 child.stdin.write("}'\nexit\n");
346 child.waitExit();
347
348 try
349 {
350 values.linux.usb = JSON.parse(child.stdout.str);
351 }
352 catch(x)
353 { }
354 child = null;
355 }
356
357 var pcidevices = require('lib-finder').findBinary('lspci');
358 if (pcidevices != null)
359 {
360 var child = require('child_process').execFile('/bin/sh', ['sh']);
361 child.stdout.str = ''; child.stdout.on('data', dataHandler);
362 child.stderr.str = ''; child.stderr.on('data', dataHandler);
363 child.stdin.write(pcidevices + " -m | tr '\\n' '`' | ");
364 child.stdin.write(" awk '");
365 child.stdin.write('{');
366 child.stdin.write(' printf("[");');
367 child.stdin.write(' comma="";');
368 child.stdin.write(' alen=split($0, lines, "`");');
369 child.stdin.write(' for(a=1;a<alen;++a)');
370 child.stdin.write(' {');
371 child.stdin.write(' match(lines[a], / /);');
372 child.stdin.write(' blen=split(lines[a], meta, "\\"");');
373 child.stdin.write(' bus=substr(lines[a], 1, RSTART);');
374 child.stdin.write(' gsub(/ /, "", bus);');
375 child.stdin.write(' printf("%s{\\"bus\\": \\"%s\\"", comma, bus); comma=",";');
376 child.stdin.write(' printf(", \\"device\\": \\"%s\\"", meta[2]);');
377 child.stdin.write(' printf(", \\"manufacturer\\": \\"%s\\"", meta[4]);');
378 child.stdin.write(' printf(", \\"description\\": \\"%s\\"", meta[6]);');
379 child.stdin.write(' if(meta[8] != "")');
380 child.stdin.write(' {');
381 child.stdin.write(' printf(", \\"subsystem\\": {");');
382 child.stdin.write(' printf("\\"manufacturer\\": \\"%s\\"", meta[8]);');
383 child.stdin.write(' printf(", \\"description\\": \\"%s\\"", meta[10]);');
384 child.stdin.write(' printf("}");');
385 child.stdin.write(' }');
386 child.stdin.write(' printf("}");');
387 child.stdin.write(' }');
388 child.stdin.write(' printf("]");');
389 child.stdin.write("}'\nexit\n");
390 child.waitExit();
391
392 try
393 {
394 values.linux.pci = JSON.parse(child.stdout.str);
395 }
396 catch (x)
397 { }
398 child = null;
399 }
400
401 // Linux Last Boot Up Time
402 try {
403 child = require('child_process').execFile('/usr/bin/uptime', ['', '-s']); // must include blank value at begining for some reason?
404 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
405 child.stderr.on('data', function () { });
406 child.waitExit();
407 var regex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
408 if (regex.test(child.stdout.str.trim())) {
409 values.linux.LastBootUpTime = child.stdout.str.trim();
410 } else {
411 child = require('child_process').execFile('/bin/sh', ['sh']);
412 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
413 child.stdin.write('date -d "@$(( $(date +%s) - $(awk \'{print int($1)}\' /proc/uptime) ))" "+%Y-%m-%d %H:%M:%S"\nexit\n');
414 child.waitExit();
415 if (regex.test(child.stdout.str.trim())) {
416 values.linux.LastBootUpTime = child.stdout.str.trim();
417 }
418 }
419 child = null;
420 } catch (ex) { }
421
422 // Linux TPM
423 try {
424 if (require('fs').statSync('/sys/class/tpm/tpm0').isDirectory()){
425 values.tpm = {
426 SpecVersion: require('fs').readFileSync('/sys/class/tpm/tpm0/tpm_version_major').toString().trim()
427 }
428 }
429 } catch (ex) { }
430
431 // Linux Batteries
432 try {
433 var batteries = require('fs').readdirSync('/sys/class/power_supply/');
434 if (batteries.length != 0) {
435 values.battery = [];
436 for (var i in batteries) {
437 const filesToRead = [
438 'capacity', 'cycle_count', 'energy_full', 'energy_full_design',
439 'energy_now', 'manufacturer', 'model_name', 'power_now',
440 'serial_number', 'status', 'technology', 'voltage_now'
441 ];
442 const thedata = {};
443 for (var x in filesToRead) {
444 try {
445 const content = require('fs').readFileSync('/sys/class/power_supply/' + batteries[i] + '/' + filesToRead[x]).toString().trim();
446 thedata[filesToRead[x]] = /^\d+$/.test(content) ? parseInt(content, 10) : content;
447 } catch (err) { }
448 }
449 if (Object.keys(thedata).length === 0) continue; // No data read, skip
450 const status = (thedata.status || '').toLowerCase();
451 const isCharging = status === 'charging';
452 const isDischarging = status === 'discharging';
453 const toMilli = function (val) { return Math.round((val || 0) / 1000) }; // Convert from µ units to m units (divide by 1000)
454 const batteryJson = {
455 "InstanceName": batteries[i],
456 "CycleCount": thedata.cycle_count || 0,
457 "FullChargedCapacity": toMilli(thedata.energy_full),
458 "Chemistry": (thedata.technology || ''),
459 "DesignedCapacity": toMilli(thedata.energy_full_design),
460 "DeviceName": thedata.model_name || "Battery",
461 "ManufactureName": thedata.manufacturer || "Unknown",
462 "SerialNumber": thedata.serial_number || "unknown",
463 "ChargeRate": isCharging ? toMilli(thedata.power_now) : 0,
464 "Charging": isCharging,
465 "DischargeRate": isDischarging ? toMilli(thedata.power_now) : 0,
466 "Discharging": isDischarging,
467 "RemainingCapacity": toMilli(thedata.energy_now),
468 "Voltage": toMilli(thedata.voltage_now),
469 "Health": (thedata.energy_full && thedata.energy_full_design ? Math.floor((thedata.energy_full / thedata.energy_full_design) * 100) : 0),
470 "BatteryCharge": (thedata.energy_now && thedata.energy_full ? Math.floor((thedata.energy_now / thedata.energy_full) * 100) : (thedata.capacity ? thedata.capacity : 0))
471 };
472 values.battery.push(batteryJson);
473 }
474 if (values.battery.length == 0) { delete values.battery; }
475 }
476 } catch (ex) { }
477
478 return (values);
479 }
480
481 function windows_wmic_results(str)
482 {
483 var lines = str.trim().split('\r\n');
484 var keys = lines[0].split(',');
485 var i, key, keyval;
486 var tokens;
487 var result = [];
488
489 console.log('Lines: ' + lines.length, 'Keys: ' + keys.length);
490
491 for (i = 1; i < lines.length; ++i)
492 {
493 var obj = {};
494 console.log('i: ' + i);
495 tokens = lines[i].split(',');
496 for (key = 0; key < keys.length; ++key)
497 {
498 var tmp = Buffer.from(tokens[key], 'binary').toString();
499 console.log(tokens[key], tmp);
500 tokens[key] = tmp == null ? '' : tmp;
501 if (tokens[key].trim())
502 {
503 obj[keys[key].trim()] = tokens[key].trim();
504 }
505 }
506 delete obj.Node;
507 result.push(obj);
508 }
509 return (result);
510 }
511
512 function windows_identifiers()
513 {
514 var ret = { windows: {} };
515 var items, item, i;
516
517 ret['identifiers'] = {};
518
519 var values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_Bios", ['ReleaseDate', 'Manufacturer', 'SMBIOSBIOSVersion', 'SerialNumber']);
520 if(values[0]){
521 ret['identifiers']['bios_date'] = values[0]['ReleaseDate'];
522 ret['identifiers']['bios_vendor'] = values[0]['Manufacturer'];
523 ret['identifiers']['bios_version'] = values[0]['SMBIOSBIOSVersion'];
524 ret['identifiers']['bios_serial'] = values[0]['SerialNumber'];
525 }
526 ret['identifiers']['bios_mode'] = 'Legacy';
527
528 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_BaseBoard", ['Product', 'SerialNumber', 'Manufacturer', 'Version']);
529 if(values[0]){
530 ret['identifiers']['board_name'] = values[0]['Product'];
531 ret['identifiers']['board_serial'] = values[0]['SerialNumber'];
532 ret['identifiers']['board_vendor'] = values[0]['Manufacturer'];
533 ret['identifiers']['board_version'] = values[0]['Version'];
534 }
535
536 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_ComputerSystemProduct", ['UUID', 'Name']);
537 if(values[0]){
538 ret['identifiers']['product_uuid'] = values[0]['UUID'];
539 ret['identifiers']['product_name'] = values[0]['Name'];
540 }
541
542 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_SystemEnclosure", ['SerialNumber', 'SMBIOSAssetTag', 'Manufacturer']);
543 if(values[0]){
544 ret['identifiers']['chassis_serial'] = values[0]['SerialNumber'];
545 ret['identifiers']['chassis_assettag'] = values[0]['SMBIOSAssetTag'];
546 ret['identifiers']['chassis_manufacturer'] = values[0]['Manufacturer'];
547 }
548
549 trimIdentifiers(ret.identifiers);
550
551 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_PhysicalMemory");
552 if(values[0]){
553 trimResults(values);
554 ret.windows.memory = values;
555 }
556
557 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_OperatingSystem");
558 if(values[0]){
559 trimResults(values);
560 ret.windows.osinfo = values[0];
561
562 try {
563 var reg = require('win-registry');
564 var ubr = reg.QueryKey(reg.HKEY.LocalMachine, 'Software\\Microsoft\\Windows NT\\CurrentVersion', 'UBR');
565 if(ubr && ret.windows.osinfo.Version){
566 ret.windows.osinfo.BuildRevision = ret.windows.osinfo.Version + "." + ubr
567 }
568 } catch (ex){}
569 }
570
571 values = require('win-wmi-fixed').query('ROOT\\CIMV2', "SELECT * FROM Win32_DiskPartition");
572 if(values[0]){
573 trimResults(values);
574 ret.windows.partitions = values;
575 for (var i in values) {
576 if (values[i].Type=='GPT: System') {
577 ret['identifiers']['bios_mode'] = 'UEFI';
578 }
579 }
580 }
581
582 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_Processor", ['Caption', 'DeviceID', 'Manufacturer', 'MaxClockSpeed', 'Name', 'SocketDesignation']);
583 if(values[0]){
584 ret.windows.cpu = values;
585 }
586
587 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_VideoController", ['Name', 'CurrentHorizontalResolution', 'CurrentVerticalResolution']);
588 if(values[0]){
589 ret.windows.gpu = values;
590 }
591
592 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_DiskDrive", ['Caption', 'DeviceID', 'Model', 'Partitions', 'Size', 'Status']);
593 if(values[0]){
594 ret.windows.drives = values;
595 }
596
597 // Insert GPU names
598 ret.identifiers.gpu_name = [];
599 for (var gpuinfo in ret.windows.gpu)
600 {
601 if (ret.windows.gpu[gpuinfo].Name) { ret.identifiers.gpu_name.push(ret.windows.gpu[gpuinfo].Name); }
602 }
603
604 // Insert Storage Devices
605 ret.identifiers.storage_devices = [];
606 for (var dv in ret.windows.drives)
607 {
608 ret.identifiers.storage_devices.push({ Caption: ret.windows.drives[dv].Caption, Model: ret.windows.drives[dv].Model, Size: ret.windows.drives[dv].Size });
609 }
610
611 try { ret.identifiers.cpu_name = ret.windows.cpu[0].Name; } catch (x) { }
612
613 // Windows TPM
614 IntToStr = function (v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
615 try {
616 values = require('win-wmi').query('ROOT\\CIMV2\\Security\\MicrosoftTpm', "SELECT * FROM Win32_Tpm", ['IsActivated_InitialValue','IsEnabled_InitialValue','IsOwned_InitialValue','ManufacturerId','ManufacturerVersion','SpecVersion']);
617 if(values[0]) {
618 ret.tpm = {
619 SpecVersion: values[0].SpecVersion.split(",")[0],
620 ManufacturerId: IntToStr(values[0].ManufacturerId).replace(/[^\x00-\x7F]/g, ""),
621 ManufacturerVersion: values[0].ManufacturerVersion,
622 IsActivated: values[0].IsActivated_InitialValue,
623 IsEnabled: values[0].IsEnabled_InitialValue,
624 IsOwned: values[0].IsOwned_InitialValue,
625 }
626 }
627 } catch (ex) { }
628
629 // Windows Batteries
630 IntToStrLE = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); };
631 try {
632 function mergeJSONArrays() {
633 var resultMap = {};
634 var result = [];
635 // Loop through all arguments (arrays)
636 for (var i = 0; i < arguments.length; i++) {
637 var currentArray = arguments[i];
638 // Skip if not an array
639 if (!currentArray || currentArray.constructor !== Array) {
640 continue;
641 }
642 // Process each object in the array
643 for (var j = 0; j < currentArray.length; j++) {
644 var obj = currentArray[j];
645 // Skip if not an object or missing InstanceName
646 if (!obj || typeof obj !== 'object' || !obj.InstanceName) {
647 continue;
648 }
649 var name = obj.InstanceName;
650 // Create new entry if it doesn't exist
651 if (!resultMap[name]) {
652 resultMap[name] = { InstanceName: name };
653 result.push(resultMap[name]);
654 }
655 // Copy all properties except InstanceName
656 for (var key in obj) {
657 if (obj.hasOwnProperty(key) && key !== 'InstanceName') {
658 resultMap[name][key] = obj[key];
659 }
660 }
661 }
662 }
663 return result;
664 }
665 values = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryCycleCount",['InstanceName','CycleCount']);
666 var values2 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryFullChargedCapacity",['InstanceName','FullChargedCapacity']);
667 var values3 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryRuntime",['InstanceName','EstimatedRuntime']);
668 var values4 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryStaticData",['InstanceName','Chemistry','DesignedCapacity','DeviceName','ManufactureDate','ManufactureName','SerialNumber']);
669 for (i = 0; i < values4.length; ++i) {
670 if (values4[i].Chemistry) { values4[i].Chemistry = IntToStrLE(parseInt(values4[i].Chemistry)); }
671 if (values4[i].ManufactureDate) { if (values4[i].ManufactureDate.indexOf('*****') != -1) delete values4[i].ManufactureDate; }
672 }
673 var values5 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryStatus",['InstanceName','ChargeRate','Charging','DischargeRate','Discharging','RemainingCapacity','Voltage']);
674 var values6 = [];
675 if (values2.length > 0 && values4.length > 0) {
676 for (i = 0; i < values2.length; ++i) {
677 for (var j = 0; j < values4.length; ++j) {
678 if (values2[i].InstanceName == values4[j].InstanceName) {
679 if ((values4[j].DesignedCapacity && values4[j].DesignedCapacity > 0) && (values2[i].FullChargedCapacity && values2[i].FullChargedCapacity > 0)) {
680 values6[i] = {
681 Health: Math.floor((values2[i].FullChargedCapacity / values4[j].DesignedCapacity) * 100),
682 InstanceName: values2[i].InstanceName
683 };
684 if (values6[i].Health > 100) { values6[i].Health = 100; }
685 } else {
686 values6[i] = { Health: 0, InstanceName: values2[i].InstanceName };
687 }
688 break;
689 }
690 }
691 }
692 }
693 var values7 = [];
694 if (values2.length > 0 && values5.length > 0) {
695 for (i = 0; i < values2.length; ++i) {
696 for (var j = 0; j < values5.length; ++j) {
697 if (values2[i].InstanceName == values5[j].InstanceName) {
698 if ((values2[i].FullChargedCapacity && values2[i].FullChargedCapacity > 0) && (values5[j].RemainingCapacity && values5[j].RemainingCapacity > 0)) {
699 values7[i] = {
700 BatteryCharge: Math.floor((values5[j].RemainingCapacity / values2[i].FullChargedCapacity) * 100),
701 InstanceName: values2[i].InstanceName
702 };
703 } else {
704 values7[i] = { BatteryCharge: 0, InstanceName: values2[i].InstanceName };
705 }
706 break;
707 }
708 }
709 }
710 }
711 ret.battery = mergeJSONArrays(values, values2, values3, values4, values5, values6, values7);
712 } catch (ex) { }
713
714 return (ret);
715 }
716 function macos_identifiers()
717 {
718 var ret = { identifiers: {}, darwin: {} };
719 var child;
720
721 child = require('child_process').execFile('/bin/sh', ['sh']);
722 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
723 child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep board-id | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
724 child.waitExit();
725 ret.identifiers.board_name = child.stdout.str.trim();
726
727 child = require('child_process').execFile('/bin/sh', ['sh']);
728 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
729 child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep IOPlatformSerialNumber | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
730 child.waitExit();
731 ret.identifiers.board_serial = child.stdout.str.trim();
732
733 child = require('child_process').execFile('/bin/sh', ['sh']);
734 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
735 child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep manufacturer | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
736 child.waitExit();
737 ret.identifiers.board_vendor = child.stdout.str.trim();
738
739 child = require('child_process').execFile('/bin/sh', ['sh']);
740 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
741 child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep version | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
742 child.waitExit();
743 ret.identifiers.board_version = child.stdout.str.trim();
744
745 child = require('child_process').execFile('/bin/sh', ['sh']);
746 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
747 child.stdin.write('ioreg -d2 -c IOPlatformExpertDevice | grep IOPlatformUUID | awk -F= \'{ split($2, res, "\\""); print res[2]; }\'\nexit\n');
748 child.waitExit();
749 ret.identifiers.product_uuid = child.stdout.str.trim();
750
751 child = require('child_process').execFile('/bin/sh', ['sh']);
752 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
753 child.stdin.write('sysctl -n machdep.cpu.brand_string\nexit\n');
754 child.waitExit();
755 ret.identifiers.cpu_name = child.stdout.str.trim();
756
757 child = require('child_process').execFile('/bin/sh', ['sh']);
758 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
759 child.stdin.write('system_profiler SPMemoryDataType\nexit\n');
760 child.waitExit();
761 var lines = child.stdout.str.trim().split('\n');
762 if(lines.length > 0) {
763 const memorySlots = [];
764 if(lines[2].trim().includes('Memory Slots:')) { // OLD MACS WITH SLOTS
765 var memorySlots1 = child.stdout.str.split(/\n{2,}/).slice(3);
766 memorySlots1.forEach(function(slot,index) {
767 var lines = slot.split('\n');
768 if(lines.length == 1){ // start here
769 if(lines[0].trim()!=''){
770 var slotObj = { DeviceLocator: lines[0].trim().replace(/:$/, '') }; // Initialize name as an empty string
771 var nextline = memorySlots1[index+1].split('\n');
772 nextline.forEach(function(line) {
773 if (line.trim() !== '') {
774 var parts = line.split(':');
775 var key = parts[0].trim();
776 var value = parts[1].trim();
777 value = (key == 'Part Number' || key == 'Manufacturer') ? hexToAscii(parts[1].trim()) : parts[1].trim();
778 slotObj[key.replace(' ','')] = value; // Store attribute in the slot object
779 }
780 });
781 memorySlots.push(slotObj);
782 }
783 }
784 });
785 } else { // NEW MACS WITHOUT SLOTS
786 memorySlots.push({ DeviceLocator: "Onboard Memory", Size: lines[2].split(":")[1].trim(), PartNumber: lines[3].split(":")[1].trim(), Manufacturer: lines[4].split(":")[1].trim() })
787 }
788 ret.darwin.memory = memorySlots;
789 }
790
791 child = require('child_process').execFile('/bin/sh', ['sh']);
792 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
793 child.stdin.write('diskutil info -all\nexit\n');
794 child.waitExit();
795 var sections = child.stdout.str.split('**********\n');
796 if(sections.length > 0){
797 var devices = [];
798 for (var i = 0; i < sections.length; i++) {
799 var lines = sections[i].split('\n');
800 var deviceInfo = {};
801 var wholeYes = false;
802 var physicalYes = false;
803 var oldmac = false;
804 for (var j = 0; j < lines.length; j++) {
805 var keyValue = lines[j].split(':');
806 var key = keyValue[0].trim();
807 var value = keyValue[1] ? keyValue[1].trim() : '';
808 if (key === 'Virtual') oldmac = true;
809 if (key === 'Whole' && value === 'Yes') wholeYes = true;
810 if (key === 'Virtual' && value === 'No') physicalYes = true;
811 if(value && key === 'Device / Media Name'){
812 deviceInfo['Caption'] = value;
813 }
814 if(value && key === 'Disk Size'){
815 deviceInfo['Size'] = value.split(' ')[0] + ' ' + value.split(' ')[1];
816 }
817 }
818 if (wholeYes) {
819 if (oldmac) {
820 if (physicalYes) devices.push(deviceInfo);
821 } else {
822 devices.push(deviceInfo);
823 }
824 }
825 }
826 ret.identifiers.storage_devices = devices;
827 }
828
829 // Fetch storage volumes using df
830 child = require('child_process').execFile('/bin/sh', ['sh']);
831 child.stdout.str = ''; child.stdout.on('data', dataHandler);
832 child.stdin.write('df -aHY | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\",\\"type\\":\\"%s\\"},", $3, $4, $5, $10, $2}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
833 child.waitExit();
834 try {
835 ret.darwin.volumes = JSON.parse(child.stdout.str.trim());
836 for (var index = 0; index < ret.darwin.volumes.length; index++) {
837 if (ret.darwin.volumes[index].type == 'auto_home'){
838 ret.darwin.volumes.splice(index,1);
839 }
840 }
841 if (ret.darwin.volumes.length == 0) { // not sonima OS so dont show type for now
842 child = require('child_process').execFile('/bin/sh', ['sh']);
843 child.stdout.str = ''; child.stdout.on('data', dataHandler);
844 child.stdin.write('df -aH | awk \'NR>1 {printf "{\\"size\\":\\"%s\\",\\"used\\":\\"%s\\",\\"available\\":\\"%s\\",\\"mount_point\\":\\"%s\\"},", $2, $3, $4, $9}\' | sed \'$ s/,$//\' | awk \'BEGIN {printf "["} {printf "%s", $0} END {printf "]"}\'\nexit\n');
845 child.waitExit();
846 try {
847 ret.darwin.volumes = JSON.parse(child.stdout.str.trim());
848 for (var index = 0; index < ret.darwin.volumes.length; index++) {
849 if (ret.darwin.volumes[index].size == 'auto_home'){
850 ret.darwin.volumes.splice(index,1);
851 }
852 }
853 } catch (xx) { }
854 }
855 } catch (xx) { }
856 child = null;
857
858 // MacOS Last Boot Up Time
859 try {
860 child = require('child_process').execFile('/usr/sbin/sysctl', ['', 'kern.boottime']); // must include blank value at begining for some reason?
861 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
862 child.stderr.on('data', function () { });
863 child.waitExit();
864 const timestampMatch = /\{ sec = (\d+), usec = \d+ \}/.exec(child.stdout.str.trim());
865 if (!ret.darwin) {
866 ret.darwin = { LastBootUpTime: parseInt(timestampMatch[1]) };
867 } else {
868 ret.darwin.LastBootUpTime = parseInt(timestampMatch[1]);
869 }
870 child = null;
871 } catch (ex) { }
872
873 trimIdentifiers(ret.identifiers);
874
875 child = null;
876 return (ret);
877 }
878
879 function hexToAscii(hexString) {
880 if(!hexString.startsWith('0x')) return hexString.trim();
881 hexString = hexString.startsWith('0x') ? hexString.slice(2) : hexString;
882 var str = '';
883 for (var i = 0; i < hexString.length; i += 2) {
884 var hexPair = hexString.substr(i, 2);
885 str += String.fromCharCode(parseInt(hexPair, 16));
886 }
887 str = str.replace(/[\u007F-\uFFFF]/g, ''); // Remove characters from 0x0080 to 0xFFFF
888 return str.trim();
889 }
890
891 function win_chassisType()
892 {
893 // use new win-wmi-fixed module to get arrays correctly for time being
894 try {
895 var tokens = require('win-wmi-fixed').query('ROOT\\CIMV2', 'SELECT ChassisTypes FROM Win32_SystemEnclosure', ['ChassisTypes']);
896 if (tokens[0]) {
897 return (parseInt(tokens[0]['ChassisTypes'][0]));
898 }
899 } catch (e) {
900 return (2); // unknown
901 }
902 }
903
904 function win_systemType()
905 {
906 try {
907 var tokens = require('win-wmi').query('ROOT\\CIMV2', 'SELECT PCSystemType FROM Win32_ComputerSystem', ['PCSystemType']);
908 if (tokens[0]) {
909 return (parseInt(tokens[0]['PCSystemType']));
910 } else {
911 return (parseInt(1)); // default is desktop
912 }
913 } catch (ex) {
914 return (parseInt(1)); // default is desktop
915 }
916
917 }
918
919 function win_formFactor(chassistype)
920 {
921 var ret = 'DESKTOP';
922 switch (chassistype)
923 {
924 case 11: // Handheld
925 case 30: // Tablet
926 case 31: // Convertible
927 case 32: // Detachable
928 ret = 'TABLET';
929 break;
930 case 9: // Laptop
931 case 10: // Notebook
932 case 14: // Sub Notebook
933 ret = 'LAPTOP';
934 break;
935 default:
936 ret = win_systemType() == 2 ? 'MOBILE' : 'DESKTOP';
937 break;
938 }
939
940 return (ret);
941 }
942
943 switch(process.platform)
944 {
945 case 'linux':
946 module.exports = { _ObjectID: 'identifiers', get: linux_identifiers };
947 break;
948 case 'win32':
949 module.exports = { _ObjectID: 'identifiers', get: windows_identifiers, chassisType: win_chassisType, formFactor: win_formFactor, systemType: win_systemType };
950 break;
951 case 'darwin':
952 module.exports = { _ObjectID: 'identifiers', get: macos_identifiers };
953 break;
954 default:
955 module.exports = { get: function () { throw ('Unsupported Platform'); } };
956 break;
957 }
958 module.exports.isDocker = function isDocker()
959 {
960 if (process.platform != 'linux') { return (false); }
961
962 var child = require('child_process').execFile('/bin/sh', ['sh']);
963 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
964 child.stdin.write("cat /proc/self/cgroup | tr '\n' '`' | awk -F'`' '{ split($1, res, " + '"/"); if(res[2]=="docker"){print "1";} }\'\nexit\n');
965 child.waitExit();
966 return (child.stdout.str != '');
967 };
968 module.exports.isBatteryPowered = function isBatteryOperated()
969 {
970 var ret = false;
971 switch(process.platform)
972 {
973 default:
974 break;
975 case 'linux':
976 var devices = require('fs').readdirSync('/sys/class/power_supply');
977 for (var i in devices)
978 {
979 if (require('fs').readFileSync('/sys/class/power_supply/' + devices[i] + '/type').toString().trim() == 'Battery')
980 {
981 ret = true;
982 break;
983 }
984 }
985 break;
986 case 'win32':
987 var GM = require('_GenericMarshal');
988 var stats = GM.CreateVariable(12);
989 var kernel32 = GM.CreateNativeProxy('Kernel32.dll');
990 kernel32.CreateMethod('GetSystemPowerStatus');
991 if (kernel32.GetSystemPowerStatus(stats).Val != 0)
992 {
993 if(stats.toBuffer()[1] != 128 && stats.toBuffer()[1] != 255)
994 {
995 ret = true;
996 }
997 else
998 {
999 // No Battery detected, so lets check if there is supposed to be one
1000 var formFactor = win_formFactor(win_chassisType());
1001 return (formFactor == 'LAPTOP' || formFactor == 'TABLET' || formFactor == 'MOBILE');
1002 }
1003 }
1004 break;
1005 case 'darwin':
1006 var child = require('child_process').execFile('/bin/sh', ['sh']);
1007 child.stdout.str = ''; child.stdout.on('data', function(c){ this.str += c.toString(); });
1008 child.stderr.str = ''; child.stderr.on('data', function(c){ this.str += c.toString(); });
1009 child.stdin.write("pmset -g batt | tr '\\n' '`' | awk -F'`' '{ if(NF>2) { print \"true\"; }}'\nexit\n");
1010 child.waitExit();
1011 if(child.stdout.str.trim() != '') { ret = true; }
1012 break;
1013 }
1014 return (ret);
1015 };
1016 module.exports.isVM = function isVM()
1017 {
1018 var ret = false;
1019 var id = this.get();
1020 if (id.linux && id.linux.sys_vendor)
1021 {
1022 switch (id.linux.sys_vendor)
1023 {
1024 case 'VMware, Inc.':
1025 case 'QEMU':
1026 case 'Xen':
1027 ret = true;
1028 break;
1029 default:
1030 break;
1031 }
1032 }
1033 if (id.identifiers.bios_vendor)
1034 {
1035 switch(id.identifiers.bios_vendor)
1036 {
1037 case 'VMware, Inc.':
1038 case 'Xen':
1039 case 'SeaBIOS':
1040 case 'EFI Development Kit II / OVMF':
1041 case 'Proxmox distribution of EDK II':
1042 ret = true;
1043 break;
1044 default:
1045 break;
1046 }
1047 }
1048 if (id.identifiers.board_vendor && id.identifiers.board_vendor == 'VMware, Inc.') { ret = true; }
1049 if (id.identifiers.board_name)
1050 {
1051 switch (id.identifiers.board_name)
1052 {
1053 case 'VirtualBox':
1054 case 'Virtual Machine':
1055 ret = true;
1056 break;
1057 default:
1058 break;
1059 }
1060 }
1061
1062 if (process.platform == 'win32' && !ret)
1063 {
1064 for(var i in id.identifiers.gpu_name)
1065 {
1066 if(id.identifiers.gpu_name[i].startsWith('VMware '))
1067 {
1068 ret = true;
1069 break;
1070 }
1071 }
1072 }
1073
1074
1075 if (!ret) { ret = this.isDocker(); }
1076 return (ret);
1077 };
1078
1079 // bios_date = BIOS->ReleaseDate
1080 // bios_vendor = BIOS->Manufacturer
1081 // bios_version = BIOS->SMBIOSBIOSVersion
1082 // board_name = BASEBOARD->Product = ioreg/board-id
1083 // board_serial = BASEBOARD->SerialNumber = ioreg/serial-number | ioreg/IOPlatformSerialNumber
1084 // board_vendor = BASEBOARD->Manufacturer = ioreg/manufacturer
1085 // board_version = BASEBOARD->Version