Tutorial

Nuevas actualizaciones y mejoras para Macfleet.

Aviso importante

Los ejemplos de código y scripts proporcionados en estos tutoriales son solo para propósitos educativos. Macfleet no es responsable de ningún problema, daño o vulnerabilidad de seguridad que pueda surgir del uso, modificación o implementación de estos ejemplos. Siempre revisa y prueba el código en un entorno seguro antes de usarlo en sistemas de producción.

Shutdown and Reboot Management for macOS

Implement enterprise-grade shutdown and reboot management across your MacFleet deployment with automated maintenance scheduling, graceful shutdown procedures, security validation, and comprehensive fleet-wide power management. This tutorial provides solutions for maintaining operational continuity while ensuring secure and efficient system power operations.

Understanding macOS Shutdown and Reboot Management

macOS provides several power control mechanisms and tools:

  • shutdown - Primary command-line tool for system power control
  • reboot - Alternative reboot command
  • halt - System halt without power off
  • Apple Menu - GUI shutdown interface
  • Power Button - Hardware-level power control

Basic Shutdown Operations

Instant Shutdown

#!/bin/bash

# Shutdown Mac instantly
sudo shutdown -h now

Scheduled Reboot

#!/bin/bash

# Reboot Mac in 5 minutes
sudo shutdown -r +5

Shutdown at Specific Time

#!/bin/bash

# Shutdown at specific time: October 18, 16:11:00 2022
sudo shutdown -h 2210181611

Reboot with Warning Message

#!/bin/bash

# Reboot with warning message
sudo shutdown -r +2 "the system will restart in 2 minutes"

Display Sleep and Lock

#!/bin/bash

# Lock device and turn off display
sudo shutdown -s

Enterprise Shutdown and Reboot Management System

Comprehensive Power Management Tool

#!/bin/bash

# MacFleet Enterprise Shutdown and Reboot Management Tool
# Advanced power control and maintenance scheduling

# Configuration
CONFIG_FILE="/etc/macfleet/shutdown_policy.conf"
LOG_FILE="/var/log/macfleet_shutdown.log"
SCHEDULES_DIR="/Library/MacFleet/ShutdownSchedules"
AUDIT_LOG="/var/log/macfleet_shutdown_audit.log"

# Create directories
mkdir -p "$(dirname "$CONFIG_FILE")" "$(dirname "$LOG_FILE")" "$SCHEDULES_DIR" "$(dirname "$AUDIT_LOG")"

# Default shutdown management policy
cat > "$CONFIG_FILE" 2>/dev/null << 'EOF' || true
# MacFleet Enterprise Shutdown and Reboot Management Policy
# Version: 2.0

# Shutdown Policy Enforcement
ENFORCE_SHUTDOWN_POLICIES=true
GRACEFUL_SHUTDOWN_ENABLED=true
SAVE_WORK_BEFORE_SHUTDOWN=true
FORCE_SHUTDOWN_TIMEOUT=300
WARNING_NOTIFICATION_TIME=300

# Security and Safety
REQUIRE_ADMIN_APPROVAL=false
VALIDATE_USER_SESSIONS=true
CHECK_RUNNING_PROCESSES=true
BACKUP_BEFORE_SHUTDOWN=false
ENCRYPTION_KEY_PROTECTION=true

# Maintenance Windows
MAINTENANCE_WINDOW_ENABLED=true
MAINTENANCE_START_TIME="02:00"
MAINTENANCE_END_TIME="06:00"
MAINTENANCE_DAYS="MTWRF"
WEEKEND_MAINTENANCE_ENABLED=true
HOLIDAY_MAINTENANCE_DISABLED=true

# Business Hours Protection
BUSINESS_HOURS_START="09:00"
BUSINESS_HOURS_END="18:00"
PREVENT_SHUTDOWN_BUSINESS_HOURS=true
EMERGENCY_SHUTDOWN_OVERRIDE=true
CRITICAL_SYSTEM_PROTECTION=true

# Notification and Communication
USER_NOTIFICATION_ENABLED=true
ADMIN_NOTIFICATION_ENABLED=true
SLACK_WEBHOOK_URL=""
EMAIL_NOTIFICATION_ENABLED=false
NOTIFICATION_LEAD_TIME=600

# Automatic Scheduling
AUTO_REBOOT_ENABLED=true
AUTO_REBOOT_FREQUENCY="weekly"
AUTO_REBOOT_DAY="sunday"
AUTO_REBOOT_TIME="03:00"
PATCH_REBOOT_REQUIRED=true

# Compliance and Audit
AUDIT_ALL_POWER_OPERATIONS=true
COMPLIANCE_REPORTING=true
UPTIME_TRACKING=true
MAINTENANCE_DOCUMENTATION=true
CHANGE_MANAGEMENT_INTEGRATION=true

# Recovery and Failsafe
AUTOMATIC_RECOVERY_ENABLED=true
BOOT_VALIDATION_REQUIRED=true
ROLLBACK_ON_FAILURE=true
EMERGENCY_CONTACT_INFO="IT Support: +1-555-0123"
EOF

# Source configuration
source "$CONFIG_FILE" 2>/dev/null || true

# Logging function
log_action() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Audit logging function
audit_log() {
    local action="$1"
    local result="$2"
    local details="$3"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ACTION:$action RESULT:$result DETAILS:$details USER:$(whoami)" >> "$AUDIT_LOG"
}

