#!/bin/bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" . "${SCRIPT_DIR}/../share/logging.sh" . "${SCRIPT_DIR}/../share/os-detection.sh" # Configuration SCRIPTS=( "../custom-metrics/disk-usage.sh" "../custom-metrics/power-usage.sh" "../custom-metrics/temperature.sh" ) OS_TYPE=$(DetectOS) # Determine LaunchAgent vs LaunchDaemon directory # If running as root on macOS, use system-wide LaunchDaemons for headless operation if [[ "$OS_TYPE" == "macos" && $EUID -eq 0 ]]; then LAUNCHD_DIR="/Library/LaunchDaemons" LAUNCHD_TYPE="daemon" else LAUNCHD_DIR="$HOME/Library/LaunchAgents" LAUNCHD_TYPE="agent" fi CheckScriptExists() { local script="$1" local script_path="${SCRIPT_DIR}/${script}" if [[ -f "$script_path" && -x "$script_path" ]]; then return 0 elif [[ -f "$script_path" ]]; then LogWarning "Script $script exists but is not executable. Making it executable..." chmod +x "$script_path" return 0 else return 1 fi } InstallCrontabEntries() { local installed_count=0 local failed_count=0 if [[ "$OS_TYPE" == "macos" ]]; then BackupLaunchAgents else BackupCrontab fi for script in "${SCRIPTS[@]}"; do LogInformation "Processing $script..." if CheckScriptExists "$script"; then LogInformation "Script $script found and is executable" if [[ "$OS_TYPE" == "macos" ]]; then if ManageLaunchAgent "$script"; then ((installed_count++)) else ((failed_count++)) fi else if ManageCrontabEntry "$script"; then ((installed_count++)) else ((failed_count++)) fi fi else LogWarning "Script $script not found or not accessible, skipping..." ((failed_count++)) fi done LogInformation "Installation summary: $installed_count installed, $failed_count failed" if [[ $failed_count -gt 0 ]]; then exit 1 fi } ManageLaunchAgent() { local script="$1" local script_path="${SCRIPT_DIR}/${script}" local script_name=$(basename "$script" .sh) local plist_name="com.servermetrics.${script_name}.plist" local plist_path="${LAUNCHD_DIR}/${plist_name}" # Ensure LaunchAgents/LaunchDaemons directory exists if [[ ! -d "$LAUNCHD_DIR" ]]; then mkdir -p "$LAUNCHD_DIR" fi # Create plist file cat > "$plist_path" << EOF Label ${plist_name%.plist} ProgramArguments ${script_path} StartInterval 60 RunAtLoad StandardErrorPath /tmp/${script_name}.err StandardOutPath /tmp/${script_name}.out EOF if [[ $? -eq 0 ]]; then # Set proper ownership for LaunchDaemons if [[ "$LAUNCHD_TYPE" == "daemon" ]]; then chown root:wheel "$plist_path" chmod 644 "$plist_path" local type_display="LaunchDaemon" else local type_display="LaunchAgent" fi LogInformation "Created $type_display plist: $plist_path" # Use appropriate launchctl commands based on type if [[ "$LAUNCHD_TYPE" == "daemon" ]]; then # Unload if already loaded (ignore errors) launchctl bootout system "$plist_path" 2>/dev/null || true # Load the plist if launchctl bootstrap system "$plist_path" 2>/dev/null; then LogInformation "Loaded $type_display for $script" return 0 else LogError "Failed to load $type_display for $script" return 1 fi else # Unload if already loaded (ignore errors) launchctl unload "$plist_path" 2>/dev/null || true # Load the plist if launchctl load "$plist_path" 2>/dev/null; then LogInformation "Loaded $type_display for $script" return 0 else LogError "Failed to load $type_display for $script" return 1 fi fi else LogError "Failed to create plist for $script" return 1 fi } ManageCrontabEntry() { local script="$1" local script_path="${SCRIPT_DIR}/${script}" local cron_pattern="* * * * * ${script_path}" local current_crontab current_crontab=$(crontab -l 2>/dev/null || echo "") if echo "$current_crontab" | grep -q "$script_path"; then LogInformation "Crontab entry for $script already exists" if echo "$current_crontab" | grep -q "^\* \* \* \* \* ${script_path}"; then LogInformation "Entry for $script is already set to run every minute" return 0 else LogInformation "Updating $script to run every minute" local new_crontab new_crontab=$(echo "$current_crontab" | grep -v "$script_path") new_crontab="${new_crontab}${new_crontab:+$'\n'}${cron_pattern}" if echo "$new_crontab" | crontab -; then LogInformation "Updated crontab entry for $script" return 0 else LogError "Failed to update crontab entry for $script" return 1 fi fi else LogInformation "Adding new crontab entry for $script" local new_crontab="${current_crontab}${current_crontab:+$'\n'}${cron_pattern}" if echo "$new_crontab" | crontab -; then LogInformation "Added crontab entry for $script" return 0 else LogError "Failed to add crontab entry for $script" return 1 fi fi } ShowStatus() { LogInformation "Scheduled task status:" LogInformation "OS: $OS_TYPE" if [[ "$OS_TYPE" == "macos" ]]; then if [[ "$LAUNCHD_TYPE" == "daemon" ]]; then LogInformation "Method: LaunchDaemons (system-wide, runs at boot)" else LogInformation "Method: LaunchAgents (user-level, requires login)" fi else LogInformation "Method: Crontab" fi LogInformation "" local found_entries=0 for script in "${SCRIPTS[@]}"; do local script_path="${SCRIPT_DIR}/${script}" local script_name=$(basename "$script") if [[ "$OS_TYPE" == "macos" ]]; then local plist_name="com.servermetrics.$(basename "$script" .sh).plist" local plist_path="${LAUNCHD_DIR}/${plist_name}" if [[ -f "$plist_path" ]]; then if launchctl list | grep -q "$(basename "$plist_name" .plist)"; then LogInformation "✓ $script_name - LOADED" else LogInformation "⚠ $script_name - INSTALLED but NOT LOADED" fi ((found_entries++)) elif [[ -f "$script_path" ]]; then LogInformation "✗ $script_name - NOT INSTALLED (script available)" else LogInformation "✗ $script_name - SCRIPT MISSING" fi else if crontab -l 2>/dev/null | grep -q "$script_path"; then LogInformation "✓ $script_name - INSTALLED" ((found_entries++)) elif [[ -f "$script_path" ]]; then LogInformation "✗ $script_name - NOT INSTALLED (script available)" else LogInformation "✗ $script_name - SCRIPT MISSING" fi fi done LogInformation "" if [[ $found_entries -gt 0 ]]; then LogInformation "Commands:" if [[ "$OS_TYPE" == "macos" ]]; then LogInformation " List ${LAUNCHD_TYPE}s: launchctl list | grep servermetrics" if [[ "$LAUNCHD_TYPE" == "daemon" ]]; then LogInformation " View logs: tail -f /tmp/*.out" LogInformation " Bootout ${LAUNCHD_TYPE}: sudo launchctl bootout system $LAUNCHD_DIR/com.servermetrics.*.plist" else LogInformation " View logs: tail -f /tmp/*.out" LogInformation " Unload agent: launchctl unload ~/Library/LaunchAgents/com.servermetrics.*.plist" fi else LogInformation " View crontab: crontab -l" LogInformation " Edit crontab: crontab -e" LogInformation " Check logs: sudo tail -f /var/log/syslog | grep CRON" fi else LogWarning "No scheduled tasks are currently installed" fi } RemoveCrontabEntries() { local removed_count=0 local type_display if [[ "$LAUNCHD_TYPE" == "daemon" ]]; then type_display="LaunchDaemon" else type_display="LaunchAgent" fi if [[ "$OS_TYPE" == "macos" ]]; then # Remove LaunchAgents/LaunchDaemons for script in "${SCRIPTS[@]}"; do local script_name=$(basename "$script" .sh) local plist_name="com.servermetrics.${script_name}.plist" local plist_path="${LAUNCHD_DIR}/${plist_name}" if [[ -f "$plist_path" ]]; then LogInformation "Removing $type_display for $script_name..." # Unload the agent/daemon if [[ "$LAUNCHD_TYPE" == "daemon" ]]; then launchctl bootout system "$plist_path" 2>/dev/null || true else launchctl unload "$plist_path" 2>/dev/null || true fi # Remove the plist file rm -f "$plist_path" ((removed_count++)) else LogDebug "No $type_display found for $script_name" fi done if [[ $removed_count -gt 0 ]]; then LogInformation "Removed $removed_count ${type_display}s" else LogWarning "No ${type_display}s were found to remove" fi else # Remove crontab entries local current_crontab current_crontab=$(crontab -l 2>/dev/null || echo "") if [[ -z "$current_crontab" ]]; then LogWarning "No crontab entries found to remove" return fi local new_crontab="$current_crontab" for script in "${SCRIPTS[@]}"; do local script_path="${SCRIPT_DIR}/${script}" local script_name=$(basename "$script") if echo "$new_crontab" | grep -q "$script_path"; then LogInformation "Removing crontab entry for $script_name..." new_crontab=$(echo "$new_crontab" | grep -v "$script_path") ((removed_count++)) else LogDebug "No crontab entry found for $script_name" fi done if [[ $removed_count -gt 0 ]]; then if [[ -z "$new_crontab" ]]; then crontab -r 2>/dev/null || true LogInformation "Removed all entries, crontab cleared" else echo "$new_crontab" | crontab - LogInformation "Updated crontab with remaining entries" fi LogInformation "Removed $removed_count crontab entries" else LogWarning "No matching crontab entries were found to remove" fi fi } BackupCrontab() { local backup_file="${SCRIPT_DIR}/crontab_backup_$(date +%Y%m%d_%H%M%S)" crontab -l > "$backup_file" 2>/dev/null if [[ $? -eq 0 ]]; then LogInformation "Crontab backed up to: $backup_file" else LogWarning "No existing crontab to backup or backup failed" fi } BackupLaunchAgents() { local backup_dir="${SCRIPT_DIR}/launchagents_backup_$(date +%Y%m%d_%H%M%S)" mkdir -p "$backup_dir" local backup_count=0 for script in "${SCRIPTS[@]}"; do local script_name=$(basename "$script" .sh) local plist_name="com.servermetrics.${script_name}.plist" local plist_path="${LAUNCHD_DIR}/${plist_name}" if [[ -f "$plist_path" ]]; then cp "$plist_path" "$backup_dir/" ((backup_count++)) fi done if [[ $backup_count -gt 0 ]]; then LogInformation "LaunchAgents backed up to: $backup_dir ($backup_count files)" else LogWarning "No existing LaunchAgents to backup" rmdir "$backup_dir" 2>/dev/null fi } ShowUsage() { local task_method="crontab" if [[ "$OS_TYPE" == "macos" ]]; then task_method="LaunchAgents" fi echo "Usage: $0 [install|remove|status|help]" echo "" echo "Commands:" echo " install - Install all scheduled tasks (default)" echo " remove - Remove all scheduled tasks" echo " status - Show status of scheduled tasks" echo " help - Show this help message" echo "" echo "OS: $OS_TYPE" echo "Method: $task_method" echo "" echo "This script manages scheduled tasks for monitoring scripts:" echo "$(printf ' %s\n' "${SCRIPTS[@]}" | sed 's|../custom-metrics/||')" echo "" echo "Examples:" echo " $0 install" echo " $0 status" } Main() { local action="${1:-install}" case "$action" in install) LogInformation "Installing scheduled tasks for server monitoring scripts (OS: $OS_TYPE)" InstallCrontabEntries LogInformation "Scheduled task setup completed successfully!" ;; remove) LogInformation "Removing scheduled tasks for server monitoring scripts (OS: $OS_TYPE)" RemoveCrontabEntries ;; status) ShowStatus ;; help|--help|-h) ShowUsage ;; *) LogError "Unknown action: $action" ShowUsage exit 1 ;; esac } Main "$@"