February 16, 20251 yr Just wanted to share the following script I use to monitor availability of some of my systems and services. The script runs in CRON at a five minute interval, runs a set of checks and then creates a HTML file that is shown by a webserver process running on the same system. The script sends notifications to PUSHOVER so you get notified of important issues. The script stores the latest test results to make sure a notification only gets sent the moment a service becomes unavailable and not every 5 minutes. #!/bin/bash # Define output file OUTPUT_FILE="/var/www/html/monitor.html" # Start of the HTML file - styling cat <<EOF >$OUTPUT_FILE <html> <head> <title>Server Status</title> <style> body { font-family: 'Courier New', monospace; background-color: #f4f4f8; color: #333; } h1 { color: #0056b3; } table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #dddddd; padding: 8px; text-align: left; } th { background-color: #0056b3; color: #ffffff; } tr:nth-child(even) { background-color: #f2f2f2; } .check-name { width: 20%; } .ip-address { width: 20%; } .result { width: 20%; } .details { width: 30%; } .status-icon { width: 10%; } .green { width: 20px; height: 20px; background-color: green; border-radius: 50%; display: inline-block; } .red { width: 20px; height: 20px; background-color: red; border-radius: 50%; display: inline-block; } .orange { width: 20px; height: 20px; background-color: orange; border-radius: 50%; display: inline-block; } </style> </head> <body> <h1>Server Status Report</h1> <p>Last updated: $(TZ='Europe/Amsterdam' date '+%Y-%m-%d %H:%M:%S')</p> <table> <tr> <th>Status</th> <th>Check Name</th> <th>IP Address</th> <th>Result</th> <th>Details</th> </tr> EOF log_status_change() { local message=$1 local log_file="/home/marc/monitor/status_changes.log" echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> $log_file # Limit shown log to 25 lines tail -n 25 $log_file > $log_file.tmp mv $log_file.tmp $log_file } get_hostname() { local ip=$1 local hostname=$(dig +short -x $ip @<YOUR DNS SERVER>) hostname=${hostname%%.*} echo "${hostname:-Unknown}" } send_pushover() { curl -s \ --form-string "token=pushover app token" \ --form-string "user=pushover user token" \ --form-string "message=$1" \ --form-string "title=Monitoring Alert !" \ https://api.pushover.net/1/messages.json } # Store current state of service save_status() { local service=$1 local status=$2 local status_file="/home/marc/monitor/${service}.status" echo "$status" > "$status_file" } # Retrieve latest status of service get_status() { local service=$1 local status_file="/home/marc/monitor/${service}.status" if [[ -f "$status_file" ]]; then cat "$status_file" else echo "unknown" # Standard value fi } # Functions for checking several things check_server() { local ip=$1 local response=$(curl -s -o /dev/null -w "%{http_code}" http://$ip:80) local current_status="" local hostname=$(get_hostname $ip) if [[ "$response" =~ ^2 || "$response" =~ ^3 ]]; then current_status="online" else current_status="offline" fi local old_status=$(get_status "HTTP_Server_$ip") old_status=${old_status:-online} # If no old status assume 'online' if [ "$old_status" == "online" ] && [ "$current_status" == "offline" ]; then echo "<tr><td><span class='red'></span></td><td>HTTP Server Check</td><td>$ip ($hostname)</td><td>Offline</td><td>Status changed from online</td></tr>" >> $OUTPUT_FILE send_pushover "Monitoring: HTTP server at $ip has become offline." log_status_change "Server at $ip changed from Online to Offline." elif [ "$old_status" == "offline" ] && [ "$current_status" == "offline" ]; then echo "<tr><td><span class='orange'></span></td><td>HTTP Server Check</td><td>$ip ($hostname)</td><td>Offline</td><td>Continues to be offline</td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "offline" ] && [ "$current_status" == "online" ]; then echo "<tr><td><span class='green'></span></td><td>HTTP Server Check</td><td>$ip ($hostname)</td><td>Online</td><td>Server became online again</td></tr>" >> $OUTPUT_FILE log_status_change "Server at $ip changed from Offline to Online." elif [ "$old_status" == "online" ] && [ "$current_status" == "online" ]; then echo "<tr><td><span class='green'></span></td><td>HTTP Server Check</td><td>$ip ($hostname)</td><td>Online</td><td>Status code: $response</td></tr>" >> $OUTPUT_FILE fi save_status "HTTP_Server_$ip" "$current_status" } check_dhcp() { local ip=$1 local dhcp_check=$(sudo nmap -sU -p 67 --script dhcp-discover $ip 2>&1) local current_status="" local hostname=$(get_hostname $ip) if echo "$dhcp_check" | grep -q "67/udp open\|67/udp open|filtered"; then current_status="active" else current_status="inactive" fi local old_status=$(get_status "DHCP_Server_$ip") old_status=${old_status:-active} # If no old status assume 'active' if [ "$old_status" == "active" ] && [ "$current_status" == "inactive" ]; then echo "<tr><td><span class='red'></span></td><td>DHCP Server Check</td><td>$ip ($hostname)</td><td>Inactive</td><td>DHCP Became unactive</td></tr>" >> $OUTPUT_FILE send_pushover "Monitoring: DHCP server at $ip has become inactive." log_status_change "DHCP Server at $ip changed from Online to Offline." elif [ "$old_status" == "inactive" ] && [ "$current_status" == "inactive" ]; then echo "<tr><td><span class='orange'></span></td><td>DHCP Server Check</td><td>$ip ($hostname)</td><td>Inactive</td><td>Continues to be inactive</td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "active" ] && [ "$current_status" == "active" ]; then echo "<tr><td><span class='green'></span></td><td>DHCP Server Check</td><td>$ip ($hostname)</td><td>Active</td><td></td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "inactive" ] && [ "$current_status" == "active" ] ; then echo "<tr><td><span class='green'></span></td><td>DHCP Server Check</td><td>$ip ($hostname)</td><td>Active</td><td>Server became active again</td></tr>" >> $OUTPUT_FILE log_status_change "DHCP Server at $ip changed from Offline to Online." fi save_status "DHCP_Server_$ip" "$current_status" } check_ip() { local ip=$1 local current_status="" local hostname=$(get_hostname $ip) if ping -c 1 -W 1 $ip > /dev/null 2>&1; then current_status="reachable" else current_status="not reachable" fi local old_status=$(get_status "ICMP_Ping_$ip") old_status=${old_status:-reachable} # If no old status assume 'reachable' if [ "$old_status" == "reachable" ] && [ "$current_status" == "not reachable" ]; then echo "<tr><td><span class='red'></span></td><td>ICMP Ping Check</td><td>$ip ($hostname)</td><td>Not Reachable</td><td>ICMP ping became unresponsive</td></tr>" >> $OUTPUT_FILE send_pushover "Monitoring: ICMP Ping failed for $ip" log_status_change "$ip become unreachable." elif [ "$old_status" == "not reachable" ] && [ "$current_status" == "not reachable" ]; then echo "<tr><td><span class='orange'></span></td><td>ICMP Ping Check</td><td>$ip ($hostname)</td><td>Not Reachable</td><td>Continues to be unresponsive</td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "reachable" ] && [ "$current_status" == "reachable" ]; then echo "<tr><td><span class='green'></span></td><td>ICMP Ping Check</td><td>$ip ($hostname)</td><td>Reachable</td><td></td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "not reachable" ] && [ "$current_status" == "reachable" ]; then echo "<tr><td><span class='green'></span></td><td>ICMP Ping Check</td><td>$ip ($hostname)</td><td>Reachable</td><td>Ping response restored</td></tr>" >> $OUTPUT_FILE log_status_change "$ip became reachable." fi save_status "ICMP_Ping_$ip" "$current_status" } check_dns() { local ip=$1 local dns_check=$(dig @$ip +nocmd +noall +answer +time=1 +tries=1 SOA google.com) local current_status="" local hostname=$(get_hostname $ip) # Check for 'google.com' in the answer if echo "$dns_check" | grep -q "google.com"; then current_status="active" else current_status="inactive" fi local old_status=$(get_status "DNS_Server_$ip") old_status=${old_status:-active} # If no old status assume 'active' if [ "$old_status" == "active" ] && [ "$current_status" == "inactive" ]; then echo "<tr><td><span class='red'></span></td><td>DNS Server Check</td><td>$ip ($hostname)</td><td>Inactive</td><td>DNS became inactive</td></tr>" >> $OUTPUT_FILE send_pushover "Monitoring: DNS server at $ip has become inactive." log_status_change "DNS Server at $ip changed from Online to Offline." elif [ "$old_status" == "inactive" ] && [ "$current_status" == "inactive" ]; then echo "<tr><td><span class='orange'></span></td><td>DNS Server Check</td><td>$ip ($hostname)</td><td>Inactive</td><td>Continues to be inactive</td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "active" ] && [ "$current_status" == "active" ]; then echo "<tr><td><span class='green'></span></td><td>DNS Server Check</td><td>$ip ($hostname)</td><td>Active</td><td></td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "inactive" ] && [ "$current_status" == "active" ] ; then echo "<tr><td><span class='green'></span></td><td>DNS Server Check</td><td>$ip ($hostname)</td><td>Active</td><td>DNS became active again</td></tr>" >> $OUTPUT_FILE log_status_change "DNS Server at $ip changed from Offline to Online." fi save_status "DNS_Server_$ip" "$current_status" } check_external_ip() { # Get external address local external_ip=$(curl -s ifconfig.me) local current_status="" local hostname=$(get_hostname $ip) # Check if IP was received if [[ -n "$external_ip" ]]; then current_status="success" else current_status="failed" fi local old_status=$(get_status "External_IP_Check") old_status=${old_status:-success} # If no old status assume 'success' if [ "$old_status" == "success" ] && [ "$current_status" == "failed" ]; then echo "<tr><td><span class='red'></span></td><td>External IP Check</td><td>N/A</td><td>Failed</td><td>Could not retrieve external IP</td></tr>" >> $OUTPUT_FILE send_pushover "Monitoring: Get external IP failed" log_status_change "Internet became unavailable." elif [ "$old_status" == "failed" ] && [ "$current_status" == "failed" ]; then echo "<tr><td><span class='orange'></span></td><td>External IP Check</td><td>N/A</td><td>Failed</td><td>Continues to fail retrieving external IP</td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "success" ] && [ "$current_status" == "success" ]; then echo "<tr><td><span class='green'></span></td><td>External IP Check</td><td>$external_ip</td><td>Active</td><td>Internet up</td></tr>" >> $OUTPUT_FILE elif [ "$old_status" == "failed" ] && [ "$current_status" == "success" ] ; then echo "<tr><td><span class='green'></span></td><td>External IP Check</td><td>$external_ip</td><td>Active</td><td>Internet back up</td></tr>" >> $OUTPUT_FILE log_status_change "Internet became available." fi save_status "External_IP_Check" "$current_status" } # Main process, do the checks check_external_ip check_server 192.168.2.5 check_server 192.168.2.78 check_dhcp 192.168.2.206 check_dhcp 192.168.2.220 check_dns 192.168.2.206 check_dns 192.168.2.220 # Add recent status changes to page echo "<table><tr><th>Time</th><th>Change</th></tr>" >> $OUTPUT_FILE echo " " >> $OUTPUT_FILE echo "<h2>Recent Status Changes</h2>" >> $OUTPUT_FILE # Read log and add lines to page if [[ -f "/home/marc/monitor/status_changes.log" ]]; then tac /home/marc/monitor/status_changes.log | while read line do echo "<tr><td>${line%% - *}</td><td>${line##* - }</td></tr>" >> $OUTPUT_FILE done fi echo "</table>" >> $OUTPUT_FILE # Close HTML page echo "</body></html>" >> $OUTPUT_FILE
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.