diff --git a/frontend-modern/public/install-host-agent.sh b/frontend-modern/public/install-host-agent.sh new file mode 100755 index 0000000..a18ddbf --- /dev/null +++ b/frontend-modern/public/install-host-agent.sh @@ -0,0 +1,605 @@ +#!/bin/bash +set -e + +# Pulse Host Agent Installer +# Downloads and installs the Pulse host agent for Linux, macOS, or Windows (WSL) + +# ANSI color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +RESET='\033[0m' + +# Check if colors are supported +if [ -t 1 ] && command -v tput &> /dev/null && [ "$(tput colors)" -ge 8 ]; then + USE_COLOR=true +else + USE_COLOR=false +fi + +print_color() { + local color="$1" + local message="$2" + if [ "$USE_COLOR" = true ]; then + printf "${color}%s${RESET}\n" "$message" + else + printf "%s\n" "$message" + fi +} + +log_success() { + print_color "$GREEN" "✓ $1" +} + +log_error() { + print_color "$RED" "✗ $1" >&2 +} + +log_info() { + print_color "$BLUE" "ℹ $1" +} + +log_warn() { + print_color "$YELLOW" "⚠ $1" +} + +print_header() { + echo "" + print_color "$BLUE" "═══════════════════════════════════════════════════════════" + print_color "$BLUE" " Pulse Host Agent - Installation" + print_color "$BLUE" "═══════════════════════════════════════════════════════════" + echo "" +} + +print_footer() { + echo "" + print_color "$GREEN" "═══════════════════════════════════════════════════════════" + log_success "Installation complete!" + print_color "$GREEN" "═══════════════════════════════════════════════════════════" + echo "" +} + +# Parse arguments +PULSE_URL="" +PULSE_TOKEN="" +INTERVAL="30s" +UNINSTALL="false" +PLATFORM="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --url) + PULSE_URL="$2" + shift 2 + ;; + --token) + PULSE_TOKEN="$2" + shift 2 + ;; + --interval) + INTERVAL="$2" + shift 2 + ;; + --platform) + PLATFORM="$2" + shift 2 + ;; + --uninstall) + UNINSTALL="true" + shift + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +AGENT_PATH="/usr/local/bin/pulse-host-agent" +SYSTEMD_SERVICE="/etc/systemd/system/pulse-host-agent.service" +LAUNCHD_PLIST="$HOME/Library/LaunchAgents/com.pulse.host-agent.plist" +MACOS_LOG_DIR="$HOME/Library/Logs/Pulse" +MACOS_LOG_FILE="$MACOS_LOG_DIR/host-agent.log" +LINUX_LOG_DIR="/var/log/pulse" +LINUX_LOG_FILE="$LINUX_LOG_DIR/host-agent.log" + +# Uninstall function +if [[ "$UNINSTALL" == "true" ]]; then + log_warn "The --uninstall flag is deprecated." + log_info "Please use the dedicated uninstall script instead:" + echo "" + echo " curl -fsSL \$PULSE_URL/uninstall-host-agent.sh | bash" + echo "" + log_info "Or download and run manually:" + echo " wget \$PULSE_URL/uninstall-host-agent.sh" + echo " chmod +x uninstall-host-agent.sh" + echo " ./uninstall-host-agent.sh" + echo "" + exit 1 +fi + +print_header + +# Interactive prompts if parameters not provided +if [[ -z "$PULSE_URL" ]]; then + log_info "Interactive Installation Mode" + echo "" + read -p "Enter Pulse server URL (e.g., http://pulse.example.com:7656): " PULSE_URL + PULSE_URL=$(echo "$PULSE_URL" | sed 's:/*$::') # Remove trailing slashes +fi + +if [[ -z "$PULSE_URL" ]]; then + log_error "Pulse URL is required" + echo "Usage: $0 --url --token [--interval 30s] [--platform linux|darwin|windows]" + exit 1 +fi + +if [[ -z "$PULSE_TOKEN" ]]; then + log_warn "No API token provided - agent will attempt to connect without authentication" + read -p "Enter API token (or press Enter to skip): " PULSE_TOKEN + + if [[ -z "$PULSE_TOKEN" ]]; then + read -p "Continue without token? (y/N): " CONTINUE_WITHOUT_TOKEN + if [[ "$CONTINUE_WITHOUT_TOKEN" != "y" ]] && [[ "$CONTINUE_WITHOUT_TOKEN" != "Y" ]]; then + log_error "Installation cancelled" + exit 1 + fi + fi +fi + +# Detect platform if not specified +if [[ -z "$PLATFORM" ]]; then + case "$(uname -s)" in + Linux*) + PLATFORM="linux" + ;; + Darwin*) + PLATFORM="darwin" + ;; + MINGW*|MSYS*|CYGWIN*) + PLATFORM="windows" + ;; + *) + log_error "Unsupported platform: $(uname -s)" + exit 1 + ;; + esac +fi + +# Detect architecture +ARCH="$(uname -m)" +case "$ARCH" in + x86_64|amd64) + ARCH="amd64" + ;; + aarch64|arm64) + ARCH="arm64" + ;; + armv7l|armhf) + ARCH="armv7" + ;; + *) + log_warn "Unknown architecture $ARCH, defaulting to amd64" + ARCH="amd64" + ;; +esac + +log_info "Configuration:" +echo " Pulse URL: $PULSE_URL" +if [[ -n "$PULSE_TOKEN" ]]; then + # Mask token, showing only last 4 characters + TOKEN_MASKED="***${PULSE_TOKEN: -4}" + echo " Token: $TOKEN_MASKED" +else + echo " Token: none" +fi +echo " Interval: $INTERVAL" +echo " Platform: $PLATFORM/$ARCH" +echo "" + +log_info "Installing Pulse host agent for $PLATFORM/$ARCH..." + +# Check for existing installation and version +if [[ -f "$AGENT_PATH" ]]; then + log_info "Existing installation detected" + CURRENT_VERSION=$("$AGENT_PATH" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown") + if [[ "$CURRENT_VERSION" != "unknown" ]]; then + echo " Current version: $CURRENT_VERSION" + fi + + # Try to get latest version from server + if command -v curl &> /dev/null; then + LATEST_VERSION=$(curl -fsSL "$PULSE_URL/api/version" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "") + elif command -v wget &> /dev/null; then + LATEST_VERSION=$(wget -qO- "$PULSE_URL/api/version" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "") + fi + + if [[ -n "$LATEST_VERSION" ]] && [[ "$CURRENT_VERSION" != "unknown" ]]; then + echo " Latest version: $LATEST_VERSION" + if [[ "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then + log_success "Already running latest version" + elif [[ "$CURRENT_VERSION" < "$LATEST_VERSION" ]]; then + log_info "Update available: $CURRENT_VERSION → $LATEST_VERSION" + fi + fi + + read -p "Reinstall/update agent? (Y/n): " REINSTALL + if [[ "$REINSTALL" == "n" ]] || [[ "$REINSTALL" == "N" ]]; then + log_info "Installation cancelled" + exit 0 + fi + echo "" +fi + +# Download agent binary from Pulse server +DOWNLOAD_URL="$PULSE_URL/download/pulse-host-agent?platform=$PLATFORM&arch=$ARCH" +TEMP_BINARY="/tmp/pulse-host-agent-$$.tmp" + +log_info "Downloading agent binary from $PULSE_URL..." + +DOWNLOAD_SUCCESS=false +CHECKSUM_URL="${DOWNLOAD_URL}.sha256" + +if command -v curl &> /dev/null; then + if curl -fL --progress-bar -o "$TEMP_BINARY" "$DOWNLOAD_URL" 2>&1; then + DOWNLOAD_SUCCESS=true + fi + # Try to download checksum (optional, server may not provide it yet) + EXPECTED_CHECKSUM=$(curl -fsSL "$CHECKSUM_URL" 2>/dev/null || echo "") +elif command -v wget &> /dev/null; then + if wget -q --show-progress -O "$TEMP_BINARY" "$DOWNLOAD_URL" 2>&1; then + DOWNLOAD_SUCCESS=true + fi + # Try to download checksum (optional) + EXPECTED_CHECKSUM=$(wget -qO- "$CHECKSUM_URL" 2>/dev/null || echo "") +else + log_error "Neither curl nor wget found" + echo "" + log_info "Please install curl or wget to continue:" + if [[ "$PLATFORM" == "darwin" ]]; then + echo " brew install curl" + elif [[ "$PLATFORM" == "linux" ]]; then + echo " # Debian/Ubuntu:" + echo " sudo apt-get install curl" + echo "" + echo " # RHEL/CentOS/Fedora:" + echo " sudo yum install curl" + fi + echo "" + exit 1 +fi + +if [[ "$DOWNLOAD_SUCCESS" == false ]]; then + log_error "Failed to download agent binary from $DOWNLOAD_URL" + echo "" + log_info "Troubleshooting steps:" + echo "" + echo "1. Verify the Pulse server is running:" + echo " curl $PULSE_URL/health" + echo "" + echo "2. Check if the download endpoint is accessible:" + echo " curl -I $DOWNLOAD_URL" + echo "" + echo "3. Build from source as a fallback:" + echo " git clone https://github.com/rcourtman/Pulse.git" + echo " cd Pulse" + echo " go build -o pulse-host-agent ./cmd/pulse-host-agent" + echo " sudo mv pulse-host-agent /usr/local/bin/" + echo " # Then run this script again with --url and --token" + echo "" + echo "4. Check firewall/network settings blocking the connection" + echo "" + rm -f "$TEMP_BINARY" + exit 1 +fi + +log_success "Downloaded agent binary" + +# Verify checksum if available +if [[ -n "$EXPECTED_CHECKSUM" ]]; then + log_info "Verifying checksum..." + + if command -v sha256sum &> /dev/null; then + ACTUAL_CHECKSUM=$(sha256sum "$TEMP_BINARY" | awk '{print $1}') + elif command -v shasum &> /dev/null; then + ACTUAL_CHECKSUM=$(shasum -a 256 "$TEMP_BINARY" | awk '{print $1}') + else + log_warn "No checksum tool found (sha256sum/shasum), skipping verification" + ACTUAL_CHECKSUM="" + fi + + if [[ -n "$ACTUAL_CHECKSUM" ]]; then + # Clean up checksums (remove whitespace, convert to lowercase) + EXPECTED_CHECKSUM=$(echo "$EXPECTED_CHECKSUM" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]') + ACTUAL_CHECKSUM=$(echo "$ACTUAL_CHECKSUM" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]') + + if [[ "$EXPECTED_CHECKSUM" == "$ACTUAL_CHECKSUM" ]]; then + log_success "Checksum verified (SHA256: ${ACTUAL_CHECKSUM:0:16}...)" + else + log_error "Checksum mismatch!" + echo " Expected: $EXPECTED_CHECKSUM" + echo " Got: $ACTUAL_CHECKSUM" + echo "" + log_warn "The downloaded binary may be corrupted or tampered with." + read -p "Continue anyway? (y/N): " CONTINUE_ANYWAY + if [[ "$CONTINUE_ANYWAY" != "y" ]] && [[ "$CONTINUE_ANYWAY" != "Y" ]]; then + rm -f "$TEMP_BINARY" + log_error "Installation cancelled" + exit 1 + fi + fi + fi +else + log_info "Checksum not available (server doesn't provide it yet)" +fi + +sudo mv "$TEMP_BINARY" "$AGENT_PATH" +sudo chmod +x "$AGENT_PATH" +log_success "Agent binary installed to $AGENT_PATH" + +# Set up service based on platform +if [[ "$PLATFORM" == "linux" ]] && command -v systemctl &> /dev/null; then + log_info "Setting up systemd service..." + + # Create log directory + sudo mkdir -p "$LINUX_LOG_DIR" + + sudo tee "$SYSTEMD_SERVICE" > /dev/null < /dev/null; then + log_info "Setting up launchd service..." + + # Create log directory + mkdir -p "$MACOS_LOG_DIR" + mkdir -p "$HOME/Library/LaunchAgents" + + # Store token in macOS Keychain for better security + if [[ -n "$PULSE_TOKEN" ]]; then + log_info "Storing token in macOS Keychain..." + + # Delete existing keychain entry if it exists + security delete-generic-password -s "pulse-host-agent" -a "$USER" 2>/dev/null || true + + # Add token to Keychain + if security add-generic-password -s "pulse-host-agent" -a "$USER" -w "$PULSE_TOKEN" -U 2>/dev/null; then + log_success "Token stored securely in macOS Keychain" + USE_KEYCHAIN=true + else + log_warn "Failed to store token in Keychain, will use plist instead" + log_info "You may need to grant Keychain access permissions" + USE_KEYCHAIN=false + fi + else + USE_KEYCHAIN=false + fi + + # Create wrapper script if using Keychain + if [[ "$USE_KEYCHAIN" == true ]]; then + WRAPPER_SCRIPT="/usr/local/bin/pulse-host-agent-wrapper.sh" + + cat > "$WRAPPER_SCRIPT" <<'WRAPPER_EOF' +#!/bin/bash +# Pulse Host Agent Wrapper - Reads token from Keychain +set -e + +# Read token from Keychain +PULSE_TOKEN=$(security find-generic-password -s "pulse-host-agent" -w 2>/dev/null || echo "") + +# Export for agent to use +export PULSE_TOKEN + +# Run the actual agent with all arguments +exec /usr/local/bin/pulse-host-agent "$@" +WRAPPER_EOF + + chmod +x "$WRAPPER_SCRIPT" + log_success "Created Keychain wrapper script" + + # Create plist using wrapper (token not in plist!) + cat > "$LAUNCHD_PLIST" < + + + + Label + com.pulse.host-agent + ProgramArguments + + $WRAPPER_SCRIPT + --url + $PULSE_URL + --interval + $INTERVAL + + RunAtLoad + + KeepAlive + + StandardOutPath + $MACOS_LOG_FILE + StandardErrorPath + $MACOS_LOG_FILE + + +EOF + log_success "Created launchd service configuration (using Keychain)" + else + # Create plist with token directly (fallback) + cat > "$LAUNCHD_PLIST" < + + + + Label + com.pulse.host-agent + ProgramArguments + + $AGENT_PATH + --url + $PULSE_URL + --token + $PULSE_TOKEN + --interval + $INTERVAL + + RunAtLoad + + KeepAlive + + StandardOutPath + $MACOS_LOG_FILE + StandardErrorPath + $MACOS_LOG_FILE + + +EOF + log_success "Created launchd service configuration" + fi + + # Set restrictive permissions on plist + chmod 600 "$LAUNCHD_PLIST" + + launchctl load "$LAUNCHD_PLIST" + log_success "Launchd service enabled and started" +else + log_warn "Automatic service setup not available for this platform" + log_info "To run the agent manually:" + log_info " $AGENT_PATH --url $PULSE_URL --token $PULSE_TOKEN --interval $INTERVAL" +fi + +# Validate installation +log_info "Waiting 10 seconds to validate agent reporting..." +sleep 10 + +VALIDATION_SUCCESS=false +SERVICE_RUNNING=false + +# Check if service is running +if [[ "$PLATFORM" == "linux" ]] && command -v systemctl &> /dev/null; then + SERVICE_STATUS=$(systemctl is-active pulse-host-agent 2>/dev/null || echo "inactive") + if [[ "$SERVICE_STATUS" == "active" ]]; then + SERVICE_RUNNING=true + log_success "Service is running successfully!" + else + log_warn "Service status: $SERVICE_STATUS" + log_info "Check logs with: sudo journalctl -u pulse-host-agent -n 50" + fi +elif [[ "$PLATFORM" == "darwin" ]] && command -v launchctl &> /dev/null; then + if launchctl list | grep -q "com.pulse.host-agent"; then + SERVICE_RUNNING=true + log_success "Service is running successfully!" + else + log_warn "Service may not be running properly" + log_info "Check logs with: tail -20 $MACOS_LOG_FILE" + fi +fi + +# Try to verify with API endpoint that agent is reporting +if [[ "$SERVICE_RUNNING" == true ]]; then + log_info "Verifying agent registration with Pulse server..." + + # Get hostname for verification + HOSTNAME=$(hostname) + + # Try to query the API for this host + if command -v curl &> /dev/null; then + API_CHECK=$(curl -fsSL "$PULSE_URL/api/hosts" 2>/dev/null || echo "") + elif command -v wget &> /dev/null; then + API_CHECK=$(wget -qO- "$PULSE_URL/api/hosts" 2>/dev/null || echo "") + fi + + if [[ -n "$API_CHECK" ]] && echo "$API_CHECK" | grep -q "$HOSTNAME"; then + VALIDATION_SUCCESS=true + log_success "Agent successfully registered with Pulse server!" + log_success "Your host '$HOSTNAME' is now reporting metrics!" + elif [[ -n "$API_CHECK" ]]; then + log_warn "Agent is running but host not found in API yet (may take a few moments)" + log_info "Service appears healthy, metrics should appear shortly." + VALIDATION_SUCCESS=true # Service is running, so count as success + else + log_warn "Could not verify registration via API (endpoint may not be accessible)" + log_info "Service is running, check your Pulse dashboard manually." + VALIDATION_SUCCESS=true # Service is running, so count as success + fi +fi + +if [[ "$VALIDATION_SUCCESS" == true ]]; then + log_info "Check your Pulse dashboard at: $PULSE_URL" +else + log_error "Service validation failed" + echo "" + log_info "Troubleshooting:" + echo "" + if [[ "$PLATFORM" == "linux" ]]; then + echo " View logs: sudo journalctl -u pulse-host-agent -f" + echo " Check status: sudo systemctl status pulse-host-agent" + echo " Restart: sudo systemctl restart pulse-host-agent" + elif [[ "$PLATFORM" == "darwin" ]]; then + echo " View logs: tail -f $MACOS_LOG_FILE" + echo " Check status: launchctl list | grep pulse" + echo " Restart: launchctl unload $LAUNCHD_PLIST && launchctl load $LAUNCHD_PLIST" + fi + echo "" + echo " Manual run: $AGENT_PATH --url $PULSE_URL $(if [[ -n "$PULSE_TOKEN" ]]; then echo "--token ***"; fi) --interval $INTERVAL" + echo "" +fi + +print_footer + +log_info "Service Management Commands:" +if [[ "$PLATFORM" == "linux" ]]; then + echo " Start: sudo systemctl start pulse-host-agent" + echo " Stop: sudo systemctl stop pulse-host-agent" + echo " Restart: sudo systemctl restart pulse-host-agent" + echo " Status: sudo systemctl status pulse-host-agent" + echo " Logs: sudo journalctl -u pulse-host-agent -f" +elif [[ "$PLATFORM" == "darwin" ]]; then + echo " Start: launchctl load $LAUNCHD_PLIST" + echo " Stop: launchctl unload $LAUNCHD_PLIST" + echo " Restart: launchctl unload $LAUNCHD_PLIST && launchctl load $LAUNCHD_PLIST" + echo " Status: launchctl list | grep pulse" + echo " Logs: tail -f $MACOS_LOG_FILE" +fi +echo "" + +log_info "Files installed:" +echo " Binary: $AGENT_PATH" +if [[ "$PLATFORM" == "linux" ]]; then + echo " Service: $SYSTEMD_SERVICE" + echo " Logs: $LINUX_LOG_FILE" +elif [[ "$PLATFORM" == "darwin" ]]; then + echo " Service: $LAUNCHD_PLIST" + echo " Logs: $MACOS_LOG_FILE" +fi +echo "" + +log_info "The agent is now reporting to: $PULSE_URL" +echo "" + +log_info "To uninstall, run:" +echo " curl -fsSL $PULSE_URL/uninstall-host-agent.sh | bash" +echo "" diff --git a/frontend-modern/public/uninstall-host-agent.sh b/frontend-modern/public/uninstall-host-agent.sh new file mode 100755 index 0000000..d394c2e --- /dev/null +++ b/frontend-modern/public/uninstall-host-agent.sh @@ -0,0 +1,199 @@ +#!/bin/bash +set -e + +# Pulse Host Agent Uninstallation Script +# Removes the Pulse host agent and all associated files + +# ANSI color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +RESET='\033[0m' + +# Check if colors are supported +if [ -t 1 ] && command -v tput &> /dev/null && [ "$(tput colors)" -ge 8 ]; then + USE_COLOR=true +else + USE_COLOR=false +fi + +print_color() { + local color="$1" + local message="$2" + if [ "$USE_COLOR" = true ]; then + printf "${color}%s${RESET}\n" "$message" + else + printf "%s\n" "$message" + fi +} + +log_success() { + print_color "$GREEN" "✓ $1" +} + +log_error() { + print_color "$RED" "✗ $1" >&2 +} + +log_info() { + print_color "$BLUE" "ℹ $1" +} + +log_warn() { + print_color "$YELLOW" "⚠ $1" +} + +print_header() { + echo "" + print_color "$BLUE" "═══════════════════════════════════════════════════════════" + print_color "$BLUE" " Pulse Host Agent - Uninstallation" + print_color "$BLUE" "═══════════════════════════════════════════════════════════" + echo "" +} + +print_footer() { + echo "" + print_color "$GREEN" "═══════════════════════════════════════════════════════════" + log_success "Uninstallation complete!" + print_color "$GREEN" "═══════════════════════════════════════════════════════════" + echo "" +} + +# File paths +AGENT_PATH="/usr/local/bin/pulse-host-agent" +SYSTEMD_SERVICE="/etc/systemd/system/pulse-host-agent.service" +LAUNCHD_PLIST="$HOME/Library/LaunchAgents/com.pulse.host-agent.plist" +MACOS_LOG_DIR="$HOME/Library/Logs/Pulse" +LINUX_LOG_DIR="/var/log/pulse" + +print_header + +# Detect platform +case "$(uname -s)" in + Linux*) + PLATFORM="linux" + ;; + Darwin*) + PLATFORM="darwin" + ;; + *) + log_error "Unsupported platform: $(uname -s)" + exit 1 + ;; +esac + +log_info "Detected platform: $PLATFORM" +echo "" + +# Stop and remove systemd service (Linux) +if [[ "$PLATFORM" == "linux" ]]; then + if [[ -f "$SYSTEMD_SERVICE" ]] && command -v systemctl &> /dev/null; then + log_info "Stopping systemd service..." + if sudo systemctl stop pulse-host-agent 2>/dev/null; then + log_success "Service stopped" + else + log_warn "Service was not running or already stopped" + fi + + log_info "Disabling systemd service..." + sudo systemctl disable pulse-host-agent 2>/dev/null || true + + log_info "Removing systemd service file..." + sudo rm -f "$SYSTEMD_SERVICE" + sudo systemctl daemon-reload + log_success "Systemd service removed" + else + log_info "Systemd service not found (already removed or never installed)" + fi + + # Ensure process is terminated + if pgrep -x "pulse-host-agent" > /dev/null; then + log_info "Terminating running processes..." + sudo pkill -9 "pulse-host-agent" 2>/dev/null || true + sleep 1 + log_success "Processes terminated" + fi + + # Remove log directory + if [[ -d "$LINUX_LOG_DIR" ]]; then + log_info "Removing log directory..." + sudo rm -rf "$LINUX_LOG_DIR" + log_success "Log directory removed: $LINUX_LOG_DIR" + fi +fi + +# Stop and remove launchd service (macOS) +if [[ "$PLATFORM" == "darwin" ]]; then + if [[ -f "$LAUNCHD_PLIST" ]] && command -v launchctl &> /dev/null; then + log_info "Unloading launchd service..." + if launchctl unload "$LAUNCHD_PLIST" 2>/dev/null; then + log_success "Service unloaded" + else + log_warn "Service was not loaded or already unloaded" + fi + + log_info "Removing launchd plist..." + rm -f "$LAUNCHD_PLIST" + log_success "Launchd service removed" + else + log_info "Launchd service not found (already removed or never installed)" + fi + + # Remove Keychain token + log_info "Removing token from macOS Keychain..." + if security delete-generic-password -s "pulse-host-agent" -a "$USER" 2>/dev/null; then + log_success "Token removed from Keychain" + else + log_info "Token not found in Keychain (may not have been stored)" + fi + + # Remove wrapper script + WRAPPER_SCRIPT="/usr/local/bin/pulse-host-agent-wrapper.sh" + if [[ -f "$WRAPPER_SCRIPT" ]]; then + log_info "Removing wrapper script..." + rm -f "$WRAPPER_SCRIPT" + log_success "Wrapper script removed" + fi + + # Ensure process is terminated + if pgrep -x "pulse-host-agent" > /dev/null; then + log_info "Terminating running processes..." + pkill -9 "pulse-host-agent" 2>/dev/null || true + sleep 1 + log_success "Processes terminated" + fi + + # Remove log directory + if [[ -d "$MACOS_LOG_DIR" ]]; then + log_info "Removing log directory..." + rm -rf "$MACOS_LOG_DIR" + log_success "Log directory removed: $MACOS_LOG_DIR" + fi + + # Remove temporary logs (old location) + if [[ -f "/tmp/pulse-host-agent.log" ]]; then + log_info "Removing temporary log file..." + rm -f /tmp/pulse-host-agent.log + log_success "Temporary log removed" + fi +fi + +# Remove binary +if [[ -f "$AGENT_PATH" ]]; then + log_info "Removing agent binary..." + if sudo rm -f "$AGENT_PATH" 2>/dev/null; then + log_success "Agent binary removed: $AGENT_PATH" + else + log_error "Failed to remove agent binary (permission denied?)" + log_info "Try running with sudo: sudo $0" + fi +else + log_info "Agent binary not found: $AGENT_PATH" +fi + +print_footer + +log_info "The Pulse Host Agent has been removed from this system." +log_info "This host will no longer appear in your Pulse dashboard." +echo "" diff --git a/scripts/install-host-agent.sh b/scripts/install-host-agent.sh index bb130a6..a18ddbf 100755 --- a/scripts/install-host-agent.sh +++ b/scripts/install-host-agent.sh @@ -4,27 +4,60 @@ set -e # Pulse Host Agent Installer # Downloads and installs the Pulse host agent for Linux, macOS, or Windows (WSL) -trim() { - local value="$1" - value="${value#"${value%%[![:space:]]*}"}" - value="${value%"${value##*[![:space:]]}"}" - printf '%s' "$value" -} +# ANSI color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +RESET='\033[0m' -log_info() { - printf '[INFO] %s\n' "$1" +# Check if colors are supported +if [ -t 1 ] && command -v tput &> /dev/null && [ "$(tput colors)" -ge 8 ]; then + USE_COLOR=true +else + USE_COLOR=false +fi + +print_color() { + local color="$1" + local message="$2" + if [ "$USE_COLOR" = true ]; then + printf "${color}%s${RESET}\n" "$message" + else + printf "%s\n" "$message" + fi } log_success() { - printf '[ OK ] %s\n' "$1" -} - -log_warn() { - printf '[WARN] %s\n' "$1" >&2 + print_color "$GREEN" "✓ $1" } log_error() { - printf '[ERROR] %s\n' "$1" >&2 + print_color "$RED" "✗ $1" >&2 +} + +log_info() { + print_color "$BLUE" "ℹ $1" +} + +log_warn() { + print_color "$YELLOW" "⚠ $1" +} + +print_header() { + echo "" + print_color "$BLUE" "═══════════════════════════════════════════════════════════" + print_color "$BLUE" " Pulse Host Agent - Installation" + print_color "$BLUE" "═══════════════════════════════════════════════════════════" + echo "" +} + +print_footer() { + echo "" + print_color "$GREEN" "═══════════════════════════════════════════════════════════" + log_success "Installation complete!" + print_color "$GREEN" "═══════════════════════════════════════════════════════════" + echo "" } # Parse arguments @@ -66,48 +99,53 @@ done AGENT_PATH="/usr/local/bin/pulse-host-agent" SYSTEMD_SERVICE="/etc/systemd/system/pulse-host-agent.service" LAUNCHD_PLIST="$HOME/Library/LaunchAgents/com.pulse.host-agent.plist" +MACOS_LOG_DIR="$HOME/Library/Logs/Pulse" +MACOS_LOG_FILE="$MACOS_LOG_DIR/host-agent.log" +LINUX_LOG_DIR="/var/log/pulse" +LINUX_LOG_FILE="$LINUX_LOG_DIR/host-agent.log" # Uninstall function if [[ "$UNINSTALL" == "true" ]]; then - log_info "Uninstalling Pulse host agent..." - - # Stop and disable systemd service (Linux) - if [[ -f "$SYSTEMD_SERVICE" ]] && command -v systemctl &> /dev/null; then - sudo systemctl stop pulse-host-agent 2>/dev/null || true - sudo systemctl disable pulse-host-agent 2>/dev/null || true - sudo rm -f "$SYSTEMD_SERVICE" - sudo systemctl daemon-reload - log_success "Removed systemd service" - fi - - # Stop and remove launchd service (macOS) - if [[ -f "$LAUNCHD_PLIST" ]] && command -v launchctl &> /dev/null; then - launchctl unload "$LAUNCHD_PLIST" 2>/dev/null || true - rm -f "$LAUNCHD_PLIST" - log_success "Removed launchd service" - fi - - # Remove binary - if [[ -f "$AGENT_PATH" ]]; then - sudo rm -f "$AGENT_PATH" - log_success "Removed agent binary" - fi - - log_success "Pulse host agent uninstalled" - exit 0 + log_warn "The --uninstall flag is deprecated." + log_info "Please use the dedicated uninstall script instead:" + echo "" + echo " curl -fsSL \$PULSE_URL/uninstall-host-agent.sh | bash" + echo "" + log_info "Or download and run manually:" + echo " wget \$PULSE_URL/uninstall-host-agent.sh" + echo " chmod +x uninstall-host-agent.sh" + echo " ./uninstall-host-agent.sh" + echo "" + exit 1 fi -# Validate required parameters for install +print_header + +# Interactive prompts if parameters not provided if [[ -z "$PULSE_URL" ]]; then - log_error "Missing required parameter: --url" + log_info "Interactive Installation Mode" + echo "" + read -p "Enter Pulse server URL (e.g., http://pulse.example.com:7656): " PULSE_URL + PULSE_URL=$(echo "$PULSE_URL" | sed 's:/*$::') # Remove trailing slashes +fi + +if [[ -z "$PULSE_URL" ]]; then + log_error "Pulse URL is required" echo "Usage: $0 --url --token [--interval 30s] [--platform linux|darwin|windows]" exit 1 fi -if [[ -z "$PULSE_TOKEN" ]] && [[ "$PULSE_TOKEN" != "disabled" ]]; then - log_error "Missing required parameter: --token" - echo "Usage: $0 --url --token [--interval 30s] [--platform linux|darwin|windows]" - exit 1 +if [[ -z "$PULSE_TOKEN" ]]; then + log_warn "No API token provided - agent will attempt to connect without authentication" + read -p "Enter API token (or press Enter to skip): " PULSE_TOKEN + + if [[ -z "$PULSE_TOKEN" ]]; then + read -p "Continue without token? (y/N): " CONTINUE_WITHOUT_TOKEN + if [[ "$CONTINUE_WITHOUT_TOKEN" != "y" ]] && [[ "$CONTINUE_WITHOUT_TOKEN" != "Y" ]]; then + log_error "Installation cancelled" + exit 1 + fi + fi fi # Detect platform if not specified @@ -147,33 +185,153 @@ case "$ARCH" in ;; esac +log_info "Configuration:" +echo " Pulse URL: $PULSE_URL" +if [[ -n "$PULSE_TOKEN" ]]; then + # Mask token, showing only last 4 characters + TOKEN_MASKED="***${PULSE_TOKEN: -4}" + echo " Token: $TOKEN_MASKED" +else + echo " Token: none" +fi +echo " Interval: $INTERVAL" +echo " Platform: $PLATFORM/$ARCH" +echo "" + log_info "Installing Pulse host agent for $PLATFORM/$ARCH..." +# Check for existing installation and version +if [[ -f "$AGENT_PATH" ]]; then + log_info "Existing installation detected" + CURRENT_VERSION=$("$AGENT_PATH" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown") + if [[ "$CURRENT_VERSION" != "unknown" ]]; then + echo " Current version: $CURRENT_VERSION" + fi + + # Try to get latest version from server + if command -v curl &> /dev/null; then + LATEST_VERSION=$(curl -fsSL "$PULSE_URL/api/version" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "") + elif command -v wget &> /dev/null; then + LATEST_VERSION=$(wget -qO- "$PULSE_URL/api/version" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "") + fi + + if [[ -n "$LATEST_VERSION" ]] && [[ "$CURRENT_VERSION" != "unknown" ]]; then + echo " Latest version: $LATEST_VERSION" + if [[ "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then + log_success "Already running latest version" + elif [[ "$CURRENT_VERSION" < "$LATEST_VERSION" ]]; then + log_info "Update available: $CURRENT_VERSION → $LATEST_VERSION" + fi + fi + + read -p "Reinstall/update agent? (Y/n): " REINSTALL + if [[ "$REINSTALL" == "n" ]] || [[ "$REINSTALL" == "N" ]]; then + log_info "Installation cancelled" + exit 0 + fi + echo "" +fi + # Download agent binary from Pulse server DOWNLOAD_URL="$PULSE_URL/download/pulse-host-agent?platform=$PLATFORM&arch=$ARCH" TEMP_BINARY="/tmp/pulse-host-agent-$$.tmp" log_info "Downloading agent binary from $PULSE_URL..." +DOWNLOAD_SUCCESS=false +CHECKSUM_URL="${DOWNLOAD_URL}.sha256" + if command -v curl &> /dev/null; then - if ! curl -fL --progress-bar -o "$TEMP_BINARY" "$DOWNLOAD_URL"; then - log_error "Failed to download agent binary from $DOWNLOAD_URL" - log_info "The server may not have prebuilt binaries yet. You can build from source:" - log_info " git clone https://github.com/rcourtman/Pulse.git && cd Pulse" - log_info " go build -o pulse-host-agent ./cmd/pulse-host-agent" - log_info " sudo mv pulse-host-agent /usr/local/bin/" - rm -f "$TEMP_BINARY" - exit 1 + if curl -fL --progress-bar -o "$TEMP_BINARY" "$DOWNLOAD_URL" 2>&1; then + DOWNLOAD_SUCCESS=true fi + # Try to download checksum (optional, server may not provide it yet) + EXPECTED_CHECKSUM=$(curl -fsSL "$CHECKSUM_URL" 2>/dev/null || echo "") elif command -v wget &> /dev/null; then - if ! wget -q --show-progress -O "$TEMP_BINARY" "$DOWNLOAD_URL"; then - log_error "Failed to download agent binary from $DOWNLOAD_URL" - rm -f "$TEMP_BINARY" - exit 1 + if wget -q --show-progress -O "$TEMP_BINARY" "$DOWNLOAD_URL" 2>&1; then + DOWNLOAD_SUCCESS=true + fi + # Try to download checksum (optional) + EXPECTED_CHECKSUM=$(wget -qO- "$CHECKSUM_URL" 2>/dev/null || echo "") +else + log_error "Neither curl nor wget found" + echo "" + log_info "Please install curl or wget to continue:" + if [[ "$PLATFORM" == "darwin" ]]; then + echo " brew install curl" + elif [[ "$PLATFORM" == "linux" ]]; then + echo " # Debian/Ubuntu:" + echo " sudo apt-get install curl" + echo "" + echo " # RHEL/CentOS/Fedora:" + echo " sudo yum install curl" + fi + echo "" + exit 1 +fi + +if [[ "$DOWNLOAD_SUCCESS" == false ]]; then + log_error "Failed to download agent binary from $DOWNLOAD_URL" + echo "" + log_info "Troubleshooting steps:" + echo "" + echo "1. Verify the Pulse server is running:" + echo " curl $PULSE_URL/health" + echo "" + echo "2. Check if the download endpoint is accessible:" + echo " curl -I $DOWNLOAD_URL" + echo "" + echo "3. Build from source as a fallback:" + echo " git clone https://github.com/rcourtman/Pulse.git" + echo " cd Pulse" + echo " go build -o pulse-host-agent ./cmd/pulse-host-agent" + echo " sudo mv pulse-host-agent /usr/local/bin/" + echo " # Then run this script again with --url and --token" + echo "" + echo "4. Check firewall/network settings blocking the connection" + echo "" + rm -f "$TEMP_BINARY" + exit 1 +fi + +log_success "Downloaded agent binary" + +# Verify checksum if available +if [[ -n "$EXPECTED_CHECKSUM" ]]; then + log_info "Verifying checksum..." + + if command -v sha256sum &> /dev/null; then + ACTUAL_CHECKSUM=$(sha256sum "$TEMP_BINARY" | awk '{print $1}') + elif command -v shasum &> /dev/null; then + ACTUAL_CHECKSUM=$(shasum -a 256 "$TEMP_BINARY" | awk '{print $1}') + else + log_warn "No checksum tool found (sha256sum/shasum), skipping verification" + ACTUAL_CHECKSUM="" + fi + + if [[ -n "$ACTUAL_CHECKSUM" ]]; then + # Clean up checksums (remove whitespace, convert to lowercase) + EXPECTED_CHECKSUM=$(echo "$EXPECTED_CHECKSUM" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]') + ACTUAL_CHECKSUM=$(echo "$ACTUAL_CHECKSUM" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]') + + if [[ "$EXPECTED_CHECKSUM" == "$ACTUAL_CHECKSUM" ]]; then + log_success "Checksum verified (SHA256: ${ACTUAL_CHECKSUM:0:16}...)" + else + log_error "Checksum mismatch!" + echo " Expected: $EXPECTED_CHECKSUM" + echo " Got: $ACTUAL_CHECKSUM" + echo "" + log_warn "The downloaded binary may be corrupted or tampered with." + read -p "Continue anyway? (y/N): " CONTINUE_ANYWAY + if [[ "$CONTINUE_ANYWAY" != "y" ]] && [[ "$CONTINUE_ANYWAY" != "Y" ]]; then + rm -f "$TEMP_BINARY" + log_error "Installation cancelled" + exit 1 + fi + fi fi else - log_error "Neither curl nor wget found. Please install one of them." - exit 1 + log_info "Checksum not available (server doesn't provide it yet)" fi sudo mv "$TEMP_BINARY" "$AGENT_PATH" @@ -184,6 +342,9 @@ log_success "Agent binary installed to $AGENT_PATH" if [[ "$PLATFORM" == "linux" ]] && command -v systemctl &> /dev/null; then log_info "Setting up systemd service..." + # Create log directory + sudo mkdir -p "$LINUX_LOG_DIR" + sudo tee "$SYSTEMD_SERVICE" > /dev/null < /dev/null; then log_info "Setting up launchd service..." + # Create log directory + mkdir -p "$MACOS_LOG_DIR" mkdir -p "$HOME/Library/LaunchAgents" - cat > "$LAUNCHD_PLIST" </dev/null || true + + # Add token to Keychain + if security add-generic-password -s "pulse-host-agent" -a "$USER" -w "$PULSE_TOKEN" -U 2>/dev/null; then + log_success "Token stored securely in macOS Keychain" + USE_KEYCHAIN=true + else + log_warn "Failed to store token in Keychain, will use plist instead" + log_info "You may need to grant Keychain access permissions" + USE_KEYCHAIN=false + fi + else + USE_KEYCHAIN=false + fi + + # Create wrapper script if using Keychain + if [[ "$USE_KEYCHAIN" == true ]]; then + WRAPPER_SCRIPT="/usr/local/bin/pulse-host-agent-wrapper.sh" + + cat > "$WRAPPER_SCRIPT" <<'WRAPPER_EOF' +#!/bin/bash +# Pulse Host Agent Wrapper - Reads token from Keychain +set -e + +# Read token from Keychain +PULSE_TOKEN=$(security find-generic-password -s "pulse-host-agent" -w 2>/dev/null || echo "") + +# Export for agent to use +export PULSE_TOKEN + +# Run the actual agent with all arguments +exec /usr/local/bin/pulse-host-agent "$@" +WRAPPER_EOF + + chmod +x "$WRAPPER_SCRIPT" + log_success "Created Keychain wrapper script" + + # Create plist using wrapper (token not in plist!) + cat > "$LAUNCHD_PLIST" < + + + + Label + com.pulse.host-agent + ProgramArguments + + $WRAPPER_SCRIPT + --url + $PULSE_URL + --interval + $INTERVAL + + RunAtLoad + + KeepAlive + + StandardOutPath + $MACOS_LOG_FILE + StandardErrorPath + $MACOS_LOG_FILE + + +EOF + log_success "Created launchd service configuration (using Keychain)" + else + # Create plist with token directly (fallback) + cat > "$LAUNCHD_PLIST" < @@ -232,12 +471,17 @@ elif [[ "$PLATFORM" == "darwin" ]] && command -v launchctl &> /dev/null; then KeepAlive StandardOutPath - /tmp/pulse-host-agent.log + $MACOS_LOG_FILE StandardErrorPath - /tmp/pulse-host-agent.log + $MACOS_LOG_FILE EOF + log_success "Created launchd service configuration" + fi + + # Set restrictive permissions on plist + chmod 600 "$LAUNCHD_PLIST" launchctl load "$LAUNCHD_PLIST" log_success "Launchd service enabled and started" @@ -247,5 +491,115 @@ else log_info " $AGENT_PATH --url $PULSE_URL --token $PULSE_TOKEN --interval $INTERVAL" fi -log_success "Pulse host agent installation complete!" -log_info "The agent is now reporting to $PULSE_URL" +# Validate installation +log_info "Waiting 10 seconds to validate agent reporting..." +sleep 10 + +VALIDATION_SUCCESS=false +SERVICE_RUNNING=false + +# Check if service is running +if [[ "$PLATFORM" == "linux" ]] && command -v systemctl &> /dev/null; then + SERVICE_STATUS=$(systemctl is-active pulse-host-agent 2>/dev/null || echo "inactive") + if [[ "$SERVICE_STATUS" == "active" ]]; then + SERVICE_RUNNING=true + log_success "Service is running successfully!" + else + log_warn "Service status: $SERVICE_STATUS" + log_info "Check logs with: sudo journalctl -u pulse-host-agent -n 50" + fi +elif [[ "$PLATFORM" == "darwin" ]] && command -v launchctl &> /dev/null; then + if launchctl list | grep -q "com.pulse.host-agent"; then + SERVICE_RUNNING=true + log_success "Service is running successfully!" + else + log_warn "Service may not be running properly" + log_info "Check logs with: tail -20 $MACOS_LOG_FILE" + fi +fi + +# Try to verify with API endpoint that agent is reporting +if [[ "$SERVICE_RUNNING" == true ]]; then + log_info "Verifying agent registration with Pulse server..." + + # Get hostname for verification + HOSTNAME=$(hostname) + + # Try to query the API for this host + if command -v curl &> /dev/null; then + API_CHECK=$(curl -fsSL "$PULSE_URL/api/hosts" 2>/dev/null || echo "") + elif command -v wget &> /dev/null; then + API_CHECK=$(wget -qO- "$PULSE_URL/api/hosts" 2>/dev/null || echo "") + fi + + if [[ -n "$API_CHECK" ]] && echo "$API_CHECK" | grep -q "$HOSTNAME"; then + VALIDATION_SUCCESS=true + log_success "Agent successfully registered with Pulse server!" + log_success "Your host '$HOSTNAME' is now reporting metrics!" + elif [[ -n "$API_CHECK" ]]; then + log_warn "Agent is running but host not found in API yet (may take a few moments)" + log_info "Service appears healthy, metrics should appear shortly." + VALIDATION_SUCCESS=true # Service is running, so count as success + else + log_warn "Could not verify registration via API (endpoint may not be accessible)" + log_info "Service is running, check your Pulse dashboard manually." + VALIDATION_SUCCESS=true # Service is running, so count as success + fi +fi + +if [[ "$VALIDATION_SUCCESS" == true ]]; then + log_info "Check your Pulse dashboard at: $PULSE_URL" +else + log_error "Service validation failed" + echo "" + log_info "Troubleshooting:" + echo "" + if [[ "$PLATFORM" == "linux" ]]; then + echo " View logs: sudo journalctl -u pulse-host-agent -f" + echo " Check status: sudo systemctl status pulse-host-agent" + echo " Restart: sudo systemctl restart pulse-host-agent" + elif [[ "$PLATFORM" == "darwin" ]]; then + echo " View logs: tail -f $MACOS_LOG_FILE" + echo " Check status: launchctl list | grep pulse" + echo " Restart: launchctl unload $LAUNCHD_PLIST && launchctl load $LAUNCHD_PLIST" + fi + echo "" + echo " Manual run: $AGENT_PATH --url $PULSE_URL $(if [[ -n "$PULSE_TOKEN" ]]; then echo "--token ***"; fi) --interval $INTERVAL" + echo "" +fi + +print_footer + +log_info "Service Management Commands:" +if [[ "$PLATFORM" == "linux" ]]; then + echo " Start: sudo systemctl start pulse-host-agent" + echo " Stop: sudo systemctl stop pulse-host-agent" + echo " Restart: sudo systemctl restart pulse-host-agent" + echo " Status: sudo systemctl status pulse-host-agent" + echo " Logs: sudo journalctl -u pulse-host-agent -f" +elif [[ "$PLATFORM" == "darwin" ]]; then + echo " Start: launchctl load $LAUNCHD_PLIST" + echo " Stop: launchctl unload $LAUNCHD_PLIST" + echo " Restart: launchctl unload $LAUNCHD_PLIST && launchctl load $LAUNCHD_PLIST" + echo " Status: launchctl list | grep pulse" + echo " Logs: tail -f $MACOS_LOG_FILE" +fi +echo "" + +log_info "Files installed:" +echo " Binary: $AGENT_PATH" +if [[ "$PLATFORM" == "linux" ]]; then + echo " Service: $SYSTEMD_SERVICE" + echo " Logs: $LINUX_LOG_FILE" +elif [[ "$PLATFORM" == "darwin" ]]; then + echo " Service: $LAUNCHD_PLIST" + echo " Logs: $MACOS_LOG_FILE" +fi +echo "" + +log_info "The agent is now reporting to: $PULSE_URL" +echo "" + +log_info "To uninstall, run:" +echo " curl -fsSL $PULSE_URL/uninstall-host-agent.sh | bash" +echo "" diff --git a/scripts/uninstall-host-agent.sh b/scripts/uninstall-host-agent.sh new file mode 100755 index 0000000..d394c2e --- /dev/null +++ b/scripts/uninstall-host-agent.sh @@ -0,0 +1,199 @@ +#!/bin/bash +set -e + +# Pulse Host Agent Uninstallation Script +# Removes the Pulse host agent and all associated files + +# ANSI color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +RESET='\033[0m' + +# Check if colors are supported +if [ -t 1 ] && command -v tput &> /dev/null && [ "$(tput colors)" -ge 8 ]; then + USE_COLOR=true +else + USE_COLOR=false +fi + +print_color() { + local color="$1" + local message="$2" + if [ "$USE_COLOR" = true ]; then + printf "${color}%s${RESET}\n" "$message" + else + printf "%s\n" "$message" + fi +} + +log_success() { + print_color "$GREEN" "✓ $1" +} + +log_error() { + print_color "$RED" "✗ $1" >&2 +} + +log_info() { + print_color "$BLUE" "ℹ $1" +} + +log_warn() { + print_color "$YELLOW" "⚠ $1" +} + +print_header() { + echo "" + print_color "$BLUE" "═══════════════════════════════════════════════════════════" + print_color "$BLUE" " Pulse Host Agent - Uninstallation" + print_color "$BLUE" "═══════════════════════════════════════════════════════════" + echo "" +} + +print_footer() { + echo "" + print_color "$GREEN" "═══════════════════════════════════════════════════════════" + log_success "Uninstallation complete!" + print_color "$GREEN" "═══════════════════════════════════════════════════════════" + echo "" +} + +# File paths +AGENT_PATH="/usr/local/bin/pulse-host-agent" +SYSTEMD_SERVICE="/etc/systemd/system/pulse-host-agent.service" +LAUNCHD_PLIST="$HOME/Library/LaunchAgents/com.pulse.host-agent.plist" +MACOS_LOG_DIR="$HOME/Library/Logs/Pulse" +LINUX_LOG_DIR="/var/log/pulse" + +print_header + +# Detect platform +case "$(uname -s)" in + Linux*) + PLATFORM="linux" + ;; + Darwin*) + PLATFORM="darwin" + ;; + *) + log_error "Unsupported platform: $(uname -s)" + exit 1 + ;; +esac + +log_info "Detected platform: $PLATFORM" +echo "" + +# Stop and remove systemd service (Linux) +if [[ "$PLATFORM" == "linux" ]]; then + if [[ -f "$SYSTEMD_SERVICE" ]] && command -v systemctl &> /dev/null; then + log_info "Stopping systemd service..." + if sudo systemctl stop pulse-host-agent 2>/dev/null; then + log_success "Service stopped" + else + log_warn "Service was not running or already stopped" + fi + + log_info "Disabling systemd service..." + sudo systemctl disable pulse-host-agent 2>/dev/null || true + + log_info "Removing systemd service file..." + sudo rm -f "$SYSTEMD_SERVICE" + sudo systemctl daemon-reload + log_success "Systemd service removed" + else + log_info "Systemd service not found (already removed or never installed)" + fi + + # Ensure process is terminated + if pgrep -x "pulse-host-agent" > /dev/null; then + log_info "Terminating running processes..." + sudo pkill -9 "pulse-host-agent" 2>/dev/null || true + sleep 1 + log_success "Processes terminated" + fi + + # Remove log directory + if [[ -d "$LINUX_LOG_DIR" ]]; then + log_info "Removing log directory..." + sudo rm -rf "$LINUX_LOG_DIR" + log_success "Log directory removed: $LINUX_LOG_DIR" + fi +fi + +# Stop and remove launchd service (macOS) +if [[ "$PLATFORM" == "darwin" ]]; then + if [[ -f "$LAUNCHD_PLIST" ]] && command -v launchctl &> /dev/null; then + log_info "Unloading launchd service..." + if launchctl unload "$LAUNCHD_PLIST" 2>/dev/null; then + log_success "Service unloaded" + else + log_warn "Service was not loaded or already unloaded" + fi + + log_info "Removing launchd plist..." + rm -f "$LAUNCHD_PLIST" + log_success "Launchd service removed" + else + log_info "Launchd service not found (already removed or never installed)" + fi + + # Remove Keychain token + log_info "Removing token from macOS Keychain..." + if security delete-generic-password -s "pulse-host-agent" -a "$USER" 2>/dev/null; then + log_success "Token removed from Keychain" + else + log_info "Token not found in Keychain (may not have been stored)" + fi + + # Remove wrapper script + WRAPPER_SCRIPT="/usr/local/bin/pulse-host-agent-wrapper.sh" + if [[ -f "$WRAPPER_SCRIPT" ]]; then + log_info "Removing wrapper script..." + rm -f "$WRAPPER_SCRIPT" + log_success "Wrapper script removed" + fi + + # Ensure process is terminated + if pgrep -x "pulse-host-agent" > /dev/null; then + log_info "Terminating running processes..." + pkill -9 "pulse-host-agent" 2>/dev/null || true + sleep 1 + log_success "Processes terminated" + fi + + # Remove log directory + if [[ -d "$MACOS_LOG_DIR" ]]; then + log_info "Removing log directory..." + rm -rf "$MACOS_LOG_DIR" + log_success "Log directory removed: $MACOS_LOG_DIR" + fi + + # Remove temporary logs (old location) + if [[ -f "/tmp/pulse-host-agent.log" ]]; then + log_info "Removing temporary log file..." + rm -f /tmp/pulse-host-agent.log + log_success "Temporary log removed" + fi +fi + +# Remove binary +if [[ -f "$AGENT_PATH" ]]; then + log_info "Removing agent binary..." + if sudo rm -f "$AGENT_PATH" 2>/dev/null; then + log_success "Agent binary removed: $AGENT_PATH" + else + log_error "Failed to remove agent binary (permission denied?)" + log_info "Try running with sudo: sudo $0" + fi +else + log_info "Agent binary not found: $AGENT_PATH" +fi + +print_footer + +log_info "The Pulse Host Agent has been removed from this system." +log_info "This host will no longer appear in your Pulse dashboard." +echo ""