Add robustness improvements: bash version check, cleanup trap, hostname sanitization, parallel job limit

- Add bash 4.2+ version check since script uses declare -g -A
- Add cleanup trap (EXIT/INT/TERM) for SMART_CACHE_DIR temp directory
- Sanitize hostname to strip unexpected characters
- Limit parallel SMART collection to 10 concurrent jobs

Fixes #25

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 18:09:40 -05:00
parent b79c69be99
commit 71f83e82c5

View File

@@ -11,8 +11,25 @@
# Note: Not using -u (nounset) as script uses ${var:-default} patterns # Note: Not using -u (nounset) as script uses ${var:-default} patterns
set -o pipefail set -o pipefail
# Require bash 4.2+ for declare -g -A (global associative arrays)
if ((BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 2))); then
echo "ERROR: This script requires Bash 4.2 or higher (current: $BASH_VERSION)" >&2
exit 1
fi
VERSION="1.1.0" VERSION="1.1.0"
#------------------------------------------------------------------------------
# Cleanup Trap
# Ensures temporary directories are removed on exit or interruption
#------------------------------------------------------------------------------
cleanup() {
if [[ -n "${SMART_CACHE_DIR:-}" && -d "$SMART_CACHE_DIR" ]]; then
rm -rf "$SMART_CACHE_DIR"
fi
}
trap cleanup EXIT INT TERM
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Path Constants # Path Constants
# Centralized path definitions to avoid hardcoding throughout the script # Centralized path definitions to avoid hardcoding throughout the script
@@ -607,7 +624,7 @@ get_storage_controllers() {
# Values: PCI path strings (for --show-pci option) # Values: PCI path strings (for --show-pci option)
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
build_drive_map() { build_drive_map() {
local host="$(hostname)" local host="$(hostname | tr -cd '[:alnum:]-_.')"
local mapping="${SERVER_MAPPINGS[$host]}" local mapping="${SERVER_MAPPINGS[$host]}"
# Declare global arrays directly # Declare global arrays directly
@@ -886,7 +903,7 @@ get_drive_smart_info() {
# Main Display Logic # Main Display Logic
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
HOSTNAME=$(hostname) HOSTNAME=$(hostname | tr -cd '[:alnum:]-_.')
CHASSIS_TYPE=${CHASSIS_TYPES[$HOSTNAME]:-"unknown"} CHASSIS_TYPE=${CHASSIS_TYPES[$HOSTNAME]:-"unknown"}
# Display chassis layout # Display chassis layout
@@ -977,15 +994,22 @@ if [[ "$SKIP_SMART" != true ]]; then
SMART_CACHE_DIR="$(mktemp -d)" SMART_CACHE_DIR="$(mktemp -d)"
log_info "Collecting SMART data in parallel..." log_info "Collecting SMART data in parallel..."
local max_parallel_jobs=10
local job_count=0
for bay in $all_bays; do for bay in $all_bays; do
device="${DRIVE_MAP[$bay]}" device="${DRIVE_MAP[$bay]}"
if [[ -n "$device" && "$device" != "EMPTY" && -b "/dev/$device" ]]; then if [[ -n "$device" && "$device" != "EMPTY" && -b "/dev/$device" ]]; then
# Launch background job to collect raw smartctl data # Launch background job to collect raw smartctl data
(sudo smartctl -A -i -H "/dev/$device" > "$SMART_CACHE_DIR/${device}.raw" 2>/dev/null) & (sudo smartctl -A -i -H "/dev/$device" > "$SMART_CACHE_DIR/${device}.raw" 2>/dev/null) &
((job_count++))
if ((job_count >= max_parallel_jobs)); then
wait -n 2>/dev/null || wait # wait -n requires bash 4.3+, fall back to wait
((job_count--))
fi
fi fi
done done
# Wait for all background SMART queries to complete # Wait for all remaining background SMART queries to complete
wait wait
log_info "SMART data collection complete" log_info "SMART data collection complete"
fi fi
@@ -1078,11 +1102,6 @@ for bay in $all_bays; do
fi fi
done done
# Clean up SMART cache directory
if [[ -n "${SMART_CACHE_DIR:-}" && -d "$SMART_CACHE_DIR" ]]; then
rm -rf "$SMART_CACHE_DIR"
fi
# NVMe drives (only show unmapped ones - mapped NVMe drives appear in main table) # NVMe drives (only show unmapped ones - mapped NVMe drives appear in main table)
nvme_devices=$(lsblk -d -n -o NAME,SIZE | grep "^nvme" 2>/dev/null) nvme_devices=$(lsblk -d -n -o NAME,SIZE | grep "^nvme" 2>/dev/null)
if [[ -n "$nvme_devices" ]]; then if [[ -n "$nvme_devices" ]]; then