'use strict'; // ================================================================================== // index.js // ---------------------------------------------------------------------------------- // Description: System Information - library // for Node.js // Copyright: (c) 2014 - 2016 // Author: Sebastian Hildebrandt // ---------------------------------------------------------------------------------- // Contributors: Guillaume Legrain (https://github.com/glegrain) // Riccardo Novaglia (https://github.com/richy24) // Quentin Busuttil (https://github.com/Buzut) // Lapsio (https://github.com/lapsio) // ---------------------------------------------------------------------------------- // License: MIT // ================================================================================== // // Sections // -------------------------------- // 1. General // 2. System (HW) // 3. OS - Operating System // 4. CPU // 5. Memory // 6. Battery // 7. File System // 8. Network // 9. Processes // 10. Users/Sessions // 11. Internet // 12. Docker // 13. GetAll - get all data // // ================================================================================== // // Installation // -------------------------------- // // # npm install systeminformation --save // // Since version 2.0 systeminformation has no more dependencies. // // ================================================================================== // // Usage // -------------------------------- // All functions (except `version` and `time`) are asynchronous functions. Here a small example how to use them: // // var si = require('systeminformation'); // // // callback style // si.cpu(function(data) { // console.log('CPU-Information:'); // console.log(data); // }) // // // promises style // si.cpu() // .then(data => console.log(data)) // .catch(error => console.error(error)); // // ================================================================================== // // Comments // -------------------------------- // // This library is still work in progress. Version 3 comes with further improvements. First it // requires now node.js version 4.0 and above. Another big change is, that all functions now // return promises. You can use them like before with callbacks OR with promises (see documentation // below. I am sure, there is for sure room for improvement. I was only able to test it on several // Debian, Raspbian, Ubuntu distributions as well as OS X (Mavericks, Yosemite, El Captain). // Since version 2 nearly all functionality is available on OS X/Darwin platforms. // But be careful, this library will definitely NOT work on Windows platforms! // // Comments, suggestions & reports are very welcome! // // ================================================================================== // // Version history // -------------------------------- // // version date comment // 3.4.4 2016-09-02 tiny fixes system.model, getDefaultNetworkInterface // 3.4.3 2016-09-02 tiny bug fix fsStats, disksIO OSX // 3.4.2 2016-09-01 improved default network interface // 3.4.1 2016-08-30 updated docs // 3.4.0 2016-08-30 rewritten current process cpu usage (linux) // 3.3.0 2016-08-24 added process list // 3.2.1 2016-08-20 updated docs, improvement system // 3.2.0 2016-08-19 added battery info // 3.1.1 2016-08-18 improved system and os detection (vm, ...), bug fix disksIO // 3.1.0 2016-08-18 added docker stats // 3.0.1 2016-08-17 Bug-Fix disksIO, users, updated docs // 3.0.0 2016-08-03 new major version 3.0 // 2.0.5 2016-02-22 some more tiny correction ... // 2.0.4 2016-02-22 tiny correction - removed double quotes CPU brand, ... // 2.0.3 2016-02-22 optimized cpuCurrentspeed // 2.0.2 2016-02-22 added CoreOS identification // 2.0.1 2016-01-07 minor patch // 2.0.0 2016-01-07 new major version 2.0 // 1.0.7 2015-11-27 fixed: si.network_speed() // 1.0.6 2015-09-17 fixed: si.users() // 1.0.5 2015-09-14 updated dependencies // 1.0.4 2015-07-18 updated docs // 1.0.3 2015-07-18 bugfix cpu cores // 1.0.2 2015-07-18 bugfix cpu_currentspeed, cpu_temperature // 1.0.1 2015-07-18 documentation update // 1.0.0 2015-07-18 bug-fixes, version bump, published as npm component // 0.0.3 2014-04-14 bug-fix (cpu_speed) // 0.0.2 2014-03-14 Optimization FS-Speed & CPU current speed // 0.0.1 2014-03-13 initial release // // ================================================================================== // ---------------------------------------------------------------------------------- // Dependencies // ---------------------------------------------------------------------------------- const os = require('os') , exec = require('child_process').exec , execSync = require('child_process').execSync , fs = require('fs') , lib_version = require('../package.json').version; var _cores = 0; var _platform = os.type(); var _linux = (_platform == 'Linux'); var _darwin = (_platform == 'Darwin'); var _windows = (_platform == 'Windows_NT'); var _network = {}; var _cpu_speed = '0.00'; var _fs_speed = {}; var _disk_io = {}; var _default_iface; var _docker_container_stats = {}; var _process_cpu = { all: 0, list: {}, ms: 0 }; var _current_cpu = { user: 0, nice: 0, system: 0, idle: 0, iowait: 0, irq: 0, softirq: 0, steal: 0, guest: 0, guest_nice: 0, all: 0 }; const NOT_SUPPORTED = 'not supported'; // ---------------------------------------------------------------------------------- // 0. helper functions // ---------------------------------------------------------------------------------- function isFunction(functionToCheck) { var getType = {}; return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; } // ---------------------------------------------------------------------------------- // 1. System (Hardware) // ---------------------------------------------------------------------------------- function system(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { manufacturer: '', model: 'Computer', version: '', serial: '-', uuid: '-' }; if (_linux) { exec("dmidecode -t system", function (error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); lines.forEach(function (line) { if (line.indexOf(':') != -1) { if (line.toLowerCase().indexOf('manufacturer') != -1) result.manufacturer = result.manufacturer || line.split(':')[1].trim(); if (line.toLowerCase().indexOf('product name') != -1) result.model = line.split(':')[1].trim(); if (line.toLowerCase().indexOf('version') != -1) result.version = result.version || line.split(':')[1].trim(); if (line.toLowerCase().indexOf('serial number') != -1) result.serial = line.split(':')[1].trim(); if (line.toLowerCase().indexOf('uuid') != -1) result.uuid = line.split(':')[1].trim(); } }); if (result.serial.toLowerCase().indexOf('o.e.m.') != -1) result.serial = '-'; if (result.manufacturer.toLowerCase().indexOf('o.e.m.') != -1) result.manufacturer = ''; if (result.model.toLowerCase().indexOf('o.e.m.') != -1) result.model = 'Computer'; if (result.version.toLowerCase().indexOf('o.e.m.') != -1) result.version = '-'; if (result.manufacturer == '' && result.model == 'Computer' && result.version == '-') { // Check Raspberry Pi exec("grep Hardware /proc/cpuinfo; grep Serial /proc/cpuinfo; grep Revision /proc/cpuinfo", function (error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); lines.forEach(function (line) { if (line.indexOf(':') != -1) { if (line.toLowerCase().indexOf('hardware') != -1) result.model = line.split(':')[1].trim(); if (line.toLowerCase().indexOf('revision') != -1) result.version = line.split(':')[1].trim(); if (line.toLowerCase().indexOf('serial') != -1) result.serial = line.split(':')[1].trim(); } }); if (result.model == 'BCM2709') { result.manufacturer = 'Raspberry Pi Foundation'; result.model = result.model + ' - Pi 2 Model B'; if (['a01041', 'a21041'].indexOf(result.version) >= 0) { result.version = result.version + ' - Rev. 1.1' } } if (result.model == 'BCM2708') { result.manufacturer = 'Raspberry Pi Foundation'; if (['0002', '0003'].indexOf(result.version) >= 0) { result.model = result.model + ' - Pi Model B'; result.version = result.version + ' - Rev 1.0'; } if (['0007', '0008', '0009'].indexOf(result.version) >= 0) { result.model = result.model + ' - Pi Model A'; result.version = result.version + ' - Rev 2.0'; } if (['0004', '0005', '0006', '000d', '000e', '000f'].indexOf(result.version) >= 0) { result.model = result.model + ' - Pi Model B'; result.version = result.version + ' - Rev 2.0'; } if (['0012'].indexOf(result.version) >= 0) { result.model = result.model + ' - Pi Model A+'; result.version = result.version + ' - Rev 1.0'; } if (['0010'].indexOf(result.version) >= 0) { result.model = result.model + ' - Pi Model B+'; result.version = result.version + ' - Rev 1.0'; } if (['0013'].indexOf(result.version) >= 0) { result.model = result.model + ' - Pi Model B+'; result.version = result.version + ' - Rev 1.2'; } } } if (callback) { callback(result) } resolve(result); }) } else { if (callback) { callback(result) } resolve(result); } } else { exec("dmesg | grep -i virtual | grep -iE 'vmware|qemu|kvm|xen'", function (error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); if (lines.length > 0) result.model = 'Virtual machine' } if (fs.existsSync('/.dockerenv') || fs.existsSync('/.dockerinit')) { result.model = 'Docker Container' } if (callback) { callback(result) } resolve(result); }); } }) } if (_darwin) { exec("ioreg -c IOPlatformExpertDevice -d 2", function (error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.replace(/[<>"]/g, ""); if (line.indexOf('=') != -1) { if (line.toLowerCase().indexOf('manufacturer') != -1) result.manufacturer = line.split('=')[1].trim(); if (line.toLowerCase().indexOf('model') != -1) result.model = line.split('=')[1].trim(); if (line.toLowerCase().indexOf('version') != -1) result.version = line.split('=')[1].trim(); if (line.toLowerCase().indexOf('ioplatformserialnumber') != -1) result.serial = line.split('=')[1].trim(); if (line.toLowerCase().indexOf('ioplatformuuid') != -1) result.uuid = line.split('=')[1].trim(); } }); } if (callback) { callback(result) } resolve(result); }) } }); }); } exports.system = system; // ---------------------------------------------------------------------------------- // 2. General // ---------------------------------------------------------------------------------- function version() { return lib_version; } exports.version = version; // ---------------------------------------------------------------------------------- // 3. Operating System // ---------------------------------------------------------------------------------- // -------------------------- // Get current time and OS uptime function time() { return { current: Date.now(), uptime: os.uptime() }; } exports.time = time; // -------------------------- // Get logo filename of OS distribution function getLogoFile(distro) { distro = distro.toLowerCase(); let result = 'linux'; if (distro.indexOf('mac os') != -1) { result = 'apple' } else if (distro.indexOf('arch') != -1) { result = 'arch' } else if (distro.indexOf('centos') != -1) { result = 'centos' } else if (distro.indexOf('coreos') != -1) { result = 'coreos' } else if (distro.indexOf('debian') != -1) { result = 'debian' } else if (distro.indexOf('elementary') != -1) { result = 'elementary' } else if (distro.indexOf('fedora') != -1) { result = 'fedora' } else if (distro.indexOf('gentoo') != -1) { result = 'gentoo' } else if (distro.indexOf('mageia') != -1) { result = 'mageia' } else if (distro.indexOf('mandriva') != -1) { result = 'mandriva' } else if (distro.indexOf('manjaro') != -1) { result = 'manjaro' } else if (distro.indexOf('mint') != -1) { result = 'mint' } else if (distro.indexOf('openbsd') != -1) { result = 'openbsd' } else if (distro.indexOf('opensuse') != -1) { result = 'opensuse' } else if (distro.indexOf('pclinuxos') != -1) { result = 'pclinuxos' } else if (distro.indexOf('puppy') != -1) { result = 'puppy' } else if (distro.indexOf('raspbian') != -1) { result = 'raspbian' } else if (distro.indexOf('reactos') != -1) { result = 'reactos' } else if (distro.indexOf('redhat') != -1) { result = 'redhat' } else if (distro.indexOf('slackware') != -1) { result = 'slackware' } else if (distro.indexOf('sugar') != -1) { result = 'sugar' } else if (distro.indexOf('steam') != -1) { result = 'steam' } else if (distro.indexOf('suse') != -1) { result = 'suse' } else if (distro.indexOf('mate') != -1) { result = 'ubuntu-mate' } else if (distro.indexOf('lubuntu') != -1) { result = 'lubuntu' } else if (distro.indexOf('xubuntu') != -1) { result = 'xubuntu' } else if (distro.indexOf('ubuntu') != -1) { result = 'ubuntu' } return result; } // -------------------------- // OS Information function osInfo(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { platform: _platform, distro: 'unknown', release: 'unknown', codename: '', kernel: os.release(), arch: os.arch(), hostname: os.hostname(), logofile: '' }; if (_linux) { exec("cat /etc/*-release", function (error, stdout) { //if (!error) { /** * @namespace * @property {string} DISTRIB_ID * @property {string} NAME * @property {string} DISTRIB_RELEASE * @property {string} VERSION_ID * @property {string} DISTRIB_CODENAME */ var release = {}; var lines = stdout.toString().split('\n'); lines.forEach(function (line) { if (line.indexOf('=') != -1) { release[line.split('=')[0].trim().toUpperCase()] = line.split('=')[1].trim(); } }); result.distro = (release.DISTRIB_ID || release.NAME || 'unknown').replace(/"/g, ''); result.logofile = getLogoFile(result.distro); result.release = (release.DISTRIB_RELEASE || release.VERSION_ID || 'unknown').replace(/"/g, ''); result.codename = (release.DISTRIB_CODENAME || '').replace(/"/g, ''); //} if (callback) { callback(result) } resolve(result); }) } if (_darwin) { exec("sw_vers", function (error, stdout) { var lines = stdout.toString().split('\n'); lines.forEach(function (line) { if (line.indexOf('ProductName') != -1) { result.distro = line.split(':')[1].trim(); result.logofile = getLogoFile(result.distro); } if (line.indexOf('ProductVersion') != -1) result.release = line.split(':')[1].trim(); }); if (callback) { callback(result) } resolve(result); }) } }); }); } exports.osInfo = osInfo; // ---------------------------------------------------------------------------------- // 4. CPU // ---------------------------------------------------------------------------------- function cpuBrandManufacturer(res) { res.brand = res.brand.replace(/\(R\)+/g, "®"); res.brand = res.brand.replace(/\(TM\)+/g, "™"); res.brand = res.brand.replace(/\(C\)+/g, "©"); res.brand = res.brand.replace(/CPU+/g, "").trim(); res.manufacturer = res.brand.split(' ')[0]; let parts = res.brand.split(' '); parts.shift(); res.brand = parts.join(' '); return res; } // -------------------------- // CPU - brand, speed function getCpu() { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { manufacturer: 'unknown', brand: 'unknown', speed: '0.00', cores: _cores }; if (_darwin) { exec("sysctl -n machdep.cpu.brand_string", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); result.brand = lines[0].split('@')[0].trim(); result.speed = lines[0].split('@')[1].trim(); result.speed = parseFloat(result.speed.replace(/GHz+/g, "")); _cpu_speed = result.speed; } result = cpuBrandManufacturer(result); resolve(result); }); } if (_linux) { exec("cat /proc/cpuinfo | grep 'model name'", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); let line = lines[0].split(':')[1]; result.brand = line.split('@')[0].trim(); result.speed = line.split('@')[1] ? parseFloat(line.split('@')[1].trim()).toFixed(2) : '0.00'; if (result.speed == '0.00') { let current = getCpuCurrentSpeedSync(); if (current != '0.00') result.speed = current; } _cpu_speed = result.speed; } result = cpuBrandManufacturer(result); resolve(result); }) } }); }); } // -------------------------- // CPU - Processor cores function cores() { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } let result = os.cpus().length; resolve(result); }); }); } // -------------------------- // CPU - Processor Data function cpu(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } if (_cores == 0) { cores() .then(data => { _cores = data; getCpu().then(result => { if (callback) { callback(result) } resolve(result); }) }) } else { getCpu().then(result => { if (callback) { callback(result) } resolve(result); }) } }); }); } exports.cpu = cpu; // -------------------------- // CPU - current speed - in GHz function getCpuCurrentSpeedSync() { let cpus = os.cpus(); let minFreq = 999999999; let maxFreq = 0; let avgFreq = 0; if (cpus.length) { for (let i in cpus) { if (cpus.hasOwnProperty(i)) { avgFreq = avgFreq + cpus[i].speed; if (cpus[i].speed > maxFreq) maxFreq = cpus[i].speed; if (cpus[i].speed < minFreq) minFreq = cpus[i].speed; } } avgFreq = avgFreq / cpus.length; return { min: parseFloat((minFreq / 1000).toFixed(2)), max: parseFloat((maxFreq / 1000).toFixed(2)), avg: parseFloat((avgFreq / 1000).toFixed(2)) } } else { return { min: 0, max: 0, avg: 0 } } } function cpuCurrentspeed(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } let result = getCpuCurrentSpeedSync(); if (result == 0 && _cpu_speed != '0.00') result = parseFloat(_cpu_speed); if (callback) { callback(result) } resolve(result); }); }); } exports.cpuCurrentspeed = cpuCurrentspeed; // -------------------------- // CPU - temperature // if sensors are installed function cpuTemperature(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { main: -1.0, cores: [], max: -1.0 }; if (_linux) { exec("sensors", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { let regex = /\+([^°]*)/g; let temps = line.match(regex); if (line.split(':')[0].toUpperCase().indexOf('PHYSICAL') != -1) { result.main = parseFloat(temps); } if (line.split(':')[0].toUpperCase().indexOf('CORE ') != -1) { result.cores.push(parseFloat(temps)); } }); if (result.cores.length > 0) { let maxtmp = Math.max.apply(Math, result.cores); result.max = (maxtmp > result.main) ? maxtmp : result.main; } if (callback) { callback(result) } resolve(result); } else { exec("/opt/vc/bin/vcgencmd measure_temp", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); if (lines.length > 0 && lines[0].indexOf('=')) { result.main = parseFloat(lines[0].split("=")[1]); result.max = result.main } } if (callback) { callback(result) } resolve(result); }); } }); } if (_darwin) { if (callback) { callback(result) } resolve(result); } }); }); } exports.cpuTemperature = cpuTemperature; // ---------------------------------------------------------------------------------- // 5. Memory // ---------------------------------------------------------------------------------- // | R A M | H D | // |______________________|_________________________| | | // | active buffers/cache | | | // |________________________________________________|___________|_________|______________| // | used free | used free | // |____________________________________________________________|________________________| // | total | swap | // | | | // free (older versions) // ---------------------------------- // # free // total used free shared buffers cached // Mem: 16038 (1) 15653 (2) 384 (3) 0 (4) 236 (5) 14788 (6) // -/+ buffers/cache: 628 (7) 15409 (8) // Swap: 16371 83 16288 // // |------------------------------------------------------------| // | R A M | // |______________________|_____________________________________| // | active (2-(5+6) = 7) | available (3+5+6 = 8) | // |______________________|_________________________|___________| // | active | buffers/cache (5+6) | | // |________________________________________________|___________| // | used (2) | free (3) | // |____________________________________________________________| // | total (1) | // |____________________________________________________________| // // free (since free von procps-ng 3.3.10) // ---------------------------------- // # free // total used free shared buffers/cache available // Mem: 16038 (1) 628 (2) 386 (3) 0 (4) 15024 (5) 14788 (6) // Swap: 16371 83 16288 // // |------------------------------------------------------------| // | R A M | // |______________________|_____________________________________| // | | available (6) estimated | // |______________________|_________________________|___________| // | active (2) | buffers/cache (5) | free (3) | // |________________________________________________|___________| // | total (1) | // |____________________________________________________________| // // Reference: http://www.software-architect.net/blog/article/date/2015/06/12/-826c6e5052.html function mem(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { total: os.totalmem(), free: os.freemem(), used: os.totalmem() - os.freemem(), active: os.totalmem() - os.freemem(), // temporarily (fallback) available: os.freemem(), // temporarily (fallback) buffcache: 0, swaptotal: 0, swapused: 0, swapfree: 0 }; if (_linux) { exec("free -b", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); let mem = lines[1].replace(/ +/g, " ").split(' '); result.total = parseInt(mem[1]); result.free = parseInt(mem[3]); if (lines.length === 4) { // free (since free von procps-ng 3.3.10) result.buffcache = parseInt(mem[5]); result.available = parseInt(mem[6]); mem = lines[2].replace(/ +/g, " ").split(' '); } else { // free (older versions) result.buffcache = parseInt(mem[5]) + parseInt(mem[6]); result.available = result.free + result.buffcache; mem = lines[3].replace(/ +/g, " ").split(' '); } result.active = result.total - result.free - result.buffcache; result.swaptotal = parseInt(mem[1]); result.swapfree = parseInt(mem[3]); result.swapused = parseInt(mem[2]); } if (callback) { callback(result) } resolve(result); }); } if (_darwin) { exec("vm_stat | grep 'Pages active'", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); result.active = parseInt(lines[0].split(':')[1]) * 4096; result.buffcache = result.used - result.active; result.available = result.free + result.buffcache; } exec("sysctl -n vm.swapusage", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); if (lines.length > 0) { let line = lines[0].replace(/,/g, ".").replace(/M/g, ""); line = line.trim().split(' '); for (let i = 0; i < line.length; i++) { if (line[i].toLowerCase().indexOf('total') != -1) result.swaptotal = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024; if (line[i].toLowerCase().indexOf('used') != -1) result.swapused = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024; if (line[i].toLowerCase().indexOf('free') != -1) result.swapfree = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024; } } } if (callback) { callback(result) } resolve(result); }); }); } }); }); } exports.mem = mem; // ---------------------------------------------------------------------------------- // 6. Battery // ---------------------------------------------------------------------------------- function battery(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } let result = { hasbattery: false, cyclecount: 0, ischarging: false, maxcapacity: 0, currentcapacity: 0, percent: 0 }; if (_linux) { let battery_path = ''; if (fs.existsSync('/sys/class/power_supply/BAT1/status')) { battery_path = '/sys/class/power_supply/BAT1/' } else if (fs.existsSync('/sys/class/power_supply/BAT0/status')) { battery_path = '/sys/class/power_supply/BAT0/' } if (battery_path) { exec("cat " + battery_path + "status", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); if (lines.length > 0 && lines[0]) result.ischarging = (lines[0].trim().toLowerCase() == 'charging') } exec("cat " + battery_path + "cyclec_ount", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); if (lines.length > 0 && lines[0]) result.cyclecount = parseFloat(lines[0].trim()); } exec("cat " + battery_path + "charge_full", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); if (lines.length > 0 && lines[0]) result.maxcapacity = parseFloat(lines[0].trim()); } exec("cat " + battery_path + "charge_now", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); if (lines.length > 0 && lines[0]) result.currentcapacity = parseFloat(lines[0].trim()); } if (result.maxcapacity && result.currentcapacity) { result.hasbattery = true; result.percent = 100.0 * result.currentcapacity / result.maxcapacity; } if (callback) { callback(result) } resolve(result); }) }) }) }) } else { if (callback) { callback(result) } resolve(result); } } if (_darwin) { exec("ioreg -n AppleSmartBattery -r | grep '\"CycleCount\"';ioreg -n AppleSmartBattery -r | grep '\"IsCharging\"';ioreg -n AppleSmartBattery -r | grep '\"MaxCapacity\"';ioreg -n AppleSmartBattery -r | grep '\"CurrentCapacity\"'", function (error, stdout) { if (!error) { let lines = stdout.toString().replace(/ +/g, "").replace(/"+/g, "").split('\n'); lines.forEach(function (line) { if (line.indexOf('=') != -1) { if (line.toLowerCase().indexOf('cyclecount') != -1) result.cyclecount = parseFloat(line.split('=')[1].trim()); if (line.toLowerCase().indexOf('ischarging') != -1) result.ischarging = (line.split('=')[1].trim().toLowerCase() == 'yes'); if (line.toLowerCase().indexOf('maxcapacity') != -1) result.maxcapacity = parseFloat(line.split('=')[1].trim()); if (line.toLowerCase().indexOf('currentcapacity') != -1) result.currentcapacity = parseFloat(line.split('=')[1].trim()); } }); } if (result.maxcapacity && result.currentcapacity) { result.hasbattery = true; result.percent = 100.0 * result.currentcapacity / result.maxcapacity; } if (callback) { callback(result) } resolve(result); }); } }); }); } exports.battery = battery; // ---------------------------------------------------------------------------------- // 7. File System // ---------------------------------------------------------------------------------- // -------------------------- // FS - devices function fsSize(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } exec("df -lk | grep ^/", function (error, stdout) { let data = []; if (!error) { let lines = stdout.toString().split('\n'); //lines.splice(0, 1); lines.forEach(function (line) { if (line != '') { line = line.replace(/ +/g, " ").split(' '); data.push({ 'fs': line[0], 'size': parseInt(line[1]) * 1024, 'used': parseInt(line[2]) * 1024, 'use': parseFloat((100.0 * line[2] / line[1]).toFixed(2)), 'mount': line[line.length - 1] }) } }); } if (callback) { callback(data) } resolve(data); }); }); }); } exports.fsSize = fsSize; // -------------------------- // FS - speed function fsStats(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { rx: 0, wx: 0, tx: 0, rx_sec: -1, wx_sec: -1, tx_sec: -1, ms: 0 }; if (_linux) { // exec("df -k | grep /dev/", function(error, stdout) { exec("lsblk | grep /", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); let fs_filter = []; lines.forEach(function (line) { if (line != '') { line = line.replace(/[├─│└]+/g, "").trim().split(' '); if (fs_filter.indexOf(line[0]) == -1) fs_filter.push(line[0]) } }); let output = fs_filter.join('|'); exec("cat /proc/diskstats | egrep '" + output + "'", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.trim(); if (line != '') { line = line.replace(/ +/g, " ").split(' '); result.rx += parseInt(line[5]) * 512; result.wx += parseInt(line[9]) * 512; } }); result.tx = result.rx + result.wx; if (_fs_speed && _fs_speed.ms) { result.ms = Date.now() - _fs_speed.ms; result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000); result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000); result.tx_sec = result.rx_sec + result.wx_sec; } else { result.rx_sec = -1; result.wx_sec = -1; result.tx_sec = -1; } _fs_speed.bytes_read = result.rx; _fs_speed.bytes_write = result.wx; _fs_speed.bytes_overall = result.rx + result.wx; _fs_speed.ms = Date.now(); } if (callback) { callback(result) } resolve(result); }) } else { if (callback) { callback(result) } resolve(result); } }) } if (_darwin) { exec("ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n '/IOBlockStorageDriver/,/Statistics/p' | grep 'Statistics' | tr -cd '01234567890,\n' | awk -F',' '{print $3, $10}'", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.trim(); if (line != '') { line = line.split(' '); result.rx += parseInt(line[0]); result.wx += parseInt(line[1]); } }); result.tx = result.rx + result.wx; if (_fs_speed && _fs_speed.ms) { result.ms = Date.now() - _fs_speed.ms; result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000); result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000); result.tx_sec = result.rx_sec + result.wx_sec; } else { result.rx_sec = -1; result.wx_sec = -1; result.tx_sec = -1; } _fs_speed.bytes_read = result.rx; _fs_speed.bytes_write = result.wx; _fs_speed.bytes_overall = result.rx + result.wx; _fs_speed.ms = Date.now(); } if (callback) { callback(result) } resolve(result); }) } }); }); } exports.fsStats = fsStats; function disksIO(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { rIO: 0, wIO: 0, tIO: 0, rIO_sec: -1, wIO_sec: -1, tIO_sec: -1, ms: 0 }; if (_linux) { // prints Block layer statistics for all mounted volumes // var cmd = "for mount in `lsblk | grep / | sed -r 's/│ └─//' | cut -d ' ' -f 1`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done"; // var cmd = "for mount in `lsblk | grep / | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done"; let cmd = "for mount in `lsblk | grep ' disk ' | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done"; exec(cmd, function (error, stdout) { if (!error) { let lines = stdout.split('\n'); lines.forEach(function (line) { // ignore empty lines if (!line) return; // sum r/wIO of all disks to compute all disks IO let stats = line.split(';'); result.rIO += parseInt(stats[0]); result.wIO += parseInt(stats[4]); }); result.tIO = result.rIO + result.wIO; if (_disk_io && _disk_io.ms) { result.ms = Date.now() - _disk_io.ms; result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000); result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000); result.tIO_sec = result.rIO_sec + result.wIO_sec; } else { result.rIO_sec = -1; result.wIO_sec = -1; result.tIO_sec = -1; } _disk_io.rIO = result.rIO; _disk_io.wIO = result.wIO; _disk_io.tIO = result.tIO; _disk_io.ms = Date.now(); if (callback) { callback(result) } resolve(result); } else { if (callback) { callback(result) } resolve(result); } }); } if (_darwin) { exec("ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n '/IOBlockStorageDriver/,/Statistics/p' | grep 'Statistics' | tr -cd '01234567890,\n' | awk -F',' '{print $1, $11}'", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.trim(); if (line != '') { line = line.split(' '); result.rIO += parseInt(line[1]); result.wIO += parseInt(line[0]); } }); result.tIO = result.rIO + result.wIO; if (_disk_io && _disk_io.ms) { result.ms = Date.now() - _disk_io.ms; result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000); result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000); result.tIO_sec = result.rIO_sec + result.wIO_sec; } else { result.rIO_sec = -1; result.wIO_sec = -1; result.tIO_sec = -1; } _disk_io.rIO = result.rIO; _disk_io.wIO = result.wIO; _disk_io.tIO = result.tIO; _disk_io.ms = Date.now(); } if (callback) { callback(result) } resolve(result); }) } }); }); } exports.disksIO = disksIO; // ---------------------------------------------------------------------------------- // 8. Network // ---------------------------------------------------------------------------------- function getDefaultNetworkInterface() { let ifacename = ''; let cmd = (_linux ? "route 2>/dev/null | grep default | awk '{print $8}'" : "route get 0.0.0.0 2>/dev/null | grep interface: | awk '{print $2}'"); let result = execSync(cmd); ifacename = result.toString().split('\n')[0]; if (!ifacename) { // fallback - "first" external interface const sortObject = o => Object.keys(o).sort().reduce((r, k) => (r[k] = o[k], r), {}); let ifaces = sortObject(os.networkInterfaces()); for (let dev in ifaces) { if (ifaces.hasOwnProperty(dev)) { ifaces[dev].forEach(function (details) { if (details && details.internal == false) { ifacename = ifacename || dev; } }) } } } if (ifacename) _default_iface = ifacename; return ifacename; } function networkInterfaceDefault(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } let result = getDefaultNetworkInterface(); if (callback) { callback(result) } resolve(result); }); }); } exports.networkInterfaceDefault = networkInterfaceDefault; // -------------------------- // NET - interfaces function networkInterfaces(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } let ifaces = os.networkInterfaces(); let result = []; for (let dev in ifaces) { let ip4 = ''; let ip6 = ''; if (ifaces.hasOwnProperty(dev)) { ifaces[dev].forEach(function (details) { if (details.family == 'IPv4') { ip4 = details.address } if (details.family == 'IPv6') { ip6 = details.address } }); let internal = (ifaces[dev] && ifaces[dev][0]) ? ifaces[dev][0].internal : null; result.push({ iface: dev, ip4: ip4, ip6: ip6, internal: internal }) } } if (callback) { callback(result) } resolve(result); }); }); } exports.networkInterfaces = networkInterfaces; // -------------------------- // NET - Speed function calcNetworkSpeed(iface, rx, tx) { let rx_sec = -1; let tx_sec = -1; let ms = 0; if (_network[iface] && _network[iface].ms) { ms = Date.now() - _network[iface].ms; rx_sec = (rx - _network[iface].rx) / (ms / 1000); tx_sec = (tx - _network[iface].tx) / (ms / 1000); } else { _network[iface] = {}; } _network[iface].rx = rx; _network[iface].tx = tx; _network[iface].ms = Date.now(); return ({ rx_sec: rx_sec, tx_sec: tx_sec, ms: ms }) } function networkStats(iface, callback) { // fallback - if only callback is given if (isFunction(iface) && !callback) { callback = iface; iface = ''; } return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } _default_iface = _default_iface || getDefaultNetworkInterface(); iface = iface || _default_iface; // (_darwin ? 'en0' : 'eth0'); let result = { iface: iface, operstate: 'unknown', rx: 0, tx: 0, rx_sec: -1, tx_sec: -1, ms: 0 }; let cmd, lines, stats, speed; if (_linux) { if (fs.existsSync('/sys/class/net/' + iface)) { cmd = "cat /sys/class/net/" + iface + "/operstate; " + "cat /sys/class/net/" + iface + "/statistics/rx_bytes; " + "cat /sys/class/net/" + iface + "/statistics/tx_bytes; "; exec(cmd, function (error, stdout) { if (!error) { lines = stdout.toString().split('\n'); result.operstate = lines[0].trim(); result.rx = parseInt(lines[1]); result.tx = parseInt(lines[2]); speed = calcNetworkSpeed(iface, result.rx, result.tx); result.rx_sec = speed.rx_sec; result.tx_sec = speed.tx_sec; result.ms = speed.ms; } if (callback) { callback(result) } resolve(result); }); } else { if (callback) { callback(result) } resolve(result); } } if (_darwin) { cmd = "ifconfig " + iface + " | grep 'status'"; exec(cmd, function (error, stdout) { result.operstate = (stdout.toString().split(':')[1] || '').trim(); result.operstate = (result.operstate || '').toLowerCase(); result.operstate = (result.operstate == 'active' ? 'up' : (result.operstate == 'inactive' ? 'down' : 'unknown')); cmd = "netstat -bI " + iface; exec(cmd, function (error, stdout) { if (!error) { lines = stdout.toString().split('\n'); // if there is less than 2 lines, no information for this interface was found if (lines.length > 1 && lines[1].trim() != '') { // skip header line // use the second line because it is tied to the NIC instead of the ipv4 or ipv6 address stats = lines[1].replace(/ +/g, " ").split(' '); result.rx = parseInt(stats[6]); result.tx = parseInt(stats[9]); speed = calcNetworkSpeed(iface, result.rx, result.tx); result.rx_sec = speed.rx_sec; result.tx_sec = speed.tx_sec; } } if (callback) { callback(result) } resolve(result); }); }); } }); }); } exports.networkStats = networkStats; // -------------------------- // NET - connections (sockets) function networkConnections(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = []; if (_linux) { let cmd = "netstat -tuna | grep 'ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN\\|VERBUNDEN'"; exec(cmd, function (error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.replace(/ +/g, " ").split(' '); if (line.length >= 6) { let localip = line[3]; let localport = ''; let localaddress = line[3].split(':'); if (localaddress.length > 1) { localport = localaddress[localaddress.length - 1]; localaddress.pop(); localip = localaddress.join(':'); } let peerip = line[4]; let peerport = ''; let peeraddress = line[4].split(':'); if (peeraddress.length > 1) { peerport = peeraddress[peeraddress.length - 1]; peeraddress.pop(); peerip = peeraddress.join(':'); } let connstate = line[5]; if (connstate == 'VERBUNDEN') connstate = 'ESTABLISHED'; if (connstate) { result.push({ protocol: line[0], localaddress: localip, localport: localport, peeraddress: peerip, peerport: peerport, state: connstate }) } } }); if (callback) { callback(result) } resolve(result); } else { cmd = "ss -tuna | grep 'ESTAB\\|SYN-SENT\\|SYN-RECV\\|FIN-WAIT1\\|FIN-WAIT2\\|TIME-WAIT\\|CLOSE\\|CLOSE-WAIT\\|LAST-ACK\\|LISTEN\\|CLOSING'"; exec(cmd, function (error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.replace(/ +/g, " ").split(' '); if (line.length >= 6) { let localip = line[4]; let localport = ''; let localaddress = line[4].split(':'); if (localaddress.length > 1) { localport = localaddress[localaddress.length - 1]; localaddress.pop(); localip = localaddress.join(':'); } let peerip = line[5]; let peerport = ''; let peeraddress = line[5].split(':'); if (peeraddress.length > 1) { peerport = peeraddress[peeraddress.length - 1]; peeraddress.pop(); peerip = peeraddress.join(':'); } let connstate = line[1]; if (connstate == 'ESTAB') connstate = 'ESTABLISHED'; if (connstate == 'TIME-WAIT') connstate = 'TIME_WAIT'; if (connstate) { result.push({ protocol: line[0], localaddress: localip, localport: localport, peeraddress: peerip, peerport: peerport, state: connstate }) } } }); } if (callback) { callback(result) } resolve(result); }) } }) } if (_darwin) { let cmd = "netstat -nat | grep 'ESTABLISHED\\|SYN_SENT\\|SYN_RECV\\|FIN_WAIT1\\|FIN_WAIT2\\|TIME_WAIT\\|CLOSE\\|CLOSE_WAIT\\|LAST_ACK\\|LISTEN\\|CLOSING\\|UNKNOWN'"; exec(cmd, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.replace(/ +/g, " ").split(' '); if (line.length >= 6) { let localip = line[3]; let localport = ''; let localaddress = line[3].split('.'); if (localaddress.length > 1) { localport = localaddress[localaddress.length - 1]; localaddress.pop(); localip = localaddress.join('.'); } let peerip = line[4]; let peerport = ''; let peeraddress = line[4].split('.'); if (peeraddress.length > 1) { peerport = peeraddress[peeraddress.length - 1]; peeraddress.pop(); peerip = peeraddress.join('.'); } let connstate = line[5]; if (connstate) { result.push({ protocol: line[0], localaddress: localip, localport: localport, peeraddress: peerip, peerport: peerport, state: connstate }) } } }); if (callback) { callback(result) } resolve(result); } }) } }); }); } exports.networkConnections = networkConnections; // ---------------------------------------------------------------------------------- // 9. Processes // ---------------------------------------------------------------------------------- // -------------------------- // PS - current load - in % function getLoad() { return new Promise((resolve) => { process.nextTick(() => { let result = {}; let loads = os.loadavg().map(function (x) { return x / _cores; }); result.avgload = parseFloat((Math.max.apply(Math, loads)).toFixed(2)); result.currentload = -1; result.currentload_user = -1; result.currentload_nice = -1; result.currentload_system = -1; if (_darwin) { exec("ps -cax -o pcpu", function (error, stdout) { if (!error) { let lines = stdout.toString().replace(/,+/g, ".").split('\n'); lines.shift(); lines.pop(); result.currentload = parseFloat(((lines.reduce(function (pv, cv) { return pv + parseFloat(cv.trim()); }, 0)) / _cores).toFixed(2)); } resolve(result); }); } if (_linux) { exec("cat /proc/stat | grep 'cpu '", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); let parts = lines[0].replace(/ +/g, " ").split(' '); let user = (parts.length >= 2 ? parseInt(parts[1]) : 0); let nice = (parts.length >= 3 ? parseInt(parts[2]) : 0); let system = (parts.length >= 4 ? parseInt(parts[3]) : 0); let idle = (parts.length >= 5 ? parseInt(parts[4]) : 0); let iowait = (parts.length >= 6 ? parseInt(parts[5]) : 0); let irq = (parts.length >= 7 ? parseInt(parts[6]) : 0); let softirq = (parts.length >= 8 ? parseInt(parts[7]) : 0); let steal = (parts.length >= 9 ? parseInt(parts[8]) : 0); let guest = (parts.length >= 10 ? parseInt(parts[9]) : 0); let guest_nice = (parts.length >= 11 ? parseInt(parts[10]) : 0); let all = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice; result.currentload = (user + nice + system - _current_cpu.user - _current_cpu.nice - _current_cpu.system) / (all - _current_cpu.all) * 100; result.currentload_user = (user - _current_cpu.user) / (all - _current_cpu.all) * 100; result.currentload_nice = (nice - _current_cpu.nice) / (all - _current_cpu.all) * 100; result.currentload_system = (system - _current_cpu.system) / (all - _current_cpu.all) * 100; _current_cpu = { user: user, nice: nice, system: system, idle: idle, iowait: iowait, irq: irq, softirq: softirq, steal: steal, guest: guest, guest_nice: guest_nice, all: all } } resolve(result); }); } }); }); } function currentLoad(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } if (_cores == 0) { cores() .then(data => { _cores = data; getLoad().then(result => { if (callback) { callback(result) } resolve(result); }) }) } else { getLoad().then(result => { if (callback) { callback(result) } resolve(result); }) } }); }); } exports.currentLoad = currentLoad; // -------------------------- // PS - full load // since bootup function getFullLoad() { return new Promise((resolve) => { process.nextTick(() => { let result = {}; if (_linux) { if (fs.existsSync('/proc/uptime')) { let output = fs.readFileSync('/proc/uptime').toString(); output = output.replace(/ +/g, " ").split(' '); let uptime = parseFloat(output[0]); let idletime = parseFloat(output[1]) / _cores; result.fullload = (uptime - idletime) / uptime * 100.0; resolve(result); } } if (_darwin) { result.fullload = 0; resolve(result); } }); }); } function fullLoad(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } if (_cores == 0) { cores() .then(data => { _cores = data; getFullLoad().then(result => { if (callback) { callback(result) } resolve(result); }) }) } else { getFullLoad().then(result => { if (callback) { callback(result) } resolve(result); }) } }); }); } exports.fullLoad = fullLoad; // -------------------------- // PS - services // pass a comma separated string with services to check (mysql, apache, postgresql, ...) // this function gives an array back, if the services are running. function services(srv, callback) { // fallback - if only callback is given if (isFunction(srv) && !callback) { callback = srv; srv = ''; } return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } srv = srv.trim().replace(/,+/g, " ").replace(/ +/g, " ").replace(/ +/g, "|"); var srvs = srv.split('|'); var comm = (_darwin) ? "ps -caxm -o pcpu,pmem,comm" : "ps axo pcpu,pmem,comm"; var data = []; if (srv != '' && srvs.length > 0) { exec(comm + " | grep -v grep | egrep '" + srv + "'", function (error, stdout) { if (!error) { var lines = stdout.toString().replace(/ +/g, " ").replace(/,+/g, ".").split('\n'); srvs.forEach(function (srv) { var ps = lines.filter(function (e) { return e.indexOf(srv) != -1 }); data.push({ 'name': srv, 'running': ps.length > 0, 'pcpu': parseFloat((ps.reduce(function (pv, cv) { return pv + parseFloat(cv.trim().split(' ')[0]); }, 0)).toFixed(2)), 'pmem': parseFloat((ps.reduce(function (pv, cv) { return pv + parseFloat(cv.trim().split(' ')[1]); }, 0)).toFixed(2)) }) }); if (callback) { callback(data) } resolve(data); } else { srvs.forEach(function (srv) { data.push({ 'name': srv, 'running': false, 'pcpu': 0, 'pmem': 0 }) }); if (callback) { callback(data) } resolve(data); } }); } else { if (callback) { callback(data) } resolve(data); } }); }); } exports.services = services; // -------------------------- // running processes function processes(callback) { let parsedhead = []; function parseHead(head, rights) { let space = (rights > 0); let count = 1; let from = 0; let to = 0; let result = []; for (let i = 0; i < head.length; i++) { if (count <= rights) { if (head[i] == ' ' && !space) { to = i - 1; result.push({ from: from, to: to+1, cap: head.substring(from, to+1) }); from = to + 2; count++; } space = head[i] == ' '; } else { if (head[i] != ' ' && space) { to = i - 1; if (from < to) { result.push({ from: from, to: to, cap: head.substring(from, to) }); } from = to + 1; count++; } space = head[i] == ' '; } } to = 1000; result.push({ from: from, to: to, cap: head.substring(from, to) }); return result; } function parseLine(line) { let pid = parseInt(line.substring(parsedhead[0].from,parsedhead[0].to)); let pcpu = parseFloat(line.substring(parsedhead[1].from,parsedhead[1].to).replace(/,/g, ".")); let pmem = parseFloat(line.substring(parsedhead[2].from,parsedhead[2].to).replace(/,/g, ".")); let priority = parseInt(line.substring(parsedhead[3].from,parsedhead[3].to)); let vsz = parseInt(line.substring(parsedhead[4].from,parsedhead[4].to)); let rss = parseInt(line.substring(parsedhead[5].from,parsedhead[5].to)); let started = line.substring(parsedhead[6].from,parsedhead[6].to).trim(); let state = line.substring(parsedhead[7].from,parsedhead[7].to).trim(); state = (state[0] == 'R' ? 'running': (state[0] == 'S' ? 'sleeping': (state[0] == 'T' ? 'stopped': (state[0] == 'W' ? 'paging': (state[0] == 'X' ? 'dead': (state[0] == 'Z' ? 'zombie': ((state[0] == 'D' || state[0] == 'U') ? 'blocked': 'unknown'))))))); let tty = line.substring(parsedhead[8].from,parsedhead[8].to).trim(); if (tty == '?' || tty == '??') tty = ''; let user = line.substring(parsedhead[9].from,parsedhead[9].to).trim(); let command = line.substring(parsedhead[10].from,parsedhead[10].to).trim().replace(/\[/g, "").replace(/]/g, ""); return ({ pid: pid, pcpu: pcpu, pmem: pmem, priority: priority, mem_vsz: vsz, mem_rss: rss, started: started, state: state, tty: tty, user: user, command: command }) } function parseProcesses(lines) { let result = []; if (lines.length > 1) { let head = lines[0]; parsedhead = parseHead(head, 7); lines.shift(); lines.forEach(function (line) { if (line.trim() != '') { result.push(parseLine(line)); } }); } return result; } function parseProcStat(line) { let parts = line.replace(/ +/g, " ").split(' '); let user = (parts.length >= 2 ? parseInt(parts[1]) : 0); let nice = (parts.length >= 3 ? parseInt(parts[2]) : 0); let system = (parts.length >= 4 ? parseInt(parts[3]) : 0); let idle = (parts.length >= 5 ? parseInt(parts[4]) : 0); let iowait = (parts.length >= 6 ? parseInt(parts[5]) : 0); let irq = (parts.length >= 7 ? parseInt(parts[6]) : 0); let softirq = (parts.length >= 8 ? parseInt(parts[7]) : 0); let steal = (parts.length >= 9 ? parseInt(parts[8]) : 0); let guest = (parts.length >= 10 ? parseInt(parts[9]) : 0); let guest_nice = (parts.length >= 11 ? parseInt(parts[10]) : 0); return user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice; } function parseProcPidStat(line, all) { let parts = line.replace(/ +/g, " ").split(' '); if (parts.length >= 17) { let pid = parseInt(parts[0]); let utime = parseInt(parts[13]); let stime = parseInt(parts[14]); let cutime = parseInt(parts[15]); let cstime = parseInt(parts[16]); // calc let pcpuu = 0; let pcpus = 0; if (_process_cpu.all > 0 && _process_cpu.list[pid]) { pcpuu = (utime + cutime - _process_cpu.list[pid].utime - _process_cpu.list[pid].cutime) / (all - _process_cpu.all) * 100; // user pcpus = (stime + cstime - _process_cpu.list[pid].stime - _process_cpu.list[pid].cstime) / (all - _process_cpu.all) * 100; // system } else { pcpuu = (utime + cutime) / (all) * 100; // user pcpus = (stime + cstime) / (all) * 100; // system } return { pid: pid, utime: utime, stime: stime, cutime: cutime, cstime: cstime, pcpuu: pcpuu, pcpus: pcpus } } else { return { pid: 0, utime: 0, stime: 0, cutime: 0, cstime: 0, pcpuu: 0, pcpus: 0 } } } return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } let result = { all: 0, running: 0, blocked: 0, sleeping: 0, list: [] }; let cmd = ""; if (_linux) cmd = "ps axo pid:10,pcpu:6,pmem:6,pri:5,vsz:10,rss:10,start:20,state:20,tty:20,user:20,command"; if (_darwin) cmd = "ps acxo pid,pcpu,pmem,pri,vsz,rss,start,state,tty,user,command -r"; exec(cmd, function (error, stdout) { if (!error) { result.list = parseProcesses(stdout.toString().split('\n')); result.all = result.list.length; result.running = result.list.filter(function (e) { return e.state == 'running' }).length; result.blocked = result.list.filter(function (e) { return e.state == 'blocked' }).length; result.sleeping = result.list.filter(function (e) { return e.state == 'sleeping' }).length; if (_linux) { // calc process_cpu - ps is not accurate in linux! cmd = "cat /proc/stat | grep 'cpu '"; for (let i=0; i< result.list.length; i++) { cmd += (';cat /proc/' + result.list[i].pid + '/stat') } exec(cmd, function (error, stdout) { let curr_processes = stdout.toString().split('\n'); // first line (all - /proc/stat) let all = parseProcStat(curr_processes.shift()); // process let list_new = {}; let resultProcess = {}; for (let i=0; i< curr_processes.length; i++) { resultProcess = parseProcPidStat(curr_processes[i], all); if (resultProcess.pid) { // store pcpu in outer array let listPos = result.list.map(function(e) { return e.pid; }).indexOf(resultProcess.pid); if (listPos >= 0) { result.list[listPos].pcpu = resultProcess.pcpuu + resultProcess.pcpus } // save new values list_new[resultProcess.pid] = { pcpuu: resultProcess.pcpuu, pcpus: resultProcess.pcpus, utime: resultProcess.utime, stime: resultProcess.stime, cutime: resultProcess.cutime, cstime: resultProcess.cstime } } } // store old values _process_cpu.all = all; _process_cpu.list = list_new; _process_cpu.ms = Date.now() - _process_cpu.ms; if (callback) { callback(result) } resolve(result); }) } else { if (callback) { callback(result) } resolve(result); } } }); }); }); } exports.processes = processes; // -------------------------- // PS - process load // get detailed information about a certain process // (PID, CPU-Usage %, Mem-Usage %) function processLoad(proc, callback) { // fallback - if only callback is given if (isFunction(proc) && !callback) { callback = proc; proc = ''; } return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { 'proc': proc, 'pid': -1, 'cpu': 0, 'mem': 0 }; if (proc) { exec("ps aux | grep " + proc + " | grep -v grep", function (error, stdout) { if (!error) { let data = stdout.replace(/ +/g, " ").split(' '); if (data.length > 2) { result = { 'proc': proc, 'pid': data[1], 'cpu': parseFloat(data[2].replace(',', '.')), 'mem': parseFloat(data[3].replace(',', '.')) } } } if (callback) { callback(result) } resolve(result); }); } else { if (callback) { callback(result) } resolve(result); } }); }); } exports.processLoad = processLoad; // ---------------------------------------------------------------------------------- // 10. Users/Sessions // ---------------------------------------------------------------------------------- // -------------------------- // array of users online = sessions function parseUsers1(lines) { var result = []; var result_who = []; var result_w = {}; var w_first = true; var w_header = []; var w_pos = []; var w_headerline = ''; var who_line = {}; var is_whopart = true; lines.forEach(function (line) { if (line == '---') { is_whopart = false; } else { var l = line.replace(/ +/g, " ").split(' '); // who part if (is_whopart) { result_who.push({ user: l[0], tty: l[1], date: l[2], time: l[3], ip: (l && l.length > 4) ? l[4].replace(/\(/g, "").replace(/\)/g, "") : '' }) } else { // w part if (w_first) { // header w_header = l; w_headerline = line; w_header.forEach(function(item) { w_pos.push(line.indexOf(item)) }); w_first = false; } else { // split by w_pos result_w.user = line.substring(w_pos[0], w_pos[1]-1).trim(); result_w.tty = line.substring(w_pos[1], w_pos[2]-1).trim(); result_w.ip = line.substring(w_pos[2], w_pos[3]-1).replace(/\(/g, "").replace(/\)/g, "").trim(); result_w.command = line.substring(w_pos[7], 1000).trim(); // find corresponding 'who' line who_line = result_who.filter(function(obj) { return (obj.user.substring(0,8).trim() == result_w.user && obj.tty == result_w.tty) }); if (who_line.length == 1) { result.push({ user: who_line[0].user, tty: who_line[0].tty, date: who_line[0].date, time: who_line[0].time, ip: who_line[0].ip, command: result_w.command }) } } } } }); return result; } function parseUsers2(lines) { var result = []; var result_who = []; var result_w = {}; var who_line = {}; var is_whopart = true; lines.forEach(function (line) { if (line == '---') { is_whopart = false; } else { var l = line.replace(/ +/g, " ").split(' '); // who part if (is_whopart) { result_who.push({ user: l[0], tty: l[1], date: ("" + new Date().getFullYear()) + '-' + ("0" + ("JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC".indexOf(l[2].toUpperCase()) / 3 + 1)).slice(-2) + '-' + ("0" + l[3]).slice(-2), time: l[4], }) } else { // w part // split by w_pos result_w.user = l[0]; result_w.tty = l[1]; result_w.ip = (l[2] != '-') ? l[2] : ''; result_w.command = l.slice(5, 1000).join(' '); // find corresponding 'who' line who_line = result_who.filter(function(obj) { return (obj.user == result_w.user && (obj.tty.substring(3,1000) == result_w.tty || obj.tty == result_w.tty)) }); if (who_line.length == 1) { result.push({ user: who_line[0].user, tty: who_line[0].tty, date: who_line[0].date, time: who_line[0].time, ip: result_w.ip, command: result_w.command }) } } } }); return result; } function users(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = []; // linux if (_linux) { exec("who --ips; echo '---'; w | tail -n +2", function (error, stdout) { if (!error) { // lines / split var lines = stdout.toString().split('\n'); result = parseUsers1(lines); if (result.length == 0) { exec("who; echo '---'; w | tail -n +2", function (error, stdout) { if (!error) { // lines / split lines = stdout.toString().split('\n'); result = parseUsers1(lines); if (callback) { callback(result) } resolve(result); } else { if (callback) { callback(result) } resolve(result); } }); } else { if (callback) { callback(result) } resolve(result); } } else { if (callback) { callback(result) } resolve(result); } }); } if (_darwin) { exec("who; echo '---'; w -ih", function (error, stdout) { if (!error) { // lines / split var lines = stdout.toString().split('\n'); result = parseUsers2(lines); if (callback) { callback(result) } resolve(result); } else { if (callback) { callback(result) } resolve(result); } }); } }); }); } exports.users = users; // ---------------------------------------------------------------------------------- // 11. Internet // ---------------------------------------------------------------------------------- // -------------------------- // check if external site is available function inetChecksite(url, callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { url: url, ok: false, status: 404, ms: -1 }; if (url) { var t = Date.now(); let args = " -I --connect-timeout 5 -m 5 " + url + " 2>/dev/null | head -n 1 | cut -d ' ' -f2"; let cmd = "curl"; exec(cmd + args, function (error, stdout) { let statusCode = parseInt(stdout.toString()); result.status = statusCode || 404; result.ok = !error && (statusCode == 200 || statusCode == 301 || statusCode == 302 || statusCode == 304); result.ms = (result.ok ? Date.now() - t : -1); if (callback) { callback(result) } resolve(result); }) } else { if (callback) { callback(result) } resolve(result); } }); }); } exports.inetChecksite = inetChecksite; // -------------------------- // check inet latency function inetLatency(host, callback) { // fallback - if only callback is given if (isFunction(host) && !callback) { callback = host; host = ''; } host = host || '8.8.8.8'; return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var t = Date.now(); let cmd; if (_linux) { cmd = "ping -c 2 -w 3 " + host + " | grep rtt | cut -d'/' -f4 | awk '{ print $3 }'"; } if (_darwin) { cmd = "ping -c 2 -t 3 " + host + " | grep avg | cut -d'/' -f4 | awk '{ print $3 }'"; } exec(cmd, function (error, stdout) { let result = -1; if (!error) { result = parseFloat(stdout.toString()); } if (callback) { callback(result) } resolve(result); }) }); }); } exports.inetLatency = inetLatency; // ---------------------------------------------------------------------------------- // 12. Docker // ---------------------------------------------------------------------------------- // -------------------------- // get containers (parameter all: get also inactive/exited containers) function dockerContainers(all, callback) { function inContainers(containers, id) { let filtered = containers.filter(obj => { /** * @namespace * @property {string} Id */ return (obj.Id && (obj.Id == id)) }); return (filtered.length > 0); } // fallback - if only callback is given if (isFunction(all) && !callback) { callback = all; all = false; } all = all || false; var result = []; return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } let cmd = "curl --unix-socket /var/run/docker.sock http:/containers/json" + (all ? "?all=1": ""); exec(cmd, function (error, stdout) { if (!error) { try { let jsonString = stdout.toString(); var docker_containers = JSON.parse(jsonString); if (docker_containers && Object.prototype.toString.call(docker_containers) === '[object Array]' && docker_containers.length > 0) { docker_containers.forEach(function (element) { /** * @namespace * @property {string} Id * @property {string} Name * @property {string} Image * @property {string} ImageID * @property {string} Command * @property {number} Created * @property {string} State * @property {Array} Names * @property {Array} Ports * @property {Array} Mounts */ if (element.Names && Object.prototype.toString.call(element.Names) === '[object Array]' && element.Names.length > 0) { element.Name = element.Names[0].replace(/^\/|\/$/g, ''); } result.push({ id: element.Id, name: element.Name, image: element.Image, imageID: element.ImageID, command: element.Command, created: element.Created, state: element.State, ports: element.Ports, mounts: element.Mounts, // hostconfig: element.HostConfig, // network: element.NetworkSettings }) }); } } catch (err) { } } // GC in _docker_container_stats for (var key in _docker_container_stats) { if (_docker_container_stats.hasOwnProperty(key)) { if (!inContainers(docker_containers, key)) delete _docker_container_stats[key]; } } if (callback) { callback(result) } resolve(result); }); }); }); } exports.dockerContainers = dockerContainers; // -------------------------- // helper functions for calculation of docker stats function docker_calcCPUPercent(cpu_stats, id) { /** * @namespace * @property {object} cpu_usage * @property {number} cpu_usage.total_usage * @property {number} system_cpu_usage * @property {object} cpu_usage * @property {Array} cpu_usage.percpu_usage */ var cpuPercent = 0.0; // calculate the change for the cpu usage of the container in between readings var cpuDelta = cpu_stats.cpu_usage.total_usage - (_docker_container_stats[id] && _docker_container_stats[id].prev_CPU ? _docker_container_stats[id].prev_CPU : 0); // calculate the change for the entire system between readings var systemDelta = cpu_stats.system_cpu_usage - (_docker_container_stats[id] && _docker_container_stats[id].prev_system ? _docker_container_stats[id].prev_system : 0); if (systemDelta > 0.0 && cpuDelta > 0.0) { cpuPercent = (cpuDelta / systemDelta) * cpu_stats.cpu_usage.percpu_usage.length * 100.0; } if (!_docker_container_stats[id]) _docker_container_stats[id] = {}; _docker_container_stats[id].prev_CPU = cpu_stats.cpu_usage.total_usage; _docker_container_stats[id].prev_system = cpu_stats.system_cpu_usage; return cpuPercent } function docker_calcNetworkIO(networks) { var rx; var tx; for (var key in networks) { // skip loop if the property is from prototype if (!networks.hasOwnProperty(key)) continue; /** * @namespace * @property {number} rx_bytes * @property {number} tx_bytes */ var obj = networks[key]; rx =+ obj.rx_bytes; tx =+ obj.tx_bytes; } return { rx: rx, tx: tx } } function docker_calcBlockIO(blkio_stats) { let result = { r: 0, w: 0 }; /** * @namespace * @property {Array} io_service_bytes_recursive */ if (blkio_stats && blkio_stats.io_service_bytes_recursive && Object.prototype.toString.call( blkio_stats.io_service_bytes_recursive ) === '[object Array]' && blkio_stats.io_service_bytes_recursive.length > 0) { blkio_stats.io_service_bytes_recursive.forEach( function(element) { /** * @namespace * @property {string} op * @property {number} value */ if (element.op && element.op.toLowerCase() == 'read' && element.value) { result.r += element.value; } if (element.op && element.op.toLowerCase() == 'write' && element.value) { result.w += element.value; } }) } return result; } // -------------------------- // container Stats (for one container) function dockerContainerStats(containerID, callback) { containerID = containerID || ''; var result = { id: containerID, mem_usage: 0, mem_limit: 0, mem_percent: 0, cpu_percent: 0, pids: 0, netIO: { rx: 0, wx: 0 }, blockIO: { r: 0, w: 0 } }; return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } if (containerID) { let cmd = "curl --unix-socket /var/run/docker.sock http:/containers/" + containerID + "/stats?stream=0"; exec(cmd, function (error, stdout) { if (!error) { let jsonString = stdout.toString(); try { let stats = JSON.parse(jsonString); /** * @namespace * @property {Object} memory_stats * @property {number} memory_stats.usage * @property {number} memory_stats.limit * @property {Object} cpu_stats * @property {Object} pids_stats * @property {number} pids_stats.current * @property {Object} networks * @property {Object} blkio_stats */ if (!stats.message) { result.mem_usage = (stats.memory_stats && stats.memory_stats.usage ? stats.memory_stats.usage : 0); result.mem_limit = (stats.memory_stats && stats.memory_stats.limit ? stats.memory_stats.limit : 0); result.mem_percent = (stats.memory_stats && stats.memory_stats.usage && stats.memory_stats.limit ? stats.memory_stats.usage / stats.memory_stats.limit * 100.0 : 0); result.cpu_percent = (stats.cpu_stats ? docker_calcCPUPercent(stats.cpu_stats, containerID) : 0); result.pids = (stats.pids_stats && stats.pids_stats.current ? stats.pids_stats.current : 0); if (stats.networks) result.netIO = docker_calcNetworkIO(stats.networks); if (stats.blkio_stats)result.blockIO = docker_calcBlockIO(stats.blkio_stats); } } catch (err) { } } if (callback) { callback(result) } resolve(result); }); } else { if (callback) { callback(result) } resolve(result); } }); }); } exports.dockerContainerStats = dockerContainerStats; function dockerAll(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } dockerContainers(true).then(result => { if (result && Object.prototype.toString.call( result ) === '[object Array]' && result.length > 0) { var l = result.length; result.forEach( function(element) { dockerContainerStats(element.id).then(res => { // include stats in array element.mem_usage = res.mem_usage; element.mem_limit = res.mem_limit; element.mem_percent = res.mem_percent; element.cpu_percent = res.cpu_percent; element.pids = res.pids; element.netIO = res.netIO; element.blockIO = res.blockIO; // all done?? l -= 1; if (l == 0) { if (callback) { callback(result) } resolve(result); } }) }) } else { if (callback) { callback(result) } resolve(result); } }) }); }); } exports.dockerAll = dockerAll; // ---------------------------------------------------------------------------------- // 13. get all // ---------------------------------------------------------------------------------- // -------------------------- // get static data - they should not change until restarted function getStaticData(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var data = {}; data.version = version(); system().then(res => { data.system = res; osInfo().then(res => { data.os = res; cpu().then(res => { data.cpu = res; networkInterfaces().then(res => { data.net = res; if (callback) { callback(data) } resolve(data); }) }) }) }) }); }); } exports.getStaticData = getStaticData; // -------------------------- // get all dynamic data - e.g. for monitoring agents // may take some seconds to get all data // -------------------------- // 2 additional parameters needed // - srv: comma separated list of services to monitor e.g. "mysql, apache, postgresql" // - iface: define network interface for which you like to monitor network speed e.g. "eth0" function getDynamicData(srv, iface, callback) { if (isFunction(iface)) { callback = iface; iface = ''; } if (isFunction(srv)) { callback = srv; srv = ''; } return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } iface = iface || getDefaultNetworkInterface(); srv = srv || ''; // use closure to track ƒ completion var functionProcessed = (function () { var totalFunctions = 14; return function () { if (--totalFunctions === 0) { if (callback) { callback(data) } resolve(data); } }; })(); // var totalFunctions = 14; // function functionProcessed() { // if (--totalFunctions === 0) { // if (callback) { callback(data) } // resolve(data); // } // } var data = {}; // get time data.time = time(); /** * @namespace * @property {Object} versions * @property {string} versions.node * @property {string} versions.v8 */ data.node = process.versions.node; data.v8 = process.versions.v8; cpuCurrentspeed().then(res => { data.cpuCurrentspeed = res; functionProcessed(); }); users().then(res => { data.users = res; functionProcessed(); }); processes().then(res => { data.processes = res; functionProcessed(); }); currentLoad().then(res => { data.currentLoad = res; functionProcessed(); }); cpuTemperature().then(res => { data.temp = res; functionProcessed(); }); networkStats(iface).then(res => { data.networkStats = res; functionProcessed(); }); networkConnections().then(res => { data.networkConnections = res; functionProcessed(); }); mem().then(res => { data.mem = res; functionProcessed(); }); battery().then(res => { data.battery = res; functionProcessed(); }); services(srv).then(res => { data.services = res; functionProcessed(); }); fsSize().then(res => { data.fsSize = res; functionProcessed(); }); fsStats().then(res => { data.fsStats = res; functionProcessed(); }); disksIO().then(res => { data.disksIO = res; functionProcessed(); }); inetLatency().then(res => { data.inetLatency = res; functionProcessed(); }); }); }); } exports.getDynamicData = getDynamicData; // -------------------------- // get all data at once // -------------------------- // 2 additional parameters needed // - srv: comma separated list of services to monitor e.g. "mysql, apache, postgresql" // - iface: define network interface for which you like to monitor network speed e.g. "eth0" function getAllData(srv, iface, callback) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var data = {}; getStaticData().then(res => { data = res; getDynamicData(srv, iface).then(res => { for (var key in res) { if (res.hasOwnProperty(key)) { data[key] = res[key]; } } if (callback) { callback(data) } resolve(data); }); }) }); }); } exports.getAllData = getAllData;