testsAndMisc/scripts/digital_wellbeing/setup_midnight_shutdown.sh
Krzysztof kuhy Rudnicki 92cc79a972 Add shutdown timer monitor service to prevent disabling
- Remove 'disable' option from setup_midnight_shutdown.sh
- Add shutdown-timer-monitor.sh that watches the timer every 30s
- Re-enables timer automatically if someone tries to disable it
- Monitor service installed alongside the timer
- Makes it significantly harder to bypass the shutdown schedule
- Similar pattern to hosts-file-monitor.service
2025-12-07 14:08:13 +01:00

667 lines
19 KiB
Bash
Executable File

#!/bin/bash
# Script to set up automatic PC shutdown with day-specific time windows
# Monday-Wednesday: Shutdown between 21:00-05:00
# Thursday-Sunday: Shutdown between 22:00-05:00
# Handles sudo privileges automatically
set -e # Exit on any error
# Function to show usage
show_usage() {
echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
echo "==============================================="
echo "Usage: $0 [enable|status]"
echo ""
echo "Commands:"
echo " enable - Set up automatic shutdown with day-specific windows (default)"
echo " status - Show current status"
echo ""
echo "Shutdown Schedule:"
echo " Monday-Wednesday: 21:00-05:00"
echo " Thursday-Sunday: 22:00-05:00"
echo ""
echo "NOTE: There is no 'disable' option. This is intentional."
echo " The shutdown timer is protected by a monitor service."
echo ""
}
# Function to check and request sudo privileges
check_sudo() {
if [[ $EUID -ne 0 ]]; then
echo "This script requires sudo privileges to manage systemd services."
echo "Requesting sudo access..."
exec sudo "$0" "$@"
fi
}
# Get the actual user (even when running with sudo)
if [[ -n $SUDO_USER ]]; then
ACTUAL_USER="$SUDO_USER"
USER_HOME="/home/$SUDO_USER"
else
ACTUAL_USER="$USER"
USER_HOME="$HOME"
fi
# Function to show current status
show_current_status() {
echo "Day-Specific Auto-Shutdown Status"
echo "================================="
echo "Current Date: $(date)"
echo "User: $ACTUAL_USER"
echo ""
local timer_exists=false
# Check if files exist
if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
timer_exists=true
echo "✓ Timer file exists"
else
echo "✗ Timer file missing"
fi
if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
echo "✓ Service file exists"
else
echo "✗ Service file missing"
fi
if [[ -f "/usr/local/bin/day-specific-shutdown-manager.sh" ]]; then
echo "✓ Management script exists"
else
echo "✗ Management script missing"
fi
if [[ -f "/usr/local/bin/shutdown-timer-monitor.sh" ]]; then
echo "✓ Monitor script exists"
else
echo "✗ Monitor script missing"
fi
echo ""
# Check systemd status
if $timer_exists; then
if systemctl is-enabled day-specific-shutdown.timer &>/dev/null; then
echo "✓ Timer is enabled"
if systemctl is-active day-specific-shutdown.timer &>/dev/null; then
echo "✓ Timer is active"
echo ""
echo "Next scheduled shutdown check:"
systemctl list-timers day-specific-shutdown.timer --no-pager 2>/dev/null | grep day-specific-shutdown || echo "Timer information not available"
else
echo "✗ Timer is not active"
fi
else
echo "✗ Timer is not enabled"
fi
else
echo "Status: NOT CONFIGURED"
fi
echo ""
# Check monitor service status
echo "Monitor Service Status:"
if systemctl is-enabled shutdown-timer-monitor.service &>/dev/null; then
echo "✓ Monitor is enabled"
if systemctl is-active shutdown-timer-monitor.service &>/dev/null; then
echo "✓ Monitor is active (will re-enable timer if disabled)"
else
echo "✗ Monitor is not active"
fi
else
echo "✗ Monitor is not enabled"
fi
echo ""
echo "Shutdown Schedule:"
echo " Monday-Wednesday: 21:00-05:00"
echo " Thursday-Sunday: 22:00-05:00"
echo ""
echo "NOTE: The shutdown timer is protected by a monitor service."
echo " If you try to disable the timer, it will be automatically re-enabled."
echo ""
}
# Function to create the shutdown service
create_shutdown_service() {
echo ""
echo "1. Creating Systemd Shutdown Service..."
echo "======================================"
local service_file="/etc/systemd/system/day-specific-shutdown.service"
cat >"$service_file" <<'EOF'
[Unit]
Description=Automatic PC shutdown with day-specific time windows
DefaultDependencies=false
Before=shutdown.target reboot.target halt.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/day-specific-shutdown-check.sh
TimeoutStartSec=0
StandardOutput=journal
StandardError=journal
EOF
echo "✓ Created systemd service: $service_file"
}
# Function to create the shutdown timer
create_shutdown_timer() {
echo ""
echo "2. Creating Systemd Shutdown Timer..."
echo "==================================="
local timer_file="/etc/systemd/system/day-specific-shutdown.timer"
cat >"$timer_file" <<'EOF'
[Unit]
Description=Timer for automatic PC shutdown with day-specific windows
Requires=day-specific-shutdown.service
[Timer]
OnCalendar=*-*-* 21:00:00
OnCalendar=*-*-* 21:30:00
OnCalendar=*-*-* 22:00:00
OnCalendar=*-*-* 22:30:00
OnCalendar=*-*-* 23:00:00
OnCalendar=*-*-* 23:30:00
OnCalendar=*-*-* 00:00:00
OnCalendar=*-*-* 00:30:00
OnCalendar=*-*-* 01:00:00
OnCalendar=*-*-* 01:30:00
OnCalendar=*-*-* 02:00:00
OnCalendar=*-*-* 02:30:00
OnCalendar=*-*-* 03:00:00
OnCalendar=*-*-* 03:30:00
OnCalendar=*-*-* 04:00:00
OnCalendar=*-*-* 04:30:00
OnCalendar=*-*-* 05:00:00
Persistent=false
AccuracySec=1s
WakeSystem=false
RandomizedDelaySec=0
[Install]
WantedBy=timers.target
EOF
echo "✓ Created systemd timer: $timer_file"
}
# Function to create management script
create_management_script() {
echo ""
echo "3. Creating Management Script..."
echo "=============================="
local script_file="/usr/local/bin/day-specific-shutdown-manager.sh"
cat >"$script_file" <<'EOF'
#!/bin/bash
# Day-Specific Auto-Shutdown Manager
# Provides easy management of the day-specific shutdown feature
TIMER_NAME="day-specific-shutdown.timer"
SERVICE_NAME="day-specific-shutdown.service"
show_status() {
echo "Day-Specific Auto-Shutdown Status"
echo "================================="
if systemctl is-enabled "$TIMER_NAME" &>/dev/null; then
echo "Status: ENABLED"
if systemctl is-active "$TIMER_NAME" &>/dev/null; then
echo "Timer: ACTIVE"
else
echo "Timer: INACTIVE"
fi
else
echo "Status: NOT ENABLED"
fi
echo ""
echo "Shutdown Schedule:"
echo " Monday-Wednesday: 21:00-05:00"
echo " Thursday-Sunday: 22:00-05:00"
echo ""
echo "Next scheduled checks:"
systemctl list-timers "$TIMER_NAME" --no-pager 2>/dev/null | grep "$TIMER_NAME" || echo "Timer not active"
echo ""
echo "Recent logs:"
journalctl -u "$SERVICE_NAME" --no-pager -n 5 2>/dev/null || echo "No recent logs"
}
case "$1" in
"status")
show_status
;;
"logs")
echo "Day-Specific Auto-Shutdown Logs"
echo "==============================="
journalctl -u "$SERVICE_NAME" --no-pager -n 20
;;
*)
echo "Day-Specific Auto-Shutdown Manager"
echo "Usage: $0 {status|logs}"
echo ""
echo "Commands:"
echo " status - Show current status and next shutdown checks"
echo " logs - Show recent shutdown logs"
echo ""
echo "Shutdown Schedule:"
echo " Monday-Wednesday: 21:00-05:00"
echo " Thursday-Sunday: 22:00-05:00"
echo ""
show_status
;;
esac
EOF
chmod +x "$script_file"
echo "✓ Created management script: $script_file"
}
# Function to create smart shutdown check script
create_shutdown_check_script() {
echo ""
echo "4. Creating Smart Shutdown Check Script..."
echo "========================================"
local check_script="/usr/local/bin/day-specific-shutdown-check.sh"
cat >"$check_script" <<'EOF'
#!/bin/bash
# Smart day-specific shutdown check script
# Different shutdown windows based on day of week:
# Monday-Wednesday: 21:00-05:00
# Thursday-Sunday: 22:00-05:00
# Get current time and day
current_hour=$(date +%H)
current_minute=$(date +%M)
current_time_minutes=$((10#$current_hour * 60 + 10#$current_minute))
day_of_week=$(date +%u) # 1=Monday, 7=Sunday
day_name=$(date +%A)
# Convert time to minutes for easier comparison
# 21:00 = 1260 minutes, 22:00 = 1320 minutes, 05:00 = 300 minutes
# 00:00 = 0 minutes, 05:00 = 300 minutes
logger -t day-specific-shutdown "Checking shutdown conditions at $(date) - Day: $day_name ($day_of_week), Time: $current_hour:$current_minute"
# Determine if we should shutdown based on day and time
should_shutdown=false
if [[ $day_of_week -ge 1 ]] && [[ $day_of_week -le 3 ]]; then
# Monday (1), Tuesday (2), Wednesday (3): shutdown window 21:00-05:00
logger -t day-specific-shutdown "Today is $day_name - checking 21:00-05:00 window"
# Check if time is between 21:00 (1260 minutes) and 23:59 (1439 minutes)
# OR between 00:00 (0 minutes) and 05:00 (300 minutes)
if [[ $current_time_minutes -ge 1260 ]] || [[ $current_time_minutes -le 300 ]]; then
should_shutdown=true
if [[ $current_time_minutes -ge 1260 ]]; then
logger -t day-specific-shutdown "Time $current_hour:$current_minute is within evening shutdown window (21:00-23:59)"
else
logger -t day-specific-shutdown "Time $current_hour:$current_minute is within morning shutdown window (00:00-05:00)"
fi
else
logger -t day-specific-shutdown "Time $current_hour:$current_minute is outside shutdown window (21:00-05:00)"
fi
else
# Thursday (4), Friday (5), Saturday (6), Sunday (7): shutdown window 22:00-05:00
logger -t day-specific-shutdown "Today is $day_name - checking 22:00-05:00 window"
# Check if time is between 22:00 (1320 minutes) and 23:59 (1439 minutes)
# OR between 00:00 (0 minutes) and 05:00 (300 minutes)
if [[ $current_time_minutes -ge 1320 ]] || [[ $current_time_minutes -le 300 ]]; then
should_shutdown=true
if [[ $current_time_minutes -ge 1320 ]]; then
logger -t day-specific-shutdown "Time $current_hour:$current_minute is within evening shutdown window (22:00-23:59)"
else
logger -t day-specific-shutdown "Time $current_hour:$current_minute is within morning shutdown window (00:00-05:00)"
fi
else
logger -t day-specific-shutdown "Time $current_hour:$current_minute is outside shutdown window (22:00-05:00)"
fi
fi
if [[ $should_shutdown == true ]]; then
echo "$(date): Executing shutdown - current time $current_hour:$current_minute is within shutdown window for $day_name"
logger -t day-specific-shutdown "Executing scheduled shutdown at $(date)"
/usr/bin/systemctl poweroff
else
echo "$(date): Skipping shutdown - not within shutdown window for $day_name (current: $current_hour:$current_minute)"
logger -t day-specific-shutdown "Skipped shutdown - not within shutdown window for $day_name (current: $current_hour:$current_minute)"
fi
EOF
chmod +x "$check_script"
echo "✓ Created smart shutdown check script: $check_script"
}
# Function to enable the timer
enable_timer() {
echo ""
echo "5. Enabling Shutdown Timer..."
echo "============================"
# Reload systemd daemon
systemctl daemon-reload
echo "✓ Reloaded systemd daemon"
# Enable the timer
systemctl enable day-specific-shutdown.timer
echo "✓ Enabled day-specific-shutdown timer"
# Start the timer
systemctl start day-specific-shutdown.timer
echo "✓ Started day-specific-shutdown timer"
}
# Function to install the monitor service
install_monitor_service() {
echo ""
echo "6. Installing Shutdown Timer Monitor Service..."
echo "=============================================="
local monitor_script="/usr/local/bin/shutdown-timer-monitor.sh"
local monitor_service="/etc/systemd/system/shutdown-timer-monitor.service"
# Create the monitor script
cat >"$monitor_script" <<'EOF'
#!/bin/bash
# Shutdown timer monitor script
# Watches the day-specific-shutdown timer and re-enables it if disabled
set -euo pipefail
LOG_FILE="/var/log/shutdown-timer-monitor.log"
TIMER_NAME="day-specific-shutdown.timer"
SERVICE_NAME="day-specific-shutdown.service"
CHECK_INTERVAL=30
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" >&2
}
timer_needs_restoration() {
if ! systemctl is-enabled "$TIMER_NAME" &>/dev/null; then
log_message "Timer $TIMER_NAME is not enabled"
return 0
fi
if ! systemctl is-active "$TIMER_NAME" &>/dev/null; then
log_message "Timer $TIMER_NAME is not active"
return 0
fi
if [[ ! -f "/etc/systemd/system/$TIMER_NAME" ]]; then
log_message "Timer unit file missing"
return 0
fi
if [[ ! -f "/etc/systemd/system/$SERVICE_NAME" ]]; then
log_message "Service unit file missing"
return 0
fi
if [[ ! -f "/usr/local/bin/day-specific-shutdown-check.sh" ]]; then
log_message "Check script missing"
return 0
fi
return 1
}
restore_timer() {
log_message "Shutdown timer tampering detected - initiating restoration"
systemctl daemon-reload
if ! systemctl is-enabled "$TIMER_NAME" &>/dev/null; then
log_message "Re-enabling $TIMER_NAME"
systemctl enable "$TIMER_NAME" 2>/dev/null || true
fi
if ! systemctl is-active "$TIMER_NAME" &>/dev/null; then
log_message "Re-starting $TIMER_NAME"
systemctl start "$TIMER_NAME" 2>/dev/null || true
fi
if systemctl is-active "$TIMER_NAME" &>/dev/null; then
log_message "Timer restoration completed successfully"
else
log_message "WARNING: Timer restoration may have failed"
fi
}
log_message "=== Shutdown Timer Monitor Started ==="
log_message "Monitoring timer: $TIMER_NAME"
if timer_needs_restoration; then
log_message "Initial check: Timer needs restoration"
restore_timer
else
log_message "Initial check: Timer is properly configured"
fi
while true; do
if timer_needs_restoration; then
restore_timer
fi
sleep "$CHECK_INTERVAL"
done
EOF
chmod +x "$monitor_script"
echo "✓ Created monitor script: $monitor_script"
# Create the monitor service
cat >"$monitor_service" <<'EOF'
[Unit]
Description=Shutdown Timer Monitor and Auto-Restore Service
After=network-online.target day-specific-shutdown.timer
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/shutdown-timer-monitor.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NoNewPrivileges=false
PrivateTmp=true
MemoryMax=50M
CPUQuota=10%
[Install]
WantedBy=multi-user.target
EOF
echo "✓ Created monitor service: $monitor_service"
# Reload and enable monitor
systemctl daemon-reload
systemctl enable shutdown-timer-monitor.service
systemctl start shutdown-timer-monitor.service
echo "✓ Enabled and started shutdown-timer-monitor.service"
}
# Function to test the setup
test_setup() {
echo ""
echo "7. Testing Setup..."
echo "=================="
echo "Service files:"
if [[ -f "/etc/systemd/system/day-specific-shutdown.service" ]]; then
echo "✓ Service file exists"
else
echo "✗ Service file missing"
fi
if [[ -f "/etc/systemd/system/day-specific-shutdown.timer" ]]; then
echo "✓ Timer file exists"
else
echo "✗ Timer file missing"
fi
if [[ -f "/etc/systemd/system/shutdown-timer-monitor.service" ]]; then
echo "✓ Monitor service file exists"
else
echo "✗ Monitor service file missing"
fi
echo ""
echo "Timer status:"
if systemctl is-enabled day-specific-shutdown.timer &>/dev/null; then
echo "✓ Timer is enabled"
else
echo "✗ Timer is not enabled"
fi
if systemctl is-active day-specific-shutdown.timer &>/dev/null; then
echo "✓ Timer is active"
else
echo "✗ Timer is not active"
fi
echo ""
echo "Monitor status:"
if systemctl is-enabled shutdown-timer-monitor.service &>/dev/null; then
echo "✓ Monitor is enabled"
else
echo "✗ Monitor is not enabled"
fi
if systemctl is-active shutdown-timer-monitor.service &>/dev/null; then
echo "✓ Monitor is active"
else
echo "✗ Monitor is not active"
fi
echo ""
echo "Next scheduled checks:"
systemctl list-timers day-specific-shutdown.timer --no-pager 2>/dev/null | head -5 | grep day-specific-shutdown || echo "Timer information not available"
}
# Function to show final instructions
show_instructions() {
echo ""
echo "================================================="
echo "Day-Specific Auto-Shutdown Setup Complete"
echo "================================================="
echo "Summary:"
echo "✓ Systemd service created (/etc/systemd/system/day-specific-shutdown.service)"
echo "✓ Systemd timer created (/etc/systemd/system/day-specific-shutdown.timer)"
echo "✓ Management script created (/usr/local/bin/day-specific-shutdown-manager.sh)"
echo "✓ Smart check script created (/usr/local/bin/day-specific-shutdown-check.sh)"
echo "✓ Timer enabled and started"
echo "✓ Monitor service installed and started (protects timer from being disabled)"
echo ""
echo "Shutdown Schedule:"
echo " Monday-Wednesday: 21:00-05:00 (9:00 PM to 5:00 AM)"
echo " Thursday-Sunday: 22:00-05:00 (10:00 PM to 5:00 AM)"
echo ""
echo "Management commands:"
echo " sudo day-specific-shutdown-manager.sh status - Check status"
echo " sudo day-specific-shutdown-manager.sh logs - View shutdown logs"
echo ""
echo "How it works:"
echo "• Timer checks every 30 minutes during potential shutdown windows"
echo "• Smart logic determines shutdown eligibility based on day and time"
echo "• Monitor service watches the timer and re-enables it if disabled"
echo "• There is NO disable option - this is intentional for digital wellbeing"
echo ""
echo "WARNING: This will automatically shutdown your PC during designated hours."
echo "Make sure to save your work before the shutdown windows!"
echo ""
}
# Function to prompt for confirmation
confirm_setup() {
echo ""
echo "WARNING: Day-Specific Auto-Shutdown Confirmation"
echo "==============================================="
echo "This will set up your PC to automatically shutdown during specific time windows."
echo ""
echo "Shutdown Schedule:"
echo " Monday-Wednesday: 21:00-05:00 (9:00 PM to 5:00 AM)"
echo " Thursday-Sunday: 22:00-05:00 (10:00 PM to 5:00 AM)"
echo ""
echo "Important considerations:"
echo "- Any unsaved work will be lost during shutdown windows"
echo "- Running processes will be terminated"
echo "- Downloads/uploads in progress will be interrupted"
echo "- You'll need to manually power on your PC each day"
echo "- Timer checks every 30 minutes during potential shutdown windows"
echo "- There is NO disable option - this is protected by a monitor service"
echo ""
read -r -p "Do you want to proceed? (y/N): " confirm
case "$confirm" in
[yY] | [yY][eE][sS])
echo "Proceeding with setup..."
return 0
;;
*)
echo "Setup cancelled."
exit 0
;;
esac
}
# Main execution flow for enable
enable_midnight_shutdown() {
echo "Day-Specific Auto-Shutdown Setup for Arch Linux"
echo "==============================================="
echo "Current Date: $(date)"
echo "User: $ACTUAL_USER"
echo "Target user: $ACTUAL_USER"
echo "User home: $USER_HOME"
# Confirm setup
confirm_setup
# Create systemd files
create_shutdown_service
create_shutdown_timer
create_management_script
create_shutdown_check_script
# Enable and start timer
enable_timer
# Install monitor service (protects timer from being disabled)
install_monitor_service
# Test setup
test_setup
# Show instructions
show_instructions
}
# Parse command line arguments
case "${1:-enable}" in
"enable")
check_sudo "$@"
enable_midnight_shutdown
;;
"status")
check_sudo "$@"
show_current_status
;;
"help" | "-h" | "--help")
show_usage
;;
*)
echo "Error: Unknown command '$1'"
echo ""
show_usage
exit 1
;;
esac