# Check if shutdown is allowed
check_shutdown_allowed() {
    local operation="$1"
    local force="${2:-false}"
    
    echo "=== Validating Shutdown Authorization ==="
    
    # Check business hours protection
    if [[ "$PREVENT_SHUTDOWN_BUSINESS_HOURS" == "true" && "$force" != "true" ]]; then
        local current_hour
        current_hour=$(date +%H)
        local start_hour
        start_hour=$(echo "$BUSINESS_HOURS_START" | cut -d':' -f1)
        local end_hour
        end_hour=$(echo "$BUSINESS_HOURS_END" | cut -d':' -f1)
        
        if [[ $current_hour -ge $start_hour && $current_hour -lt $end_hour ]]; then
            echo "❌ Shutdown blocked: Currently in business hours ($BUSINESS_HOURS_START - $BUSINESS_HOURS_END)"
            log_action "SHUTDOWN BLOCKED: Business hours protection active"
            audit_log "SHUTDOWN_BLOCKED" "BUSINESS_HOURS" "Operation: $operation Time: $(date '+%H:%M')"
            return 1
        fi
    fi
    
    # Check for active user sessions
    if [[ "$VALIDATE_USER_SESSIONS" == "true" ]]; then
        local active_users
        active_users=$(who | wc -l)
        
        if [[ $active_users -gt 0 && "$force" != "true" ]]; then
            echo "⚠️  Warning: $active_users active user session(s) detected"
            log_action "WARNING: Active user sessions during $operation"
            
            if [[ "$GRACEFUL_SHUTDOWN_ENABLED" != "true" ]]; then
                echo "❌ Shutdown blocked: Active users and graceful shutdown disabled"
                audit_log "SHUTDOWN_BLOCKED" "ACTIVE_USERS" "Users: $active_users Operation: $operation"
                return 1
            fi
        fi
    fi
    
    # Check for critical running processes
    if [[ "$CHECK_RUNNING_PROCESSES" == "true" ]]; then
        local critical_processes=(
            "backupd"
            "softwareupdate"
            "installer"
            "diskutil"
            "fsck"
        )
        
        for process in "${critical_processes[@]}"; do
            if pgrep -f "$process" >/dev/null 2>&1; then
                echo "⚠️  Critical process running: $process"
                
                if [[ "$force" != "true" ]]; then
                    echo "❌ Shutdown blocked: Critical process $process is running"
                    log_action "SHUTDOWN BLOCKED: Critical process $process running"
                    audit_log "SHUTDOWN_BLOCKED" "CRITICAL_PROCESS" "Process: $process Operation: $operation"
                    return 1
                else
                    echo "🚨 Force shutdown: Will terminate critical process $process"
                    log_action "FORCE SHUTDOWN: Terminating critical process $process"
                fi
            fi
        done
    fi
    
    # Admin approval check
    if [[ "$REQUIRE_ADMIN_APPROVAL" == "true" && "$force" != "true" ]]; then
        echo "❌ Shutdown blocked: Admin approval required"
        log_action "SHUTDOWN BLOCKED: Admin approval required"
        audit_log "SHUTDOWN_BLOCKED" "ADMIN_APPROVAL" "Operation: $operation"
        return 1
    fi
    
    echo "✅ Shutdown authorization validated"
    audit_log "SHUTDOWN_AUTHORIZED" "APPROVED" "Operation: $operation"
    return 0
}

# Send notification before shutdown
send_shutdown_notification() {
    local operation="$1"
    local delay_minutes="$2"
    local message="$3"
    
    echo "=== Sending Shutdown Notifications ==="
    
    local notification_title="MacFleet System $operation Notification"
    local notification_message="${message:-System $operation scheduled in $delay_minutes minutes}"
    
    # User notification
    if [[ "$USER_NOTIFICATION_ENABLED" == "true" ]]; then
        # Display notification to all logged-in users
        local logged_users
        logged_users=$(who | awk '{print $1}' | sort -u)
        
        for user in $logged_users; do
            if [[ -n "$user" ]]; then
                sudo -u "$user" osascript -e "display notification \"$notification_message\" with title \"$notification_title\"" 2>/dev/null || true
                echo "📱 Notification sent to user: $user"
            fi
        done
        
        # Terminal notification
        wall "$notification_title: $notification_message" 2>/dev/null || true
    fi
    
    # Admin notification
    if [[ "$ADMIN_NOTIFICATION_ENABLED" == "true" ]]; then
        log_action "ADMIN NOTIFICATION: $notification_title - $notification_message"
    fi
    
    # Slack notification
    if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
        local slack_payload="{\"text\":\"🖥️ *$(hostname)*: $notification_title\\n$notification_message\"}"
        curl -s -X POST -H 'Content-type: application/json' --data "$slack_payload" "$SLACK_WEBHOOK_URL" >/dev/null 2>&1 || true
        echo "📢 Slack notification sent"
    fi
    
    log_action "Shutdown notifications sent for $operation (delay: $delay_minutes minutes)"
}

