diff --git a/CHANGELOG.md b/CHANGELOG.md index 3699c43..4d75fe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,7 @@ Other changes | Version | Date | Comment | | -------------- | -------------- | -------- | +| 3.25.0 | 2017-08-07 | improved windows support `networkStats()`, `cpuCache()`, bug fix `getStaticData()` | | 3.24.0 | 2017-08-05 | extended windows support `networkStats()`, `networkConnections()` | | 3.23.7 | 2017-07-11 | bug fix `diskLayout()` | | 3.23.6 | 2017-07-11 | added `cpuFlags()` to `getStaticData()`, bug fix `graphics()` (Win) | diff --git a/README.md b/README.md index 20a853a..cdd3537 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ si.cpu() ## News and Changes ### Latest Activity +- Version 3.25.0: improved windows support `networkStats()`, `cpuCache()`, bug fix `getStaticData()` - Version 3.24.0: extended windows support `networkStats()`, `networkConnections()` - Version 3.23.0: added `memLayout`, `diskLayout`, extended windows support (`inetChecksite`) - Version 3.22.0: extended windows support (`users`, `inetLatency`) @@ -128,8 +129,8 @@ I also created a nice little command line tool called [mmon][mmon-github-url] ( | | stepping | X | X | X | Processor Stepping | | | revision | X | X | X | Revision | | | cache | X | X | X | cache in bytes (object) | -| | cache.l1d | X | X | | L1D size | -| | cache.l1i | X | X | | L1I size | +| | cache.l1d | X | X | | L1D (data) size | +| | cache.l1i | X | X | | L1I (instruction) size | | | cache.l2 | X | X | X | L2 size | | | cache.l3 | X | X | X | L3 size | | si.cpuFlags(cb) | : string | X | X | | CPU flags| @@ -146,9 +147,9 @@ I also created a nice little command line tool called [mmon][mmon-github-url] ( | | main | X | X | X | main temperature | | | cores | X | X | X | array of temperatures | | | max | X | X | X | max temperature | -| si.mem(cb) | {...} | X | X | X | Memory information| -| | total | X | X | X | total memory | -| | free | X | X | X | not used | +| si.mem(cb) | {...} | X | X | X | Memory information (in bytes)| +| | total | X | X | X | total memory in bytes | +| | free | X | X | X | not used in bytes | | | used | X | X | X | used (incl. buffers/cache) | | | active | X | X | X | used actively (excl. buffers/cache) | | | buffcache | X | X | | used by buffers+cache | @@ -194,14 +195,14 @@ I also created a nice little command line tool called [mmon][mmon-github-url] ( | | controllers[0].vendor | X | X | X | e.g. ATI | | | controllers[0].bus | X | X | X| on which bus (e.g. PCIe) | | | controllers[0].vram | X | X | X | VRAM size (in MB) | -| | controllers[0].vramDynamic | X | X | | true if dynamicly allocated ram | -| | displays[0].model | X | X | | Monitor/Display Model | +| | controllers[0].vramDynamic | X | X | X | true if dynamicly allocated ram | +| | displays[0].model | X | X | X | Monitor/Display Model | | | displays[0].main | X | X | | true if main monitor | | | displays[0].builtin | X | X | | true if built in monitor | | | displays[0].connection | X | X | | e.g. DisplayPort or HDMI | | | displays[0].resolutionx | X | X | X | pixel horizontal | | | displays[0].resolutiony | X | X | X | pixel vertical | -| | displays[0].depth | X | X | X | color depth in bits | +| | displays[0].pixeldepth | X | X | X | color depth in bits | | | displays[0].sizex | X | X | | size in mm horizontal | | | displays[0].sizey | X | X | | size in mm vertical | @@ -280,9 +281,9 @@ I also created a nice little command line tool called [mmon][mmon-github-url] ( | | [0].mac | X | X | X | MAC address | | | [0].internal | X | X | X | true if internal interface | | si.networkInterfaceDefault(cb) | : string | X | X | X | get name of default network interface | -| si.networkStats(iface,cb) | {...} | X | X | X | current network stats of given interface
iface parameter is optional
defaults to first external network interface
Windows: no iface, only overall stats!| -| | iface | X | X | | interface (Windows: only overall stats)| -| | operstate | X | X | | up / down | +| si.networkStats(iface,cb) | {...} | X | X | X | current network stats of given interface
iface parameter is optional
defaults to first external network interface| +| | iface | X | X | X | interface | +| | operstate | X | X | X | up / down | | | rx | X | X | X | received bytes overall | | | tx | X | X | X | transferred bytes overall| | | rx_sec | X | X | X | received bytes / second (* see notes) | diff --git a/lib/cpu.js b/lib/cpu.js index 170d569..6074b51 100644 --- a/lib/cpu.js +++ b/lib/cpu.js @@ -167,6 +167,8 @@ function getCpu() { _cpu_speed = result.speed; result = cpuBrandManufacturer(result); result.revision = getValue(lines, 'revision', '='); + result.cache.l1d = 0; + result.cache.l1i = 0; result.cache.l2 = getValue(lines, 'l2cachesize', '='); result.cache.l3 = getValue(lines, 'l3cachesize', '='); if (result.cache.l2) { result.cache.l2 = parseInt(result.cache.l2) * 1024} @@ -188,7 +190,25 @@ function getCpu() { } } } - resolve(result); + exec("wmic path Win32_CacheMemory get CacheType,InstalledSize,Purpose", function (error, stdout) { + if (!error) { + let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0); + lines.forEach(function (line) { + if (line !== '') { + line = line.trim().split(/\s\s+/); + // L1 Instructions + if (line[2] === 'L1 Cache' && line[0] === '3') { + result.cache.l1i = parseInt(line[1], 10) + } + // L1 Data + if (line[2] === 'L1 Cache' && line[0] === '4') { + result.cache.l1d = parseInt(line[1], 10) + } + } + }); + } + resolve(result); + }) }) } }); @@ -374,13 +394,12 @@ function cpuFlags(callback) { return new Promise((resolve, reject) => { process.nextTick(() => { + let result = ''; if (_windows) { - let error = new Error(NOT_SUPPORTED); - if (callback) { callback(NOT_SUPPORTED) } - reject(error); + if (callback) { callback(result) } + resolve(result); } - let result = ''; if (_linux) { exec("lscpu", function (error, stdout) { if (!error) { @@ -474,13 +493,33 @@ function cpuCache(callback) { exec("wmic cpu get l2cachesize, l3cachesize /value", function (error, stdout) { if (!error) { let lines = stdout.split('\r\n'); + result.l1d = 0; + result.l1i = 0; result.l2 = getValue(lines, 'l2cachesize', '='); result.l3 = getValue(lines, 'l3cachesize', '='); if (result.l2) { result.l2 = parseInt(result.l2) * 1024} if (result.l3) { result.l3 = parseInt(result.l3) * 1024} } - if (callback) { callback(result) } - resolve(result); + exec("wmic path Win32_CacheMemory get CacheType,InstalledSize,Purpose", function (error, stdout) { + if (!error) { + let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0); + lines.forEach(function (line) { + if (line !== '') { + line = line.trim().split(/\s\s+/); + // L1 Instructions + if (line[2] === 'L1 Cache' && line[0] === '3') { + result.l1i = parseInt(line[1], 10) + } + // L1 Data + if (line[2] === 'L1 Cache' && line[0] === '4') { + result.l1d = parseInt(line[1], 10) + } + } + }); + } + if (callback) { callback(result) } + resolve(result); + }) }) } }); diff --git a/lib/dockerSocket.js b/lib/dockerSocket.js index 459ee12..fde4ceb 100644 --- a/lib/dockerSocket.js +++ b/lib/dockerSocket.js @@ -1,4 +1,16 @@ 'use strict'; +// ================================================================================== +// dockerSockets.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2017 +// Author: Sebastian Hildebrandt +// ---------------------------------------------------------------------------------- +// License: MIT +// ================================================================================== +// 13. DockerSockets +// ---------------------------------------------------------------------------------- const net = require('net'); const isWin = require('os').type() === 'Windows_NT'; diff --git a/lib/graphics.js b/lib/graphics.js index 38afcba..6e33ee3 100644 --- a/lib/graphics.js +++ b/lib/graphics.js @@ -23,6 +23,9 @@ const _linux = (_platform === 'Linux'); const _darwin = (_platform === 'Darwin'); const _windows = (_platform === 'Windows_NT'); const NOT_SUPPORTED = 'not supported'; +let _resolutionx = 0; +let _resolutiony = 0; +let _pixeldepth = 0; function getValue(lines, property, separator) { separator = separator || ':'; @@ -38,6 +41,14 @@ function getValue(lines, property, separator) { return ''; } +function toInt(value) { + let result = parseInt(value,10); + if (isNaN(result)) { + result = 0; + } + return result +} + function graphics(callback) { function parseLinesDarwin(lines) { @@ -313,7 +324,7 @@ function graphics(callback) { } if (_windows) { // https://blogs.technet.microsoft.com/heyscriptingguy/2013/10/03/use-powershell-to-discover-multi-monitor-information/ - exec("wmic path win32_VideoController get AdapterCompatibility, AdapterDACType, name, PNPDeviceID, CurrentVerticalResolution, CurrentHorizontalResolution, CurrentNumberOfColors, AdapterRAM, CurrentBitsPerPixel, CurrentRefreshRate, MinRefreshRate, MaxRefreshRate /value", function (error, stdout) { + exec("wmic path win32_VideoController get AdapterCompatibility, AdapterDACType, name, PNPDeviceID, CurrentVerticalResolution, CurrentHorizontalResolution, CurrentNumberOfColors, AdapterRAM, CurrentBitsPerPixel, CurrentRefreshRate, MinRefreshRate, MaxRefreshRate, VideoMemoryType /value", function (error, stdout) { if (!error) { let csections = stdout.split(/\n\s*\n/); result.controllers = parseLinesWindowsControllers(csections); @@ -321,6 +332,17 @@ function graphics(callback) { let dsections = stdout.split(/\n\s*\n/); if (!error) { result.displays = parseLinesWindowsDisplays(dsections); + if (result.controllers.length === 1 && result.displays.length === 1) { + if (_resolutionx && !result.displays[0].resolutionx) { + result.displays[0].resolutionx = _resolutionx + } + if (_resolutiony && !result.displays[0].resolutiony) { + result.displays[0].resolutiony = _resolutiony + } + if (_pixeldepth) { + result.displays[0].pixeldepth = _pixeldepth + } + } } if (callback) { callback(result) @@ -330,7 +352,6 @@ function graphics(callback) { } }) } - }); }); @@ -345,8 +366,12 @@ function graphics(callback) { model: getValue(lines, 'name', '='), vendor: getValue(lines, 'AdapterCompatibility', '='), bus: getValue(lines, 'PNPDeviceID', '=').startsWith('PCI') ? 'PCI' : '', - vram: getValue(lines, 'AdapterRAM', '=') + vram: getValue(lines, 'AdapterRAM', '='), + vramDynamic: (getValue(lines, 'VideoMemoryType', '=') === '2') }); + _resolutionx = toInt(getValue(lines, 'CurrentHorizontalResolution', '=')); + _resolutiony = toInt(getValue(lines, 'CurrentVerticalResolution', '=')); + _pixeldepth = toInt(getValue(lines, 'CurrentBitsPerPixel', '=')); } } } @@ -361,8 +386,8 @@ function graphics(callback) { let lines = sections[i].trim().split('\r\n'); displays.push({ model: getValue(lines, 'MonitorManufacturer', '='), - resolutionx: getValue(lines, 'ScreenWidth', '='), - resolutiony: getValue(lines, 'ScreenHeight', '=') + resolutionx: toInt(getValue(lines, 'ScreenWidth', '=')), + resolutiony: toInt(getValue(lines, 'ScreenHeight', '=')), }); } } diff --git a/lib/network.js b/lib/network.js index b51c551..8b964ca 100644 --- a/lib/network.js +++ b/lib/network.js @@ -28,26 +28,49 @@ const NOT_SUPPORTED = 'not supported'; let _network = {}; let _default_iface; +function getValue(lines, property, separator) { + separator = separator || ':'; + property = property.toLowerCase(); + for (let i = 0; i < lines.length; i++) { + if (lines[i].toLowerCase().startsWith(property)) { + const parts = lines[i].split(separator); + if (parts.length > 1) { + return parts[1].trim(); + } + } + } + return ''; +} + function getDefaultNetworkInterface() { if (!_default_iface) { let ifacename = ''; + let scopeid = 9999; if (_linux || _darwin) { 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), {}); + if (!ifacename) { // fallback - "first" external interface (sorted by scopeid) - let ifaces = sortObject(os.networkInterfaces()); + const compare = function(a,b) { + if (a.scopeid < b.scopeid) + return -1; + if (a.scopeid > b.scopeid) + return 1; + return 0; + } + + 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; + if (details && details.internal === false && details.scopeid && details.scopeid < scopeid) { + ifacename = dev; + scopeid = details.scopeid; } }) } @@ -64,12 +87,6 @@ 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); @@ -157,6 +174,46 @@ function calcNetworkSpeed(iface, rx, tx, operstate) { function networkStats(iface, callback) { + function parseLinesWindowsNics(sections){ + let nics = []; + for (let i in sections) { + if (sections.hasOwnProperty(i)) { + if (sections[i].trim() !== "") { + + let lines = sections[i].trim().split('\r\n'); + let netEnabled = getValue(lines, 'NetEnabled', '='); + if (netEnabled) { + nics.push({ + mac: getValue(lines, 'MACAddress', '=').toLowerCase(), + name: getValue(lines, 'Name', '=').replace(/[()\[\] ]+/g, "").toLowerCase(), + netEnabled: netEnabled === 'TRUE' + }); + } + } + } + } + return nics; + } + + function parseLinesWindowsPerfData(sections){ + let perfData = []; + for (let i in sections) { + if (sections.hasOwnProperty(i)) { + if (sections[i].trim() !== "") { + + let lines = sections[i].trim().split('\r\n'); + perfData.push({ + name: getValue(lines, 'Name', '=').replace(/[()\[\] ]+/g, "").toLowerCase(), + rx: parseInt(getValue(lines, 'BytesReceivedPersec', '='),10), + tx: parseInt(getValue(lines, 'BytesSentPersec', '='),10) + }); + } + } + } + return perfData; + } + + // fallback - if only callback is given if (util.isFunction(iface) && !callback) { callback = iface; @@ -168,9 +225,6 @@ function networkStats(iface, callback) { _default_iface = _default_iface || getDefaultNetworkInterface(); iface = iface || _default_iface; // (_darwin ? 'en0' : 'eth0'); - if (_windows) { - iface = 'all' - } let result = { iface: iface, @@ -239,21 +293,60 @@ function networkStats(iface, callback) { }); } if (_windows) { - cmd = "netstat -e"; + // NICs + let perfData = []; + let nics = []; + cmd = "wmic nic get MACAddress, name, NetEnabled /value"; exec(cmd, function (error, stdout) { - const lines = stdout.split('\r\n'); - for (let i = 0; i < lines.length; i++) { - if (lines[i].toLowerCase().startsWith('bytes')) { - const parts = lines[i].substr(5).trim().replace(/ +/g, " ").split(' '); - if (parts.length > 1) { - rx = parseInt(parts[0]); - tx = parseInt(parts[1]); - result = calcNetworkSpeed(iface, rx, tx, operstate); + if (!error) { + const nsections = stdout.split(/\n\s*\n/); + nics = parseLinesWindowsNics(nsections); + + // Performance Data + cmd = "wmic path Win32_PerfRawData_Tcpip_NetworkInterface Get name,BytesReceivedPersec,BytesSentPersec,BytesTotalPersec /value"; + exec(cmd, function (error, stdout) { + if (!error) { + const psections = stdout.split(/\n\s*\n/); + perfData = parseLinesWindowsPerfData(psections); } - } + + // Network Interfaces + networkInterfaces().then(interfaces => { + // get mac from 'interfaces' by interfacename + let mac = ''; + interfaces.forEach(detail => { + if (detail.iface === iface) { + mac = detail.mac; + } + }) + + // get name from 'nics' (by macadress) + let name = ''; + nics.forEach(detail => { + if (detail.mac === mac) { + name = detail.name; + operstate = (detail.netEnabled ? 'up' : 'down') + } + }) + + // get bytes sent, received from perfData by name + rx = 0; + tx = 0; + perfData.forEach(detail => { + if (detail.name === name) { + rx = detail.rx; + tx = detail.tx; + } + }); + + if (rx && tx) { + result = calcNetworkSpeed(iface, parseInt(rx), parseInt(tx), operstate); + } + if (callback) { callback(result) } + resolve(result); + }) + }) } - if (callback) { callback(result) } - resolve(result); }); } } else { diff --git a/package.json b/package.json index 967e02f..0dbb9ba 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "netstats", "network", "network connections", + "network stats", "processes", "users", "internet",