#!/bin/sh # .. vim: foldmarker=[[[,]]]:foldmethod=marker # This script will try to run a smart test on all compatible devices. # The test can be passed as first argument. # Vars [[[ debug="0" ## Colors [[[ c_redb='\033[1;31m' c_magentab='\033[1;35m' c_reset='\033[0m' ## ]]] temp_dir=$(mktemp -d -t smart.run.test-XXXXXX.tmp) smart_device_list="${temp_dir}/smart.device.list" # ]]] # Functions ## Print help message [[[ print_help_message() { cat << HELP usage: $(basename "${0}") [TEST TYPE] Try to run a SMART test on all compatible devices. positional arguments: TEST TYPE Test type to run on all devices. Can be: short (default) - runs SMART Short Self Test (usually under ten minutes) long - runs SMART Extended Self Test (tens of minutes to several hours). EXAMPLE : - Run a short test $(basename "${0}") short HELP } ## ]]] ## Check arguments [[[ ## Ensure arguments have the expected value check_arguments() { if ! printf -- '%s' "${test_to_run}" | grep -q -E -- "(short|long)"; then printf "${c_redb}%-6b${c_reset}\n" "ERROR : Can't manage '${test_to_run}' test type." print_help_message exit 3 fi } ## ]]] ## Manage the arguments [[[ manage_arguments() { _nb_arg="${#}" _all_args="${*}" [ "${debug}" -eq "0" ] && printf "${c_magentab}%-6b${c_reset}\n" "DEBUG : manage_arguments func − ${_nb_arg} argument(s) to manage. List:\n ${_all_args}" ## If help is needed [[[ if printf -- '%s' "${_all_args}" | grep -q -E -- "-h|--help|help"; then print_help_message exit 0 fi ## ]]] ## Manage arguments [[[ case "${_nb_arg}" in 0 ) ## Set test to run to default (short) test_to_run="short" ;; 1 ) test_to_run="${1}" ;; * ) ## More than managed number of arguments print_help_message exit 2 ;; esac ## ]]] ## Verify the content of arguments check_arguments } ## ]]] ## Test if a disk really support SMART [[[ ## Smartctl can give an health status even without a full support ## of SMART for some type (eg. scsi or megaraid). ## Exemple : SMART support is: Unavailable - device lacks SMART capability. is_disk_path_support_smart() { _disk_path="${1}" _disk_type="${2}" _smarctl_support_result="${temp_dir}/smart.support.$(basename "${_disk_path}")" smart_support_msg="" [ "${debug}" -eq "0" ] && printf "${c_magentab}%-6b${c_reset}\n" "DEBUG : is_disk_path_support_smart func − check if SMART is supported on : ${_disk_path} with ${_disk_type} TYPE." ## Create file true > "${_smarctl_support_result}" ## Grep only "support" lines from disk's informations smartctl --device="${_disk_type}" --info -- "${_disk_path}" | grep -E "^SMART support is:" -- >> "${_smarctl_support_result}" ## If the file is not empty if test -s "${_smarctl_support_result}"; then ## Parse all "support" lines while IFS= read -r _line; do ### If a line doesn't contain enable or available if ! printf -- '%s' "${_line}" | grep -q -E -- "(Enabled|Available)" then smart_support_msg="${_line}" [ "${debug}" -eq "0" ] && printf "${c_magentab}%-6b${c_reset}\n" "DEBUG : is_disk_path_support_smart func − SMART is not fully supported on : ${_disk_path} with ${_disk_type} TYPE. See smartctl informations :\n${smart_support_msg}" fi done < "${_smarctl_support_result}" else smart_support_msg="ERROR : Enable to open ${_disk_path} DEVICE with ${_disk_type} TYPE. Be sure to have sufficient permission for this device." printf "${c_redb}%-6b${c_reset}\n" "ERROR : Enable to open ${_disk_path} DEVICE with ${_disk_type} TYPE. Be sure to have sufficient permission for this device." fi } ## ]]] ## Test the type of disk with smartctl [[[ ## Cause the scanned one might not be the one to use choose_correct_type() { _disk_path="${1}" _scanned_disk_type="${2}" _default_disk_type="auto" disk_type="" for _test_disk_type in "${_default_disk_type}" "${_scanned_disk_type}"; do is_disk_path_support_smart "${_disk_path}" "${_test_disk_type}" ## If no message, the type is correct if [ -z "${smart_support_msg}" ]; then disk_type="${_test_disk_type}" [ "${debug}" -eq "0" ] && printf "${c_magentab}%-6b${c_reset}\n" "DEBUG : choose_correct_disk_type func − SMART seems fully supported on : ${_disk_path} with ${_test_disk_type} TYPE." return fi done } ## ]]] # Get the arguments's list and check values manage_arguments "${@}" # Create file true > "${smart_device_list}" # Get the list of all available devices smartctl --scan >> "${smart_device_list}" # If the devices list is not empty if test -s "${smart_device_list}"; then while IFS= read -r LINE; do ## Get device path disk_path=$(echo "${LINE}" | cut -d" " -f1) ## Get scanned type scanned_disk_type=$(echo "${LINE}" | cut -d" " -f3) [ "${debug}" -eq "0" ] && printf "\n${c_magentab}%-6b${c_reset}\n" "DEBUG : smartctl scan ${disk_path} with TYPE ${scanned_disk_type}." ## Try to determine the best type choose_correct_type "${disk_path}" "${scanned_disk_type}" ## If no correct type was found for this device if [ -z "${disk_type}" ]; then [ "${debug}" -eq "0" ] && printf "${c_magentab}%-6b${c_reset}\n" "DEBUG : SMART is not fully supported on ${disk_path}." else [ "${debug}" -eq "0" ] && printf "${c_magentab}%-6b${c_reset}\n" "DEBUG : Run a ${test_to_run} SMART test on disk ${disk_path}, with ${disk_type} TYPE." smartctl --device="${disk_type}" --test="${test_to_run}" -- "${disk_path}" fi done < "${smart_device_list}" # If the file is empty else printf "${c_redb}%-6b${c_reset}\n" "ERROR : The device list looks empty, check ${smart_device_list} file and \`smartctl --scan\` command." exit 1 fi # Remove temp_dir if debug is disable [ "${debug}" -eq "0" ] || rm -rf -- "${temp_dir}" exit 0