# Graceful shutdown with user session handling
graceful_shutdown() {
    local operation="$1"
    local delay_minutes="$2"
    local message="$3"
    
    echo "=== Initiating Graceful $operation ==="
    
    # Send initial notification
    send_shutdown_notification "$operation" "$delay_minutes" "$message"
    
    # Save user work if enabled
    if [[ "$SAVE_WORK_BEFORE_SHUTDOWN" == "true" ]]; then
        echo "💾 Attempting to save user work..."
        
        # Try to save work in common applications
        local save_commands=(
            "osascript -e 'tell application \"System Events\" to keystroke \"s\" using command down'"
            "pkill -USR1 TextEdit"  # Signal TextEdit to save
        )
        
        for cmd in "${save_commands[@]}"; do
            eval "$cmd" 2>/dev/null || true
        done
        
        log_action "User work save attempted before $operation"
    fi
    
    # Create backup if enabled
    if [[ "$BACKUP_BEFORE_SHUTDOWN" == "true" ]]; then
        echo "💾 Creating system backup before $operation..."
        create_pre_shutdown_backup
    fi
    
    # FileVault key protection
    if [[ "$ENCRYPTION_KEY_PROTECTION" == "true" ]]; then
        echo "🔐 Ensuring FileVault key security..."
        sudo pmset -a destroyfvkeyonstandby 1 2>/dev/null || true
        log_action "FileVault key protection enabled before $operation"
    fi
    
    # Wait for delay period with countdown
    if [[ $delay_minutes -gt 0 ]]; then
        echo "⏱️  Waiting $delay_minutes minutes before $operation..."
        
        local remaining_seconds=$((delay_minutes * 60))
        local notification_intervals=(300 120 60 30 10)  # 5min, 2min, 1min, 30s, 10s
        
        while [[ $remaining_seconds -gt 0 ]]; do
            # Send reminder notifications at specific intervals
            for interval in "${notification_intervals[@]}"; do
                if [[ $remaining_seconds -eq $interval ]]; then
                    local remaining_minutes=$((remaining_seconds / 60))
                    if [[ $remaining_minutes -gt 0 ]]; then
                        send_shutdown_notification "$operation" "$remaining_minutes" "Final warning: System $operation in $remaining_minutes minute(s)"
                    else
                        send_shutdown_notification "$operation" "0" "Final warning: System $operation in $remaining_seconds seconds"
                    fi
                    break
                fi
            done
            
            sleep 1
            remaining_seconds=$((remaining_seconds - 1))
        done
    fi
    
    echo "✅ Graceful $operation preparation completed"
    log_action "Graceful $operation preparation completed"
}

# Create pre-shutdown backup
create_pre_shutdown_backup() {
    echo "=== Creating Pre-Shutdown Backup ==="
    
    local backup_dir="/Library/MacFleet/Backups"
    local backup_file="$backup_dir/pre_shutdown_backup_$(date +%Y%m%d_%H%M%S).tar.gz"
    
    mkdir -p "$backup_dir"
    
    # Backup critical system configurations
    local backup_paths=(
        "/etc"
        "/Library/Preferences"
        "/System/Library/LaunchDaemons"
        "/Library/LaunchDaemons"
        "/usr/local/etc"
    )
    
    echo "Creating backup of critical system files..."
    tar -czf "$backup_file" "${backup_paths[@]}" 2>/dev/null || true
    
    if [[ -f "$backup_file" ]]; then
        echo "✅ Pre-shutdown backup created: $backup_file"
        log_action "Pre-shutdown backup created: $backup_file"
    else
        echo "⚠️  Pre-shutdown backup creation failed"
        log_action "WARNING: Pre-shutdown backup creation failed"
    fi
}

# Enterprise shutdown with comprehensive validation
enterprise_shutdown() {
    local delay="${1:-now}"
    local message="$2"
    local force="${3:-false}"
    
    echo "=== Enterprise Shutdown Initiated ==="
    
    # Validate shutdown authorization
    if ! check_shutdown_allowed "shutdown" "$force"; then
        return 1
    fi
    
    # Parse delay
    local delay_minutes=0
    if [[ "$delay" != "now" ]]; then
        if [[ "$delay" =~ ^\+([0-9]+)$ ]]; then
            delay_minutes="${BASH_REMATCH[1]}"
        else
            echo "❌ Invalid delay format. Use 'now' or '+minutes'"
            return 1
        fi
    fi
    
    # Graceful shutdown if enabled
    if [[ "$GRACEFUL_SHUTDOWN_ENABLED" == "true" ]]; then
        graceful_shutdown "shutdown" "$delay_minutes" "$message"
    fi
    
    # Log shutdown action
    log_action "ENTERPRISE SHUTDOWN: Executing shutdown with delay: $delay"
    audit_log "SHUTDOWN" "INITIATED" "Delay: $delay Message: ${message:-None} Force: $force"
    
    # Execute shutdown
    if [[ "$delay" == "now" ]]; then
        echo "🔌 Executing immediate shutdown..."
        sudo shutdown -h now "$message"
    else
        echo "🔌 Scheduling shutdown in $delay_minutes minutes..."
        sudo shutdown -h +"$delay_minutes" "$message"
    fi
    
    audit_log "SHUTDOWN" "EXECUTED" "Command completed successfully"
    return 0
}

