diff --git a/setup/start/sudoers b/setup/start/sudoers index 12dc1e08c..a058529b7 100644 --- a/setup/start/sudoers +++ b/setup/start/sudoers @@ -68,4 +68,7 @@ yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/remountmount.sh Defaults!/home/yellowtent/box/src/scripts/du.sh env_keep="HOME BOX_ENV" yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/du.sh +Defaults!/home/yellowtent/box/src/scripts/hdparm.sh env_keep="HOME BOX_ENV" +yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/hdparm.sh + cloudron-support ALL=(ALL) NOPASSWD: ALL diff --git a/src/scripts/hdparm.sh b/src/scripts/hdparm.sh new file mode 100755 index 000000000..d831fb468 --- /dev/null +++ b/src/scripts/hdparm.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -eu -o pipefail + +if [[ ${EUID} -ne 0 ]]; then + echo "This script should be run as root." > /dev/stderr + exit 1 +fi + +if [[ $# -eq 0 ]]; then + echo "No arguments supplied" + exit 1 +fi + +if [[ "$1" == "--check" ]]; then + echo "OK" + exit 0 +fi + +device="$1" + +# -t performs a non-cache device read speed test +hdparm -t "${device}" diff --git a/src/system.js b/src/system.js index eb4a996fb..757ab91e2 100644 --- a/src/system.js +++ b/src/system.js @@ -26,6 +26,7 @@ const apps = require('./apps.js'), volumes = require('./volumes.js'); const DU_CMD = path.join(__dirname, 'scripts/du.sh'); +const HDPARM_CMD = path.join(__dirname, 'scripts/hdparm.sh'); async function du(file) { assert.strictEqual(typeof file, 'string'); @@ -36,6 +37,22 @@ async function du(file) { return parseInt(stdoutResult.trim(), 10); } +async function hdparm(file) { + assert.strictEqual(typeof file, 'string'); + + const [error, stdoutResult] = await safe(shell.promises.sudo('system', [ HDPARM_CMD, file ], {})); + if (error) throw new BoxError(BoxError.FS_ERROR, error); + + const lines = stdoutResult.split('\n'); + + if (lines.length != 4) return -1; + if (lines[2].split('=').length !== 2) return -1; + + const speed = lines[2].split('=')[1].slice(0, 'MB/sec'.length).trim(); + + return Number(speed); +} + async function getSwaps() { const stdout = safe.child_process.execSync('swapon --noheadings --raw --bytes --show=type,size,used,name', { encoding: 'utf8' }); if (!stdout) return {}; @@ -194,6 +211,11 @@ async function updateDiskUsage(progressCallback) { for (const filesystem of filesystems) { const disk = disks[filesystem]; + const [speedError, speed] = await safe(hdparm(filesystem)); + if (speedError) progressCallback({ message: `hdparm error: ${speedError.message}`}); + + disk.speed = speed; + percent += (100/filesystems.length); progressCallback({ percent, message: `Checking contents of ${filesystem}`}); @@ -205,6 +227,7 @@ async function updateDiskUsage(progressCallback) { const [error, usage] = await safe(du(content.path)); if (error) progressCallback({ message: `du error: ${error.message}`}); // can happen if app is installing etc content.usage = usage || 0; + } progressCallback({ message: `du of ${JSON.stringify(content)}: ${content.usage}`}); } diff --git a/src/test/system-test.js b/src/test/system-test.js index 0538a7afd..2efd01931 100644 --- a/src/test/system-test.js +++ b/src/test/system-test.js @@ -51,4 +51,15 @@ describe('System', function () { const usage = await system.getDiskUsage(); expect(usage).to.be(null); // nothing cached }); + + it('can updateDiskUsage', async function () { + const usage = await system.updateDiskUsage(function () {}); + const tmp = usage[Object.keys(usage)[0]]; + + expect(tmp.size).to.be.greaterThan(0); + expect(tmp.used).to.be.greaterThan(0); + expect(tmp.available).to.be.greaterThan(0); + expect(tmp.capacity).to.be.greaterThan(0); + expect(tmp.speed).to.be.a('number'); + }); });