'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. File System // 7. Network // 8. Processes // 9. Users/Sessions // 10. Internet // 11. Docker // 12. 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.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 , 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 = {}; 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: '-', 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 = 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 = 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 == '-' && result.model == '-' && 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); }) } 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. 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 -d [:alpha:] | tr -d [:punct:] | awk '{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 -d [:alpha:] | tr -d [:punct:] | awk '{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[0]); result.wIO += parseInt(line[1]); } }); 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; // ---------------------------------------------------------------------------------- // 7. Network // ---------------------------------------------------------------------------------- function getFirstExternalNetworkInterface() { let ifacename = ''; let ifaces = os.networkInterfaces(); for (let dev in ifaces) { if (ifaces.hasOwnProperty(dev)) { ifaces[dev].forEach(function (details) { if (details && details.internal == false) { ifacename = ifacename || dev; } }) } } return ifacename; } // -------------------------- // 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 || getFirstExternalNetworkInterface(); 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; // ---------------------------------------------------------------------------------- // 8. 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; let cmd = (_darwin) ? "ps -cax -o pcpu" : "ps axo pcpu"; exec(cmd, 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); }); }); }); } 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) { return new Promise((resolve, reject) => { process.nextTick(() => { if (_windows) { let error = new Error(NOT_SUPPORTED); if (callback) { callback(NOT_SUPPORTED) } reject(error); } var result = { all: 0, running: 0, blocked: 0 }; exec("ps aux | grep -v 'ps aux' | wc -l", function (error, stdout) { if (!error) { result.all = parseInt(stdout.toString()); if (_darwin) { exec("ps axo state | grep 'R' | wc -l; ps axo state | grep 'U' | wc -l", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); result.running = parseInt(lines[0]); result.blocked = parseInt(lines[1]); } if (callback) { callback(result) } resolve(result); }) } if (_linux) { exec("cat /proc/stat | grep procs_", function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { if (line.toUpperCase().indexOf('PROCS_RUNNING') != -1) { result.running = parseInt(line.replace(/ +/g, " ").split(' ')[1]); } if (line.toUpperCase().indexOf('PROCS_BLOCKED') != -1) { result.blocked = parseInt(line.replace(/ +/g, " ").split(' ')[1]); } }) } 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; // ---------------------------------------------------------------------------------- // 9. 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; // console.log(w_pos); // console.log(result_who); } 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(); // console.log(result_w); // 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; // ---------------------------------------------------------------------------------- // 10. 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; // ---------------------------------------------------------------------------------- // 11. 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); // console.log(docker_containers) 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 */ //console.log(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; // ---------------------------------------------------------------------------------- // 12. 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 || getFirstExternalNetworkInterface(); srv = srv || ''; // use closure to track ƒ completion var functionProcessed = (function () { var totalFunctions = 13; return function () { if (--totalFunctions === 0) { if (callback) { callback(data) } resolve(data); } }; })(); // var totalFunctions = 12; // 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(); }); 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;