# Enterprise reboot with comprehensive validation
enterprise_reboot() {
    local delay="${1:-now}"
    local message="$2"
    local force="${3:-false}"
    
    echo "=== Enterprise Reboot Initiated ==="
    
    # Validate reboot authorization
    if ! check_shutdown_allowed "reboot" "$force"; then
        return 1
    fi
    
    # Parse delay
    local delay_minutes=0
    if [[ "$delay" != "now" ]]; then
        if [[ "$delay" =~ ^\+([0-9]+)$ ]]; then
            delay_minutes="${BASH_REMATCH[1]}"
        else
            echo "❌ Invalid delay format. Use 'now' or '+minutes'"
            return 1
        fi
    fi
    
    # Graceful reboot if enabled
    if [[ "$GRACEFUL_SHUTDOWN_ENABLED" == "true" ]]; then
        graceful_shutdown "reboot" "$delay_minutes" "$message"
    fi
    
    # Log reboot action
    log_action "ENTERPRISE REBOOT: Executing reboot with delay: $delay"
    audit_log "REBOOT" "INITIATED" "Delay: $delay Message: ${message:-None} Force: $force"
    
    # Execute reboot
    if [[ "$delay" == "now" ]]; then
        echo "🔄 Executing immediate reboot..."
        sudo shutdown -r now "$message"
    else
        echo "🔄 Scheduling reboot in $delay_minutes minutes..."
        sudo shutdown -r +"$delay_minutes" "$message"
    fi
    
    audit_log "REBOOT" "EXECUTED" "Command completed successfully"
    return 0
}

# Schedule maintenance reboot
schedule_maintenance_reboot() {
    local schedule_time="$1"
    local reason="$2"
    
    echo "=== Scheduling Maintenance Reboot ==="
    
    if [[ -z "$schedule_time" ]]; then
        echo "❌ Schedule time required (format: YYMMDDHHMM)"
        return 1
    fi
    
    # Validate schedule time format
    if [[ ! "$schedule_time" =~ ^[0-9]{10}$ ]]; then
        echo "❌ Invalid schedule time format. Use YYMMDDHHMM"
        return 1
    fi
    
    # Check if scheduled time is in maintenance window
    if [[ "$MAINTENANCE_WINDOW_ENABLED" == "true" ]]; then
        local schedule_hour
        schedule_hour=$(echo "$schedule_time" | cut -c 7-8)
        local maintenance_start_hour
        maintenance_start_hour=$(echo "$MAINTENANCE_START_TIME" | cut -d':' -f1)
        local maintenance_end_hour
        maintenance_end_hour=$(echo "$MAINTENANCE_END_TIME" | cut -d':' -f1)
        
        if [[ $schedule_hour -lt $maintenance_start_hour || $schedule_hour -ge $maintenance_end_hour ]]; then
            echo "⚠️  Warning: Scheduled time outside maintenance window ($MAINTENANCE_START_TIME - $MAINTENANCE_END_TIME)"
        fi
    fi
    
    # Schedule the reboot
    local message="Scheduled maintenance reboot: ${reason:-System maintenance}"
    
    log_action "MAINTENANCE REBOOT SCHEDULED: Time: $schedule_time Reason: ${reason:-System maintenance}"
    audit_log "MAINTENANCE_SCHEDULED" "CREATED" "Time: $schedule_time Reason: ${reason:-System maintenance}"
    
    echo "🗓️  Scheduling maintenance reboot..."
    sudo shutdown -r "$schedule_time" "$message"
    
    echo "✅ Maintenance reboot scheduled for $(date -j -f "%Y%m%d%H%M" "20$schedule_time" "+%Y-%m-%d %H:%M")"
    
    # Create maintenance record
    local maintenance_file="$SCHEDULES_DIR/maintenance_$(date +%Y%m%d_%H%M%S).json"
    cat > "$maintenance_file" << EOF
{
  "type": "maintenance_reboot",
  "scheduled_time": "$schedule_time",
  "reason": "${reason:-System maintenance}",
  "created_by": "$(whoami)",
  "created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "device": "$(hostname)"
}
EOF
    
    log_action "Maintenance record created: $maintenance_file"
    return 0
}

# Cancel scheduled shutdown/reboot
cancel_scheduled_operation() {
    echo "=== Cancelling Scheduled Operations ==="
    
    # Get list of scheduled operations
    local scheduled_ops
    scheduled_ops=$(ps aux | grep "shutdown" | grep -v grep | wc -l)
    
    if [[ $scheduled_ops -eq 0 ]]; then
        echo "ℹ️  No scheduled shutdown/reboot operations found"
        return 0
    fi
    
    # Cancel shutdown operations
    if sudo pkill -f "shutdown"; then
        echo "✅ Scheduled shutdown/reboot operations cancelled"
        log_action "Scheduled shutdown/reboot operations cancelled"
        audit_log "SCHEDULE_CANCELLED" "SUCCESS" "Operations cancelled by $(whoami)"
        
        # Notify users of cancellation
        send_shutdown_notification "cancellation" "0" "Scheduled system operation has been cancelled"
    else
        echo "❌ Failed to cancel scheduled operations"
        log_action "FAILED: Could not cancel scheduled operations"
        audit_log "SCHEDULE_CANCELLED" "FAILED" "Cancellation attempt by $(whoami)"
        return 1
    fi
    
    return 0
}

