diff --git a/disk-burnin.sh b/disk-burnin.sh index 9cde881..0c49291 100755 --- a/disk-burnin.sh +++ b/disk-burnin.sh @@ -1,10 +1,35 @@ #!/bin/sh +################################################################################ +# +# disk-burnin.sh +# +################################################################################ + +################################################################################ +# PRE-EXECUTION VALIDATION +################################################################################ + +# Check if running as root +if [ "$(id -u)" -ne 0 ]; then + echo "ERROR: Must be run as root" >&2 + exit 2 +fi + +# Check required dependencies +readonly DEPENDENCIES="awk badblocks grep sed sleep smartctl" +for dependency in ${DEPENDENCIES}; do + if ! command -v "${dependency}" > /dev/null 2>&1; then + echo "ERROR: Command '${dependency}' not found" >&2 + exit 2 + fi +done + readonly USAGE=\ "NAME $(basename "$0") -- disk burn-in program SYNOPSIS - $(basename "$0") [-h] [-e] [-f] [-o ] + $(basename "$0") [-h] [-e] [-f] [-o ] [-x] DESCRIPTION A script to simplify the process of burning-in disks. Only intended for use @@ -19,12 +44,13 @@ DESCRIPTION In order to perform tests on drives, you will need to provide the -f option. OPTIONS - -h show help text - -e show extended help text - -f run in destructive, non-dry mode + -h Show help text + -e Show extended help text + -f Force script to run in destructive mode ALL DATA ON THE DISK WILL BE LOST! - -o write log files to (default: $(pwd)) - disk to burn-in (/dev/ may be omitted) + -o Write log files to (default: $(pwd)) + -x Run full pass of badblocks instead of exiting on first error + Disk to burn-in (/dev/ may be omitted) EXAMPLES $(basename "$0") sda @@ -184,14 +210,21 @@ VERSIONS Check for root privileges during runtime. Add option parsing, most notably (-h)elp and -f for non-dry-run mode. Add dry_run_wrapper() function. - Add disk type detection to skip badblocks for non-mechanical drives." + Add disk type detection to skip badblocks for non-mechanical drives. -################################################################################ -# PRE-EXECUTION VALIDATION -################################################################################ + KN, 5 Oct 2020 + Added -x option to control the badblocks -e option, allowing extended testing. + Added smartctl to the list of dependencies. + Changed disk type detection so that we assume all drives are mechanical drives + unless they explicitly return 'Solid State Drive' for Rotational Rate. + Removed datestamp from every line of log output, only emitting it in log headers. + Minor reformatting." + +# badblocks default -e option is 1, stop testing if a single error occurs +BB_E_ARG=1 # parse options -while getopts ':hefo:' option; do +while getopts ':hefo:x' option; do case "${option}" in h) echo "${USAGE}" exit @@ -204,6 +237,8 @@ while getopts ':hefo:' option; do ;; o) LOG_DIR="${OPTARG}" ;; + x) BB_E_ARG=0 + ;; :) printf 'Missing argument for -%s\n' "${OPTARG}" >&2 echo "${USAGE}" >&2 exit 2 @@ -217,30 +252,17 @@ done shift $(( OPTIND - 1 )) if [ -z "$1" ]; then - echo "Missing option: " >&2 + echo "ERROR: Missing disk argument" >&2 echo "${USAGE}" >&2 exit 2 fi -# Check required dependencies -readonly DEPENDENCIES="awk badblocks grep sed sleep" -for dependency in ${DEPENDENCIES}; do - if ! command -v "${dependency}" > /dev/null 2>&1; then - echo "Command '${dependency}' not found" >&2 - exit 2 - fi -done - -# Check if running as root -if [ "$(id -u)" -ne 0 ]; then - echo "Please run as root" >&2 - exit 2 -fi - ################################################################################ # CONSTANTS ################################################################################ +readonly BB_E_ARG + # Drive to burn-in DRIVE="$1" # prepend /dev/ if necessary @@ -249,6 +271,12 @@ if ! printf '%s' "${DRIVE}" | grep "/dev/\w*" > /dev/null 2>&1; then fi readonly DRIVE +if [ ! -e "$DRIVE" ]; then + echo "ERROR: Disk does not exist: $DRIVE" >&2 + echo "${USAGE}" >&2 + exit 2 +fi + # Set to working directory if -o wasn't provided [ -z "${LOG_DIR}" ] && LOG_DIR="$(pwd)" # Trim trailing slashes @@ -314,12 +342,11 @@ DISK_MODEL="$(get_smart_info_value "Device Model")" [ -z "${DISK_MODEL}" ] && DISK_MODEL="$(get_smart_info_value "Model Number")" readonly DISK_MODEL -# Get disk type +# Get disk type; unless we get 'Solid State Device' as return value, assume +# we have a mechanical drive. DISK_TYPE="$(get_smart_info_value "Rotation Rate")" -if printf '%s' "${DISK_TYPE}" | grep "rpm" > /dev/null 2>&1; then - DISK_TYPE="mechanical" -else - DISK_TYPE="non-mechanical" +if printf '%s' "${DISK_TYPE}" | grep -i "solid_state_device" > /dev/null 2>&1; then + DISK_TYPE="SSD" fi readonly DISK_TYPE @@ -358,10 +385,10 @@ readonly BB_File="${LOG_DIR}/burnin-${DISK_MODEL}_${SERIAL_NUMBER}.bb" # Outputs: # Write message to stdout and log file. ################################################## -log_info() -{ - now="$(date +"%F %T %Z")" - printf "%s\n" "[${now}] $1" | tee -a "${LOG_FILE}" +log_info() { +# now="$(date +"%F %T %Z")" +# printf "%s\n" "[${now}] $1" | tee -a "${LOG_FILE}" + printf "%s\n" "$1" | tee -a "${LOG_FILE}" } ################################################## @@ -369,10 +396,9 @@ log_info() # Arguments: # Message to log. ################################################## -log_header() -{ +log_header() { log_info "+-----------------------------------------------------------------------------" - log_info "+ $1" + log_info "+ $1: $(date)" log_info "+-----------------------------------------------------------------------------" } @@ -422,8 +448,7 @@ cleanup_log() { # Arguments: # Command to run. ################################################## -dry_run_wrapper() -{ +dry_run_wrapper() { if [ -z "$DRY_RUN" ]; then log_info "DRY RUN: $*" return 0 @@ -451,7 +476,7 @@ dry_run_wrapper() ################################################## log_runtime_info() { log_info "Host: ${HOSTNAME}" - log_info "OS Flavor: ${OS_FLAVOR}" + log_info "OS: ${OS_FLAVOR}" log_info "Drive: ${DRIVE}" log_info "Disk Type: ${DISK_TYPE}" log_info "Drive Model: ${DISK_MODEL}" @@ -476,8 +501,7 @@ log_runtime_info() { # 0 if success or failure. # 1 if timeout threshold exceeded. ################################################## -poll_selftest_complete() -{ +poll_selftest_complete() { l_poll_duration_seconds=0 while [ "${l_poll_duration_seconds}" -lt "${POLL_TIMEOUT_SECONDS}" ]; do smartctl --all "${DRIVE}" \ @@ -512,8 +536,7 @@ poll_selftest_complete() # - long # Test duration in seconds. ################################################## -run_smart_test() -{ +run_smart_test() { log_header "Running SMART $1 test" dry_run_wrapper "smartctl --test=\"$1\" \"${DRIVE}\"" log_info "SMART $1 test started, awaiting completion for $2 seconds ..." @@ -533,11 +556,10 @@ run_smart_test() # Arguments: # None ################################################## -run_badblocks_test() -{ +run_badblocks_test() { log_header "Running badblocks test" - if [ "${DISK_TYPE}" = "mechanical" ]; then - dry_run_wrapper "badblocks -b 4096 -wsv -e 1 -o \"${BB_File}\" \"${DRIVE}\"" + if [ "${DISK_TYPE}" != "SSD" ]; then + dry_run_wrapper "badblocks -b 4096 -wsv -e ${BB_E_ARG} -o \"${BB_File}\" \"${DRIVE}\"" else log_info "SKIPPED: badblocks for ${DISK_TYPE} device" fi @@ -553,7 +575,7 @@ run_badblocks_test() # None ################################################## log_full_device_info() { - log_header "SMART and non-SMART information" + log_header "Drive information" dry_run_wrapper "smartctl --xall --vendorattribute=7,hex48 \"${DRIVE}\" | tee -a \"${LOG_FILE}\"" }