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