# Emergency shutdown (bypasses all checks)
emergency_shutdown() {
    local reason="$1"
    
    echo "🚨 EMERGENCY SHUTDOWN INITIATED 🚨"
    
    if [[ -z "$reason" ]]; then
        echo "❌ Emergency reason required"
        return 1
    fi
    
    log_action "EMERGENCY SHUTDOWN: Reason: $reason"
    audit_log "EMERGENCY_SHUTDOWN" "INITIATED" "Reason: $reason User: $(whoami)"
    
    # Send emergency notification
    send_shutdown_notification "EMERGENCY SHUTDOWN" "0" "EMERGENCY: System shutdown initiated - $reason"
    
    # Emergency contact notification
    if [[ -n "$EMERGENCY_CONTACT_INFO" ]]; then
        echo "📞 Emergency contact: $EMERGENCY_CONTACT_INFO"
        log_action "Emergency contact info: $EMERGENCY_CONTACT_INFO"
    fi
    
    # Force immediate shutdown
    echo "🔌 Executing emergency shutdown (force)..."
    sudo shutdown -h now "EMERGENCY SHUTDOWN: $reason"
    
    audit_log "EMERGENCY_SHUTDOWN" "EXECUTED" "Immediate shutdown completed"
}

# Check system status for shutdown readiness
check_system_status() {
    echo "=== System Status Check ==="
    
    local status_issues=()
    local status_warnings=()
    
    # Check uptime
    local uptime_seconds
    uptime_seconds=$(uptime | awk '{print $3}' | cut -d',' -f1)
    echo "System Uptime: $uptime_seconds"
    
    # Check load average
    local load_average
    load_average=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')
    if (( $(echo "$load_average > 5.0" | bc -l) )); then
        status_warnings+=("High system load: $load_average")
    fi
    
    # Check disk space
    local disk_usage
    disk_usage=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
    if [[ $disk_usage -gt 90 ]]; then
        status_warnings+=("Low disk space: ${disk_usage}% used")
    fi
    
    # Check memory usage
    local memory_pressure
    memory_pressure=$(memory_pressure 2>/dev/null | grep "System-wide memory free percentage" | awk '{print $5}' | sed 's/%//' || echo "unknown")
    if [[ "$memory_pressure" != "unknown" && $memory_pressure -lt 10 ]]; then
        status_warnings+=("Low memory: ${memory_pressure}% free")
    fi
    
    # Check for pending updates
    if softwareupdate -l 2>/dev/null | grep -q "Software Update found"; then
        status_warnings+=("Pending software updates available")
    fi
    
    # Check network connectivity
    if ! ping -c 1 8.8.8.8 >/dev/null 2>&1; then
        status_warnings+=("Network connectivity issue detected")
    fi
    
    # Report status
    if [[ ${#status_issues[@]} -eq 0 && ${#status_warnings[@]} -eq 0 ]]; then
        echo "✅ System status: Ready for shutdown/reboot"
        return 0
    else
        if [[ ${#status_issues[@]} -gt 0 ]]; then
            echo "❌ System status issues:"
            printf '  - %s\n' "${status_issues[@]}"
        fi
        
        if [[ ${#status_warnings[@]} -gt 0 ]]; then
            echo "⚠️  System status warnings:"
            printf '  - %s\n' "${status_warnings[@]}"
        fi
        
        return 1
    fi
}

# Generate shutdown management report
generate_shutdown_report() {
    local report_file="$SCHEDULES_DIR/shutdown_report_$(date +%Y%m%d_%H%M%S).json"
    
    echo "=== Generating Shutdown Management Report ==="
    
    # Get system information
    local last_boot
    last_boot=$(who -b | awk '{print $3 " " $4}')
    
    local uptime_info
    uptime_info=$(uptime | awk -F'up ' '{print $2}' | awk -F',' '{print $1}')
    
    # Count recent operations from audit log
    local recent_shutdowns=0
    local recent_reboots=0
    local recent_cancellations=0
    
    if [[ -f "$AUDIT_LOG" ]]; then
        recent_shutdowns=$(grep -c "ACTION:SHUTDOWN" "$AUDIT_LOG" 2>/dev/null || echo 0)
        recent_reboots=$(grep -c "ACTION:REBOOT" "$AUDIT_LOG" 2>/dev/null || echo 0)
        recent_cancellations=$(grep -c "ACTION:SCHEDULE_CANCELLED" "$AUDIT_LOG" 2>/dev/null || echo 0)
    fi
    
    # Check for scheduled operations
    local scheduled_operations=0
    scheduled_operations=$(ps aux | grep -c "shutdown" | grep -v grep || echo 0)
    
    # Create JSON report
    cat > "$report_file" << EOF
{
  "report_type": "shutdown_management",
  "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "device_info": {
    "hostname": "$(hostname)",
    "os_version": "$(sw_vers -productVersion)",
    "serial_number": "$(system_profiler SPHardwareDataType | grep "Serial Number" | awk -F: '{print $2}' | xargs)"
  },
  "system_status": {
    "last_boot": "$last_boot",
    "uptime": "$uptime_info",
    "scheduled_operations": $scheduled_operations,
    "system_ready": $(check_system_status >/dev/null 2>&1 && echo true || echo false)
  },
  "operation_history": {
    "total_shutdowns": $recent_shutdowns,
    "total_reboots": $recent_reboots,
    "cancelled_operations": $recent_cancellations
  },
  "policy_configuration": {
    "graceful_shutdown_enabled": $GRACEFUL_SHUTDOWN_ENABLED,
    "business_hours_protection": $PREVENT_SHUTDOWN_BUSINESS_HOURS,
    "maintenance_window_enabled": $MAINTENANCE_WINDOW_ENABLED,
    "auto_reboot_enabled": $AUTO_REBOOT_ENABLED
  },
  "compliance_status": {
    "audit_enabled": $AUDIT_ALL_POWER_OPERATIONS,
    "user_notification_enabled": $USER_NOTIFICATION_ENABLED,
    "backup_before_shutdown": $BACKUP_BEFORE_SHUTDOWN
  }
}
EOF
    
    echo "Shutdown management report saved to: $report_file"
    log_action "Shutdown management report generated: $report_file"
}

# Main function with argument handling
main() {
    log_action "=== MacFleet Shutdown Management Tool Started ==="
    
    case "${1:-status}" in
        "shutdown")
            enterprise_shutdown "$2" "$3" "$4"
            ;;
        "reboot")
            enterprise_reboot "$2" "$3" "$4"
            ;;
        "schedule")
            schedule_maintenance_reboot "$2" "$3"
            ;;
        "cancel")
            cancel_scheduled_operation
            ;;
        "emergency")
            emergency_shutdown "$2"
            ;;
        "status")
            check_system_status
            ;;
        "report")
            generate_shutdown_report
            ;;
        *)
            echo "MacFleet Enterprise Shutdown and Reboot Management Tool"
            echo "Usage: $0 [command] [options]"
            echo ""
            echo "Commands:"
            echo "  shutdown [delay] [message] [force]  - Shutdown system with optional delay"
            echo "  reboot [delay] [message] [force]    - Reboot system with optional delay"
            echo "  schedule [time] [reason]            - Schedule maintenance reboot (YYMMDDHHMM)"
            echo "  cancel                              - Cancel scheduled operations"
            echo "  emergency [reason]                  - Emergency shutdown (bypasses checks)"
            echo "  status                              - Check system status for shutdown readiness"
            echo "  report                              - Generate shutdown management report"
            echo ""
            echo "Delay Formats:"
            echo "  now                                 - Immediate operation"
            echo "  +minutes                            - Delay in minutes (e.g., +5)"
            echo ""
            echo "Examples:"
            echo "  $0 shutdown now                     - Immediate shutdown"
            echo "  $0 reboot +10 \"System maintenance\" - Reboot in 10 minutes with message"
            echo "  $0 schedule 2501020300 \"Patch Tuesday\" - Schedule reboot Jan 2, 3:00 AM"
            echo "  $0 emergency \"Security incident\"   - Emergency shutdown"
            ;;
    esac
    
    log_action "=== Shutdown management operation completed ==="
}

