From 63a7ae51a54addbb1654a377964b93969133b31c Mon Sep 17 00:00:00 2001 From: Sebastian Hildebrandt Date: Thu, 7 Jan 2016 11:33:10 +0100 Subject: [PATCH] Version 2.0.0 --- LICENSE | 2 +- README.md | 214 ++++++--- lib/index.js | 1271 +++++++++++++++++++++++++++++++++++++------------- package.json | 18 +- 4 files changed, 1108 insertions(+), 397 deletions(-) diff --git a/LICENSE b/LICENSE index 817aa6d..f9dc944 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2015 Sebastian Hildebrandt +Copyright (c) 2014-2016 Sebastian Hildebrandt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 2205968..90d37af 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,6 @@ Simple system and OS information library for [node.js][nodejs-url] [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![MIT license][license-img]][license-url] - [![deps status][daviddm-img]][daviddm-url] - -### --- Working already on Version 2 - stay tuned --- ## Quick Start @@ -19,7 +16,7 @@ $ npm install systeminformation --save ### Usage -All functions are implemented as asynchronous functions. Here a small example how to use them: +All functions (except `version` and `time`) are implemented as asynchronous functions. Here a small example how to use them: ``` var si = require('systeminformation'); @@ -30,9 +27,63 @@ si.cpu(function(data) { }) ``` +### Major Changes - Version 2 + +There are a lot of changes in version 2 of systeminformation! Here is a quick overview: + +New Functions + +- `version`: returns systeminformation version (semver) +- `system`: hardware info (manufacturer, product/model name, version, serial, uuid) +- `networkConnections`: number of active connections +- `inetLatency`: latency in ms to external resource (internet) +- `getSystemData`: returns on json object with static data at once (OS, CPU, Network Interfaces - they should not change until restarted) +- `getDynamicData`: returns on json object with all dynamic data at once (e.g. for monitoring agents) +- `getAllData`: returns on json object with all data (static and dynamic) at once + +Renamed Functions (now all camelCase) + +- `osinfo`: renamed to `osInfo` +- `cpu_currentspeed`: renamed to `cpuCurrentspeed` +- `cpu_temperature`: renamed to `cpuTemperature` +- `fs_size`: renamed to `fsSize` +- `fs_speed`: renamed to `fsStats` +- `network_interfaces`: renamed to `networkInterfaces` +- `network_speed`: renamed to `networkStats` +- `network_connections`: renamed to `networkConnections` +- `currentload`: renamed to `currentLoad` +- `fullload`: renamed to `fullLoad` +- `processload`: renamed to `processLoad` +- `checksite`: renamed to `inetChecksite` + +Function Changes + +- `cpu_temperature`/`cpuTemperature`: -1 is new default (and indicates that non sensors are installed) +- `cpu_temperature`/`cpuTemperature`: new result `max` which returns max temperature of all cores +- `cpu_currentspeed`/`cpuCurrentspeed`: now in GHz +- `cpu`: splitted `manufacturer` (e.g. Intel) and `brand` (e.g. Core 2 Duo) +- `network_speed`/`networkStats`: now better support for OS X (also support for `operstate`) +- `network_speed`/`networkStats`: overall received and transferred bytes (rx, tx) +- `mem`: now better support for OS X (also support for `swaptotal`, `swapused`, `swapfree`) +- `fs_size`/`fsSize`: use-values now in % (0 - 100% instead of 0 - 1) +- `fs_speed`/`fsStats`: now also full support for OS X +- `checksite`/`inetChecksite`: new result structure - see command reference +- `checksite`/`inetChecksite`: ms (former `response_ms`): -1 if not ok + +Other changes + +- no more external dependencies: `request` is not longer needed +- where possible results are now integer or float values (instead of strings) because it is easier to calculate with numbers ;-) + +**Be aware**, that the new version 2.x is **NOT backward compatible** to version 1.x .... + ## Core concept -Node.JS comes with some basic OS-informations, but I always wanted a little more. So I came up to write this little library. This library is work in progress. It is quite "fresh" - means, there might be a lot of inconsistencies or even bugs. I was only able to test it on some Debian and Ubuntu distributions as well as OSX (Maveriks). But be carefull, not all options will work on OSX. AND: this library will definitely NOT work on Windows platforms! +[Node.js][nodejs-url] comes with some basic OS information, but I always wanted a little more. So I came up to write this +little library. This library is still work in progress. In version 2 I cleaned up a lot of inconsistencies and bugs, but +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). Version 2 now also supports nearly all functionality on OS X/Darwin platforms. +But be careful, this library will definitely NOT work on Windows platforms! If you have comments, suggestions & reports, please feel free to contact me! @@ -42,95 +93,143 @@ If you have comments, suggestions & reports, please feel free to contact me! This library is splitted in several sections: -1. Operating System -2. CPU -3. Memory -4. File System -5. Network -6. Processes -7. Users -8. Internet +1. General +2. System (HW) +3. Operating System +4. CPU +5. Memory +6. File System +7. Network +8. Processes +9. Users +10. Internet +11. GetAll -### Command Reference and OS Support +### Function Reference and OS Support -| command | Linux | OSX | Comments | +| function | Linux | OS X | Comments | | -------------- | ------ | ------ | ------- | -| si.osinfo() | X | X | OS information| +| si.version() | X | X | systeminformation version (no callback!) | +| si.time() | X | X | time information (no callback!) | +| - current | X | X | local time | +| - uptime | X | X | uptime | +| si.system(cb) | X | X | hardware information | +| - manufacturer | X | X | e.g. 'MSI' | +| - model | X | X | model/product e.g. 'MS-7823' | +| - version | X | X | version e.g. '1.0' | +| - serial | X | X | serial number | +| - uuid | X | X | UUID | +| si.osInfo(cb) | X | X | OS information | | - platform | X | X | 'Linux' or 'Darwin' | | - distro | X | X | | | - release | X | X | | | - codename | | X | | -| - kernel | X | X | kernel release - same as os.release()| +| - kernel | X | X | kernel release - same as os.release() | | - arch | X | X | same as os.arch() | | - hostname | X | X | same as os.hostname() | | - logofile | X | X | e.g. 'apple', 'debian', 'fedora', ... | -| si.cpu() | X | X | CPU information| -| - brand | X | X | e.g. 'Intel(R)' | -| - speed | X | X | e.g. '3.40GHz' | +| si.cpu(cb) | X | X | CPU information| +| - manufacturer | X | X | e.g. 'Intel(R)' | +| - brand | X | X | e.g. 'Core(TM)2 Duo' | +| - speed | X | X | in GHz e.g. '3.40' | | - cores | X | X | # cores | -| si.cpu_currentspeed() | X | X | current speed (GHz)| -| si.cpu_temperature() | X | | CPU temperature (if sensors is installed) | -| - main | X | X | main temperature | -| - cores | X | X | array of temperatures | -| si.mem() | X | X | Memory information| +| si.cpuCurrentspeed(cb) | X | X | current speed (in GHz)| +| si.cpuTemperature(cb) | X | | CPU temperature (if sensors is installed) | +| - main | X | | main temperature | +| - cores | X | | array of temperatures | +| - max | X | | max temperature | +| si.mem(cb) | X | X | Memory information| | - total | X | X | | | - free | X | X | | | - used | X | X | | | - active | X | X | | | - buffcache | X | X | | -| - swaptotal | X | | | -| - swapused | X | | | -| - swapfree | X | | | -| si.fs_size() | X | X | returns array of mounted file systems | +| - swaptotal | X | X | | +| - swapused | X | X | | +| - swapfree | X | X | | +| si.fsSize(cb) | X | X | returns array of mounted file systems | | - [0].fs | X | X | name of file system | | - [0].size | X | X | sizes in Bytes | | - [0].used | X | X | used in Bytes | | - [0].use | X | X | used in % | | - [0].mount | X | X | mount point | -| si.fs_speed() | X | | currend transfer speed | -| - read_sec | X | | bytes read / second | -| - write_sec | X | | bytes written / second | -| si.network_interfaces() | X | X | array of network interfaces | +| si.fsStats(cb) | X | X | current transfer stats | +| - rx | X | X | bytes read since startup | +| - wx | X | X | bytes written since startup | +| - rx_sec | X | X | bytes read / second (* see notes) | +| - wx_sec | X | X | bytes written / second (* see notes) | +| si.networkInterfaces(cb) | X | X | array of network interfaces | | - [0].iface | X | X | interface name | | - [0].ip4 | X | X | ip4 address | | - [0].ip6 | X | X | ip6 address | -| si.network_speed('eth1') | X | | current network speed of given interface | -| - operstate | X | | up / down | -| - rx_sec | X | X | received bytes / second | -| - tx_sec | X | X | transferred bytes per second | -| si.currentload() | X | X | CPU-Load in % | -| si.fullload() | X | X | CPU-full load since bootup in % | -| si.services('mysql, apache2, nginx') | X | X | pass comma separated string of services | -| - [0].service | X | X | name of service | +| si.networkStats('eth1',cb) | X | X | current network stats of given interface | +| - iface | X | X | interface | +| - operstate | X | X | up / down | +| - rx | X | X | received bytes overall | +| - tx | X | X | transferred bytes overall| +| - rx_sec | X | X | received bytes / second (* see notes) | +| - tx_sec | X | X | transferred bytes per second (* see notes) | +| si.currentLoad(cb) | X | X | CPU-Load in % | +| si.fullLoad(cb) | X | X | CPU-full load since bootup in % | +| si.services('mysql, apache2', cb) | X | X | pass comma separated string of services | +| - [0].name | X | X | name of service | | - [0].running | X | X | true / false | | - [0].pcpu | X | X | process % CPU | | - [0].pmem | X | X | process % MEM | -| si.processes() | X | X | # running processes | -| si.processload('apache2') | X | X | detailed information about given process | +| si.processes(cb) | X | X | # running processes | +| si.processLoad('apache2',cb) | X | X | detailed information about given process | | - proc | X | X | process name | | - pid | X | X | PID | | - cpu | X | X | process % CPU | | - mem | X | X | process % MEM | -| si.users() | X | X | array of users online | -| si.checksite(url) | X | X | response-time (ms) to fetch given URL | +| si.users(cb) | X | X | array of users online | +| si.inetChecksite(url, cb) | X | X | response-time (ms) to fetch given URL | +| - url | X | X | given url | +| - ok | X | X | status code OK (2xx, 3xx) | +| - status | X | X | status code | +| - ms | X | X | response time in ms | +| si.inetLatency(cb) | X | X | response-time (ms) to external ressource | +| si.getSystemData(cb) | X | X | all static data at once | +| si.getDynamicData(cb,srv,iface) | X | X | all dynamic data at once | +| si.getAllData(cb,srv,iface) | X | X | all data at once | -Remember: All functions are implemented as asynchronous functions! So another example, how to use a specific function might be: +### cb: Asynchronous Function Calls (callback) + +Remember: all functions (except `version` and `time`) are implemented as asynchronous (callback) functions! +So another example, how to use a specific function might be: ``` var si = require('systeminformation'); -si.network_speed('eth1', function(data) { - console.log('Network Interface Speed (eth1):'); +si.networkStats('eth1', function(data) { + console.log('Network Interface Stats (eth1):'); console.log('- is up: ' + data.operstate); - console.log('- RX speed/sec: ' + data.rx_sec); - console.log('- TX speed/sec: ' + data.tx_sec); + console.log('- RX bytes overall: ' + data.rx); + console.log('- TX bytes overall: ' + data.tx); + console.log('- RX bytes/sec: ' + data.rx_sec); + console.log('- TX bytes/sec: ' + data.tx_sec); }) ``` +### *: Additional Notes + +In `fsStats` and `networkStats` the results per second values (rx_sec, ...) are calculated beginning +with the second call of the function. It is determined by calculating the difference of transferred bytes +divided by the time between two calls of the function. + +## Known Issues + +There is one major things, that I was still not able to solve: + +For OS X, I did not find a reliable way to get the CPU temperature. All suggestions I found did not work on current version of OS X on different machines (intel platform). So if anyone has an idea, this would be helpful. + +I am happy to discuss any comments and suggestions. Please feel free to contact me if you see any possibility of improvement! + ## Version history | Version | Date | Comment | | -------------- | -------------- | -------- | +| 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 | @@ -138,7 +237,7 @@ si.network_speed('eth1', function(data) { | 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 bumb, published as npm component | +| 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 | @@ -163,11 +262,19 @@ Written by Sebastian Hildebrandt [sebhildebrandt](https://github.com/sebhildebra - Guillaume Legrain [glegrain](https://github.com/glegrain) - Riccardo Novaglia [richy24](https://github.com/richy24) +## Copyright Information + +Linux is a registered trademark of Linus Torvalds, OS X is a registered trademark of Apple Inc., +Windows is a registered trademark of Microsoft Corporation. Node.js is a trademark of Joyent Inc., +Intel is a trademark of Intel Corporation, Raspberry Pi is a trademark of the Raspberry Pi Foundation, +Debian is a trademark of the Debian Project, Ubuntu is a trademark of Canonical Ltd. +All other trademarks are the property of their respective owners. + ## License [![MIT license][license-img]][license-url] >The [`MIT`][license-url] License (MIT) > ->Copyright © 2015 Sebastian Hildebrandt, [+innovations](http://www.plus-innovations.com). +>Copyright © 2014-2016 Sebastian Hildebrandt, [+innovations](http://www.plus-innovations.com). > >Permission is hereby granted, free of charge, to any person obtaining a copy >of this software and associated documentation files (the "Software"), to deal @@ -200,6 +307,3 @@ Written by Sebastian Hildebrandt [sebhildebrandt](https://github.com/sebhildebra [npmjs-license]: https://img.shields.io/npm/l/systeminformation.svg?style=flat-square [nodejs-url]: https://nodejs.org/en/ - -[daviddm-url]: https://david-dm.org/sebhildebrandt/systeminformation -[daviddm-img]: https://img.shields.io/david/sebhildebrandt/systeminformation.svg?style=flat-square diff --git a/lib/index.js b/lib/index.js index 7c81911..036af94 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,46 +1,49 @@ // ================================================================================== -// sysinfo.js +// index.js // ---------------------------------------------------------------------------------- // Description: System Information - library // for Node.js -// Copyright: (c) 2014 - 2015 +// Copyright: (c) 2014 - 2016 // Author: Sebastian Hildebrandt // ---------------------------------------------------------------------------------- // Contributors: Guillaume Legrain (https://github.com/glegrain) +// Riccardo Novaglia (https://github.com/richy24) // ---------------------------------------------------------------------------------- // License: MIT // ================================================================================== // // Sections // -------------------------------- -// 1. Operating System -// 2. CPU -// 3. Memory -// 4. File System -// 5. Network -// 6. Processes -// 7. Users -// 8. Internet +// 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. GetAll - get all data // // ================================================================================== // // Installation // -------------------------------- -// At the time of writing, this library is dependent on the "request" module, -// which needs to be installed seperately. I created a npm package.json file, -// to be able to install it easily: // -// npm install +// # npm install systeminformation --save +// +// The new version 2.0 has no more dependencies. // // ================================================================================== // // Usage // -------------------------------- -// All functions are asynchronous functions. Here a small example how to use them: +// All functions (except `version` and `time`) are asynchronous functions. Here a small example how to use them: // -// var sysinfo = require('./sysinfo.js'); +// var si = require('systeminformation'); // -// sysinfo.cpu(function(data) { +// si.cpu(function(data) { // console.log('CPU-Information:'); // console.log(data); // }) @@ -49,19 +52,33 @@ // // Comments // -------------------------------- -// This library is work in progress. It is quite "fresh" - means, there might be a -// lot of inconsoistencies or even bugs. I was only able to test it on some -// Debian and Ubuntu distributions as well as OSX (Maveriks). // -// Comments, suggestions & reports are very wellcome! +// This library is still work in progress. In version 2 I cleaned up a lot of inconsistencies +// and bugs, but 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). +// Version 2 now also supports nearly all functionality 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 // -------------------------------- -// Verion 0.0.2 - 14.03.2014 - Optimization FS-Speed & CPU current speed // -// Verion 0.0.1 - 13.03.2014 - initial release +// version date comment +// 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 // // ================================================================================== @@ -72,35 +89,172 @@ var os = require('os') , exec = require('child_process').exec , fs = require('fs') - , request = require('request'); + , lib_version = require('../package.json').version; -var tmp_cores = 0; -var tmp_platform = os.type(); -var tmp_network = {}; -var tmp_cpu_speed = 0; -var tmp_fs_speed = {}; +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 = {}; -exports.time = function() { - result = { - current : Date.now(), - uptime : os.uptime() - } - return result; +const NOT_SUPPORTED = 'not supported'; + +// ---------------------------------------------------------------------------------- +// 0. helper functions +// ---------------------------------------------------------------------------------- + +function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; } // ---------------------------------------------------------------------------------- -// 1. Operating System +// 1. System (Hardware) // ---------------------------------------------------------------------------------- +function system(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + 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'; + } + } + } + }) + } + } + callback(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(); + } + }); + } + callback(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) { - result = 'linux'; + var result = 'linux'; if (distro.toLowerCase().indexOf('mac os') != -1) { result = 'apple' } else if (distro.toLowerCase().indexOf('arch') != -1) { result = 'arch' } else if (distro.toLowerCase().indexOf('centos') != -1) { result = 'centos' } else if (distro.toLowerCase().indexOf('debian') != -1) { result = 'debian' } else + if (distro.toLowerCase().indexOf('elementary') != -1) { result = 'elementary' } else if (distro.toLowerCase().indexOf('fedora') != -1) { result = 'fedora' } else if (distro.toLowerCase().indexOf('gentoo') != -1) { result = 'gentoo' } else if (distro.toLowerCase().indexOf('mageia') != -1) { result = 'mageia' } else @@ -111,10 +265,15 @@ function getLogoFile(distro) { if (distro.toLowerCase().indexOf('opensuse') != -1) { result = 'opensuse' } else if (distro.toLowerCase().indexOf('pclinuxos') != -1) { result = 'pclinuxos' } else if (distro.toLowerCase().indexOf('puppy') != -1) { result = 'puppy' } else + if (distro.toLowerCase().indexOf('raspbian') != -1) { result = 'raspbian' } else if (distro.toLowerCase().indexOf('reactos') != -1) { result = 'reactos' } else if (distro.toLowerCase().indexOf('redhat') != -1) { result = 'redhat' } else if (distro.toLowerCase().indexOf('slackware') != -1) { result = 'slackware' } else + if (distro.toLowerCase().indexOf('sugar') != -1) { result = 'sugar' } else + if (distro.toLowerCase().indexOf('steam') != -1) { result = 'steam' } else if (distro.toLowerCase().indexOf('suse') != -1) { result = 'suse' } else + if (distro.toLowerCase().indexOf('mate') != -1) { result = 'ubuntu-mate' } else + if (distro.toLowerCase().indexOf('lubuntu') != -1) { result = 'lubuntu' } else if (distro.toLowerCase().indexOf('xubuntu') != -1) { result = 'xubuntu' } else if (distro.toLowerCase().indexOf('ubuntu') != -1) { result = 'ubuntu' } return result; @@ -123,11 +282,15 @@ function getLogoFile(distro) { // -------------------------- // OS Information -exports.osinfo =function(callback) { +function osInfo(callback) { + + if (_windows) { + callback(NOT_SUPPORTED); + } var result = { - platform : tmp_platform, + platform : _platform, distro : 'unknown', release : 'unknown', codename : '', @@ -135,26 +298,29 @@ exports.osinfo =function(callback) { arch : os.arch(), hostname : os.hostname(), logofile : '' - } + }; - if (result.platform == 'Linux') { + if (_linux) { - exec("cat /etc/*-release", function(error, stdout, stderr) { + exec("cat /etc/*-release", function(error, stdout) { if (!error) { + var release = {}; var lines = stdout.toString().split('\n'); lines.forEach(function(line) { - if (line.toUpperCase().indexOf('DISTRIB_ID') != -1) { - result.distro = line.split('=')[1].trim(); - result.logofile = getLogoFile(result.distro); - } - if (line.toUpperCase().indexOf('DISTRIB_RELEASE') != -1) result.release = line.split('=')[1].trim(); - if (line.toUpperCase().indexOf('DISTRIB_CODENAME') != -1) result.codename = line.split('=')[1].trim(); - }) + if (line.indexOf('=') != -1) { + release[line.split('=')[0].trim().toUpperCase()] = line.split('=')[1].trim(); + } + }); + result.distro = release.DISTRIB_ID || release.NAME || 'unknown'; + result.logofile = getLogoFile(result.distro); + result.release = release.DISTRIB_RELEASE || release.VERSION_ID || 'unknown'; + result.codename = release.DISTRIB_CODENAME || ''; } callback(result); }) - } else if (result.platform == 'Darwin') { - exec("sw_vers", function(error, stdout, stderr) { + } + if (_darwin) { + exec("sw_vers", function(error, stdout) { var lines = stdout.toString().split('\n'); lines.forEach(function(line) { if (line.indexOf('ProductName') != -1) { @@ -162,48 +328,66 @@ exports.osinfo =function(callback) { result.logofile = getLogoFile(result.distro); } if (line.indexOf('ProductVersion') != -1) result.release = line.split(':')[1].trim(); - }) + }); callback(result); }) - } else callback(result); + } } +exports.osInfo = osInfo; + // ---------------------------------------------------------------------------------- -// 2. CPU +// 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]; + var parts = res.brand.split(' '); + parts.shift(); + res.brand = parts.join(' '); + return res; +} + // -------------------------- // CPU - brand, speed -function getcpu(callback) { +function getCpu(callback) { + var result = { + manufacturer : 'unknown', brand : 'unknown', speed : 'unknown', - cores : tmp_cores + cores : _cores }; - // grep "^model name" /proc/cpuinfo 2>/dev/null || sysctl -n machdep.cpu.brand_string - if (tmp_platform == 'Darwin') { - exec("sysctl -n machdep.cpu.brand_string", function(error, stdout, stderr) { + if (_darwin) { + exec("sysctl -n machdep.cpu.brand_string", function(error, stdout) { if (!error) { var 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); callback(result); }); - } else { - exec("cat /proc/cpuinfo | grep 'model name'", function(error, stdout, stderr) { + } + if (_linux) { + exec("cat /proc/cpuinfo | grep 'model name'", function(error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); var line = lines[0].split(':')[1]; result.brand = line.split('@')[0].trim(); - result.speed = line.split('@')[1].trim(); - tmp_cpu_speed = parseFloat(result.speed) * 1000000000; + result.speed = line.split('@')[1] ? parseFloat(line.split('@')[1].trim()) : getCpuCurrentSpeedSync(); + _cpu_speed = result.speed; } - + result = cpuBrandManufacturer(result); callback(result); - }); + }) } } @@ -211,69 +395,93 @@ function getcpu(callback) { // CPU - Processor cores function cores(callback) { - exec("grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu", function(error, stdout, stderr) { + exec("grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu", function(error, stdout) { var result = {cores: 1}; if (!error) { result.cores = parseInt(stdout.toString()); - tmp_cores = result.cores; + _cores = result.cores; } if (callback) callback(result); - }); + }) } // -------------------------- // CPU - Processor Data -exports.cpu = function(callback) { - if (tmp_cores == 0) { - cores(function(data) { - getcpu(callback) +function cpu(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + if (_cores == 0) { + cores(function() { + getCpu(callback) }) } else { - getcpu(callback) + getCpu(callback) } } -// -------------------------- -// CPU - current speed +exports.cpu = cpu; -exports.cpu_currentspeed = function(callback) { - var result = {current : tmp_cpu_speed}; - if (tmp_platform == 'Darwin') { - exec("sysctl -n hw.cpufrequency", function(error, stdout, stderr) { +// -------------------------- +// CPU - current speed - in GHz + +function getCpuCurrentSpeedSync() { + var output = ""; + var result = "0.00"; + if (fs.existsSync("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq")) { + output = fs.readFileSync("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq").toString(); + } else if (fs.existsSync("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq")) { + output = fs.readFileSync("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq").toString(); + } + if (output.trim()) { + var lines = output.toString().split('\n'); + result = parseFloat((parseInt(lines[0]) / 1000 / 1000).toFixed(2)); + } + return result; +} + +function cpuCurrentspeed(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + var result = _cpu_speed; + if (_darwin) { + exec("sysctl -n hw.cpufrequency", function(error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); - result = parseInt(lines[0]); + result = parseFloat((parseInt(lines[0]) / 1000 / 1000 / 1000).toFixed(2)); } callback(result); }); - } else { - var output = ""; - if (fs.existsSync("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq")) { - output = fs.readFileSync("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq").toString(); - } else if (fs.existsSync("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq")) { - output = fs.readFileSync("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq").toString(); - } - if (output.trim()) { - var lines = output.toString().split('\n'); - result = parseInt(lines[0]) * 1000; - } + } + if (_linux) { + result = getCpuCurrentSpeedSync(); callback(result); } } +exports.cpuCurrentspeed = cpuCurrentspeed; + // -------------------------- // CPU - temperature // if sensors are installed -exports.cpu_temperature = function(callback) { - var result = { - main : 0.0, - cores : [] +function cpuTemperature(callback) { + if (_windows) { + callback(NOT_SUPPORTED); } - if (tmp_platform == 'Linux') { + + var result = { + main : -1.0, + cores : [], + max : -1.0 + }; + if (_linux) { var regex = /\+([^°]*)/g; - exec("sensors", function(error, stdout, stderr) { + exec("sensors", function(error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); lines.forEach(function(line) { @@ -284,18 +492,36 @@ exports.cpu_temperature = function(callback) { if (line.split(':')[0].toUpperCase().indexOf('CORE ') != -1) { result.cores.push(parseFloat(temps)); } - }) - } - callback(result) + }); + if (result.cores.length > 0) { + var maxtmp = Math.max.apply(Math, result.cores); + result.max = (maxtmp > result.main) ? maxtmp : result.main; + } + callback(result) + } else { + exec("/opt/vc/bin/vcgencmd measure_temp", function(error, stdout) { + if (!error) { + var lines = stdout.toString().split('\n'); + if (lines.length > 0 && lines[0].indexOf('=')) { + result.main = parseFloat(lines[0].split("=")[1]); + result.max = result.main + } + } + callback(result) + }); + + } }); - } else { + } + if (_darwin) { callback(result) } -}; +} +exports.cpuTemperature = cpuTemperature; // ---------------------------------------------------------------------------------- -// 3. Memory +// 5. Memory // ---------------------------------------------------------------------------------- // | R A M | H D | @@ -307,7 +533,10 @@ exports.cpu_temperature = function(callback) { // | total | swap | // | | | -exports.mem = function(callback) { +function mem(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } var result = { total : os.totalmem(), @@ -320,10 +549,10 @@ exports.mem = function(callback) { swaptotal : 0, swapused : 0, swapfree : 0 - } + }; - if (tmp_platform == 'Linux') { - exec("free -b", function(error, stdout, stderr) { + if (_linux) { + exec("free -b", function(error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); @@ -331,315 +560,493 @@ exports.mem = function(callback) { result.total = parseInt(mem[1]); result.free = parseInt(mem[3]); result.buffcache = parseInt(mem[5]) + parseInt(mem[6]); - result.active = result.total - result.free - result.buffcache + result.active = result.total - result.free - result.buffcache; - var mem = lines[3].replace(/ +/g, " ").split(' '); + mem = lines[3].replace(/ +/g, " ").split(' '); result.swaptotal = parseInt(mem[1]); result.swapfree = parseInt(mem[3]); result.swapused = parseInt(mem[2]); - callback(result); - } else { - callback(result); } + callback(result); }); - } else { - exec("vm_stat | grep 'Pages active'", function(error, stdout, stderr) { + } + if (_darwin) { + exec("vm_stat | grep 'Pages active'", function(error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); result.active = parseInt(lines[0].split(':')[1]) * 4096; result.buffcache = result.used - result.active; - callback(result); - } else { - callback(result); } + exec("sysctl -n vm.swapusage", function(error, stdout) { + if (!error) { + var lines = stdout.toString().split('\n'); + if (lines.length > 0) { + var line = lines[0].replace(/,/g, ".").replace(/M/g, ""); + line = line.trim().split(' '); + for (var 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; + + } + } + } + callback(result); + }); }); } } +exports.mem = mem; + // ---------------------------------------------------------------------------------- -// 4. File System +// 6. File System // ---------------------------------------------------------------------------------- // -------------------------- // FS - devices -exports.fs_size = function(callback) { - exec("df -lk | grep ^/", function(error, stdout, stderr) { - var lines = stdout.toString().split('\n'); - //lines.splice(0, 1); +function fsSize(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + exec("df -lk | grep ^/", function(error, stdout) { var data = []; - lines.forEach(function(line) { - if (line != '') { - var line = line.replace(/ +/g, " ").split(' '); - data.push({ - 'fs': line[0], - 'size': parseInt(line[1])*1024, - 'used': parseInt(line[2])*1024, - 'use': 1.0 * line[2] / line[1], - 'mount': line[line.length-1] - }) - } - }) + if (!error) { + var 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] + }) + } + }); + } callback(data) }); } +exports.fsSize = fsSize; + // -------------------------- // FS - speed -exports.fs_speed = function(callback) { - var result = { - read_sec : -1, - write_sec : -1 +function fsStats(callback) { + if (_windows) { + callback(NOT_SUPPORTED); } + + var result = { + rx : -1, + wx : -1, + rx_sec : -1, + wx_sec : -1 + }; var bytes_read = 0; var bytes_write = 0; + var lines; - if (tmp_platform == 'Linux') { -// exec("df -k | grep /dev/ | cut -d' ' -f1 | sed 's/\/dev\///g' | sed ':a;N;$!ba;s/\n/|/g'", function(error, stdout, stderr) { - exec("df -k | grep /dev/", function(error, stdout, stderr) { - var lines = stdout.toString().split('\n'); - var fs_filter = []; - lines.forEach(function(line) { - if (line != '') { - var line = line.replace(/ +/g, " ").split(' '); - fs_filter.push(line[0].replace(/\/dev\/+/g, "")) - } - }); + if (_linux) { +// exec("df -k | grep /dev/", function(error, stdout) { + exec("lsblk | grep /", function(error, stdout) { + if (!error) { + lines = stdout.toString().split('\n'); + var 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]) + } + }); - var output = fs_filter.join('|') - exec("cat /proc/diskstats | egrep '" + output + "'", function(error, stdout, stderr) { - var lines = stdout.toString().split('\n'); - lines.forEach(function(line) { + var output = fs_filter.join('|'); + exec("cat /proc/diskstats | egrep '" + output + "'", function (error, stdout) { + if (!error) { + lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + line = line.trim(); + if (line != '') { + line = line.replace(/ +/g, " ").split(' '); + + bytes_read = bytes_read + parseInt(line[5]) * 512; + bytes_write = bytes_write + parseInt(line[9]) * 512; + } + }); + if (_fs_speed && _fs_speed.ms) { + var ms = Date.now() - _fs_speed.ms; + result.rx = bytes_read; + result.wx = bytes_write; + result.rx_sec = (bytes_read - _fs_speed.bytes_read) / (ms / 1000); + result.wx_sec = (bytes_write - _fs_speed.bytes_write) / (ms / 1000); + } else { + result.rx = bytes_read; + result.wx = bytes_write; + result.rx_sec = 0; + result.wx_sec = 0; + } + _fs_speed.bytes_read = bytes_read; + _fs_speed.bytes_write = bytes_write; + _fs_speed.bytes_overall = bytes_read + bytes_write; + _fs_speed.ms = Date.now(); + } + callback(result); + }) + } else callback(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) { + lines = stdout.toString().split('\n'); + lines.forEach(function (line) { line = line.trim(); if (line != '') { - var line = line.replace(/ +/g, " ").split(' '); + line = line.split(' '); - bytes_read = bytes_read + parseInt(line[5]) * 512; - bytes_write = bytes_write + parseInt(line[9]) * 512; - } - }); - if (tmp_fs_speed && tmp_fs_speed.ms) { - var ms = Date.now() - tmp_fs_speed.ms; - result.read_sec = (bytes_read - tmp_fs_speed.bytes_read) / (ms / 1000); - result.write_sec = (bytes_write - tmp_fs_speed.bytes_write) / (ms / 1000); - } else { - result.read_sec = 0; - result.write_sec = 0; - } - tmp_fs_speed.bytes_read = bytes_read; - tmp_fs_speed.bytes_write = bytes_write; - tmp_fs_speed.ms = Date.now(); + bytes_read = bytes_read + parseInt(line[0]); + bytes_write = bytes_write + parseInt(line[1]); + } + }); - callback(result); - }) + if (_fs_speed && _fs_speed.ms) { + var ms = Date.now() - _fs_speed.ms; + result.rx = bytes_read; + result.wx = bytes_write; + result.rx_sec = (bytes_read - _fs_speed.bytes_read) / (ms / 1000); + result.wx_sec = (bytes_write - _fs_speed.bytes_write) / (ms / 1000); + } else { + result.rx = bytes_read; + result.wx = bytes_write; + result.rx_sec = 0; + result.wx_sec = 0; + } + _fs_speed.bytes_read = bytes_read; + _fs_speed.bytes_write = bytes_write; + _fs_speed.bytes_overall = bytes_read + bytes_write; + _fs_speed.ms = Date.now(); + } + callback(result) }) - } else { - callback(result) } } +exports.fsStats = fsStats; + // ---------------------------------------------------------------------------------- -// 5. Network +// 7. Network // ---------------------------------------------------------------------------------- // -------------------------- // NET - interfaces -exports.network_interfaces = function(callback) { +function networkInterfaces(callback) { var ifaces=os.networkInterfaces(); - result = []; + var result = []; for (var dev in ifaces) { - var alias=0; var ip4 = ''; var ip6 = ''; - ifaces[dev].forEach(function(details){ - if (details.family=='IPv4') { - ip4 = details.address - } - if (details.family=='IPv6') { - ip6 = details.address - } - }); - ++alias; - //result.push({iface : dev+(alias?':'+alias:''), ip4 : ip4, ip6 : ip6}) - result.push({iface : dev, ip4 : ip4, ip6 : ip6}) + if (ifaces.hasOwnProperty(dev)) { + ifaces[dev].forEach(function(details){ + if (details.family=='IPv4') { + ip4 = details.address + } + if (details.family=='IPv6') { + ip6 = details.address + } + }); + result.push({iface : dev, ip4 : ip4, ip6 : ip6}) + } } callback(result); } +exports.networkInterfaces = networkInterfaces; + // -------------------------- // NET - Speed -exports.network_speed = function(interface, callback) { - var iface = interface || 'eth0'; - if (tmp_platform == 'Linux') { +function calcNetworkSpeed(iface, rx, tx) { + var rx_sec = -1; + var tx_sec = -1; + if (_network[iface]) { + var 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 + }) +} +function networkStats(iface, callback) { + // fallback - if only callback is given + if (isFunction(iface) && !callback) { + callback = iface; + iface = ''; + } + iface = iface || (_darwin ? 'en0' : 'eth0'); + + if (_windows) { + callback(NOT_SUPPORTED); + } + + var result = { + iface : iface, + operstate : 'unknown', + rx: 0, + tx: 0, + rx_sec : -1, + tx_sec : -1 + }; + + var cmd, lines, stats, speed; + + if (_linux) { if (fs.existsSync('/sys/class/net/'+ iface)) { - var cmd = + 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, stderr) { + "cat /sys/class/net/" + iface + "/statistics/tx_bytes; "; + exec(cmd, function(error, stdout) { if (!error) { - var lines = stdout.toString().split('\n'); - var operstate = lines[0].trim(); - var rx = parseInt(lines[1]); - var tx = parseInt(lines[2]); + lines = stdout.toString().split('\n'); + result.operstate = lines[0].trim(); + result.rx = parseInt(lines[1]); + result.tx = parseInt(lines[2]); - if (tmp_network["iface"]) { - var ms = Date.now() - tmp_network["iface"].ms; - rx_sec = (rx - tmp_network["iface"].rx) / (ms / 1000); - tx_sec = (tx - tmp_network["iface"].tx) / (ms / 1000); - } else { - rx_sec = 0; - tx_sec = 0; - tmp_network["iface"] = {}; - } - tmp_network["iface"].rx = rx; - tmp_network["iface"].tx = tx; - tmp_network["iface"].ms = Date.now(); + speed = calcNetworkSpeed(iface, result.rx, result.tx); - callback({ - operstate : operstate, - rx_sec : rx_sec, - tx_sec : tx_sec - }); + result.rx_sec = speed.rx_sec; + result.tx_sec = speed.tx_sec; } + callback(result); }); - } else callback(null); - } else { - var cmd = "netstat -ibI " + iface; - exec(cmd, function(error, stdout, stderr) { - if (!error) { - var lines = stdout.toString().split('\n'); - // if there is less than 2 lines, no information for this interface was found - if (lines.length > 1) { - // skip header line - // TODO: operstate - // use the second line because it is tied to the NIC instead of the ipv4 or ipv6 address - var stats = lines[1].replace(/ +/g, " ").split(' '); - var rx = parseInt(stats[6]); - var tx = parseInt(stats[9]); + } else callback(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]); - if (tmp_network["iface"]) { - var ms = Date.now() - tmp_network["iface"].ms; - rx_sec = (rx - tmp_network["iface"].rx) / (ms / 1000); - tx_sec = (tx - tmp_network["iface"].tx) / (ms / 1000); - } else { - rx_sec = 0; - tx_sec = 0; - tmp_network["iface"] = {}; - } - tmp_network["iface"].rx = rx; - tmp_network["iface"].tx = tx; - tmp_network["iface"].ms = Date.now(); + speed = calcNetworkSpeed(iface, result.rx, result.tx); - callback({ - rx_sec : rx_sec, - tx_sec : tx_sec - }); - } - } else callback(null); - }); - } + result.rx_sec = speed.rx_sec; + result.tx_sec = speed.tx_sec; + } + } + callback(result); + }); + }); + } } +exports.networkStats = networkStats; + +// -------------------------- +// NET - connections (sockets) + +function networkConnections(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + var cmd = 'netstat -tun | tail -n +3 | wc -l'; + var result; + exec(cmd, function(error, stdout) { + if (!error) { + result = parseInt(stdout.toString()); + callback(result); + } else { + cmd = 'ss -tun | tail -n +2 | wc -l'; + exec(cmd, function(error, stdout) { + if (!error) { + result = parseInt(stdout.toString()); + } else { + result = -1; + } + callback(result); + }) + } + }) +} + +exports.networkConnections = networkConnections; // ---------------------------------------------------------------------------------- -// 6. Processes +// 8. Processes // ---------------------------------------------------------------------------------- // -------------------------- -// PS - current load +// PS - current load - in % -function getload(callback) { +function getLoad(callback) { var result = {}; - var loads = os.loadavg().map(function(x) { return x / tmp_cores; } ); - result.avgload = (Math.max.apply(Math, loads)).toFixed(2); - var comm = (tmp_platform == 'Darwin') ? "ps -caxm -o pcpu" : "ps axo pcpu" - exec(comm, function(error, stdout, stderr) { - var lines = stdout.toString().replace(/,+/g, ".").split('\n'); - lines.splice(0, 1) - lines.pop() - result.currentload = ((lines.reduce(function(pv, cv) { return pv + parseFloat(cv.trim()); }, 0)) / tmp_cores), + var loads = os.loadavg().map(function(x) { return x / _cores; } ); + result.avgload = parseFloat((Math.max.apply(Math, loads)).toFixed(2)); + result.currentload = -1; + + var cmd = (_darwin) ? "ps -caxm -o pcpu" : "ps axo pcpu"; + exec(cmd, function(error, stdout) { + if (!error) { + var 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)); + } callback(result) }); } -exports.currentload = function(callback) { - if (tmp_cores == 0) { - cores(function(data) { - getload(callback) +function currentLoad(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + if (_cores == 0) { + cores(function() { + getLoad(callback) }) } else { - getload(callback) + getLoad(callback) } } +exports.currentLoad = currentLoad; + // -------------------------- // PS - full load // since bootup -function getfullload(callback) { +function getFullLoad(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + var result = {}; - if (tmp_platform == 'Linux') { + if (_linux) { if (fs.existsSync('/proc/uptime')) { var output = fs.readFileSync('/proc/uptime').toString(); output = output.replace(/ +/g, " ").split(' '); - var uptime = parseFloat(output[0]) - var idletime = parseFloat(output[1]) / tmp_cores; - result.fullload = (uptime - idletime) / uptime * 100.0 + var uptime = parseFloat(output[0]); + var idletime = parseFloat(output[1]) / _cores; + result.fullload = (uptime - idletime) / uptime * 100.0; callback(result); } - } else { + } + if (_darwin) { result.fullload = 0; callback(result); } } -exports.fullload = function(callback) { - if (tmp_cores == 0) { - cores(function(data) { - getfullload(callback) +function fullLoad(callback) { + if (_cores == 0) { + cores(function() { + getFullLoad(callback) }) } else { - getfullload(callback) + getFullLoad(callback) } } +exports.fullLoad = fullLoad; + // -------------------------- // PS - services -// pass a koma separated string with services to check (mysql, apache, postgresql, ...) +// pass a comma separated string with services to check (mysql, apache, postgresql, ...) // this function gives an array back, if the services are running. -exports.services = function(srv, callback) { - var srv = srv.replace(/ +/g, "").replace(/,+/g, "|") - var comm = (tmp_platform == 'Darwin') ? "ps -caxm -o pcpu,pmem,comm" : "ps axo pcpu,pmem,comm" - exec(comm + " | grep -v grep | egrep '" + srv + "'", function(error, stdout, stderr) { - var lines = stdout.toString().replace(/ +/g, " ").replace(/,+/g, ".").split('\n'); - var srvs = srv.split('|'); - var data = []; - srvs.forEach(function(srv) { - var ps = lines.filter(function(e) {return e.indexOf(srv) != -1}); - data.push({ - 'service': srv, - 'running': ps.length > 0, - 'pcpu' : (ps.reduce(function(pv, cv) { return pv + parseFloat(cv.trim().split(' ')[0]); }, 0)).toFixed(2), - 'pmem' : (ps.reduce(function(pv, cv) { return pv + parseFloat(cv.trim().split(' ')[1]); }, 0)).toFixed(2) - }) - }) - callback(data) - }); +function services(srv, callback) { + // fallback - if only callback is given + if (isFunction(srv) && !callback) { + callback = srv; + srv = ''; + } + + if (_windows) { + callback(NOT_SUPPORTED); + } + + 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)) + }) + }); + callback(data) + } else { + srvs.forEach(function (srv) { + data.push({ + 'name': srv, + 'running': false, + 'pcpu': 0, + 'pmem': 0 + }) + }); + callback(data) + } + }); + } else callback(data) } +exports.services = services; + // -------------------------- // running processes -exports.processes = function(callback) { - exec("ps aux | grep -v 'ps aux' | wc -l", function(error, stdout, stderr) { +function processes(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + exec("ps aux | grep -v 'ps aux' | wc -l", function(error, stdout) { var result = { all: 0, running: 0, @@ -647,8 +1054,8 @@ exports.processes = function(callback) { }; if (!error) { result.all = parseInt(stdout.toString()); - if (tmp_platform == 'Darwin') { - exec("ps axo state | grep 'R' | wc -l; ps axo state | grep 'U' | wc -l", function(error, stdout, stderr) { + if (_darwin) { + exec("ps axo state | grep 'R' | wc -l; ps axo state | grep 'U' | wc -l", function(error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); result.running = parseInt(lines[0]); @@ -656,8 +1063,9 @@ exports.processes = function(callback) { } callback(result); }) - } else { - exec("cat /proc/stat | grep procs_", function(error, stdout, stderr) { + } + if (_linux) { + exec("cat /proc/stat | grep procs_", function(error, stdout) { if (!error) { var lines = stdout.toString().split('\n'); lines.forEach(function(line) { @@ -678,46 +1086,67 @@ exports.processes = function(callback) { }); } +exports.processes = processes; + // -------------------------- // PS - process load // get detailed information about a certain process // (PID, CPU-Usage %, Mem-Usage %) -exports.processload = function(proc, callback) { - exec("ps aux | grep " + proc + " | grep -v grep", function(error, stdout, stderr) { - var result = { - 'proc' : proc, - 'pid' : -1, - 'cpu' : 0, - 'mem' : 0 - }; - if (!error) { - var data = stdout.replace(/ +/g, " ").split(' '); +function processLoad(proc, callback) { + // fallback - if only callback is given + if (isFunction(proc) && !callback) { + callback = proc; + proc = ''; + } - if (data.length > 2) { - result = { - 'proc' : proc, - 'pid' : data[1], - 'cpu' : parseFloat(data[2].replace(',', '.')), - 'mem' : parseFloat(data[3].replace(',', '.')) + if (_windows) { + callback(NOT_SUPPORTED); + } + + 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) { + var 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(',', '.')) + } } } - } - callback(result); - }); + callback(result); + }); + } else callback(result); } +exports.processLoad = processLoad; + // ---------------------------------------------------------------------------------- -// 7. Users +// 9. Users/Sessions // ---------------------------------------------------------------------------------- // -------------------------- -// array of users online +// array of users online = sessions -exports.users = function(callback) { - result = []; - exec("users", function(error, stdout, stderr) { +function users(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + var result = []; + exec("users", function(error, stdout) { if (!error) { result = stdout.toString().replace(/ +/g, " ").replace(/\n+/g, " ").trim().split(' ').filter(function(e) {return e.trim() !== ''}); } @@ -725,19 +1154,195 @@ exports.users = function(callback) { }); } +exports.users = users; + // ---------------------------------------------------------------------------------- -// 8. Internet +// 10. Internet // ---------------------------------------------------------------------------------- // -------------------------- // check if external site is available -exports.checksite = function(url, callback) { +function inetChecksite(url, callback) { + + if (_windows) { + callback(NOT_SUPPORTED); + } + + var result = { + url: url, + ok : false, + status: 404, + ms : -1 + }; + if (url && (isFunction(callback))) { + var t = Date.now(); + var args = " -I --connect-timeout 5 -m 5 " + url + " 2>/dev/null | head -n 1 | cut -d ' ' -f2"; + var cmd = "curl"; + exec(cmd + args, function(error, stdout) { + var 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); + callback(result); + }) + } else { + callback(result) + } +} + +exports.inetChecksite = inetChecksite; + +// -------------------------- +// check inet latency + +function inetLatency(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + var t = Date.now(); - request(url, function (error, response, body) { - callback({ - ok : !error && (response.statusCode == 200 || response.statusCode == 301 || response.statusCode == 302 || response.statusCode == 304), - response_ms : Date.now() - t - }); + var cmd; + if (_linux) { + cmd = "ping -c 2 -w 3 8.8.8.8 | grep rtt | cut -d'/' -f4 | awk '{ print $3 }'"; + } + if (_darwin) { + cmd = "ping -c 2 -t 3 8.8.8.8 | grep avg | cut -d'/' -f4 | awk '{ print $3 }'"; + } + + exec(cmd, function(error, stdout) { + if (!error) { + callback(parseFloat(stdout.toString())); + } else { + callback(-1) + } }) } + +exports.inetLatency = inetLatency; + +// ---------------------------------------------------------------------------------- +// 11. get all +// ---------------------------------------------------------------------------------- + +// -------------------------- +// get static data - they should not change until restarted + + +function getSystemData(callback) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + var data = {}; + + data.version = version(); + + system(function(res) { + data.system = res; + osInfo(function(res) { + data.os = res; + cpu(function(res) { + data.cpu = res; + networkInterfaces(function(res) { + data.net = res; + callback(data); + }) + }) + }) + }) +} + +exports.getSystemData = getSystemData; + +// -------------------------- +// 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" +// - network: define network for which you like to monitor network speed e.g. "eth0" + +function getDynamicData(callback, srv, network) { + + if (_windows) { + callback(NOT_SUPPORTED); + } + + network = network || (_darwin ? 'en0' : 'eth0'); + srv = srv || ''; + + var data = {}; + + // get time + data.time = time(); + + cpuCurrentspeed(function(res) { + data.cpuCurrentspeed = res; + users(function(res) { + data.users = res; + processes(function(res) { + data.processes = res; + currentLoad(function(res) { + data.currentLoad = res; + cpuTemperature(function(res) { + data.temp = res; + networkStats(network, function(res) { + data.networkStats = res; + networkConnections(function(res) { + data.networkConnections = res; + mem(function(res) { + data.mem = res; + services(srv, function(res) { + data.services = res; + fsSize(function(res) { + data.fsSize = res; + fsStats(function(res) { + data.fsStats=res; + inetLatency(function(res) { + data.inetLatency = res; + callback(data); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); +} + +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" +// - network: define network for which you like to monitor network speed e.g. "eth0" + +function getAllData(callback, srv, network) { + if (_windows) { + callback(NOT_SUPPORTED); + } + + var data = {}; + + getSystemData(function(res) { + data = res; + getDynamicData(function(res) { + for(var key in res) { + if (res.hasOwnProperty(key)) { + data[key]=res[key]; + } + } + callback(data); + }, srv, network); + }) +} + +exports.getAllData = getAllData; diff --git a/package.json b/package.json index 2aa25a4..7b84f7b 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "systeminformation", - "version": "1.0.7", + "version": "2.0.0", "description": "Simple system and OS information library", "license": "MIT", "author": "Sebastian Hildebrandt (https://plus-innovations.com)", - "homepage": "https://github.com/sebhilderandt/systeminformation", + "homepage": "https://github.com/sebhildebrandt/systeminformation", "main": "./lib/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -12,6 +12,8 @@ "keywords": [ "system information", "sysinfo", + "monitor", + "monitoring", "os", "linux", "osx", @@ -25,13 +27,13 @@ ], "repository": { "type": "git", - "url": "https://github.com/sebhilderandt/systeminformation.git" - }, - "dependencies": { - "request": "^2.61.0" + "url": "https://github.com/sebhildebrandt/systeminformation.git" }, + "os": [ + "darwin", + "linux" + ], "engines": { "node": ">=0.10" - }, - "engineStrict": true + } }