Last active
June 24, 2026 11:53
-
-
Save amitkhare/662a985619a9ef2a2546a7762b0cf703 to your computer and use it in GitHub Desktop.
Set up daily scheduled reboot, hardware watchdog, and emergency hard‑reset on Raspberry Pi for maximum uptime and self‑recovery.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Raspberry Pi Resilience Toolkit | |
| # Interactive installer for scheduled reboots, hardware watchdog, and emergency reset | |
| set -e | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' # No Color | |
| # Ensure the script is run as root | |
| if [ "$EUID" -ne 0 ]; then | |
| echo -e "${RED}ERROR: Please run this script as root (use sudo).${NC}" | |
| exit 1 | |
| fi | |
| # -------------------------------------------------------- | |
| # Utility Functions | |
| # -------------------------------------------------------- | |
| print_header() { | |
| echo "" | |
| echo "==================================================" | |
| echo " Raspberry Pi Resilience Toolkit" | |
| echo "==================================================" | |
| } | |
| print_section() { | |
| echo "" | |
| echo -e "${BLUE}[$1]${NC} $2" | |
| echo "-----------------------------------------------" | |
| } | |
| success_msg() { | |
| echo -e "${GREEN}✓${NC} $1" | |
| } | |
| warn_msg() { | |
| echo -e "${YELLOW}⚠${NC} $1" | |
| } | |
| error_msg() { | |
| echo -e "${RED}✗${NC} $1" | |
| } | |
| check_raspberry_pi() { | |
| if grep -q "Raspberry Pi" /proc/device-tree/model 2>/dev/null || \ | |
| grep -q "BCM" /proc/cpuinfo 2>/dev/null; then | |
| return 0 | |
| else | |
| warn_msg "This system doesn't appear to be a Raspberry Pi." | |
| echo -n "Continue anyway? (y/N): " | |
| read -r response | |
| [[ "$response" =~ ^[Yy]$ ]] || exit 0 | |
| return 1 | |
| fi | |
| } | |
| remount_boot_rw() { | |
| local boot_path="$1" | |
| if [ -f "$boot_path" ] && ! touch "$boot_path" 2>/dev/null; then | |
| echo "-> Boot partition is read-only, remounting..." | |
| mount -o remount,rw "$(dirname "$boot_path")" || { | |
| error_msg "Failed to remount boot partition as writable" | |
| return 1 | |
| } | |
| return 0 | |
| fi | |
| } | |
| # -------------------------------------------------------- | |
| # Component Functions - Cron Scheduled Reboot | |
| # -------------------------------------------------------- | |
| install_cron_reboot() { | |
| print_section "CRON" "Setting up daily scheduled reboot at midnight" | |
| local CRON_JOB="0 0 * * * /sbin/shutdown -r now" | |
| if crontab -l 2>/dev/null | grep -Fq "$CRON_JOB"; then | |
| warn_msg "Cron job already exists. Skipping installation." | |
| return 0 | |
| fi | |
| if crontab -l 2>/dev/null | { cat; echo "$CRON_JOB"; } | crontab -; then | |
| success_msg "Daily midnight reboot added to root crontab." | |
| echo " Schedule: Every day at 00:00 (midnight)" | |
| return 0 | |
| else | |
| error_msg "Failed to add cron job." | |
| return 1 | |
| fi | |
| } | |
| uninstall_cron_reboot() { | |
| print_section "CRON" "Removing daily scheduled reboot" | |
| if ! crontab -l 2>/dev/null | grep -Fq "shutdown -r now"; then | |
| warn_msg "No scheduled reboot found in crontab." | |
| return 0 | |
| fi | |
| # Remove the specific line while keeping other cron jobs | |
| crontab -l 2>/dev/null | grep -Fv "shutdown -r now" | crontab - | |
| if [ $? -eq 0 ]; then | |
| success_msg "Scheduled reboot removed from crontab." | |
| return 0 | |
| else | |
| error_msg "Failed to remove cron job." | |
| return 1 | |
| fi | |
| } | |
| check_cron_reboot() { | |
| if crontab -l 2>/dev/null | grep -Fq "shutdown -r now"; then | |
| echo -e " Status: ${GREEN}Installed${NC}" | |
| return 0 | |
| else | |
| echo -e " Status: ${RED}Not installed${NC}" | |
| return 1 | |
| fi | |
| } | |
| # -------------------------------------------------------- | |
| # Component Functions - Hardware Watchdog | |
| # -------------------------------------------------------- | |
| install_watchdog() { | |
| print_section "WATCHDOG" "Setting up hardware watchdog for system hang recovery" | |
| # Find config.txt | |
| local CONFIG_FILE="" | |
| if [ -f "/boot/firmware/config.txt" ]; then | |
| CONFIG_FILE="/boot/firmware/config.txt" | |
| elif [ -f "/boot/config.txt" ]; then | |
| CONFIG_FILE="/boot/config.txt" | |
| else | |
| error_msg "Could not find config.txt on the boot partition." | |
| return 1 | |
| fi | |
| echo "-> Using config file: $CONFIG_FILE" | |
| # Remount boot if needed | |
| remount_boot_rw "$CONFIG_FILE" || return 1 | |
| # Enable hardware watchdog in firmware config | |
| if grep -q "^dtparam=watchdog=on" "$CONFIG_FILE"; then | |
| success_msg "Watchdog already enabled in $CONFIG_FILE." | |
| elif grep -q "#dtparam=watchdog=on" "$CONFIG_FILE"; then | |
| sed -i 's|#dtparam=watchdog=on|dtparam=watchdog=on|g' "$CONFIG_FILE" | |
| success_msg "Uncommented watchdog parameter in $CONFIG_FILE." | |
| else | |
| echo "dtparam=watchdog=on" >> "$CONFIG_FILE" | |
| success_msg "Added watchdog overlay to $CONFIG_FILE." | |
| fi | |
| # Install watchdog package if not present | |
| if ! command -v watchdog &>/dev/null; then | |
| echo "-> Installing watchdog package..." | |
| if apt update -qq && apt install -y watchdog; then | |
| success_msg "Watchdog package installed." | |
| else | |
| error_msg "Failed to install watchdog package." | |
| return 1 | |
| fi | |
| else | |
| success_msg "Watchdog package already installed." | |
| fi | |
| # Configure watchdog daemon | |
| local WATCHDOG_CONF="/etc/watchdog.conf" | |
| if [ -f "$WATCHDOG_CONF" ]; then | |
| echo "-> Configuring $WATCHDOG_CONF..." | |
| # Create backup only if one doesn't exist | |
| if [ ! -f "${WATCHDOG_CONF}.backup" ]; then | |
| cp "$WATCHDOG_CONF" "${WATCHDOG_CONF}.backup" | |
| success_msg "Backup created at ${WATCHDOG_CONF}.backup" | |
| fi | |
| # Enable settings | |
| if grep -q "^#watchdog-device" "$WATCHDOG_CONF"; then | |
| sed -i 's|^#watchdog-device|watchdog-device|g' "$WATCHDOG_CONF" | |
| success_msg "Enabled watchdog device." | |
| fi | |
| if grep -q "^#max-temperature" "$WATCHDOG_CONF"; then | |
| sed -i 's|^#max-temperature|max-temperature|g' "$WATCHDOG_CONF" | |
| success_msg "Enabled max temperature monitoring." | |
| fi | |
| # Set timeout | |
| if ! grep -q "^watchdog-timeout" "$WATCHDOG_CONF"; then | |
| echo "watchdog-timeout = 15" >> "$WATCHDOG_CONF" | |
| success_msg "Set watchdog timeout to 15 seconds." | |
| fi | |
| else | |
| warn_msg "$WATCHDOG_CONF not found. Creating minimal config..." | |
| cat > "$WATCHDOG_CONF" << 'EOF' | |
| watchdog-device = /dev/watchdog | |
| watchdog-timeout = 15 | |
| max-temperature = 80 | |
| EOF | |
| success_msg "Created default watchdog configuration." | |
| fi | |
| # Enable and start service | |
| echo "-> Starting watchdog service..." | |
| if systemctl enable watchdog && systemctl restart watchdog; then | |
| if systemctl is-active --quiet watchdog; then | |
| success_msg "Watchdog service is active and running." | |
| else | |
| warn_msg "Watchdog service enabled but not running. A reboot may be needed." | |
| fi | |
| return 0 | |
| else | |
| error_msg "Failed to enable/start watchdog service." | |
| return 1 | |
| fi | |
| } | |
| uninstall_watchdog() { | |
| print_section "WATCHDOG" "Removing hardware watchdog" | |
| local made_changes=false | |
| # Stop and disable service | |
| if systemctl is-active --quiet watchdog 2>/dev/null; then | |
| echo "-> Stopping watchdog service..." | |
| systemctl stop watchdog | |
| success_msg "Watchdog service stopped." | |
| made_changes=true | |
| fi | |
| if systemctl is-enabled --quiet watchdog 2>/dev/null; then | |
| echo "-> Disabling watchdog service..." | |
| systemctl disable watchdog | |
| success_msg "Watchdog service disabled." | |
| made_changes=true | |
| fi | |
| # Restore config backup if exists | |
| local WATCHDOG_CONF="/etc/watchdog.conf" | |
| if [ -f "${WATCHDOG_CONF}.backup" ]; then | |
| echo "-> Restoring original configuration..." | |
| cp "${WATCHDOG_CONF}.backup" "$WATCHDOG_CONF" | |
| success_msg "Original watchdog configuration restored." | |
| made_changes=true | |
| fi | |
| # Remove dtparam from config.txt | |
| local CONFIG_FILE="" | |
| if [ -f "/boot/firmware/config.txt" ]; then | |
| CONFIG_FILE="/boot/firmware/config.txt" | |
| elif [ -f "/boot/config.txt" ]; then | |
| CONFIG_FILE="/boot/config.txt" | |
| fi | |
| if [ -n "$CONFIG_FILE" ] && grep -q "^dtparam=watchdog=on" "$CONFIG_FILE"; then | |
| remount_boot_rw "$CONFIG_FILE" 2>/dev/null | |
| echo "-> Removing watchdog overlay from $CONFIG_FILE..." | |
| sed -i '/^dtparam=watchdog=on/d' "$CONFIG_FILE" | |
| success_msg "Watchdog overlay removed from $CONFIG_FILE." | |
| made_changes=true | |
| fi | |
| # Option to remove package | |
| if dpkg -l | grep -q "^ii.*watchdog"; then | |
| echo "" | |
| echo -n "Remove watchdog package also? (y/N): " | |
| read -r remove_pkg | |
| if [[ "$remove_pkg" =~ ^[Yy]$ ]]; then | |
| apt purge -y watchdog | |
| success_msg "Watchdog package removed." | |
| made_changes=true | |
| else | |
| warn_msg "Watchdog package kept. It can be removed later with: sudo apt purge watchdog" | |
| fi | |
| fi | |
| if [ "$made_changes" = true ]; then | |
| success_msg "Watchdog has been removed." | |
| echo " Note: A reboot may be required to fully disable the hardware watchdog." | |
| else | |
| warn_msg "No watchdog configuration found to remove." | |
| fi | |
| } | |
| check_watchdog() { | |
| local installed=false | |
| # Check for dtparam | |
| if [ -f "/boot/firmware/config.txt" ] && grep -q "^dtparam=watchdog=on" "/boot/firmware/config.txt" 2>/dev/null; then | |
| installed=true | |
| elif [ -f "/boot/config.txt" ] && grep -q "^dtparam=watchdog=on" "/boot/config.txt" 2>/dev/null; then | |
| installed=true | |
| fi | |
| # Check service is running | |
| if systemctl is-active --quiet watchdog 2>/dev/null; then | |
| installed=true | |
| echo -e " Status: ${GREEN}Installed and active${NC}" | |
| elif [ "$installed" = true ]; then | |
| echo -e " Status: ${YELLOW}Partially installed (reboot needed)${NC}" | |
| else | |
| echo -e " Status: ${RED}Not installed${NC}" | |
| fi | |
| } | |
| # -------------------------------------------------------- | |
| # Component Functions - Emergency Power Cycle | |
| # -------------------------------------------------------- | |
| install_emergency_script() { | |
| print_section "EMERGENCY RESET" "Setting up hard power cycle script" | |
| local SCRIPT_PATH="/usr/local/bin/hard_power_cycle.sh" | |
| if [ -f "$SCRIPT_PATH" ]; then | |
| warn_msg "Emergency script already exists at $SCRIPT_PATH" | |
| echo -n "Overwrite? (y/N): " | |
| read -r reply | |
| [[ "$reply" =~ ^[Yy]$ ]] || { | |
| warn_msg "Keeping existing script." | |
| return 0 | |
| } | |
| fi | |
| cat > "$SCRIPT_PATH" << 'SCRIPT_EOF' | |
| #!/bin/bash | |
| # Emergency Hard Power Cycle Script | |
| # Forces an immediate hardware-level reboot, bypassing OS shutdown | |
| # Usage: sudo hard_power_cycle.sh | |
| echo "WARNING: Triggering immediate hardware power cycle!" | |
| echo "All unsaved data will be lost!" | |
| echo "" | |
| echo "You have 5 seconds to cancel (Ctrl+C)..." | |
| sleep 5 | |
| echo "Proceeding with emergency reset..." | |
| sync | |
| sync | |
| # Method 1: For Raspberry Pi 4/5 - use PMIC (Power Management IC) | |
| if command -v vcgencmd &>/dev/null; then | |
| echo "Attempting Raspberry Pi PMIC reset..." | |
| vcgencmd power_monitor 0 2>/dev/null | |
| if [ $? -ne 0 ]; then | |
| echo "PMIC command failed, falling back to kernel SysRq." | |
| fi | |
| fi | |
| # Method 2: Universal kernel reboot via SysRq | |
| echo "Issuing emergency reboot via SysRq..." | |
| echo 1 > /proc/sys/kernel/sysrq 2>/dev/null | |
| echo b > /proc/sysrq-trigger 2>/dev/null | |
| # If we reach here, all methods failed | |
| echo "CRITICAL: All reset methods failed!" | |
| echo "System may require manual power cycle." | |
| exit 1 | |
| SCRIPT_EOF | |
| chmod 755 "$SCRIPT_PATH" | |
| success_msg "Emergency power cycle script created at $SCRIPT_PATH" | |
| echo " Usage: sudo hard_power_cycle.sh" | |
| return 0 | |
| } | |
| uninstall_emergency_script() { | |
| print_section "EMERGENCY RESET" "Removing hard power cycle script" | |
| local SCRIPT_PATH="/usr/local/bin/hard_power_cycle.sh" | |
| if [ -f "$SCRIPT_PATH" ]; then | |
| rm -f "$SCRIPT_PATH" | |
| success_msg "Emergency script removed from $SCRIPT_PATH" | |
| else | |
| warn_msg "Emergency script not found. Nothing to remove." | |
| fi | |
| } | |
| check_emergency_script() { | |
| if [ -f "/usr/local/bin/hard_power_cycle.sh" ]; then | |
| echo -e " Status: ${GREEN}Installed${NC}" | |
| return 0 | |
| else | |
| echo -e " Status: ${RED}Not installed${NC}" | |
| return 1 | |
| fi | |
| } | |
| # -------------------------------------------------------- | |
| # Status / Overview Display | |
| # -------------------------------------------------------- | |
| show_status() { | |
| print_header | |
| echo "" | |
| echo -e "${BLUE}Current Component Status:${NC}" | |
| echo "" | |
| echo "1. Scheduled Reboot (Cron)" | |
| check_cron_reboot | |
| echo "" | |
| echo "2. Hardware Watchdog" | |
| check_watchdog | |
| echo "" | |
| echo "3. Emergency Power Cycle Script" | |
| check_emergency_script | |
| echo "" | |
| } | |
| # -------------------------------------------------------- | |
| # Interactive Menu | |
| # -------------------------------------------------------- | |
| show_menu() { | |
| print_header | |
| echo "" | |
| echo "What would you like to do?" | |
| echo "" | |
| echo " ${GREEN}1${NC}) Install/Enable components" | |
| echo " ${RED}2${NC}) Uninstall/Disable components" | |
| echo " ${BLUE}3${NC}) Show current status" | |
| echo " ${YELLOW}4${NC}) Install ALL components" | |
| echo " ${RED}5${NC}) Uninstall ALL components" | |
| echo " ${NC}0${NC}) Exit" | |
| echo "" | |
| echo -n "Select option [0-5]: " | |
| } | |
| component_menu() { | |
| local action="$1" # "install" or "uninstall" | |
| local verb="$2" # "Install" or "Uninstall" | |
| while true; do | |
| echo "" | |
| echo "==================================================" | |
| echo " $verb Components" | |
| echo "==================================================" | |
| echo "" | |
| echo " 1) Scheduled Reboot (Cron)" | |
| echo " 2) Hardware Watchdog" | |
| echo " 3) Emergency Power Cycle Script" | |
| echo " 4) ALL components" | |
| echo " 0) Back to main menu" | |
| echo "" | |
| echo -n "Select component [0-4]: " | |
| read -r choice | |
| case $choice in | |
| 1) $action"_cron_reboot"; break ;; | |
| 2) $action"_watchdog"; break ;; | |
| 3) $action"_emergency_script"; break ;; | |
| 4) | |
| echo "" | |
| echo -n "$verb ALL components? (y/N): " | |
| read -r confirm | |
| if [[ "$confirm" =~ ^[Yy]$ ]]; then | |
| echo "" | |
| if [ "$action" = "install" ]; then | |
| install_cron_reboot | |
| echo "" | |
| install_watchdog | |
| echo "" | |
| install_emergency_script | |
| else | |
| uninstall_cron_reboot | |
| echo "" | |
| uninstall_watchdog | |
| echo "" | |
| uninstall_emergency_script | |
| fi | |
| echo "" | |
| echo "==================================================" | |
| echo -e "${GREEN}All components ${verb}ed!${NC}" | |
| echo "==================================================" | |
| if [ "$action" = "install" ] && grep -q "^dtparam=watchdog=on" "/boot/firmware/config.txt" 2>/dev/null; then | |
| echo "" | |
| warn_msg "A reboot is recommended to activate hardware watchdog." | |
| echo -n "Reboot now? (y/N): " | |
| read -r reboot_now | |
| [[ "$reboot_now" =~ ^[Yy]$ ]] && reboot | |
| fi | |
| fi | |
| break | |
| ;; | |
| 0) return ;; | |
| *) error_msg "Invalid option." ;; | |
| esac | |
| done | |
| } | |
| # -------------------------------------------------------- | |
| # Main Script Execution | |
| # -------------------------------------------------------- | |
| main() { | |
| # Initial check | |
| if ! grep -q "Raspberry Pi" /proc/device-tree/model 2>/dev/null && \ | |
| ! grep -q "BCM" /proc/cpuinfo 2>/dev/null; then | |
| warn_msg "This system doesn't appear to be a Raspberry Pi." | |
| echo "Some features may not work correctly on other hardware." | |
| echo -n "Continue anyway? (y/N): " | |
| read -r response | |
| [[ "$response" =~ ^[Yy]$ ]] || exit 0 | |
| fi | |
| while true; do | |
| show_menu | |
| read -r option | |
| case $option in | |
| 1) | |
| component_menu "install" "Install" | |
| ;; | |
| 2) | |
| component_menu "uninstall" "Uninstall" | |
| ;; | |
| 3) | |
| show_status | |
| echo "" | |
| echo -n "Press Enter to return to menu..." | |
| read -r | |
| ;; | |
| 4) | |
| echo "" | |
| echo -n "Install ALL components? (y/N): " | |
| read -r confirm | |
| if [[ "$confirm" =~ ^[Yy]$ ]]; then | |
| install_cron_reboot | |
| echo "" | |
| install_watchdog | |
| echo "" | |
| install_emergency_script | |
| echo "" | |
| echo "==================================================" | |
| success_msg "All components installed!" | |
| echo "==================================================" | |
| if grep -q "^dtparam=watchdog=on" "/boot/firmware/config.txt" 2>/dev/null || \ | |
| grep -q "^dtparam=watchdog=on" "/boot/config.txt" 2>/dev/null; then | |
| echo "" | |
| warn_msg "A reboot is recommended to activate hardware watchdog." | |
| echo -n "Reboot now? (y/N): " | |
| read -r reboot_now | |
| [[ "$reboot_now" =~ ^[Yy]$ ]] && reboot | |
| fi | |
| fi | |
| ;; | |
| 5) | |
| echo "" | |
| echo -e "${RED}WARNING: This will remove all resilience components.${NC}" | |
| echo -n "Uninstall ALL components? (y/N): " | |
| read -r confirm | |
| if [[ "$confirm" =~ ^[Yy]$ ]]; then | |
| uninstall_cron_reboot | |
| echo "" | |
| uninstall_watchdog | |
| echo "" | |
| uninstall_emergency_script | |
| echo "" | |
| echo "==================================================" | |
| success_msg "All components uninstalled!" | |
| echo "==================================================" | |
| fi | |
| ;; | |
| 0) | |
| echo "" | |
| success_msg "Goodbye!" | |
| exit 0 | |
| ;; | |
| *) | |
| error_msg "Invalid option. Please select 0-5." | |
| sleep 1 | |
| ;; | |
| esac | |
| done | |
| } | |
| # Run main function | |
| main |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Raspberry Pi Resilience Toolkit
A single script that hardens your Raspberry Pi against crashes and hangs by installing three layers of automatic recovery:
Run it:
sudo ./resilience.shLet me know if you need it shorter, longer, or tailored for a specific audience (e.g., GitHub README, forum post, project documentation).