# Execute main function
main "$@"

## Advanced Shutdown Management Features

### Automated Maintenance Scheduling

```bash
#!/bin/bash

# Automated maintenance window management
setup_maintenance_automation() {
    echo "=== Setting Up Maintenance Automation ==="
    
    # Create maintenance automation script
    local automation_script="/Library/MacFleet/Scripts/maintenance_automation.sh"
    mkdir -p "$(dirname "$automation_script")"
    
    cat > "$automation_script" << 'EOF'
#!/bin/bash

# Check if we're in maintenance window
current_time=$(date +%H%M)
current_day=$(date +%u)  # 1=Monday, 7=Sunday

maintenance_start=$(echo "$MAINTENANCE_START_TIME" | tr -d ':')
maintenance_end=$(echo "$MAINTENANCE_END_TIME" | tr -d ':')

# Check if current time is in maintenance window
if [[ $current_time -ge $maintenance_start && $current_time -le $maintenance_end ]]; then
    # Check if it's a maintenance day
    if echo "$MAINTENANCE_DAYS" | grep -q "$(date +%a | cut -c1)"; then
        # Check if reboot is needed
        if [[ "$AUTO_REBOOT_ENABLED" == "true" ]]; then
            # Check uptime - reboot if system has been up more than 7 days
            uptime_days=$(uptime | awk '{print $3}' | cut -d',' -f1)
            if [[ $uptime_days -gt 7 ]]; then
                echo "System uptime exceeds 7 days - initiating maintenance reboot"
                /Library/MacFleet/Scripts/shutdown_management.sh reboot +5 "Automated maintenance reboot"
            fi
        fi
    fi
fi
EOF
    
    chmod +x "$automation_script"
    
    # Create launch daemon for maintenance automation
    local launch_daemon="/Library/LaunchDaemons/com.macfleet.maintenance.plist"
    
    cat > "$launch_daemon" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.macfleet.maintenance</string>
    <key>ProgramArguments</key>
    <array>
        <string>$automation_script</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>2</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>RunAtLoad</key>
    <false/>
</dict>
</plist>
EOF
    
    # Load the launch daemon
    sudo launchctl load "$launch_daemon" 2>/dev/null
    
    echo "✅ Maintenance automation configured"
    log_action "Maintenance automation setup completed"
}

# Patch management integration
check_patch_reboot_required() {
    echo "=== Checking Patch Reboot Requirements ==="
    
    # Check for pending updates requiring reboot
    local pending_updates
    pending_updates=$(softwareupdate -l 2>/dev/null | grep -i "restart\|reboot" | wc -l)
    
    if [[ $pending_updates -gt 0 ]]; then
        echo "⚠️  $pending_updates update(s) require system reboot"
        log_action "Patch reboot required: $pending_updates updates pending"
        
        if [[ "$PATCH_REBOOT_REQUIRED" == "true" ]]; then
            echo "🔄 Scheduling patch reboot for next maintenance window"
            
            # Calculate next maintenance window
            local next_maintenance
            next_maintenance=$(date -v+1d "+%y%m%d${MAINTENANCE_START_TIME/:/}")
            
            schedule_maintenance_reboot "$next_maintenance" "Patch installation reboot"
        fi
        
        return 1
    else
        echo "✅ No patches requiring reboot"
        return 0
    fi
}

# Disaster recovery shutdown
disaster_recovery_shutdown() {
    local disaster_type="$1"
    local severity="$2"
    
    echo "🚨 DISASTER RECOVERY SHUTDOWN 🚨"
    echo "Disaster Type: $disaster_type"
    echo "Severity: $severity"
    
    log_action "DISASTER RECOVERY: Type: $disaster_type Severity: $severity"
    audit_log "DISASTER_RECOVERY" "INITIATED" "Type: $disaster_type Severity: $severity"
    
    # Send emergency notifications
    send_shutdown_notification "DISASTER RECOVERY SHUTDOWN" "0" "Emergency shutdown due to $disaster_type ($severity)"
    
    # Create disaster recovery record
    local disaster_file="$SCHEDULES_DIR/disaster_recovery_$(date +%Y%m%d_%H%M%S).json"
    cat > "$disaster_file" << EOF
{
  "type": "disaster_recovery",
  "disaster_type": "$disaster_type",
  "severity": "$severity",
  "initiated_by": "$(whoami)",
  "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "device": "$(hostname)",
  "emergency_contact": "$EMERGENCY_CONTACT_INFO"
}
EOF
    
    # Execute immediate shutdown
    sudo shutdown -h now "DISASTER RECOVERY: $disaster_type ($severity)"
}

## Important Configuration Notes

### macOS Shutdown Command Options

- **`-h`** - Halt the system (shutdown)
- **`-r`** - Restart the system (reboot)
- **`-s`** - Sleep the system (display off + lock)
- **Time formats** - `now`, `+minutes`, `YYMMDDHHMM`
- **Warning messages** - Additional text displayed to users

### Enterprise Integration Points

- **Change Management Systems** - Integration with ITSM platforms
- **Monitoring Systems** - Real-time shutdown event tracking
- **Backup Solutions** - Pre-shutdown backup automation
- **Security Frameworks** - Compliance with shutdown policies

### Best Practices for Enterprise Shutdown Management

1. **Operational Continuity**
   - Implement graceful shutdown procedures with user notifications
   - Validate system readiness before shutdown operations
   - Maintain emergency override capabilities for critical situations
   - Schedule maintenance operations during designated windows

2. **Security and Compliance**
   - Enable comprehensive audit logging for all power operations
   - Implement FileVault key protection during shutdown
   - Validate user sessions and running processes before shutdown
   - Maintain compliance with regulatory shutdown requirements

3. **Change Management**
   - Require approval for non-scheduled shutdown operations
   - Document all maintenance-related shutdowns
   - Integrate with ITSM platforms for change tracking
   - Maintain emergency contact information for critical situations

4. **User Experience**
   - Provide adequate warning before shutdown operations
   - Attempt to save user work before system shutdown
   - Send notifications through multiple channels (GUI, email, Slack)
   - Allow cancellation of scheduled operations when appropriate

### Troubleshooting Common Issues

- **Permission errors** - Ensure admin privileges for shutdown commands
- **Scheduled operation conflicts** - Check for existing scheduled shutdowns
- **Network notification failures** - Verify webhook URLs and network connectivity
- **Backup failures** - Check disk space and backup destination availability
- **Maintenance window violations** - Review business hours and maintenance schedules

Remember to test shutdown procedures thoroughly in a controlled environment before implementing across your entire MacFleet to ensure business continuity and data protection. 

Tutorial

Nuevas actualizaciones y mejoras para Macfleet.

Configurando un Runner de GitHub Actions en un Mac Mini (Apple Silicon)

Runner de GitHub Actions

GitHub Actions es una plataforma poderosa de CI/CD que te permite automatizar tus flujos de trabajo de desarrollo de software. Aunque GitHub ofrece runners hospedados, los runners auto-hospedados proporcionan mayor control y personalización para tu configuración de CI/CD. Este tutorial te guía a través de la configuración y conexión de un runner auto-hospedado en un Mac mini para ejecutar pipelines de macOS.

Prerrequisitos

Antes de comenzar, asegúrate de tener:

  • Un Mac mini (regístrate en Macfleet)
  • Un repositorio de GitHub con derechos de administrador
  • Un gestor de paquetes instalado (preferiblemente Homebrew)
  • Git instalado en tu sistema

Paso 1: Crear una Cuenta de Usuario Dedicada

Primero, crea una cuenta de usuario dedicada para el runner de GitHub Actions:

# Crear la cuenta de usuario 'gh-runner'
sudo dscl . -create /Users/gh-runner
sudo dscl . -create /Users/gh-runner UserShell /bin/bash
sudo dscl . -create /Users/gh-runner RealName "GitHub runner"
sudo dscl . -create /Users/gh-runner UniqueID "1001"
sudo dscl . -create /Users/gh-runner PrimaryGroupID 20
sudo dscl . -create /Users/gh-runner NFSHomeDirectory /Users/gh-runner

# Establecer la contraseña para el usuario
sudo dscl . -passwd /Users/gh-runner tu_contraseña

# Agregar 'gh-runner' al grupo 'admin'
sudo dscl . -append /Groups/admin GroupMembership gh-runner

Cambia a la nueva cuenta de usuario:

su gh-runner

Paso 2: Instalar Software Requerido

Instala Git y Rosetta 2 (si usas Apple Silicon):

# Instalar Git si no está ya instalado
brew install git

# Instalar Rosetta 2 para Macs Apple Silicon
softwareupdate --install-rosetta

Paso 3: Configurar el Runner de GitHub Actions

  1. Ve a tu repositorio de GitHub
  2. Navega a Configuración > Actions > Runners

Runner de GitHub Actions

  1. Haz clic en "New self-hosted runner" (https://github.com/<username>/<repository>/settings/actions/runners/new)
  2. Selecciona macOS como imagen del runner y ARM64 como arquitectura
  3. Sigue los comandos proporcionados para descargar y configurar el runner

Runner de GitHub Actions

Crea un archivo .env en el directorio _work del runner:

# archivo _work/.env
ImageOS=macos15
XCODE_15_DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
  1. Ejecuta el script run.sh en tu directorio del runner para completar la configuración.
  2. Verifica que el runner esté activo y escuchando trabajos en la terminal y revisa la configuración del repositorio de GitHub para la asociación del runner y el estado Idle.

Runner de GitHub Actions

Paso 4: Configurar Sudoers (Opcional)

Si tus acciones requieren privilegios de root, configura el archivo sudoers:

sudo visudo

Agrega la siguiente línea:

gh-runner ALL=(ALL) NOPASSWD: ALL

Paso 5: Usar el Runner en Flujos de Trabajo

Configura tu flujo de trabajo de GitHub Actions para usar el runner auto-hospedado:

name: Flujo de trabajo de muestra

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: [self-hosted, macOS, ARM64]
    steps:
      - name: Instalar NodeJS
        run: brew install node

El runner está autenticado en tu repositorio y etiquetado con self-hosted, macOS, y ARM64. Úsalo en tus flujos de trabajo especificando estas etiquetas en el campo runs-on:

runs-on: [self-hosted, macOS, ARM64]

Mejores Prácticas

  • Mantén tu software del runner actualizado
  • Monitorea regularmente los logs del runner para problemas
  • Usa etiquetas específicas para diferentes tipos de runners
  • Implementa medidas de seguridad apropiadas
  • Considera usar múltiples runners para balanceo de carga

Solución de Problemas

Problemas comunes y soluciones:

  1. Runner no conectando:

    • Verifica conectividad de red
    • Verifica validez del token de GitHub
    • Asegúrate de permisos apropiados
  2. Fallas de construcción:

    • Verifica instalación de Xcode
    • Verifica dependencias requeridas
    • Revisa logs del flujo de trabajo
  3. Problemas de permisos:

    • Verifica permisos de usuario
    • Verifica configuración de sudoers
    • Revisa permisos del sistema de archivos

Conclusión

Ahora tienes un runner auto-hospedado de GitHub Actions configurado en tu Mac mini. Esta configuración te proporciona más control sobre tu entorno de CI/CD y te permite ejecutar flujos de trabajo específicos de macOS de manera eficiente.

Recuerda mantener regularmente tu runner y mantenerlo actualizado con los últimos parches de seguridad y versiones de software.

Aplicación Nativa

Aplicación nativa de Macfleet

Guía de Instalación de Macfleet

Macfleet es una solución poderosa de gestión de flota diseñada específicamente para entornos de Mac Mini alojados en la nube. Como proveedor de hosting en la nube de Mac Mini, puedes usar Macfleet para monitorear, gestionar y optimizar toda tu flota de instancias Mac virtualizadas.

Esta guía de instalación te llevará a través de la configuración del monitoreo de Macfleet en sistemas macOS, Windows y Linux para asegurar una supervisión integral de tu infraestructura en la nube.

🍎 macOS

  • Descarga el archivo .dmg para Mac aquí
  • Haz doble clic en el archivo .dmg descargado
  • Arrastra la aplicación Macfleet a la carpeta Aplicaciones
  • Expulsa el archivo .dmg
  • Abre Preferencias del Sistema > Seguridad y Privacidad
    • Pestaña Privacidad > Accesibilidad
    • Marca Macfleet para permitir el monitoreo
  • Inicia Macfleet desde Aplicaciones
  • El seguimiento comienza automáticamente

🪟 Windows

  • Descarga el archivo .exe para Windows aquí
  • Haz clic derecho en el archivo .exe > "Ejecutar como administrador"
  • Sigue el asistente de instalación
  • Acepta los términos y condiciones
  • Permite en Windows Defender si se solicita
  • Concede permisos de monitoreo de aplicaciones
  • Inicia Macfleet desde el Menú Inicio
  • La aplicación comienza el seguimiento automáticamente

🐧 Linux

  • Descarga el paquete .deb (Ubuntu/Debian) o .rpm (CentOS/RHEL) aquí
  • Instala usando tu gestor de paquetes
    • Ubuntu/Debian: sudo dpkg -i Macfleet-linux.deb
    • CentOS/RHEL: sudo rpm -ivh Macfleet-linux.rpm
  • Permite permisos de acceso X11 si se solicita
  • Agrega el usuario a los grupos apropiados si es necesario
  • Inicia Macfleet desde el menú de Aplicaciones
  • La aplicación comienza el seguimiento automáticamente

Nota: Después de la instalación en todos los sistemas, inicia sesión con tus credenciales de Macfleet para sincronizar datos con tu panel de control.