diff --git a/custom-metrics/disk-usage.sh b/custom-metrics/disk-usage.sh
index aa24668..19e274f 100644
--- a/custom-metrics/disk-usage.sh
+++ b/custom-metrics/disk-usage.sh
@@ -2,8 +2,10 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/../share/logging.sh"
+. "${SCRIPT_DIR}/../share/os-detection.sh"
LOGFILE=/var/log/disk_usage.log
+OS_TYPE=$(DetectOS)
# Create log file with proper error handling
if ! touch "$LOGFILE" 2>/dev/null; then
@@ -12,17 +14,35 @@ if ! touch "$LOGFILE" 2>/dev/null; then
fi
chmod 644 "$LOGFILE" 2>/dev/null
-LogInformation "Starting disk usage monitoring"
+LogInformation "Starting disk usage monitoring (OS: $OS_TYPE)"
for i in {1..6}; do
timestamp=$(GetTimestamp)
# Use process substitution instead of pipe to avoid subshell issues
- while read -r mount used total; do
- if [[ -n "$mount" && -n "$used" && -n "$total" ]]; then
- echo "$timestamp $mount $used $total" >> "$LOGFILE"
- fi
- done < <(df --output=target,used,size -B1 2>/dev/null | tail -n +2)
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ # macOS df format: Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on
+ while read -r filesystem blocks used avail capacity iused ifree iusedpct mount; do
+ # Skip header line
+ if [[ "$filesystem" == "Filesystem" ]]; then
+ continue
+ fi
+
+ # Convert 512-byte blocks to bytes
+ if [[ -n "$mount" && -n "$used" && -n "$blocks" ]]; then
+ used_bytes=$((used * 512))
+ total_bytes=$((blocks * 512))
+ echo "$timestamp $mount $used_bytes $total_bytes" >> "$LOGFILE"
+ fi
+ done < <(df 2>/dev/null)
+ else
+ # Linux df with --output flag
+ while read -r mount used total; do
+ if [[ -n "$mount" && -n "$used" && -n "$total" ]]; then
+ echo "$timestamp $mount $used $total" >> "$LOGFILE"
+ fi
+ done < <(df --output=target,used,size -B1 2>/dev/null | tail -n +2)
+ fi
LogInformation "Disk usage data collected (iteration $i/6)"
diff --git a/custom-metrics/power-usage.sh b/custom-metrics/power-usage.sh
index 25e555e..6ef37f1 100644
--- a/custom-metrics/power-usage.sh
+++ b/custom-metrics/power-usage.sh
@@ -2,8 +2,10 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/../share/logging.sh"
+. "${SCRIPT_DIR}/../share/os-detection.sh"
LOGFILE=/var/log/power_usage.log
+OS_TYPE=$(DetectOS)
# Create log file with proper error handling
if ! touch "$LOGFILE" 2>/dev/null; then
@@ -14,64 +16,99 @@ chmod 644 "$LOGFILE" 2>/dev/null
# Check if bc is available
if ! command -v bc >/dev/null 2>&1; then
- LogError "bc command not found. Please install bc: sudo apt-get install bc"
+ LogError "bc command not found. Please install bc:"
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogError " macOS: brew install bc"
+ else
+ LogError " Ubuntu/Debian: sudo apt-get install bc"
+ fi
exit 1
fi
-# Path to the energy_uj file for the CPU package
-RAPL_PATH="/sys/class/powercap/intel-rapl:0/energy_uj"
-
-# Check if RAPL interface is available
-if [[ ! -r "$RAPL_PATH" ]]; then
- LogError "Intel RAPL interface not available at $RAPL_PATH"
- LogError "This may be due to:"
- LogError "1. Non-Intel CPU"
- LogError "2. RAPL not supported by kernel"
- LogError "3. Insufficient permissions"
- LogError "4. Module not loaded (try: sudo modprobe intel_rapl_common)"
+# Check power monitoring support
+if ! IsPowerMonitoringSupported; then
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogError "powermetrics not available at /usr/bin/powermetrics"
+ LogError "Power monitoring requires macOS with powermetrics support"
+ else
+ LogError "Intel RAPL interface not available"
+ LogError "This may be due to:"
+ LogError "1. Non-Intel CPU"
+ LogError "2. RAPL not supported by kernel"
+ LogError "3. Insufficient permissions"
+ LogError "4. Module not loaded (try: sudo modprobe intel_rapl_common)"
+ fi
exit 1
fi
-LogInformation "Starting power usage monitoring"
+LogInformation "Starting power usage monitoring (OS: $OS_TYPE)"
for i in {1..6}; do
- # Read initial energy value
- if ! energy_1=$(cat "$RAPL_PATH" 2>/dev/null); then
- LogError "Failed to read energy value from $RAPL_PATH"
- exit 1
- fi
-
- sleep 1
-
- # Read second energy value
- if ! energy_2=$(cat "$RAPL_PATH" 2>/dev/null); then
- LogError "Failed to read energy value from $RAPL_PATH"
- exit 1
- fi
-
- energy_diff=$((energy_2 - energy_1))
-
- # Convert microjoules to joules (1 J = 1,000,000 µJ)
- energy_joules=$(echo "scale=6; $energy_diff / 1000000" | bc 2>/dev/null)
-
- if [[ $? -ne 0 ]]; then
- LogError "Failed to calculate energy in joules"
- continue
- fi
-
- # Since we waited 1 second, the power usage is simply the energy difference in joules (W)
- power_watts=$(echo "scale=6; $energy_joules / 1" | bc 2>/dev/null)
-
- if [[ $? -ne 0 ]]; then
- LogError "Failed to calculate power in watts"
- continue
- fi
-
timestamp=$(GetTimestamp)
+
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ # Use powermetrics for macOS (requires sudo)
+ # Sample for 1 second and extract CPU power
+ power_data=$(sudo powermetrics -n 1 -i 1000 --samplers cpu_power 2>/dev/null | grep "CPU Power" | head -1)
+
+ if [[ -n "$power_data" ]]; then
+ # Extract power value (format: "CPU Power: 1234 mW")
+ power_mw=$(echo "$power_data" | grep -o '[0-9]\+ mW' | grep -o '[0-9]\+')
+
+ if [[ -n "$power_mw" ]]; then
+ # Convert milliwatts to watts
+ power_watts=$(echo "scale=6; $power_mw / 1000" | bc 2>/dev/null)
+
+ if [[ $? -eq 0 ]]; then
+ echo "$timestamp $power_watts" >> "$LOGFILE"
+ LogInformation "Power usage recorded: ${power_watts}W (iteration $i/6)"
+ else
+ LogError "Failed to calculate power in watts"
+ fi
+ fi
+ else
+ LogWarning "Could not retrieve power data from powermetrics"
+ fi
+ else
+ # Linux RAPL interface
+ RAPL_PATH="/sys/class/powercap/intel-rapl:0/energy_uj"
+
+ # Read initial energy value
+ if ! energy_1=$(cat "$RAPL_PATH" 2>/dev/null); then
+ LogError "Failed to read energy value from $RAPL_PATH"
+ exit 1
+ fi
+
+ sleep 1
- # Write power usage to log file
- echo "$timestamp $power_watts" >> "$LOGFILE"
- LogInformation "Power usage recorded: ${power_watts}W (iteration $i/6)"
+ # Read second energy value
+ if ! energy_2=$(cat "$RAPL_PATH" 2>/dev/null); then
+ LogError "Failed to read energy value from $RAPL_PATH"
+ exit 1
+ fi
+
+ energy_diff=$((energy_2 - energy_1))
+
+ # Convert microjoules to joules (1 J = 1,000,000 µJ)
+ energy_joules=$(echo "scale=6; $energy_diff / 1000000" | bc 2>/dev/null)
+
+ if [[ $? -ne 0 ]]; then
+ LogError "Failed to calculate energy in joules"
+ continue
+ fi
+
+ # Since we waited 1 second, the power usage is simply the energy difference in joules (W)
+ power_watts=$(echo "scale=6; $energy_joules / 1" | bc 2>/dev/null)
+
+ if [[ $? -ne 0 ]]; then
+ LogError "Failed to calculate power in watts"
+ continue
+ fi
+
+ # Write power usage to log file
+ echo "$timestamp $power_watts" >> "$LOGFILE"
+ LogInformation "Power usage recorded: ${power_watts}W (iteration $i/6)"
+ fi
if [ $i -lt 6 ]; then
sleep 9
diff --git a/custom-metrics/temperature.sh b/custom-metrics/temperature.sh
index d2cd6f3..1fce618 100644
--- a/custom-metrics/temperature.sh
+++ b/custom-metrics/temperature.sh
@@ -2,8 +2,10 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/../share/logging.sh"
+. "${SCRIPT_DIR}/../share/os-detection.sh"
LOGFILE=/var/log/temperature.log
+OS_TYPE=$(DetectOS)
# Create log file with proper error handling
if ! touch "$LOGFILE" 2>/dev/null; then
@@ -12,55 +14,83 @@ if ! touch "$LOGFILE" 2>/dev/null; then
fi
chmod 644 "$LOGFILE" 2>/dev/null
-# Check if thermal zones exist
-THERMAL_ZONES=(/sys/class/thermal/thermal_zone*/type)
-if [[ ! -e "${THERMAL_ZONES[0]}" ]]; then
- LogError "No thermal zones found in /sys/class/thermal/"
- LogError "This system may not support thermal monitoring"
+# Check thermal monitoring support
+if ! IsThermalMonitoringSupported; then
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogError "Temperature monitoring not available on macOS"
+ LogError "Install osx-cpu-temp: brew install osx-cpu-temp"
+ LogError "Or use powermetrics (requires sudo)"
+ else
+ LogError "No thermal zones found in /sys/class/thermal/"
+ LogError "This system may not support thermal monitoring"
+ fi
exit 1
fi
-LogInformation "Starting temperature monitoring"
+LogInformation "Starting temperature monitoring (OS: $OS_TYPE)"
for i in {1..6}; do
timestamp=$(GetTimestamp)
- # Use arrays to avoid complex pipe chains that can hang
- types=()
- temps=()
-
- # Read thermal zone types
- for type_file in /sys/class/thermal/thermal_zone*/type; do
- if [[ -r "$type_file" ]]; then
- type_value=$(cat "$type_file" 2>/dev/null)
- if [[ -n "$type_value" ]]; then
- types+=("$type_value")
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ # Try osx-cpu-temp first
+ if command -v osx-cpu-temp &> /dev/null; then
+ temp_output=$(osx-cpu-temp 2>/dev/null)
+ if [[ $? -eq 0 && -n "$temp_output" ]]; then
+ # Output format: "50.0°C"
+ echo "$timestamp CPU $temp_output" >> "$LOGFILE"
fi
- fi
- done
-
- # Read thermal zone temperatures
- for temp_file in /sys/class/thermal/thermal_zone*/temp; do
- if [[ -r "$temp_file" ]]; then
- temp_value=$(cat "$temp_file" 2>/dev/null)
- if [[ -n "$temp_value" && "$temp_value" =~ ^[0-9]+$ ]]; then
- # Convert millidegrees to degrees with one decimal place
- temp_celsius=$(echo "scale=1; $temp_value / 1000" | bc 2>/dev/null)
- if [[ $? -eq 0 ]]; then
- temps+=("${temp_celsius}°C")
- else
- temps+=("${temp_value}m°C")
+ # Fallback to powermetrics (requires sudo)
+ elif [[ -x /usr/bin/powermetrics ]]; then
+ # Run powermetrics for 1 second and extract temperature
+ temp_data=$(sudo powermetrics -n 1 -i 1000 --samplers smc 2>/dev/null | grep -i "CPU die temperature" | head -1)
+ if [[ -n "$temp_data" ]]; then
+ # Extract temperature value (format: "CPU die temperature: 50.50 C")
+ temp_value=$(echo "$temp_data" | grep -o '[0-9.]\+ C' | head -1)
+ if [[ -n "$temp_value" ]]; then
+ echo "$timestamp CPU ${temp_value}" >> "$LOGFILE"
fi
fi
fi
- done
-
- # Output paired type and temperature data
- for j in "${!types[@]}"; do
- if [[ $j -lt ${#temps[@]} ]]; then
- echo "$timestamp ${types[$j]} ${temps[$j]}" >> "$LOGFILE"
- fi
- done
+ else
+ # Linux thermal zone reading
+ # Use arrays to avoid complex pipe chains that can hang
+ types=()
+ temps=()
+
+ # Read thermal zone types
+ for type_file in /sys/class/thermal/thermal_zone*/type; do
+ if [[ -r "$type_file" ]]; then
+ type_value=$(cat "$type_file" 2>/dev/null)
+ if [[ -n "$type_value" ]]; then
+ types+=("$type_value")
+ fi
+ fi
+ done
+
+ # Read thermal zone temperatures
+ for temp_file in /sys/class/thermal/thermal_zone*/temp; do
+ if [[ -r "$temp_file" ]]; then
+ temp_value=$(cat "$temp_file" 2>/dev/null)
+ if [[ -n "$temp_value" && "$temp_value" =~ ^[0-9]+$ ]]; then
+ # Convert millidegrees to degrees with one decimal place
+ temp_celsius=$(echo "scale=1; $temp_value / 1000" | bc 2>/dev/null)
+ if [[ $? -eq 0 ]]; then
+ temps+=("${temp_celsius}°C")
+ else
+ temps+=("${temp_value}m°C")
+ fi
+ fi
+ fi
+ done
+
+ # Output paired type and temperature data
+ for j in "${!types[@]}"; do
+ if [[ $j -lt ${#temps[@]} ]]; then
+ echo "$timestamp ${types[$j]} ${temps[$j]}" >> "$LOGFILE"
+ fi
+ done
+ fi
LogInformation "Temperature data collected (iteration $i/6)"
diff --git a/orchestrator/setup-crontab.sh b/orchestrator/setup-crontab.sh
index 6cd71d5..c0bca59 100755
--- a/orchestrator/setup-crontab.sh
+++ b/orchestrator/setup-crontab.sh
@@ -2,6 +2,7 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/../share/logging.sh"
+. "${SCRIPT_DIR}/../share/os-detection.sh"
# Configuration
SCRIPTS=(
@@ -10,6 +11,9 @@ SCRIPTS=(
"../custom-metrics/temperature.sh"
)
+OS_TYPE=$(DetectOS)
+LAUNCHD_DIR="$HOME/Library/LaunchAgents"
+
CheckScriptExists() {
local script="$1"
local script_path="${SCRIPT_DIR}/${script}"
@@ -29,17 +33,29 @@ InstallCrontabEntries() {
local installed_count=0
local failed_count=0
- BackupCrontab
+ 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 ManageCrontabEntry "$script"; then
- ((installed_count++))
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ if ManageLaunchAgent "$script"; then
+ ((installed_count++))
+ else
+ ((failed_count++))
+ fi
else
- ((failed_count++))
+ if ManageCrontabEntry "$script"; then
+ ((installed_count++))
+ else
+ ((failed_count++))
+ fi
fi
else
LogWarning "Script $script not found or not accessible, skipping..."
@@ -54,6 +70,62 @@ InstallCrontabEntries() {
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 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
+ LogInformation "Created LaunchAgent plist: $plist_path"
+
+ # 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 LaunchAgent for $script"
+ return 0
+ else
+ LogError "Failed to load LaunchAgent for $script"
+ return 1
+ fi
+ else
+ LogError "Failed to create plist for $script"
+ return 1
+ fi
+}
+
ManageCrontabEntry() {
local script="$1"
local script_path="${SCRIPT_DIR}/${script}"
@@ -95,7 +167,13 @@ ManageCrontabEntry() {
}
ShowStatus() {
- LogInformation "Crontab entries status:"
+ LogInformation "Scheduled task status:"
+ LogInformation "OS: $OS_TYPE"
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogInformation "Method: LaunchAgents"
+ else
+ LogInformation "Method: Crontab"
+ fi
LogInformation ""
local found_entries=0
@@ -103,64 +181,117 @@ ShowStatus() {
local script_path="${SCRIPT_DIR}/${script}"
local script_name=$(basename "$script")
- 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)"
+ 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
- LogInformation "✗ $script_name - SCRIPT MISSING"
+ 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:"
- LogInformation " View crontab: crontab -l"
- LogInformation " Edit crontab: crontab -e"
- LogInformation " Check logs: sudo tail -f /var/log/syslog | grep CRON"
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogInformation " List agents: launchctl list | grep servermetrics"
+ LogInformation " View logs: tail -f /tmp/*.out"
+ LogInformation " Unload agent: launchctl unload ~/Library/LaunchAgents/com.servermetrics.*.plist"
+ 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 crontab entries are currently installed"
+ LogWarning "No scheduled tasks are currently installed"
fi
}
RemoveCrontabEntries() {
local removed_count=0
- 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 [[ "$OS_TYPE" == "macos" ]]; then
+ # Remove LaunchAgents
+ 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 LaunchAgent for $script_name..."
+
+ # Unload the agent
+ launchctl unload "$plist_path" 2>/dev/null || true
+
+ # Remove the plist file
+ rm -f "$plist_path"
+ ((removed_count++))
+ else
+ LogDebug "No LaunchAgent found for $script_name"
+ fi
+ done
- 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++))
+ if [[ $removed_count -gt 0 ]]; then
+ LogInformation "Removed $removed_count LaunchAgents"
else
- LogDebug "No crontab entry found for $script_name"
+ LogWarning "No LaunchAgents were found to remove"
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"
+ # 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
}
@@ -174,16 +305,48 @@ BackupCrontab() {
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 crontab entries (default)"
- echo " remove - Remove all crontab entries"
- echo " status - Show status of crontab entries"
+ 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 "This script manages crontab entries for monitoring scripts:"
+ 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:"
@@ -196,12 +359,12 @@ Main() {
case "$action" in
install)
- LogInformation "Installing crontab entries for server monitoring scripts"
+ LogInformation "Installing scheduled tasks for server monitoring scripts (OS: $OS_TYPE)"
InstallCrontabEntries
- LogInformation "Crontab setup completed successfully!"
+ LogInformation "Scheduled task setup completed successfully!"
;;
remove)
- LogInformation "Removing crontab entries for server monitoring scripts"
+ LogInformation "Removing scheduled tasks for server monitoring scripts (OS: $OS_TYPE)"
RemoveCrontabEntries
;;
status)
diff --git a/orchestrator/setup-filebeat.sh b/orchestrator/setup-filebeat.sh
index 4fc557f..1add467 100755
--- a/orchestrator/setup-filebeat.sh
+++ b/orchestrator/setup-filebeat.sh
@@ -2,12 +2,14 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/../share/logging.sh"
+. "${SCRIPT_DIR}/../share/os-detection.sh"
# Configuration
FILEBEAT_CONFIG_DIR="${SCRIPT_DIR}/../filebeat"
-FILEBEAT_INSTALL_DIR="/etc/filebeat"
+FILEBEAT_INSTALL_DIR=$(GetFilebeatConfigDir)
FILEBEAT_MODULES_D_DIR="${FILEBEAT_INSTALL_DIR}/modules.d"
CONFIG_FILE="filebeat.yml"
+OS_TYPE=$(DetectOS)
CheckRoot() {
if [[ $EUID -ne 0 ]]; then
@@ -129,6 +131,8 @@ ValidateFilebeatConfig() {
ShowStatus() {
LogInformation "Filebeat configuration status:"
+ LogInformation "OS: $OS_TYPE"
+ LogInformation "Install directory: $FILEBEAT_INSTALL_DIR"
LogInformation ""
# Check main config file
@@ -157,8 +161,15 @@ ShowStatus() {
LogInformation "Commands:"
LogInformation " Test config: sudo filebeat test config -c ${FILEBEAT_INSTALL_DIR}/${CONFIG_FILE}"
LogInformation " Test output: sudo filebeat test output -c ${FILEBEAT_INSTALL_DIR}/${CONFIG_FILE}"
- LogInformation " Start Filebeat: sudo systemctl start filebeat"
- LogInformation " Check status: sudo systemctl status filebeat"
+
+ local service_cmd=$(GetServiceCommand)
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogInformation " Start Filebeat: sudo $service_cmd start filebeat"
+ LogInformation " Check status: $service_cmd list | grep filebeat"
+ else
+ LogInformation " Start Filebeat: sudo $service_cmd start filebeat"
+ LogInformation " Check status: sudo $service_cmd status filebeat"
+ fi
}
RemoveFilebeatConfigs() {
diff --git a/orchestrator/setup-logrotate.sh b/orchestrator/setup-logrotate.sh
index cdf4108..34604ce 100755
--- a/orchestrator/setup-logrotate.sh
+++ b/orchestrator/setup-logrotate.sh
@@ -2,9 +2,11 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/../share/logging.sh"
+. "${SCRIPT_DIR}/../share/os-detection.sh"
LOGROTATE_DIR="${SCRIPT_DIR}/../logrotate"
-LOGROTATE_INSTALL_DIR="/etc/logrotate.d"
+LOGROTATE_INSTALL_DIR=$(GetLogRotateConfigDir)
+OS_TYPE=$(DetectOS)
# List of logrotate config files to manage (without .conf extension)
LOGROTATE_CONFIGS=(
@@ -19,13 +21,27 @@ CheckRoot() {
}
CheckLogrotateInstalled() {
- if ! command -v logrotate &> /dev/null; then
- LogError "logrotate is not installed. Please install it first:"
- LogError " Ubuntu/Debian: sudo apt-get install logrotate"
- LogError " CentOS/RHEL: sudo yum install logrotate"
+ local log_cmd=$(GetLogRotateCommand)
+
+ if ! IsLogRotateAvailable; then
+ LogError "$log_cmd is not installed. Please install it first:"
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogError " macOS uses newsyslog (built-in) but may need configuration at $LOGROTATE_INSTALL_DIR"
+ LogError " If directory doesn't exist: sudo mkdir -p $LOGROTATE_INSTALL_DIR"
+ else
+ LogError " Ubuntu/Debian: sudo apt-get install logrotate"
+ LogError " CentOS/RHEL: sudo yum install logrotate"
+ fi
exit 1
fi
- LogInformation "logrotate is installed"
+ LogInformation "$log_cmd is available"
+
+ # Ensure the config directory exists (especially for macOS newsyslog.d)
+ if [[ ! -d "$LOGROTATE_INSTALL_DIR" ]]; then
+ LogInformation "Creating config directory: $LOGROTATE_INSTALL_DIR"
+ mkdir -p "$LOGROTATE_INSTALL_DIR"
+ chmod 755 "$LOGROTATE_INSTALL_DIR"
+ fi
}
InstallLogrotateConfigs() {
@@ -60,10 +76,11 @@ InstallLogrotateConfigs() {
}
TestLogrotateConfigs() {
- LogInformation "Testing logrotate configurations..."
+ LogInformation "Testing log rotation configurations..."
local test_passed=0
local test_failed=0
+ local log_cmd=$(GetLogRotateCommand)
for config_name in "${LOGROTATE_CONFIGS[@]}"; do
local config_file="${LOGROTATE_INSTALL_DIR}/${config_name}"
@@ -74,13 +91,25 @@ TestLogrotateConfigs() {
fi
LogDebug "Testing configuration: $config_name"
- if logrotate -d "$config_file" &> /dev/null; then
- LogDebug "Test passed: $config_name"
- ((test_passed++))
+
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ # newsyslog uses different syntax, just check file readability
+ if [[ -r "$config_file" ]]; then
+ LogDebug "Test passed: $config_name (file readable)"
+ ((test_passed++))
+ else
+ LogError "Test failed: $config_name (file not readable)"
+ ((test_failed++))
+ fi
else
- LogError "Test failed: $config_name"
- LogError "Run for details: logrotate -d $config_file"
- ((test_failed++))
+ if logrotate -d "$config_file" &> /dev/null; then
+ LogDebug "Test passed: $config_name"
+ ((test_passed++))
+ else
+ LogError "Test failed: $config_name"
+ LogError "Run for details: logrotate -d $config_file"
+ ((test_failed++))
+ fi
fi
done
@@ -93,7 +122,11 @@ TestLogrotateConfigs() {
}
ShowStatus() {
- LogInformation "Logrotate configurations:"
+ local log_cmd=$(GetLogRotateCommand)
+ LogInformation "Log rotation configurations:"
+ LogInformation "OS: $OS_TYPE"
+ LogInformation "Tool: $log_cmd"
+ LogInformation "Install directory: $LOGROTATE_INSTALL_DIR"
LogInformation ""
local found_configs=0
@@ -114,9 +147,15 @@ ShowStatus() {
LogInformation ""
if [[ $found_configs -gt 0 ]]; then
LogInformation "Commands:"
- LogInformation " Test rotation: sudo logrotate -f /etc/logrotate.d/"
- LogInformation " Check status: sudo cat /var/lib/logrotate/status"
- LogInformation " Manual run: sudo logrotate /etc/logrotate.conf"
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogInformation " Manual run: sudo newsyslog -v"
+ LogInformation " Check config: cat /etc/newsyslog.conf"
+ LogInformation " Force rotation: sudo newsyslog -F"
+ else
+ LogInformation " Test rotation: sudo logrotate -f /etc/logrotate.d/"
+ LogInformation " Check status: sudo cat /var/lib/logrotate/status"
+ LogInformation " Manual run: sudo logrotate /etc/logrotate.conf"
+ fi
else
LogWarning "No configurations are currently installed"
fi
diff --git a/orchestrator/setup-logstash.sh b/orchestrator/setup-logstash.sh
index aec90b4..bcb0b03 100755
--- a/orchestrator/setup-logstash.sh
+++ b/orchestrator/setup-logstash.sh
@@ -2,12 +2,14 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/../share/logging.sh"
+. "${SCRIPT_DIR}/../share/os-detection.sh"
# Configuration
LOGSTASH_CONFIG_DIR="${SCRIPT_DIR}/../logstash"
-LOGSTASH_INSTALL_DIR="/etc/logstash"
+LOGSTASH_INSTALL_DIR=$(GetLogstashConfigDir)
LOGSTASH_CONF_D_DIR="${LOGSTASH_INSTALL_DIR}/conf.d"
OUTPUT_CONFIG_FILE="conf.d/90-output.conf"
+OS_TYPE=$(DetectOS)
CheckRoot() {
if [[ $EUID -ne 0 ]]; then
@@ -143,6 +145,8 @@ ValidateLogstashConfig() {
ShowStatus() {
LogInformation "Logstash configuration status:"
+ LogInformation "OS: $OS_TYPE"
+ LogInformation "Install directory: $LOGSTASH_INSTALL_DIR"
LogInformation ""
# Check main config files
@@ -175,9 +179,16 @@ ShowStatus() {
LogInformation ""
LogInformation "Commands:"
- LogInformation " Test config: sudo logstash -t --path.settings=/etc/logstash"
- LogInformation " Start Logstash: sudo systemctl start logstash"
- LogInformation " Check status: sudo systemctl status logstash"
+ local service_cmd=$(GetServiceCommand)
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogInformation " Test config: logstash -t --path.settings=$LOGSTASH_INSTALL_DIR"
+ LogInformation " Start Logstash: $service_cmd start logstash"
+ LogInformation " Check status: $service_cmd list | grep logstash"
+ else
+ LogInformation " Test config: sudo logstash -t --path.settings=/etc/logstash"
+ LogInformation " Start Logstash: sudo $service_cmd start logstash"
+ LogInformation " Check status: sudo $service_cmd status logstash"
+ fi
}
RemoveLogstashConfigs() {
diff --git a/orchestrator/setup-metricbeat.sh b/orchestrator/setup-metricbeat.sh
index 3caf1cc..8d81525 100755
--- a/orchestrator/setup-metricbeat.sh
+++ b/orchestrator/setup-metricbeat.sh
@@ -2,12 +2,14 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "${SCRIPT_DIR}/../share/logging.sh"
+. "${SCRIPT_DIR}/../share/os-detection.sh"
# Configuration
METRICBEAT_CONFIG_DIR="${SCRIPT_DIR}/../metricbeat"
-METRICBEAT_INSTALL_DIR="/etc/metricbeat"
+METRICBEAT_INSTALL_DIR=$(GetMetricbeatConfigDir)
METRICBEAT_MODULES_D_DIR="${METRICBEAT_INSTALL_DIR}/modules.d"
CONFIG_FILE="metricbeat.yml"
+OS_TYPE=$(DetectOS)
CheckRoot() {
if [[ $EUID -ne 0 ]]; then
@@ -129,6 +131,8 @@ ValidateMetricbeatConfig() {
ShowStatus() {
LogInformation "Metricbeat configuration status:"
+ LogInformation "OS: $OS_TYPE"
+ LogInformation "Install directory: $METRICBEAT_INSTALL_DIR"
LogInformation ""
# Check main config file
@@ -157,8 +161,15 @@ ShowStatus() {
LogInformation "Commands:"
LogInformation " Test config: sudo metricbeat test config -c ${METRICBEAT_INSTALL_DIR}/${CONFIG_FILE}"
LogInformation " Test output: sudo metricbeat test output -c ${METRICBEAT_INSTALL_DIR}/${CONFIG_FILE}"
- LogInformation " Start Metricbeat: sudo systemctl start metricbeat"
- LogInformation " Check status: sudo systemctl status metricbeat"
+
+ local service_cmd=$(GetServiceCommand)
+ if [[ "$OS_TYPE" == "macos" ]]; then
+ LogInformation " Start Metricbeat: sudo $service_cmd start metricbeat"
+ LogInformation " Check status: $service_cmd list | grep metricbeat"
+ else
+ LogInformation " Start Metricbeat: sudo $service_cmd start metricbeat"
+ LogInformation " Check status: sudo $service_cmd status metricbeat"
+ fi
}
RemoveMetricbeatConfigs() {
diff --git a/share/os-detection.sh b/share/os-detection.sh
new file mode 100644
index 0000000..cf919a3
--- /dev/null
+++ b/share/os-detection.sh
@@ -0,0 +1,196 @@
+#!/bin/bash
+
+# OS Detection and Path Configuration
+# This script provides OS-specific paths and configurations
+
+# Detect OS
+DetectOS() {
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ echo "macos"
+ elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
+ echo "linux"
+ else
+ echo "unknown"
+ fi
+}
+
+# Get config directory for Filebeat
+GetFilebeatConfigDir() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ echo "/usr/local/etc/filebeat"
+ ;;
+ linux)
+ echo "/etc/filebeat"
+ ;;
+ *)
+ echo "/etc/filebeat"
+ ;;
+ esac
+}
+
+# Get config directory for Metricbeat
+GetMetricbeatConfigDir() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ echo "/usr/local/etc/metricbeat"
+ ;;
+ linux)
+ echo "/etc/metricbeat"
+ ;;
+ *)
+ echo "/etc/metricbeat"
+ ;;
+ esac
+}
+
+# Get config directory for Logstash
+GetLogstashConfigDir() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ # Homebrew installation path
+ echo "/usr/local/etc/logstash"
+ ;;
+ linux)
+ echo "/etc/logstash"
+ ;;
+ *)
+ echo "/etc/logstash"
+ ;;
+ esac
+}
+
+# Get logrotate/newsyslog config directory
+GetLogRotateConfigDir() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ # macOS uses newsyslog instead of logrotate
+ echo "/usr/local/etc/newsyslog.d"
+ ;;
+ linux)
+ echo "/etc/logrotate.d"
+ ;;
+ *)
+ echo "/etc/logrotate.d"
+ ;;
+ esac
+}
+
+# Check if logrotate is available
+IsLogRotateAvailable() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ # macOS uses newsyslog
+ command -v newsyslog &> /dev/null
+ ;;
+ linux)
+ command -v logrotate &> /dev/null
+ ;;
+ *)
+ command -v logrotate &> /dev/null
+ ;;
+ esac
+}
+
+# Get log rotation command
+GetLogRotateCommand() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ echo "newsyslog"
+ ;;
+ linux)
+ echo "logrotate"
+ ;;
+ *)
+ echo "logrotate"
+ ;;
+ esac
+}
+
+# Check if thermal monitoring is supported
+IsThermalMonitoringSupported() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ # Check if osx-cpu-temp or powermetrics is available
+ command -v osx-cpu-temp &> /dev/null || [[ -x /usr/bin/powermetrics ]]
+ ;;
+ linux)
+ # Check if thermal zones exist
+ [[ -e /sys/class/thermal/thermal_zone0/type ]]
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
+# Check if power monitoring is supported
+IsPowerMonitoringSupported() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ # powermetrics requires sudo and is available on all recent macOS versions
+ [[ -x /usr/bin/powermetrics ]]
+ ;;
+ linux)
+ # Check if Intel RAPL is available
+ [[ -r /sys/class/powercap/intel-rapl:0/energy_uj ]]
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
+# Get OS-specific df flags for disk usage
+GetDfFlags() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ # macOS df doesn't support --output flag
+ echo ""
+ ;;
+ linux)
+ echo "--output=target,used,size -B1"
+ ;;
+ *)
+ echo ""
+ ;;
+ esac
+}
+
+# Get service management command (systemctl vs launchctl)
+GetServiceCommand() {
+ local os=$(DetectOS)
+ case "$os" in
+ macos)
+ echo "brew services"
+ ;;
+ linux)
+ echo "systemctl"
+ ;;
+ *)
+ echo "systemctl"
+ ;;
+ esac
+}
+
+# Export functions for sourcing
+export -f DetectOS
+export -f GetFilebeatConfigDir
+export -f GetMetricbeatConfigDir
+export -f GetLogstashConfigDir
+export -f GetLogRotateConfigDir
+export -f IsLogRotateAvailable
+export -f GetLogRotateCommand
+export -f IsThermalMonitoringSupported
+export -f IsPowerMonitoringSupported
+export -f GetDfFlags
+export -f GetServiceCommand