Last active
May 4, 2017 15:24
-
-
Save nlm/5814de072cef45891725bc2ae9887c6f to your computer and use it in GitHub Desktop.
Hard drive health testing on linux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash -u | |
usage() | |
{ | |
echo "usage: $0 [OPTIONS] DEVICE [DEVICE...]" | |
echo | |
echo "Options:" | |
echo " -s|--smart do a smart short test" | |
echo " -e|--smarterrors do a smart errors test" | |
echo " -d|--devstat do a smart devstat test" | |
echo " -b|--badblocks do a write but non-destructive test" | |
echo " -a|--all do all the tests above" | |
echo | |
echo "(example: $0 -d sda)" | |
} | |
do_smarttest() | |
{ | |
device="$1" | |
echo "[+] Stopping SMART tests on ${device}..." | |
smartctl -X "/dev/${device}" >/dev/null | |
echo "[+] Starting short SMART test on ${device}..." | |
smartctl -t short "/dev/${device}" >/dev/null | |
while true; do | |
output="$(smartctl -l selftest /dev/${device})" | |
if grep -q "Self-test routine in progress" <<< "$output"; then | |
echo -n "." | |
sleep 1 | |
else | |
echo | |
break | |
fi | |
done | |
result="$(smartctl -H "/dev/${device}" | grep 'test result')" | |
if grep -q "PASSED" <<< "$result"; then | |
echo "[+] Success" | |
else | |
echo "[-] Failure" | |
return 1 | |
fi | |
} | |
do_blocktest() | |
{ | |
device="$1" | |
lastblock="${2:-1000000}" | |
badblocksfile="$(mktemp)" | |
echo "[+] Starting bad blocks test on ${device}..." | |
badblocks -sne 1 -o "${badblocksfile}" "/dev/${device}" "${lastblock}" | |
if [ -s "${badblocksfile}" ]; then | |
echo "[-] Failure" | |
result=1 | |
else | |
echo "[+] Success" | |
result=0 | |
fi | |
rm -f "${badblocksfile}" | |
return $result | |
} | |
do_smarterrorstest() | |
{ | |
device="$1" | |
echo "[+] Starting SMART errors test on ${device}..." | |
output="$(smartctl -l error /dev/${device})" | |
errors=$(grep 'ATA Error Count:' <<< "$output" | cut -d' ' -f4) | |
if grep -q 'No Errors Logged' <<< "$output"; then | |
echo "[+] Success" | |
return 0 | |
elif [ -z "$errors" ]; then | |
echo "[+] Error retrieving error count" | |
return 2 | |
elif [ "$errors" -gt "0" ]; then | |
echo "[-] Failure ($errors errors)" | |
return 1 | |
elif [ "$errors" -eq "0" ]; then | |
echo "[+] Success" | |
return 0 | |
else | |
echo "[-] Error retrieving error count" | |
return 2 | |
fi | |
} | |
do_devstattest() | |
{ | |
device="$1" | |
echo "[+] Starting SMART devstat test on ${device}..." | |
output="$(smartctl -l devstat /dev/${device})" | |
declare -A statdesc | |
statdesc[uncorrectable_errors]='Number of Reported Uncorrectable Errors' | |
statdesc[reallocated_sectors]='Number of Reallocated Logical Sectors' | |
statdesc[mechanical_failures]='Number of Mechanical Start Failures' | |
statdesc[cmd_resets]='Resets Between Cmd Acceptance and Completion' | |
declare -A devstat | |
devstat[uncorrectable_errors]=$(grep 'Number of Reported Uncorrectable Errors' <<< "$output" | awk '{print $4}') | |
devstat[reallocated_sectors]=$(grep 'Number of Reallocated Logical Sectors' <<< "$output" | awk '{print $4}') | |
devstat[mechanical_failures]=$(grep 'Number of Mechanical Start Failures' <<< "$output" | awk '{print $4}') | |
devstat[cmd_resets]=$(grep 'Resets Between Cmd Acceptance and Completion' <<< "$output" | awk '{print $4}') | |
status=0 | |
for stype in uncorrectable_errors reallocated_sectors mechanical_failures cmd_resets; do | |
if [ -z "${devstat[$stype]}" ]; then | |
echo "warning: no info for: ${statdesc[$stype]}" | |
elif [ "${devstat[$stype]}" -ne 0 ]; then | |
echo "error: ${statdesc[$stype]}: ${devstat[$stype]}" | |
status=1 | |
fi | |
done | |
if [ "$status" -eq "0" ]; then | |
echo "[+] Success" | |
else | |
echo "[-] Failure" | |
fi | |
return "$status" | |
} | |
if [ $# -lt 2 ]; then | |
usage >&2 | |
exit 1 | |
fi | |
SMARTTEST=0 | |
BLOCKTEST=0 | |
ERRORTEST=0 | |
DEVSTATTEST=0 | |
while true; do | |
case "$1" in | |
-s|--smart) | |
SMARTTEST=1 | |
shift | |
;; | |
-e|--smarterrors) | |
ERRORTEST=1 | |
shift | |
;; | |
-b|--badblocks) | |
BLOCKTEST=1 | |
shift | |
;; | |
-d|--devstat) | |
DEVSTATTEST=1 | |
shift | |
;; | |
-a|--all) | |
SMARTTEST=1 | |
ERRORTEST=1 | |
BLOCKTEST=1 | |
DEVSTATTEST=1 | |
shift | |
;; | |
--) | |
shift | |
break | |
;; | |
-*) | |
usage | |
exit 1 | |
;; | |
*) | |
break | |
;; | |
esac | |
done | |
if [ $# -lt 1 ]; then | |
usage >&2 | |
exit 1 | |
fi | |
overall_status=0 | |
for device in $@; do | |
if [ ! -b "/dev/${device}" ]; then | |
echo "[-] error: device $device not found or not a block device" >&2 | |
exit 1 | |
fi | |
if [ "$BLOCKTEST" = "1" ]; then | |
if ! command -v badblocks >/dev/null; then | |
echo "[-] error: badblocks: command not found" | |
exit 1 | |
fi | |
do_blocktest "$device" || overall_status=1 | |
fi | |
if [ "$SMARTTEST" = "1" ]; then | |
if ! command -v smartctl >/dev/null; then | |
echo "[-] error: smartctl: command not found" | |
exit 1 | |
fi | |
do_smarttest "$device" || overall_status=1 | |
fi | |
if [ "$ERRORTEST" = "1" ]; then | |
if ! command -v smartctl >/dev/null; then | |
echo "[-] error: smartctl: command not found" | |
exit 1 | |
fi | |
do_smarterrorstest "$device" || overall_status=1 | |
fi | |
if [ "$DEVSTATTEST" = "1" ]; then | |
if ! command -v smartctl >/dev/null; then | |
echo "[-] error: smartctl: command not found" | |
exit 1 | |
fi | |
do_devstattest "$device" || overall_status=1 | |
fi | |
done | |
if [ "$overall_status" -eq "0" ]; then | |
echo "[+] Overall Result: Success" | |
else | |
echo "[-] Overall Result: Failure" | |
exit 1 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment