monitor_disk_stats.sh
· 16 KiB · Bash
Raw
#!/bin/bash
# Script to check comprehensive disk statistics
# Author: Alex@portalZINE.de
# Date: 25.11.2025
#####################################
# CONFIGURATION
#####################################
DEFAULT_DEVICE="/dev/sda1" # Default device to monitor
# Ntfy Configuration
NTFY_URL="https://your-ntfy-server.com/topic"
# Leave the ":", when using a token
NTFY_TOKEN=":your_token_here"
# NTFY_TOKEN="user:password"
NTFY_TITLE="Whatever Title You Want"
#####################################
# Initialize output variable
OUTPUT=""
# Function to add to output
add_output() {
OUTPUT="${OUTPUT}${1}\n"
}
# Function to display usage
usage() {
cat << EOF
Usage: $(basename "$0") [OPTIONS] [DEVICE]
Check comprehensive disk statistics for a device.
OPTIONS:
-h, --help Show this help message
-d, --device DEVICE Specify device to check (e.g., /dev/sda1)
-q, --quiet Quiet mode - minimal output
-v, --verbose Verbose mode - show all available stats
-s, --section NAME Show only specific section(s) (comma-separated)
-l, --list-sections List all available sections and exit
-n, --ntfy Send notification via ntfy (no console output)
--ntfy-always Send ntfy notification AND show console output
AVAILABLE SECTIONS:
all Show all sections (default)
basic Basic disk usage (size, used, available)
inodes Inode usage statistics
filesystem Filesystem information (type, blocks)
mount Mount options
device Device information (lsblk output)
io I/O statistics (reads, writes, sectors)
analysis Usage analysis and warnings
consumers Top space consumers (largest directories)
ARGUMENTS:
DEVICE Device to check (default: $DEFAULT_DEVICE)
NTFY CONFIGURATION:
Configure ntfy settings at the top of the script:
- NTFY_URL Ntfy server URL
- NTFY_TOKEN Authentication token
- NTFY_TITLE Notification title
EXAMPLES:
$(basename "$0") # Check default device ($DEFAULT_DEVICE)
$(basename "$0") /dev/sdb1 # Check /dev/sdb1
$(basename "$0") -d /dev/nvme0n1p1 # Check NVMe device
$(basename "$0") --quiet /dev/sda2 # Quiet mode for /dev/sda2
$(basename "$0") -s basic # Show only basic usage
$(basename "$0") -s basic,analysis # Show basic usage and analysis
$(basename "$0") -s io,consumers /dev/sdb1 # Show I/O stats and top consumers
$(basename "$0") --list-sections # List all available sections
$(basename "$0") --ntfy # Send notification only (no output)
$(basename "$0") --ntfy-always # Send notification AND show output
EOF
exit 0
}
# Function to list all available sections
list_sections() {
cat << EOF
Available Sections:
===================
all - Show all sections (default behavior)
basic - Basic disk usage information
• Filesystem name
• Total size
• Used space
• Available space
• Usage percentage
• Mount point
inodes - Inode usage statistics
• Total inodes
• Used inodes
• Free inodes
• Inode usage percentage
filesystem - Filesystem information
• Filesystem type (ext4, xfs, etc.)
• Block size
• Total blocks
• Free blocks
• Available blocks
mount - Mount options
• Full mount command output
• Mount flags (rw, relatime, etc.)
device - Device information
• Device name
• Size
• Type
• Filesystem type
• Mount point
• Label
• UUID
io - I/O statistics
• Read operations
• Write operations
• Sectors read/written
• Read/write time
• I/O operations in progress
• Extended iostat (verbose mode only)
analysis - Usage analysis
• Disk space status (OK/WARNING/CRITICAL)
• Inode status (OK/WARNING/CRITICAL)
• Threshold-based alerts
consumers - Top space consumers
• Largest directories on the partition
• Top 10 (normal) or top 20 (verbose)
• Human-readable sizes
Usage Examples:
$(basename "$0") -s basic # Just the essentials
$(basename "$0") -s basic,analysis # Quick health check
$(basename "$0") -s io # I/O performance only
$(basename "$0") -s all # Everything (same as no -s flag)
EOF
exit 0
}
# Parse command line arguments
DEVICE=""
QUIET_MODE=false
VERBOSE_MODE=false
SECTIONS=""
NTFY_MODE=false
NTFY_ALWAYS=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
-l|--list-sections)
list_sections
;;
-d|--device)
DEVICE="$2"
shift 2
;;
-q|--quiet)
QUIET_MODE=true
shift
;;
-v|--verbose)
VERBOSE_MODE=true
shift
;;
-s|--section)
SECTIONS="$2"
shift 2
;;
-n|--ntfy)
NTFY_MODE=true
shift
;;
--ntfy-always)
NTFY_ALWAYS=true
shift
;;
-*)
echo "Error: Unknown option: $1"
echo "Use -h or --help for usage information"
exit 1
;;
*)
# Assume it's a device path
if [ -z "$DEVICE" ]; then
DEVICE="$1"
else
echo "Error: Multiple devices specified"
exit 1
fi
shift
;;
esac
done
# Use default device if none specified
if [ -z "$DEVICE" ]; then
DEVICE="$DEFAULT_DEVICE"
fi
# Function to check if a section should be displayed
should_show_section() {
local section=$1
# If no sections specified, show all
if [ -z "$SECTIONS" ]; then
return 0
fi
# If 'all' is specified, show everything
if [[ ",$SECTIONS," == *",all,"* ]]; then
return 0
fi
# Check if section is in the comma-separated list
if [[ ",$SECTIONS," == *",$section,"* ]]; then
return 0
fi
return 1
}
add_output "========================================"
add_output "Disk Statistics Report for $DEVICE"
add_output "========================================"
if [ "$QUIET_MODE" = false ]; then
add_output "Report generated: $(date '+%Y-%m-%d %H:%M:%S')"
fi
add_output ""
# Check if device exists
if [ ! -b "$DEVICE" ]; then
echo "Error: $DEVICE does not exist or is not a block device"
exit 1
fi
# Get mount point
MOUNT_POINT=$(df "$DEVICE" 2>/dev/null | awk 'NR==2 {print $6}')
if [ -z "$MOUNT_POINT" ]; then
echo "Error: $DEVICE is not mounted"
exit 1
fi
# Quiet mode - show only essential info
if [ "$QUIET_MODE" = true ]; then
QUIET_INFO=$(df -h "$DEVICE" | awk 'NR==2 {
print "Device: " $1
print "Size: " $2
print "Used: " $3
print "Available: " $4
print "Usage: " $5
print "Mount: " $6
}')
add_output "$QUIET_INFO"
USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$USAGE_PERCENT" -ge 90 ]; then
add_output "Status: ⚠️ CRITICAL (${USAGE_PERCENT}%)"
elif [ "$USAGE_PERCENT" -ge 80 ]; then
add_output "Status: ⚠️ WARNING (${USAGE_PERCENT}%)"
else
add_output "Status: ✓ OK (${USAGE_PERCENT}%)"
fi
# Print all output at once
echo -e "$OUTPUT"
exit 0
fi
if should_show_section "basic"; then
add_output "========== BASIC DISK USAGE =========="
BASIC_USAGE=$(df -h "$DEVICE" | awk 'NR==2 {
print " Filesystem: " $1
print " Total Size: " $2
print " Used: " $3
print " Available: " $4
print " Usage: " $5
print " Mount Point: " $6
}')
add_output "$BASIC_USAGE"
add_output ""
fi
if should_show_section "inodes"; then
add_output "========== INODE USAGE =========="
INODE_USAGE=$(df -hi "$DEVICE" | awk 'NR==2 {
print " Total Inodes: " $2
print " Used Inodes: " $3
print " Free Inodes: " $4
print " Inode Usage: " $5
}')
add_output "$INODE_USAGE"
add_output ""
fi
if should_show_section "filesystem"; then
add_output "========== FILESYSTEM INFORMATION =========="
# Filesystem type
FS_TYPE=$(df -T "$DEVICE" | awk 'NR==2 {print $2}')
add_output " Filesystem Type: $FS_TYPE"
# Block size information
BLOCK_SIZE=$(stat -f -c %s "$MOUNT_POINT" 2>/dev/null)
if [ -n "$BLOCK_SIZE" ]; then
add_output " Block Size: $BLOCK_SIZE bytes"
fi
# Total blocks
TOTAL_BLOCKS=$(stat -f -c %b "$MOUNT_POINT" 2>/dev/null)
FREE_BLOCKS=$(stat -f -c %f "$MOUNT_POINT" 2>/dev/null)
AVAIL_BLOCKS=$(stat -f -c %a "$MOUNT_POINT" 2>/dev/null)
if [ -n "$TOTAL_BLOCKS" ]; then
add_output " Total Blocks: $TOTAL_BLOCKS"
add_output " Free Blocks: $FREE_BLOCKS"
add_output " Available: $AVAIL_BLOCKS"
fi
add_output ""
fi
if should_show_section "mount"; then
add_output "========== MOUNT OPTIONS =========="
MOUNT_INFO=$(mount | grep "$DEVICE")
if [ -n "$MOUNT_INFO" ]; then
add_output " $MOUNT_INFO"
fi
add_output ""
fi
if should_show_section "device"; then
add_output "========== DISK DEVICE INFO =========="
if command -v lsblk &> /dev/null; then
LSBLK_INFO=$(lsblk "$DEVICE" -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,LABEL,UUID 2>/dev/null | sed 's/^/ /')
add_output "$LSBLK_INFO"
fi
add_output ""
fi
if should_show_section "io"; then
add_output "========== I/O STATISTICS =========="
if [ -f /proc/diskstats ]; then
# Extract device name without /dev/ prefix
DEVICE_NAME=$(basename "$DEVICE")
# Extract stats for the device
DISK_STATS=$(grep " $DEVICE_NAME " /proc/diskstats)
if [ -n "$DISK_STATS" ]; then
IO_STATS=$(echo "$DISK_STATS" | awk '{
print " Device: " $3
print " Reads: " $4
print " Reads Merged: " $5
print " Sectors Read: " $6
print " Read Time (ms): " $7
print " Writes: " $8
print " Writes Merged: " $9
print " Sectors Written: " $10
print " Write Time (ms): " $11
print " I/Os in Progress:" $12
print " I/O Time (ms): " $13
}')
add_output "$IO_STATS"
fi
fi
# iostat if available - show in verbose mode or if explicitly installed
if command -v iostat &> /dev/null && [ "$VERBOSE_MODE" = true ]; then
add_output ""
add_output " Extended I/O Stats:"
IOSTAT_INFO=$(iostat -x "$DEVICE" 1 1 2>/dev/null | tail -n +4 | sed 's/^/ /')
add_output "$IOSTAT_INFO"
fi
add_output ""
fi
if should_show_section "analysis"; then
add_output "========== USAGE ANALYSIS =========="
# Get usage percentage as a number
USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
INODE_PERCENT=$(df -i "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
# Disk space warning
add_output " Disk Space Status:"
if [ "$USAGE_PERCENT" -ge 90 ]; then
add_output " ⚠️ CRITICAL: Disk usage is critically high (${USAGE_PERCENT}%)!"
elif [ "$USAGE_PERCENT" -ge 80 ]; then
add_output " ⚠️ WARNING: Disk usage is high (${USAGE_PERCENT}%)"
elif [ "$USAGE_PERCENT" -ge 70 ]; then
add_output " ⚡ NOTICE: Disk usage is moderate (${USAGE_PERCENT}%)"
else
add_output " ✓ OK: Disk usage is within normal range (${USAGE_PERCENT}%)"
fi
# Inode warning
add_output ""
add_output " Inode Status:"
if [ "$INODE_PERCENT" -ge 90 ]; then
add_output " ⚠️ CRITICAL: Inode usage is critically high (${INODE_PERCENT}%)!"
elif [ "$INODE_PERCENT" -ge 80 ]; then
add_output " ⚠️ WARNING: Inode usage is high (${INODE_PERCENT}%)"
else
add_output " ✓ OK: Inode usage is within normal range (${INODE_PERCENT}%)"
fi
add_output ""
fi
if should_show_section "consumers"; then
add_output "========== TOP SPACE CONSUMERS =========="
if [ -d "$MOUNT_POINT" ]; then
add_output " Largest directories in $MOUNT_POINT:"
if [ "$VERBOSE_MODE" = true ]; then
# Show top 20 in verbose mode
TOP_DIRS=$(du -h "$MOUNT_POINT" --max-depth=1 2>/dev/null | sort -rh | head -20 | sed 's/^/ /')
else
# Show top 10 in normal mode
TOP_DIRS=$(du -h "$MOUNT_POINT" --max-depth=1 2>/dev/null | sort -rh | head -10 | sed 's/^/ /')
fi
add_output "$TOP_DIRS"
fi
add_output ""
fi
add_output "========================================"
add_output "Report completed: $(date '+%Y-%m-%d %H:%M:%S')"
add_output "========================================"
# Function to send ntfy notification
send_ntfy() {
local message="$1"
local priority="${2:-default}"
local tags="${3:-}"
if [ -z "$NTFY_URL" ] || [ -z "$NTFY_TOKEN" ]; then
echo "Error: Ntfy configuration is incomplete"
return 1
fi
local curl_args=(-u "$NTFY_TOKEN")
if [ -n "$NTFY_TITLE" ]; then
curl_args+=(-H "Title: $NTFY_TITLE")
fi
if [ -n "$priority" ] && [ "$priority" != "default" ]; then
curl_args+=(-H "Priority: $priority")
fi
if [ -n "$tags" ]; then
curl_args+=(-H "Tags: $tags")
fi
curl_args+=(-d "$message" "$NTFY_URL")
curl -s "${curl_args[@]}" > /dev/null
return $?
}
# Prepare notification if ntfy is enabled
if [ "$NTFY_MODE" = true ] || [ "$NTFY_ALWAYS" = true ]; then
# Get key metrics for determining priority
USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
INODE_PERCENT=$(df -i "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
# Determine priority and tags based on usage
PRIORITY="default"
TAGS="white_check_mark"
if [ "$USAGE_PERCENT" -ge 90 ] || [ "$INODE_PERCENT" -ge 90 ]; then
PRIORITY="urgent"
TAGS="warning,skull"
elif [ "$USAGE_PERCENT" -ge 80 ] || [ "$INODE_PERCENT" -ge 80 ]; then
PRIORITY="high"
TAGS="warning"
elif [ "$USAGE_PERCENT" -ge 70 ] || [ "$INODE_PERCENT" -ge 70 ]; then
PRIORITY="default"
TAGS="zap"
fi
# Send the full output as notification
NTFY_MESSAGE=$(echo -e "$OUTPUT")
# Send notification
if send_ntfy "$NTFY_MESSAGE" "$PRIORITY" "$TAGS"; then
if [ "$NTFY_MODE" = true ]; then
echo "Notification sent successfully to ntfy"
exit 0
fi
else
echo "Error: Failed to send ntfy notification"
if [ "$NTFY_MODE" = true ]; then
exit 1
fi
fi
fi
# Print all output at once at the end (unless ntfy-only mode)
if [ "$NTFY_MODE" = false ]; then
echo -e "$OUTPUT"
fi
| 1 | |
| 2 | #!/bin/bash |
| 3 | |
| 4 | # Script to check comprehensive disk statistics |
| 5 | # Author: Alex@portalZINE.de |
| 6 | # Date: 25.11.2025 |
| 7 | |
| 8 | ##################################### |
| 9 | # CONFIGURATION |
| 10 | ##################################### |
| 11 | DEFAULT_DEVICE="/dev/sda1" # Default device to monitor |
| 12 | |
| 13 | # Ntfy Configuration |
| 14 | NTFY_URL="https://your-ntfy-server.com/topic" |
| 15 | # Leave the ":", when using a token |
| 16 | NTFY_TOKEN=":your_token_here" |
| 17 | # NTFY_TOKEN="user:password" |
| 18 | NTFY_TITLE="Whatever Title You Want" |
| 19 | ##################################### |
| 20 | |
| 21 | # Initialize output variable |
| 22 | OUTPUT="" |
| 23 | |
| 24 | # Function to add to output |
| 25 | add_output() { |
| 26 | OUTPUT="${OUTPUT}${1}\n" |
| 27 | } |
| 28 | |
| 29 | # Function to display usage |
| 30 | usage() { |
| 31 | cat << EOF |
| 32 | Usage: $(basename "$0") [OPTIONS] [DEVICE] |
| 33 | |
| 34 | Check comprehensive disk statistics for a device. |
| 35 | |
| 36 | OPTIONS: |
| 37 | -h, --help Show this help message |
| 38 | -d, --device DEVICE Specify device to check (e.g., /dev/sda1) |
| 39 | -q, --quiet Quiet mode - minimal output |
| 40 | -v, --verbose Verbose mode - show all available stats |
| 41 | -s, --section NAME Show only specific section(s) (comma-separated) |
| 42 | -l, --list-sections List all available sections and exit |
| 43 | -n, --ntfy Send notification via ntfy (no console output) |
| 44 | --ntfy-always Send ntfy notification AND show console output |
| 45 | |
| 46 | AVAILABLE SECTIONS: |
| 47 | all Show all sections (default) |
| 48 | basic Basic disk usage (size, used, available) |
| 49 | inodes Inode usage statistics |
| 50 | filesystem Filesystem information (type, blocks) |
| 51 | mount Mount options |
| 52 | device Device information (lsblk output) |
| 53 | io I/O statistics (reads, writes, sectors) |
| 54 | analysis Usage analysis and warnings |
| 55 | consumers Top space consumers (largest directories) |
| 56 | |
| 57 | ARGUMENTS: |
| 58 | DEVICE Device to check (default: $DEFAULT_DEVICE) |
| 59 | |
| 60 | NTFY CONFIGURATION: |
| 61 | Configure ntfy settings at the top of the script: |
| 62 | - NTFY_URL Ntfy server URL |
| 63 | - NTFY_TOKEN Authentication token |
| 64 | - NTFY_TITLE Notification title |
| 65 | |
| 66 | EXAMPLES: |
| 67 | $(basename "$0") # Check default device ($DEFAULT_DEVICE) |
| 68 | $(basename "$0") /dev/sdb1 # Check /dev/sdb1 |
| 69 | $(basename "$0") -d /dev/nvme0n1p1 # Check NVMe device |
| 70 | $(basename "$0") --quiet /dev/sda2 # Quiet mode for /dev/sda2 |
| 71 | $(basename "$0") -s basic # Show only basic usage |
| 72 | $(basename "$0") -s basic,analysis # Show basic usage and analysis |
| 73 | $(basename "$0") -s io,consumers /dev/sdb1 # Show I/O stats and top consumers |
| 74 | $(basename "$0") --list-sections # List all available sections |
| 75 | $(basename "$0") --ntfy # Send notification only (no output) |
| 76 | $(basename "$0") --ntfy-always # Send notification AND show output |
| 77 | |
| 78 | EOF |
| 79 | exit 0 |
| 80 | } |
| 81 | |
| 82 | # Function to list all available sections |
| 83 | list_sections() { |
| 84 | cat << EOF |
| 85 | Available Sections: |
| 86 | =================== |
| 87 | |
| 88 | all - Show all sections (default behavior) |
| 89 | basic - Basic disk usage information |
| 90 | • Filesystem name |
| 91 | • Total size |
| 92 | • Used space |
| 93 | • Available space |
| 94 | • Usage percentage |
| 95 | • Mount point |
| 96 | |
| 97 | inodes - Inode usage statistics |
| 98 | • Total inodes |
| 99 | • Used inodes |
| 100 | • Free inodes |
| 101 | • Inode usage percentage |
| 102 | |
| 103 | filesystem - Filesystem information |
| 104 | • Filesystem type (ext4, xfs, etc.) |
| 105 | • Block size |
| 106 | • Total blocks |
| 107 | • Free blocks |
| 108 | • Available blocks |
| 109 | |
| 110 | mount - Mount options |
| 111 | • Full mount command output |
| 112 | • Mount flags (rw, relatime, etc.) |
| 113 | |
| 114 | device - Device information |
| 115 | • Device name |
| 116 | • Size |
| 117 | • Type |
| 118 | • Filesystem type |
| 119 | • Mount point |
| 120 | • Label |
| 121 | • UUID |
| 122 | |
| 123 | io - I/O statistics |
| 124 | • Read operations |
| 125 | • Write operations |
| 126 | • Sectors read/written |
| 127 | • Read/write time |
| 128 | • I/O operations in progress |
| 129 | • Extended iostat (verbose mode only) |
| 130 | |
| 131 | analysis - Usage analysis |
| 132 | • Disk space status (OK/WARNING/CRITICAL) |
| 133 | • Inode status (OK/WARNING/CRITICAL) |
| 134 | • Threshold-based alerts |
| 135 | |
| 136 | consumers - Top space consumers |
| 137 | • Largest directories on the partition |
| 138 | • Top 10 (normal) or top 20 (verbose) |
| 139 | • Human-readable sizes |
| 140 | |
| 141 | Usage Examples: |
| 142 | $(basename "$0") -s basic # Just the essentials |
| 143 | $(basename "$0") -s basic,analysis # Quick health check |
| 144 | $(basename "$0") -s io # I/O performance only |
| 145 | $(basename "$0") -s all # Everything (same as no -s flag) |
| 146 | |
| 147 | EOF |
| 148 | exit 0 |
| 149 | } |
| 150 | |
| 151 | # Parse command line arguments |
| 152 | DEVICE="" |
| 153 | QUIET_MODE=false |
| 154 | VERBOSE_MODE=false |
| 155 | SECTIONS="" |
| 156 | NTFY_MODE=false |
| 157 | NTFY_ALWAYS=false |
| 158 | |
| 159 | while [[ $# -gt 0 ]]; do |
| 160 | case $1 in |
| 161 | -h|--help) |
| 162 | usage |
| 163 | ;; |
| 164 | -l|--list-sections) |
| 165 | list_sections |
| 166 | ;; |
| 167 | -d|--device) |
| 168 | DEVICE="$2" |
| 169 | shift 2 |
| 170 | ;; |
| 171 | -q|--quiet) |
| 172 | QUIET_MODE=true |
| 173 | shift |
| 174 | ;; |
| 175 | -v|--verbose) |
| 176 | VERBOSE_MODE=true |
| 177 | shift |
| 178 | ;; |
| 179 | -s|--section) |
| 180 | SECTIONS="$2" |
| 181 | shift 2 |
| 182 | ;; |
| 183 | -n|--ntfy) |
| 184 | NTFY_MODE=true |
| 185 | shift |
| 186 | ;; |
| 187 | --ntfy-always) |
| 188 | NTFY_ALWAYS=true |
| 189 | shift |
| 190 | ;; |
| 191 | -*) |
| 192 | echo "Error: Unknown option: $1" |
| 193 | echo "Use -h or --help for usage information" |
| 194 | exit 1 |
| 195 | ;; |
| 196 | *) |
| 197 | # Assume it's a device path |
| 198 | if [ -z "$DEVICE" ]; then |
| 199 | DEVICE="$1" |
| 200 | else |
| 201 | echo "Error: Multiple devices specified" |
| 202 | exit 1 |
| 203 | fi |
| 204 | shift |
| 205 | ;; |
| 206 | esac |
| 207 | done |
| 208 | |
| 209 | # Use default device if none specified |
| 210 | if [ -z "$DEVICE" ]; then |
| 211 | DEVICE="$DEFAULT_DEVICE" |
| 212 | fi |
| 213 | |
| 214 | # Function to check if a section should be displayed |
| 215 | should_show_section() { |
| 216 | local section=$1 |
| 217 | |
| 218 | # If no sections specified, show all |
| 219 | if [ -z "$SECTIONS" ]; then |
| 220 | return 0 |
| 221 | fi |
| 222 | |
| 223 | # If 'all' is specified, show everything |
| 224 | if [[ ",$SECTIONS," == *",all,"* ]]; then |
| 225 | return 0 |
| 226 | fi |
| 227 | |
| 228 | # Check if section is in the comma-separated list |
| 229 | if [[ ",$SECTIONS," == *",$section,"* ]]; then |
| 230 | return 0 |
| 231 | fi |
| 232 | |
| 233 | return 1 |
| 234 | } |
| 235 | |
| 236 | add_output "========================================" |
| 237 | add_output "Disk Statistics Report for $DEVICE" |
| 238 | add_output "========================================" |
| 239 | if [ "$QUIET_MODE" = false ]; then |
| 240 | add_output "Report generated: $(date '+%Y-%m-%d %H:%M:%S')" |
| 241 | fi |
| 242 | add_output "" |
| 243 | |
| 244 | # Check if device exists |
| 245 | if [ ! -b "$DEVICE" ]; then |
| 246 | echo "Error: $DEVICE does not exist or is not a block device" |
| 247 | exit 1 |
| 248 | fi |
| 249 | |
| 250 | # Get mount point |
| 251 | MOUNT_POINT=$(df "$DEVICE" 2>/dev/null | awk 'NR==2 {print $6}') |
| 252 | |
| 253 | if [ -z "$MOUNT_POINT" ]; then |
| 254 | echo "Error: $DEVICE is not mounted" |
| 255 | exit 1 |
| 256 | fi |
| 257 | |
| 258 | # Quiet mode - show only essential info |
| 259 | if [ "$QUIET_MODE" = true ]; then |
| 260 | QUIET_INFO=$(df -h "$DEVICE" | awk 'NR==2 { |
| 261 | print "Device: " $1 |
| 262 | print "Size: " $2 |
| 263 | print "Used: " $3 |
| 264 | print "Available: " $4 |
| 265 | print "Usage: " $5 |
| 266 | print "Mount: " $6 |
| 267 | }') |
| 268 | add_output "$QUIET_INFO" |
| 269 | |
| 270 | USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//') |
| 271 | if [ "$USAGE_PERCENT" -ge 90 ]; then |
| 272 | add_output "Status: ⚠️ CRITICAL (${USAGE_PERCENT}%)" |
| 273 | elif [ "$USAGE_PERCENT" -ge 80 ]; then |
| 274 | add_output "Status: ⚠️ WARNING (${USAGE_PERCENT}%)" |
| 275 | else |
| 276 | add_output "Status: ✓ OK (${USAGE_PERCENT}%)" |
| 277 | fi |
| 278 | |
| 279 | # Print all output at once |
| 280 | echo -e "$OUTPUT" |
| 281 | exit 0 |
| 282 | fi |
| 283 | |
| 284 | if should_show_section "basic"; then |
| 285 | add_output "========== BASIC DISK USAGE ==========" |
| 286 | BASIC_USAGE=$(df -h "$DEVICE" | awk 'NR==2 { |
| 287 | print " Filesystem: " $1 |
| 288 | print " Total Size: " $2 |
| 289 | print " Used: " $3 |
| 290 | print " Available: " $4 |
| 291 | print " Usage: " $5 |
| 292 | print " Mount Point: " $6 |
| 293 | }') |
| 294 | add_output "$BASIC_USAGE" |
| 295 | add_output "" |
| 296 | fi |
| 297 | |
| 298 | if should_show_section "inodes"; then |
| 299 | add_output "========== INODE USAGE ==========" |
| 300 | INODE_USAGE=$(df -hi "$DEVICE" | awk 'NR==2 { |
| 301 | print " Total Inodes: " $2 |
| 302 | print " Used Inodes: " $3 |
| 303 | print " Free Inodes: " $4 |
| 304 | print " Inode Usage: " $5 |
| 305 | }') |
| 306 | add_output "$INODE_USAGE" |
| 307 | add_output "" |
| 308 | fi |
| 309 | |
| 310 | if should_show_section "filesystem"; then |
| 311 | add_output "========== FILESYSTEM INFORMATION ==========" |
| 312 | # Filesystem type |
| 313 | FS_TYPE=$(df -T "$DEVICE" | awk 'NR==2 {print $2}') |
| 314 | add_output " Filesystem Type: $FS_TYPE" |
| 315 | |
| 316 | # Block size information |
| 317 | BLOCK_SIZE=$(stat -f -c %s "$MOUNT_POINT" 2>/dev/null) |
| 318 | if [ -n "$BLOCK_SIZE" ]; then |
| 319 | add_output " Block Size: $BLOCK_SIZE bytes" |
| 320 | fi |
| 321 | |
| 322 | # Total blocks |
| 323 | TOTAL_BLOCKS=$(stat -f -c %b "$MOUNT_POINT" 2>/dev/null) |
| 324 | FREE_BLOCKS=$(stat -f -c %f "$MOUNT_POINT" 2>/dev/null) |
| 325 | AVAIL_BLOCKS=$(stat -f -c %a "$MOUNT_POINT" 2>/dev/null) |
| 326 | |
| 327 | if [ -n "$TOTAL_BLOCKS" ]; then |
| 328 | add_output " Total Blocks: $TOTAL_BLOCKS" |
| 329 | add_output " Free Blocks: $FREE_BLOCKS" |
| 330 | add_output " Available: $AVAIL_BLOCKS" |
| 331 | fi |
| 332 | add_output "" |
| 333 | fi |
| 334 | |
| 335 | if should_show_section "mount"; then |
| 336 | add_output "========== MOUNT OPTIONS ==========" |
| 337 | MOUNT_INFO=$(mount | grep "$DEVICE") |
| 338 | if [ -n "$MOUNT_INFO" ]; then |
| 339 | add_output " $MOUNT_INFO" |
| 340 | fi |
| 341 | add_output "" |
| 342 | fi |
| 343 | |
| 344 | if should_show_section "device"; then |
| 345 | add_output "========== DISK DEVICE INFO ==========" |
| 346 | if command -v lsblk &> /dev/null; then |
| 347 | LSBLK_INFO=$(lsblk "$DEVICE" -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,LABEL,UUID 2>/dev/null | sed 's/^/ /') |
| 348 | add_output "$LSBLK_INFO" |
| 349 | fi |
| 350 | add_output "" |
| 351 | fi |
| 352 | |
| 353 | if should_show_section "io"; then |
| 354 | add_output "========== I/O STATISTICS ==========" |
| 355 | if [ -f /proc/diskstats ]; then |
| 356 | # Extract device name without /dev/ prefix |
| 357 | DEVICE_NAME=$(basename "$DEVICE") |
| 358 | # Extract stats for the device |
| 359 | DISK_STATS=$(grep " $DEVICE_NAME " /proc/diskstats) |
| 360 | if [ -n "$DISK_STATS" ]; then |
| 361 | IO_STATS=$(echo "$DISK_STATS" | awk '{ |
| 362 | print " Device: " $3 |
| 363 | print " Reads: " $4 |
| 364 | print " Reads Merged: " $5 |
| 365 | print " Sectors Read: " $6 |
| 366 | print " Read Time (ms): " $7 |
| 367 | print " Writes: " $8 |
| 368 | print " Writes Merged: " $9 |
| 369 | print " Sectors Written: " $10 |
| 370 | print " Write Time (ms): " $11 |
| 371 | print " I/Os in Progress:" $12 |
| 372 | print " I/O Time (ms): " $13 |
| 373 | }') |
| 374 | add_output "$IO_STATS" |
| 375 | fi |
| 376 | fi |
| 377 | |
| 378 | # iostat if available - show in verbose mode or if explicitly installed |
| 379 | if command -v iostat &> /dev/null && [ "$VERBOSE_MODE" = true ]; then |
| 380 | add_output "" |
| 381 | add_output " Extended I/O Stats:" |
| 382 | IOSTAT_INFO=$(iostat -x "$DEVICE" 1 1 2>/dev/null | tail -n +4 | sed 's/^/ /') |
| 383 | add_output "$IOSTAT_INFO" |
| 384 | fi |
| 385 | add_output "" |
| 386 | fi |
| 387 | |
| 388 | if should_show_section "analysis"; then |
| 389 | add_output "========== USAGE ANALYSIS ==========" |
| 390 | |
| 391 | # Get usage percentage as a number |
| 392 | USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//') |
| 393 | INODE_PERCENT=$(df -i "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//') |
| 394 | |
| 395 | # Disk space warning |
| 396 | add_output " Disk Space Status:" |
| 397 | if [ "$USAGE_PERCENT" -ge 90 ]; then |
| 398 | add_output " ⚠️ CRITICAL: Disk usage is critically high (${USAGE_PERCENT}%)!" |
| 399 | elif [ "$USAGE_PERCENT" -ge 80 ]; then |
| 400 | add_output " ⚠️ WARNING: Disk usage is high (${USAGE_PERCENT}%)" |
| 401 | elif [ "$USAGE_PERCENT" -ge 70 ]; then |
| 402 | add_output " ⚡ NOTICE: Disk usage is moderate (${USAGE_PERCENT}%)" |
| 403 | else |
| 404 | add_output " ✓ OK: Disk usage is within normal range (${USAGE_PERCENT}%)" |
| 405 | fi |
| 406 | |
| 407 | # Inode warning |
| 408 | add_output "" |
| 409 | add_output " Inode Status:" |
| 410 | if [ "$INODE_PERCENT" -ge 90 ]; then |
| 411 | add_output " ⚠️ CRITICAL: Inode usage is critically high (${INODE_PERCENT}%)!" |
| 412 | elif [ "$INODE_PERCENT" -ge 80 ]; then |
| 413 | add_output " ⚠️ WARNING: Inode usage is high (${INODE_PERCENT}%)" |
| 414 | else |
| 415 | add_output " ✓ OK: Inode usage is within normal range (${INODE_PERCENT}%)" |
| 416 | fi |
| 417 | add_output "" |
| 418 | fi |
| 419 | |
| 420 | if should_show_section "consumers"; then |
| 421 | add_output "========== TOP SPACE CONSUMERS ==========" |
| 422 | if [ -d "$MOUNT_POINT" ]; then |
| 423 | add_output " Largest directories in $MOUNT_POINT:" |
| 424 | if [ "$VERBOSE_MODE" = true ]; then |
| 425 | # Show top 20 in verbose mode |
| 426 | TOP_DIRS=$(du -h "$MOUNT_POINT" --max-depth=1 2>/dev/null | sort -rh | head -20 | sed 's/^/ /') |
| 427 | else |
| 428 | # Show top 10 in normal mode |
| 429 | TOP_DIRS=$(du -h "$MOUNT_POINT" --max-depth=1 2>/dev/null | sort -rh | head -10 | sed 's/^/ /') |
| 430 | fi |
| 431 | add_output "$TOP_DIRS" |
| 432 | fi |
| 433 | add_output "" |
| 434 | fi |
| 435 | |
| 436 | add_output "========================================" |
| 437 | add_output "Report completed: $(date '+%Y-%m-%d %H:%M:%S')" |
| 438 | add_output "========================================" |
| 439 | |
| 440 | # Function to send ntfy notification |
| 441 | send_ntfy() { |
| 442 | local message="$1" |
| 443 | local priority="${2:-default}" |
| 444 | local tags="${3:-}" |
| 445 | |
| 446 | if [ -z "$NTFY_URL" ] || [ -z "$NTFY_TOKEN" ]; then |
| 447 | echo "Error: Ntfy configuration is incomplete" |
| 448 | return 1 |
| 449 | fi |
| 450 | |
| 451 | local curl_args=(-u "$NTFY_TOKEN") |
| 452 | |
| 453 | if [ -n "$NTFY_TITLE" ]; then |
| 454 | curl_args+=(-H "Title: $NTFY_TITLE") |
| 455 | fi |
| 456 | |
| 457 | if [ -n "$priority" ] && [ "$priority" != "default" ]; then |
| 458 | curl_args+=(-H "Priority: $priority") |
| 459 | fi |
| 460 | |
| 461 | if [ -n "$tags" ]; then |
| 462 | curl_args+=(-H "Tags: $tags") |
| 463 | fi |
| 464 | |
| 465 | curl_args+=(-d "$message" "$NTFY_URL") |
| 466 | |
| 467 | curl -s "${curl_args[@]}" > /dev/null |
| 468 | return $? |
| 469 | } |
| 470 | |
| 471 | # Prepare notification if ntfy is enabled |
| 472 | if [ "$NTFY_MODE" = true ] || [ "$NTFY_ALWAYS" = true ]; then |
| 473 | # Get key metrics for determining priority |
| 474 | USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//') |
| 475 | INODE_PERCENT=$(df -i "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//') |
| 476 | |
| 477 | # Determine priority and tags based on usage |
| 478 | PRIORITY="default" |
| 479 | TAGS="white_check_mark" |
| 480 | |
| 481 | if [ "$USAGE_PERCENT" -ge 90 ] || [ "$INODE_PERCENT" -ge 90 ]; then |
| 482 | PRIORITY="urgent" |
| 483 | TAGS="warning,skull" |
| 484 | elif [ "$USAGE_PERCENT" -ge 80 ] || [ "$INODE_PERCENT" -ge 80 ]; then |
| 485 | PRIORITY="high" |
| 486 | TAGS="warning" |
| 487 | elif [ "$USAGE_PERCENT" -ge 70 ] || [ "$INODE_PERCENT" -ge 70 ]; then |
| 488 | PRIORITY="default" |
| 489 | TAGS="zap" |
| 490 | fi |
| 491 | |
| 492 | # Send the full output as notification |
| 493 | NTFY_MESSAGE=$(echo -e "$OUTPUT") |
| 494 | |
| 495 | # Send notification |
| 496 | if send_ntfy "$NTFY_MESSAGE" "$PRIORITY" "$TAGS"; then |
| 497 | if [ "$NTFY_MODE" = true ]; then |
| 498 | echo "Notification sent successfully to ntfy" |
| 499 | exit 0 |
| 500 | fi |
| 501 | else |
| 502 | echo "Error: Failed to send ntfy notification" |
| 503 | if [ "$NTFY_MODE" = true ]; then |
| 504 | exit 1 |
| 505 | fi |
| 506 | fi |
| 507 | fi |
| 508 | |
| 509 | # Print all output at once at the end (unless ntfy-only mode) |
| 510 | if [ "$NTFY_MODE" = false ]; then |
| 511 | echo -e "$OUTPUT" |
| 512 | fi |
| 513 |