diff --git a/README.md b/README.md index 1590df2..8e655cf 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ si.cpu() ### Latest Activity +- Version 3.6.0: added versions (kernel, ssl, node, npm, pm2, ...). - Version 3.5.0: added graphics info (controller and display). - Version 3.4.0: rewritten currentLoad and CPU load for processes (linux). This is now much more accurate. - Version 3.3.0: added process list. Get full process list including details like cpu and mem usage, status, command, ... @@ -69,6 +70,7 @@ Here all changes more detailed: New Functions +- `versions`: returns object of versions - kernel, ssl, node, npm, ...(new in version 3.6) - `graphics`: returns arrays of graphics controllers and displays (new in version 3.5) - `networkInterfaceDefault`: returns default network interface (new in version 3.4) - `processes`: now returns also a process list with all process details (new in version 3.3) @@ -188,6 +190,7 @@ This library is splitted in several sections: | - arch | X | X | same as os.arch() | | - hostname | X | X | same as os.hostname() | | - logofile | X | X | e.g. 'apple', 'debian', 'fedora', ... | +| si.versions(cb) | X | X | Version information (kernel, ssl, node, ...) | | si.cpu(cb) | X | X | CPU information| | - manufacturer | X | X | e.g. 'Intel(R)' | | - brand | X | X | e.g. 'Core(TM)2 Duo' | @@ -394,6 +397,7 @@ I am happy to discuss any comments and suggestions. Please feel free to contact | Version | Date | Comment | | -------------- | -------------- | -------- | +| 3.6.0 | 2016-09-14 | added versions (kernel, ssl, node, npm, pm2, ...) | | 3.5.1 | 2016-09-14 | bugfix graphics info | | 3.5.0 | 2016-09-14 | added graphics info (controller, display) | | 3.4.4 | 2016-09-02 | tiny fixes system.model, getDefaultNetworkInterface | diff --git a/lib/index.js b/lib/index.js index a05e9dc..4ae5f07 100644 --- a/lib/index.js +++ b/lib/index.js @@ -81,6 +81,7 @@ // -------------------------------- // // version date comment +// 3.6.0 2016-09-16 added versions (kernel, ssl, node, npm, pm2, ...) // 3.5.1 2016-09-14 bugfix graphics info // 3.5.0 2016-09-14 added graphics info (controller, display) // 3.4.4 2016-09-02 tiny fixes system.model, getDefaultNetworkInterface @@ -391,25 +392,25 @@ function osInfo(callback) { 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, ''); + /** + * @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); @@ -435,6 +436,45 @@ function osInfo(callback) { exports.osInfo = osInfo; +function versions(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 = { + kernel: os.release(), + node: process.versions.node, + v8: process.versions.v8, + npm: '', + pm2: '', + openssl: process.versions.openssl + }; + let lines = []; + exec("npm -v", function (error, stdout) { + if (!error) { + result.npm = stdout.toString().split('\n')[0]; + } + exec("pm2 -v", function (error, stdout) { + if (!error) { + lines = stdout.toString().split('\n'); + if (lines.length >= 2) { + result.pm2 = lines[lines.length-2]; + } + } + if (callback) { callback(result) } + resolve(result); + }); + }); + }); + }); +} + +exports.versions = versions; + // ---------------------------------------------------------------------------------- // 4. CPU // ---------------------------------------------------------------------------------- @@ -816,7 +856,6 @@ function mem(callback) { exports.mem = mem; - // ---------------------------------------------------------------------------------- // 6. Battery // ---------------------------------------------------------------------------------- @@ -1013,8 +1052,7 @@ function graphics(callback) { is_vga = true; let endpos = lines[i].search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/); let parts = lines[i].substr(vgapos, endpos - vgapos).split(':'); - if (parts.length > 1) - { + if (parts.length > 1) { parts[1] = parts[1].trim(); if (parts[1].toLowerCase().indexOf('corporation')) { currentController.vendor = parts[1].substr(0, parts[1].toLowerCase().indexOf('corporation') + 11).trim(); @@ -1059,30 +1097,30 @@ function graphics(callback) { let result = {}; // find first "Detailed Timing Description" let start = 108; - if (edid.substr(start,6) == '000000') { + if (edid.substr(start, 6) == '000000') { start += 36; } - if (edid.substr(start,6) == '000000') { + if (edid.substr(start, 6) == '000000') { start += 36; } - if (edid.substr(start,6) == '000000') { + if (edid.substr(start, 6) == '000000') { start += 36; } - if (edid.substr(start,6) == '000000') { + if (edid.substr(start, 6) == '000000') { start += 36; } - result.resolutionx = parseInt('0x0' + edid.substr(start + 8,1) + edid.substr(start + 4,2)); - result.resolutiony = parseInt('0x0' + edid.substr(start + 14,1) + edid.substr(start + 10,2)); - result.sizex = parseInt('0x0' + edid.substr(start + 28,1) + edid.substr(start + 24,2)); - result.sizey = parseInt('0x0' + edid.substr(start + 29,1) + edid.substr(start + 26,2)); + result.resolutionx = parseInt('0x0' + edid.substr(start + 8, 1) + edid.substr(start + 4, 2)); + result.resolutiony = parseInt('0x0' + edid.substr(start + 14, 1) + edid.substr(start + 10, 2)); + result.sizex = parseInt('0x0' + edid.substr(start + 28, 1) + edid.substr(start + 24, 2)); + result.sizey = parseInt('0x0' + edid.substr(start + 29, 1) + edid.substr(start + 26, 2)); // monitor name start = edid.indexOf('000000fc00'); // find first "Monitor Description Data" if (start >= 0) { - let model_raw = edid.substr(start+10, 26); + let model_raw = edid.substr(start + 10, 26); if (model_raw.indexOf('0a') != -1) { - model_raw = model_raw.substr(0,model_raw.indexOf('0a')) + model_raw = model_raw.substr(0, model_raw.indexOf('0a')) } - result.model = model_raw.match(/.{1,2}/g).map(function(v){ + result.model = model_raw.match(/.{1,2}/g).map(function (v) { return String.fromCharCode(parseInt(v, 16)); }).join(''); } else { @@ -1099,7 +1137,7 @@ function graphics(callback) { let start = 0; for (let i = 1; i < lines.length; i++) { // start with second line if ('' != lines[i].trim()) { - if (' ' != lines[i][0] && '\t' != lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') != -1 ) { // first line of new entry + if (' ' != lines[i][0] && '\t' != lines[i][0] && lines[i].toLowerCase().indexOf(' connected ') != -1) { // first line of new entry if (Object.keys(currentDisplay).length > 0) { // push last display to array displays.push(currentDisplay); currentDisplay = {}; @@ -1126,7 +1164,7 @@ function graphics(callback) { is_edid = false; } } - if (lines[i].toLowerCase().indexOf('edid:') != -1 ) { + if (lines[i].toLowerCase().indexOf('edid:') != -1) { is_edid = true; start = lines[i].search(/\S|$/); } @@ -2072,8 +2110,8 @@ function processes(callback) { to = i - 1; result.push({ from: from, - to: to+1, - cap: head.substring(from, to+1) + to: to + 1, + cap: head.substring(from, to + 1) }); from = to + 2; count++; @@ -2106,19 +2144,19 @@ function processes(callback) { } 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(); + 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, ""); + 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, @@ -2164,6 +2202,7 @@ function processes(callback) { 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) { @@ -2240,7 +2279,7 @@ function processes(callback) { 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++) { + for (let i = 0; i < result.list.length; i++) { cmd += (';cat /proc/' + result.list[i].pid + '/stat') } exec(cmd, function (error, stdout) { @@ -2252,13 +2291,13 @@ function processes(callback) { // process let list_new = {}; let resultProcess = {}; - for (let i=0; i< curr_processes.length; i++) { + 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); + let listPos = result.list.map(function (e) { return e.pid; }).indexOf(resultProcess.pid); if (listPos >= 0) { result.list[listPos].pcpu = resultProcess.pcpuu + resultProcess.pcpus } @@ -2387,19 +2426,19 @@ function parseUsers1(lines) { if (w_first) { // header w_header = l; w_headerline = line; - w_header.forEach(function(item) { + 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.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) + 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({ @@ -2447,8 +2486,8 @@ function parseUsers2(lines) { 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)) + 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({ @@ -2525,7 +2564,6 @@ function users(callback) { }); } - }); }); } @@ -2640,6 +2678,7 @@ function dockerContainers(all, callback) { }); return (filtered.length > 0); } + // fallback - if only callback is given if (isFunction(all) && !callback) { callback = all; @@ -2655,7 +2694,7 @@ function dockerContainers(all, callback) { if (callback) { callback(NOT_SUPPORTED) } reject(error); } - let cmd = "curl --unix-socket /var/run/docker.sock http:/containers/json" + (all ? "?all=1": ""); + let cmd = "curl --unix-socket /var/run/docker.sock http:/containers/json" + (all ? "?all=1" : ""); exec(cmd, function (error, stdout) { if (!error) { try { @@ -2756,8 +2795,8 @@ function docker_calcNetworkIO(networks) { * @property {number} tx_bytes */ var obj = networks[key]; - rx =+ obj.rx_bytes; - tx =+ obj.tx_bytes; + rx = +obj.rx_bytes; + tx = +obj.tx_bytes; } return { rx: rx, @@ -2775,8 +2814,8 @@ function docker_calcBlockIO(blkio_stats) { * @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) { + 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 @@ -2875,9 +2914,9 @@ function dockerAll(callback) { reject(error); } dockerContainers(true).then(result => { - if (result && Object.prototype.toString.call( result ) === '[object Array]' && result.length > 0) { + if (result && Object.prototype.toString.call(result) === '[object Array]' && result.length > 0) { var l = result.length; - result.forEach( function(element) { + result.forEach(function (element) { dockerContainerStats(element.id).then(res => { // include stats in array element.mem_usage = res.mem_usage; @@ -2932,14 +2971,17 @@ function getStaticData(callback) { data.system = res; osInfo().then(res => { data.os = res; - cpu().then(res => { - data.cpu = res; - graphics().then(res => { - data.graphics = res; - networkInterfaces().then(res => { - data.net = res; - if (callback) { callback(data) } - resolve(data); + versions().then(res => { + data.versions = res; + cpu().then(res => { + data.cpu = res; + graphics().then(res => { + data.graphics = res; + networkInterfaces().then(res => { + data.net = res; + if (callback) { callback(data) } + resolve(data); + }) }) }) })