2Copyright 2019-2021 Intel Corporation
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
17function trimIdentifiers(val)
21 if(typeof val[v] === 'string') val[v] = val[v].trim();
22 if (!val[v] || val[v] == 'None' || val[v] == '') { delete val[v]; }
25function trimResults(val)
28 for (i = 0; i < val.length; ++i)
32 if (x.startsWith('_'))
38 if (val[i][x] == null || val[i][x] == 0)
46function brief(headers, obj)
49 for (x = 0; x < obj.length; ++x)
53 if (!headers.includes(i))
62function dataHandler(c)
64 this.str += c.toString();
67function linux_identifiers()
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');
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 };
94 throw('Unknown board');
97 throw ('this platform does not have DMI statistics');
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()) {
104 ret[entries[i]] = require('fs').readFileSync('/sys/class/dmi/id/' + entries[i]).toString().trim();
106 if (ret[entries[i]] == 'None') { delete ret[entries[i]]; }
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'];
125 var uefiExist = false;
126 var assumePi = false;
128 try { uefiExist = (require('fs')).existsSync('/sys/firmware/efi'); }
129 catch (ex) { uefiExist = false; }
131 try { assumePi = (require('fs')).existsSync('/sys/firmware/devicetree/base/model'); }
132 catch (ex) { assumePi = false; }
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()
140 if (modelString.includes('Raspberry Pi')) {
141 identifiers['bios_mode'] = 'Raspberry Pi Firmware (Proprietary)';
144 identifiers['bios_mode'] = 'Legacy BIOS (MBR)';
146 } catch (ex) { identifiers['bios_mode'] = 'Legacy / Unknown'; }
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");
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");
160 try { identifiers['cpu_name'] = child.stdout.str.trim(); } catch (xx) { }
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');
169 try { identifiers['gpu_name'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
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');
176 try { identifiers['storage_devices'] = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
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');
183 try { ret.volumes = JSON.parse(child.stdout.str.trim()); } catch (xx) { }
185 values.identifiers = identifiers;
187 trimIdentifiers(values.identifiers);
189 var dmidecode = require('lib-finder').findBinary('dmidecode');
190 if (dmidecode != null)
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");
231 var j = JSON.parse(child.stdout.str);
233 for (i = 0; i < j.length; ++i)
237 delete j[i][key]['ArrayHandle'];
238 delete j[i][key]['ErrorInformationHandle'];
239 for (key2 in j[i][key])
241 if (j[i][key][key2] == 'Unknown' || j[i][key][key2] == 'Not Specified' || j[i][key][key2] == '')
243 delete j[i][key][key2];
251 for (i = 0; i < j.length; ++i)
255 if (mem[key] == null) { mem[key] = []; }
256 mem[key].push(j[i][key]);
259 values.linux.memory = mem;
267 var usbdevices = require('lib-finder').findBinary('usb-devices');
268 if (usbdevices != null)
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");
331 values.linux.usb = JSON.parse(child.stdout.str);
338 var pcidevices = require('lib-finder').findBinary('lspci');
339 if (pcidevices != null)
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");
375 values.linux.pci = JSON.parse(child.stdout.str);
382 // Linux Last Boot Up Time
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 () { });
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();
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');
396 if (regex.test(child.stdout.str.trim())) {
397 values.linux.LastBootUpTime = child.stdout.str.trim();
405 if (require('fs').statSync('/sys/class/tpm/tpm0').isDirectory()){
407 SpecVersion: require('fs').readFileSync('/sys/class/tpm/tpm0/tpm_version_major').toString().trim()
414 var batteries = require('fs').readdirSync('/sys/class/power_supply/');
415 if (batteries.length != 0) {
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'
424 for (var x in filesToRead) {
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;
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))
453 values.battery.push(batteryJson);
455 if (values.battery.length == 0) { delete values.battery; }
462function windows_wmic_results(str)
464 var lines = str.trim().split('\r\n');
465 var keys = lines[0].split(',');
470 console.log('Lines: ' + lines.length, 'Keys: ' + keys.length);
472 for (i = 1; i < lines.length; ++i)
475 console.log('i: ' + i);
476 tokens = lines[i].split(',');
477 for (key = 0; key < keys.length; ++key)
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())
484 obj[keys[key].trim()] = tokens[key].trim();
493function windows_identifiers()
495 var ret = { windows: {} };
498 ret['identifiers'] = {};
500 var values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_Bios", ['ReleaseDate', 'Manufacturer', 'SMBIOSBIOSVersion', 'SerialNumber']);
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'];
507 ret['identifiers']['bios_mode'] = 'Legacy';
509 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_BaseBoard", ['Product', 'SerialNumber', 'Manufacturer', 'Version']);
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'];
517 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_ComputerSystemProduct", ['UUID', 'Name']);
519 ret['identifiers']['product_uuid'] = values[0]['UUID'];
520 ret['identifiers']['product_name'] = values[0]['Name'];
523 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_SystemEnclosure", ['SerialNumber', 'SMBIOSAssetTag', 'Manufacturer']);
525 ret['identifiers']['chassis_serial'] = values[0]['SerialNumber'];
526 ret['identifiers']['chassis_assettag'] = values[0]['SMBIOSAssetTag'];
527 ret['identifiers']['chassis_manufacturer'] = values[0]['Manufacturer'];
530 trimIdentifiers(ret.identifiers);
532 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_PhysicalMemory");
535 ret.windows.memory = values;
538 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_OperatingSystem");
541 ret.windows.osinfo = values[0];
544 values = require('win-wmi-fixed').query('ROOT\\CIMV2', "SELECT * FROM Win32_DiskPartition");
547 ret.windows.partitions = values;
548 for (var i in values) {
549 if (values[i].Type=='GPT: System') {
550 ret['identifiers']['bios_mode'] = 'UEFI';
555 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_Processor", ['Caption', 'DeviceID', 'Manufacturer', 'MaxClockSpeed', 'Name', 'SocketDesignation']);
557 ret.windows.cpu = values;
560 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_VideoController", ['Name', 'CurrentHorizontalResolution', 'CurrentVerticalResolution']);
562 ret.windows.gpu = values;
565 values = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_DiskDrive", ['Caption', 'DeviceID', 'Model', 'Partitions', 'Size', 'Status']);
567 ret.windows.drives = values;
571 ret.identifiers.gpu_name = [];
572 for (var gpuinfo in ret.windows.gpu)
574 if (ret.windows.gpu[gpuinfo].Name) { ret.identifiers.gpu_name.push(ret.windows.gpu[gpuinfo].Name); }
577 // Insert Storage Devices
578 ret.identifiers.storage_devices = [];
579 for (var dv in ret.windows.drives)
581 ret.identifiers.storage_devices.push({ Caption: ret.windows.drives[dv].Caption, Model: ret.windows.drives[dv].Model, Size: ret.windows.drives[dv].Size });
584 try { ret.identifiers.cpu_name = ret.windows.cpu[0].Name; } catch (x) { }
587 IntToStr = function (v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); };
589 values = require('win-wmi').query('ROOT\\CIMV2\\Security\\MicrosoftTpm', "SELECT * FROM Win32_Tpm", ['IsActivated_InitialValue','IsEnabled_InitialValue','IsOwned_InitialValue','ManufacturerId','ManufacturerVersion','SpecVersion']);
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,
603 IntToStrLE = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); };
605 function mergeJSONArrays() {
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) {
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) {
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]);
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];
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; }
646 var values5 = require('win-wmi').query('ROOT\\WMI', "SELECT * FROM BatteryStatus",['InstanceName','ChargeRate','Charging','DischargeRate','Discharging','RemainingCapacity','Voltage']);
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)) {
654 Health: Math.floor((values2[i].FullChargedCapacity / values4[j].DesignedCapacity) * 100),
655 InstanceName: values2[i].InstanceName
657 if (values6[i].Health > 100) { values6[i].Health = 100; }
659 values6[i] = { Health: 0, InstanceName: values2[i].InstanceName };
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)) {
673 BatteryCharge: Math.floor((values5[j].RemainingCapacity / values2[i].FullChargedCapacity) * 100),
674 InstanceName: values2[i].InstanceName
677 values7[i] = { BatteryCharge: 0, InstanceName: values2[i].InstanceName };
684 ret.battery = mergeJSONArrays(values, values2, values3, values4, values5, values6, values7);
689function macos_identifiers()
691 var ret = { identifiers: {}, darwin: {} };
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');
698 ret.identifiers.board_name = child.stdout.str.trim();
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');
704 ret.identifiers.board_serial = child.stdout.str.trim();
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');
710 ret.identifiers.board_vendor = child.stdout.str.trim();
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');
716 ret.identifiers.board_version = child.stdout.str.trim();
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');
722 ret.identifiers.product_uuid = child.stdout.str.trim();
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');
728 ret.identifiers.cpu_name = child.stdout.str.trim();
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');
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
754 memorySlots.push(slotObj);
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() })
761 ret.darwin.memory = memorySlots;
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');
768 var sections = child.stdout.str.split('**********\n');
769 if(sections.length > 0){
771 for (var i = 0; i < sections.length; i++) {
772 var lines = sections[i].split('\n');
774 var wholeYes = false;
775 var physicalYes = 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;
787 if(value && key === 'Disk Size'){
788 deviceInfo['Size'] = value.split(' ')[0] + ' ' + value.split(' ')[1];
793 if (physicalYes) devices.push(deviceInfo);
795 devices.push(deviceInfo);
799 ret.identifiers.storage_devices = devices;
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');
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);
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');
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);
831 // MacOS Last Boot Up Time
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 () { });
837 const timestampMatch = /\{ sec = (\d+), usec = \d+ \}/.exec(child.stdout.str.trim());
839 ret.darwin = { LastBootUpTime: parseInt(timestampMatch[1]) };
841 ret.darwin.LastBootUpTime = parseInt(timestampMatch[1]);
846 trimIdentifiers(ret.identifiers);
852function hexToAscii(hexString) {
853 if(!hexString.startsWith('0x')) return hexString.trim();
854 hexString = hexString.startsWith('0x') ? hexString.slice(2) : hexString;
856 for (var i = 0; i < hexString.length; i += 2) {
857 var hexPair = hexString.substr(i, 2);
858 str += String.fromCharCode(parseInt(hexPair, 16));
860 str = str.replace(/[\u007F-\uFFFF]/g, ''); // Remove characters from 0x0080 to 0xFFFF
864function win_chassisType()
866 // use new win-wmi-fixed module to get arrays correctly for time being
868 var tokens = require('win-wmi-fixed').query('ROOT\\CIMV2', 'SELECT ChassisTypes FROM Win32_SystemEnclosure', ['ChassisTypes']);
870 return (parseInt(tokens[0]['ChassisTypes'][0]));
873 return (2); // unknown
877function win_systemType()
880 var tokens = require('win-wmi').query('ROOT\\CIMV2', 'SELECT PCSystemType FROM Win32_ComputerSystem', ['PCSystemType']);
882 return (parseInt(tokens[0]['PCSystemType']));
884 return (parseInt(1)); // default is desktop
887 return (parseInt(1)); // default is desktop
892function win_formFactor(chassistype)
899 case 31: // Convertible
900 case 32: // Detachable
905 case 14: // Sub Notebook
909 ret = win_systemType() == 2 ? 'MOBILE' : 'DESKTOP';
916switch(process.platform)
919 module.exports = { _ObjectID: 'identifiers', get: linux_identifiers };
922 module.exports = { _ObjectID: 'identifiers', get: windows_identifiers, chassisType: win_chassisType, formFactor: win_formFactor, systemType: win_systemType };
925 module.exports = { _ObjectID: 'identifiers', get: macos_identifiers };
928 module.exports = { get: function () { throw ('Unsupported Platform'); } };
931module.exports.isDocker = function isDocker()
933 if (process.platform != 'linux') { return (false); }
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');
939 return (child.stdout.str != '');
941module.exports.isBatteryPowered = function isBatteryOperated()
944 switch(process.platform)
949 var devices = require('fs').readdirSync('/sys/class/power_supply');
950 for (var i in devices)
952 if (require('fs').readFileSync('/sys/class/power_supply/' + devices[i] + '/type').toString().trim() == 'Battery')
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)
966 if(stats.toBuffer()[1] != 128 && stats.toBuffer()[1] != 255)
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');
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");
984 if(child.stdout.str.trim() != '') { ret = true; }
989module.exports.isVM = function isVM()
993 if (id.linux && id.linux.sys_vendor)
995 switch (id.linux.sys_vendor)
1006 if (id.identifiers.bios_vendor)
1008 switch(id.identifiers.bios_vendor)
1010 case 'VMware, Inc.':
1013 case 'EFI Development Kit II / OVMF':
1014 case 'Proxmox distribution of EDK II':
1021 if (id.identifiers.board_vendor && id.identifiers.board_vendor == 'VMware, Inc.') { ret = true; }
1022 if (id.identifiers.board_name)
1024 switch (id.identifiers.board_name)
1027 case 'Virtual Machine':
1035 if (process.platform == 'win32' && !ret)
1037 for(var i in id.identifiers.gpu_name)
1039 if(id.identifiers.gpu_name[i].startsWith('VMware '))
1048 if (!ret) { ret = this.isDocker(); }
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