From 68d0d1427f78f73161dc4791f797957b902a3250 Mon Sep 17 00:00:00 2001 From: Sebastian Hildebrandt Date: Tue, 4 Sep 2018 13:38:07 +0200 Subject: [PATCH] diskLayout() added S.M.A.R.T. status --- CHANGELOG.md | 1 + README.md | 7 +++- lib/filesystem.js | 103 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 6 ++- 4 files changed, 106 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b28de0d..d235bce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,7 @@ Other changes | Version | Date | Comment | | -------------- | -------------- | -------- | +| 3.45.0 | 2018-09-04 | `diskLayout()` added smartStatus | | 3.44.2 | 2018-08-28 | added code quality badges | | 3.44.1 | 2018-08-28 | code cleanup | | 3.44.0 | 2018-08-25 | `battery()` bugfix & added type, model, manufacturer, serial | diff --git a/README.md b/README.md index fd6e7bc..8899f20 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,13 @@ async function cpu() { (last 7 major and minor version releases) +- Version 3.45.0: `diskLayout()` added S.M.A.R.T. status - Version 3.44.0: `battery()` added type, model, manufacturer, serial, timeremaining - Version 3.43.0: added speed per CPU core `cpuCurrentspeed()` - Version 3.42.0: added parent process PID `processes()` - Version 3.41.0: first partial `SunOS` support - Version 3.40.0: extended `versions()` (php, redis, mongodb) - Version 3.39.0: added `versions().mysql` and `versions().nginx`, start implementing `SunOS` support -- Version 3.38.0: added `battery.acconnected` - ... You can find all changes here: [detailed changelog][changelog-url] @@ -193,6 +193,7 @@ I also created a nice little command line tool called [mmon][mmon-github-url] ( | | [0].sectorsPerTrack | | | | X | | sectors per track | | | [0].totalSectors | | | | X | | total sectors | | | [0].bytesPerSector | | | | X | | bytes per sector | +| | [0].smartStatus | X | | X | X | | S.M.A.R.T Status (see Known Issues) | | si.battery(cb) | {...} | X | X | X | X | | battery information | | | hasbattery | X | X | X | X | | indicates presence of battery | | | cyclecount | X | | X | | | numbers of recharges | @@ -518,6 +519,10 @@ In some cases we also discovered that `wmic` returned incorrect temperature valu In some cases you need to install the linux `sensors` package to be able to measure temperature e.g. on DEBIAN based systems by running `sudo apt-get install lm-sensors` +#### Linux S.M.A.R.T. Status + +To be able to detect S.M.A.R.T. status on Linux you need to install `smartmontools`. On DEBIAN based linux distributions you can install it by running `sudo apt-get install smartmontools` + ## *: Additional Notes In `fsStats`, `disksIO` and `networkStats` the `results / sec.` values (rx_sec, IOPS, ...) are calculated correctly beginning diff --git a/lib/filesystem.js b/lib/filesystem.js index b589dae..9f74ca1 100644 --- a/lib/filesystem.js +++ b/lib/filesystem.js @@ -608,6 +608,7 @@ function diskLayout(callback) { process.nextTick(() => { let result = []; + let cmd = ''; if (_linux) { exec('export LC_ALL=C; lshw -class disk; unset LC_ALL', function (error, stdout) { @@ -617,6 +618,7 @@ function diskLayout(callback) { devices.forEach(function (device) { let lines = device.split('\n'); let mediumType = ''; + const BSDName = util.getValue(lines, 'logical name', ':', true).trim(); const logical = util.getValue(lines, 'logical name', ':', true).trim().replace(/\/dev\//g, ''); try { mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational').toString().split('\n')[0]; @@ -642,14 +644,53 @@ function diskLayout(callback) { firmwareRevision: util.getValue(lines, 'version:', ':', true).trim(), serialNum: util.getValue(lines, 'serial:', ':', true).trim(), interfaceType: '', + smartStatus: 'unknown', + BSDName: BSDName }); + cmd = cmd + 'printf "\n' + BSDName + '|"; smartctl -H ' + BSDName + ' | grep overall;'; } }); } - if (callback) { - callback(result); + if (cmd) { + cmd = cmd + 'printf "\n"'; + exec(cmd, function (error, stdout) { + let lines = stdout.toString().split('\n'); + lines.forEach(line => { + if (line) { + let parts = line.split('|'); + if (parts.length === 2) { + let BSDName = parts[0]; + parts[1] = parts[1].trim(); + let parts2 = parts[1].split(':'); + if (parts2.length === 2) { + parts2[1] = parts2[1].trim(); + let status = parts2[1].toLowerCase(); + for (let i = 0; i < result.length; i++) { + if (result[i].BSDName === BSDName) { + result[i].smartStatus = (status === 'passed' ? 'Ok' : (status === 'failed!' ? 'Predicted Failure' : 'unknown')); + } + } + } + } + } + }); + for (let i = 0; i < result.length; i++) { + delete result[i].BSDName; + } + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + for (let i = 0; i < result.length; i++) { + delete result[i].BSDName; + } + if (callback) { + callback(result); + } + resolve(result); } - resolve(result); }); } if (_freebsd || _openbsd) { @@ -673,6 +714,7 @@ function diskLayout(callback) { let lines = device.split('\n'); const mediumType = util.getValue(lines, 'Medium Type', ':', true).trim(); const sizeStr = util.getValue(lines, 'capacity', ':', true).trim(); + const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim(); if (sizeStr) { let sizeValue = 0; if (sizeStr.indexOf('(') >= 0) { @@ -696,8 +738,11 @@ function diskLayout(callback) { sectorsPerTrack: -1, firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(), serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(), - interfaceType: util.getValue(lines, 'InterfaceType', ':', true).trim() + interfaceType: util.getValue(lines, 'InterfaceType', ':', true).trim(), + smartStatus: 'unknown', + BSDName: BSDName }); + cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;'; } } }); @@ -709,6 +754,7 @@ function diskLayout(callback) { let lines = device.split('\n'); const linkWidth = util.getValue(lines, 'link width', ':', true).trim(); const sizeStr = util.getValue(lines, '!capacity', ':', true).trim(); + const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim(); if (sizeStr) { let sizeValue = 0; if (sizeStr.indexOf('(') >= 0) { @@ -733,16 +779,55 @@ function diskLayout(callback) { firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(), serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(), interfaceType: ('PCIe ' + linkWidth).trim(), + smartStatus: 'unknown', + BSDName: BSDName }); + cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;'; } } }); } } - if (callback) { - callback(result); + if (cmd) { + cmd = cmd + 'printf "\n"'; + exec(cmd, function (error, stdout) { + let lines = stdout.toString().split('\n'); + lines.forEach(line => { + if (line) { + let parts = line.split('|'); + if (parts.length === 2) { + let BSDName = parts[0]; + parts[1] = parts[1].trim(); + let parts2 = parts[1].split(':'); + if (parts2.length === 2) { + parts2[1] = parts2[1].trim(); + let status = parts2[1].toLowerCase(); + for (let i = 0; i < result.length; i++) { + if (result[i].BSDName === BSDName) { + result[i].smartStatus = (status === 'not supported' ? 'not supported' : (status === 'verified' ? 'Ok' : (status === 'failing' ? 'Predicted Failure' : 'unknown'))); + } + } + } + } + } + }); + for (let i = 0; i < result.length; i++) { + delete result[i].BSDName; + } + if (callback) { + callback(result); + } + resolve(result); + }); + } else { + for (let i = 0; i < result.length; i++) { + delete result[i].BSDName; + } + if (callback) { + callback(result); + } + resolve(result); } - resolve(result); }); } if (_windows) { @@ -753,6 +838,7 @@ function diskLayout(callback) { devices.forEach(function (device) { let lines = device.split('\r\n'); const size = util.getValue(lines, 'Size', '=').trim(); + const status = util.getValue(lines, 'Status', '=').trim(); if (size) { result.push({ type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD', // not really correct(!) ... maybe this one is better: MSFT_PhysicalDisk - Media Type?? @@ -768,7 +854,8 @@ function diskLayout(callback) { sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', '=')), firmwareRevision: util.getValue(lines, 'FirmwareRevision', '=').trim(), serialNum: util.getValue(lines, 'SerialNumber', '=').trim(), - interfaceType: util.getValue(lines, 'InterfaceType', '=').trim() + interfaceType: util.getValue(lines, 'InterfaceType', '=').trim(), + smartStatus: (status === 'Ok' ? 'Ok' : (status === 'Degraded' ? 'Degraded' : (status === 'Pred Fail' ? 'Predicted Failure' : 'Unknown'))) }); } }); diff --git a/package.json b/package.json index e2f0a4e..baa8c12 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,9 @@ "graphics", "graphic card", "graphic controller", - "display" + "display", + "smart", + "disk layout" ], "repository": { "type": "git", @@ -57,4 +59,4 @@ "engines": { "node": ">=4.0.0" } -} +} \ No newline at end of file