Skip to content

Instantly share code, notes, and snippets.

@nlm
Last active May 4, 2017 15:24
Show Gist options
  • Save nlm/5814de072cef45891725bc2ae9887c6f to your computer and use it in GitHub Desktop.
Save nlm/5814de072cef45891725bc2ae9887c6f to your computer and use it in GitHub Desktop.
Hard drive health testing on linux
#!/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