diff --git a/agents/patchmon_install.sh b/agents/patchmon_install.sh index 0dd3adf..3a7e7a8 100644 --- a/agents/patchmon_install.sh +++ b/agents/patchmon_install.sh @@ -1,7 +1,32 @@ -#!/bin/bash +#!/bin/sh +# PatchMon Agent Installation Script +# This script requires bash for full functionality +# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | sh + +# Check if bash is available, if not try to install it (for Alpine Linux) +if ! command -v bash >/dev/null 2>&1; then + if command -v apk >/dev/null 2>&1; then + echo "Installing bash for script compatibility..." + apk add --no-cache bash >/dev/null 2>&1 || true + fi +fi + +# If bash is available and we're not already running in bash, switch to bash +# When piped, we can't re-execute easily, so we'll continue with sh +# but ensure bash is available for bash-specific features +if command -v bash >/dev/null 2>&1 && [ -z "${BASH_VERSION:-}" ]; then + # Check if we're being piped (stdin is not a terminal) + if [ -t 0 ]; then + # Direct execution, re-execute with bash + exec bash "$0" "$@" + exit $? + fi + # When piped, we continue with sh but bash is now available + # The script will use bash-specific features which should work if bash is installed +fi # PatchMon Agent Installation Script -# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | bash +# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | sh set -e @@ -36,7 +61,7 @@ warning() { } # Check if running as root -if [[ $EUID -ne 0 ]]; then +if [ "$(id -u)" -ne 0 ]; then error "This script must be run as root (use sudo)" fi @@ -45,8 +70,8 @@ verify_datetime() { info "๐Ÿ• Verifying system datetime and timezone..." # Get current system time - local system_time=$(date) - local timezone=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Unknown") + system_time=$(date) + timezone=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Unknown") # Display current datetime info echo "" @@ -56,14 +81,17 @@ verify_datetime() { echo "" # Check if we can read from stdin (interactive terminal) - if [[ -t 0 ]]; then + if [ -t 0 ]; then # Interactive terminal - ask user - read -p "Does this date/time look correct to you? (y/N): " -r response - if [[ "$response" =~ ^[Yy]$ ]]; then - success "โœ… Date/time verification passed" - echo "" - return 0 - else + printf "Does this date/time look correct to you? (y/N): " + read -r response + case "$response" in + [Yy]*) + success "โœ… Date/time verification passed" + echo "" + return 0 + ;; + *) echo "" echo -e "${RED}โŒ Date/time verification failed${NC}" echo "" @@ -72,9 +100,10 @@ verify_datetime() { echo " sudo timedatectl set-timezone 'America/New_York' # or your timezone" echo " sudo timedatectl list-timezones # to see available timezones" echo "" - echo -e "${BLUE}โ„น๏ธ After fixing the date/time, re-run this installation script.${NC}" - error "Installation cancelled - please fix date/time and re-run" - fi + echo -e "${BLUE}โ„น๏ธ After fixing the date/time, re-run this installation script.${NC}" + error "Installation cancelled - please fix date/time and re-run" + ;; + esac else # Non-interactive (piped from curl) - show warning and continue echo -e "${YELLOW}โš ๏ธ Non-interactive installation detected${NC}" @@ -121,9 +150,9 @@ cleanup_old_files # Generate or retrieve machine ID get_machine_id() { # Try multiple sources for machine ID - if [[ -f /etc/machine-id ]]; then + if [ -f /etc/machine-id ]; then cat /etc/machine-id - elif [[ -f /var/lib/dbus/machine-id ]]; then + elif [ -f /var/lib/dbus/machine-id ]; then cat /var/lib/dbus/machine-id else # Fallback: generate from hardware info (less ideal but works) @@ -132,12 +161,12 @@ get_machine_id() { } # Parse arguments from environment (passed via HTTP headers) -if [[ -z "$PATCHMON_URL" ]] || [[ -z "$API_ID" ]] || [[ -z "$API_KEY" ]]; then +if [ -z "$PATCHMON_URL" ] || [ -z "$API_ID" ] || [ -z "$API_KEY" ]; then error "Missing required parameters. This script should be called via the PatchMon web interface." fi # Auto-detect architecture if not explicitly set -if [[ -z "$ARCHITECTURE" ]]; then +if [ -z "$ARCHITECTURE" ]; then arch_raw=$(uname -m 2>/dev/null || echo "unknown") # Map architecture to supported values @@ -162,13 +191,16 @@ if [[ -z "$ARCHITECTURE" ]]; then fi # Validate architecture -if [[ "$ARCHITECTURE" != "amd64" && "$ARCHITECTURE" != "386" && "$ARCHITECTURE" != "arm64" && "$ARCHITECTURE" != "arm" ]]; then +if [ "$ARCHITECTURE" != "amd64" ] && [ "$ARCHITECTURE" != "386" ] && [ "$ARCHITECTURE" != "arm64" ] && [ "$ARCHITECTURE" != "arm" ]; then error "Invalid architecture '$ARCHITECTURE'. Must be one of: amd64, 386, arm64, arm" fi # Check if --force flag is set (for bypassing broken packages) FORCE_INSTALL="${FORCE_INSTALL:-false}" -if [[ "$*" == *"--force"* ]] || [[ "$FORCE_INSTALL" == "true" ]]; then +case "$*" in + *"--force"*) FORCE_INSTALL="true" ;; +esac +if [ "$FORCE_INSTALL" = "true" ]; then FORCE_INSTALL="true" warning "โš ๏ธ Force mode enabled - will bypass broken packages" fi @@ -224,7 +256,7 @@ install_apt_packages() { # Build apt-get command based on force mode local apt_cmd="apt-get install ${missing_packages[*]} -y" - if [[ "$FORCE_INSTALL" == "true" ]]; then + if [ "$FORCE_INSTALL" = "true" ]; then info "Using force mode - bypassing broken packages..." apt_cmd="$apt_cmd -o APT::Get::Fix-Broken=false -o DPkg::Options::=\"--force-confold\" -o DPkg::Options::=\"--force-confdef\"" fi @@ -240,7 +272,7 @@ install_apt_packages() { local all_ok=true for pkg in "${packages[@]}"; do if ! command_exists "$pkg"; then - if [[ "$FORCE_INSTALL" == "true" ]]; then + if [ "$FORCE_INSTALL" = "true" ]; then error "Critical dependency '$pkg' is not available even with --force. Please install manually." else error "Critical dependency '$pkg' is not available. Try again with --force flag or install manually: apt-get install $pkg" @@ -279,7 +311,7 @@ install_yum_dnf_packages() { info "Need to install: ${missing_packages[*]}" - if [[ "$pkg_manager" == "yum" ]]; then + if [ "$pkg_manager" = "yum" ]; then yum install -y "${missing_packages[@]}" else dnf install -y "${missing_packages[@]}" @@ -365,7 +397,7 @@ install_apk_packages() { local all_ok=true for pkg in "${packages[@]}"; do if ! command_exists "$pkg"; then - if [[ "$FORCE_INSTALL" == "true" ]]; then + if [ "$FORCE_INSTALL" = "true" ]; then error "Critical dependency '$pkg' is not available even with --force. Please install manually." else error "Critical dependency '$pkg' is not available. Try again with --force flag or install manually: apk add $pkg" @@ -391,7 +423,7 @@ if command -v apt-get >/dev/null 2>&1; then # Check for broken packages if dpkg -l | grep -q "^iH\|^iF" 2>/dev/null; then - if [[ "$FORCE_INSTALL" == "true" ]]; then + if [ "$FORCE_INSTALL" = "true" ]; then warning "Detected broken packages on system - force mode will work around them" else warning "โš ๏ธ Broken packages detected on system" @@ -446,7 +478,7 @@ echo "" info "๐Ÿ“ Setting up configuration directory..." # Check if configuration directory already exists -if [[ -d "/etc/patchmon" ]]; then +if [ -d "/etc/patchmon" ]; then warning "โš ๏ธ Configuration directory already exists at /etc/patchmon" warning "โš ๏ธ Preserving existing configuration files" @@ -463,8 +495,8 @@ fi # Check if agent is already configured and working (before we overwrite anything) info "๐Ÿ” Checking if agent is already configured..." -if [[ -f /etc/patchmon/config.yml ]] && [[ -f /etc/patchmon/credentials.yml ]]; then - if [[ -f /usr/local/bin/patchmon-agent ]]; then +if [ -f /etc/patchmon/config.yml ] && [ -f /etc/patchmon/credentials.yml ]; then + if [ -f /usr/local/bin/patchmon-agent ]; then info "๐Ÿ“‹ Found existing agent configuration" info "๐Ÿงช Testing existing configuration with ping..." @@ -495,7 +527,7 @@ fi info "๐Ÿ” Creating configuration files..." # Check if config file already exists -if [[ -f "/etc/patchmon/config.yml" ]]; then +if [ -f "/etc/patchmon/config.yml" ]; then warning "โš ๏ธ Config file already exists at /etc/patchmon/config.yml" warning "โš ๏ธ Moving existing file out of the way for fresh installation" @@ -508,7 +540,7 @@ if [[ -f "/etc/patchmon/config.yml" ]]; then fi # Check if credentials file already exists -if [[ -f "/etc/patchmon/credentials.yml" ]]; then +if [ -f "/etc/patchmon/credentials.yml" ]; then warning "โš ๏ธ Credentials file already exists at /etc/patchmon/credentials.yml" warning "โš ๏ธ Moving existing file out of the way for fresh installation" @@ -521,7 +553,7 @@ if [[ -f "/etc/patchmon/credentials.yml" ]]; then fi # Clean up old credentials file if it exists (from previous installations) -if [[ -f "/etc/patchmon/credentials" ]]; then +if [ -f "/etc/patchmon/credentials" ]; then warning "โš ๏ธ Found old credentials file, removing it..." rm -f /etc/patchmon/credentials info "๐Ÿ“‹ Removed old credentials file" @@ -557,7 +589,7 @@ info "๐Ÿ“ฅ Downloading PatchMon agent binary..." BINARY_NAME="patchmon-agent-linux-${ARCHITECTURE}" # Check if agent binary already exists -if [[ -f "/usr/local/bin/patchmon-agent" ]]; then +if [ -f "/usr/local/bin/patchmon-agent" ]; then warning "โš ๏ธ Agent binary already exists at /usr/local/bin/patchmon-agent" warning "โš ๏ธ Moving existing file out of the way for fresh installation" @@ -570,7 +602,7 @@ if [[ -f "/usr/local/bin/patchmon-agent" ]]; then fi # Clean up old shell script if it exists (from previous installations) -if [[ -f "/usr/local/bin/patchmon-agent.sh" ]]; then +if [ -f "/usr/local/bin/patchmon-agent.sh" ]; then warning "โš ๏ธ Found old shell script agent, removing it..." rm -f /usr/local/bin/patchmon-agent.sh info "๐Ÿ“‹ Removed old shell script agent" @@ -596,7 +628,7 @@ info "๐Ÿ“ Setting up log directory..." mkdir -p /etc/patchmon/logs # Handle existing log files -if [[ -f "/etc/patchmon/logs/patchmon-agent.log" ]]; then +if [ -f "/etc/patchmon/logs/patchmon-agent.log" ]; then warning "โš ๏ธ Existing log file found at /etc/patchmon/logs/patchmon-agent.log" warning "โš ๏ธ Rotating log file for fresh start" @@ -613,23 +645,26 @@ else error "โŒ Failed to validate API credentials or reach server" fi -# Step 5: Setup systemd service for WebSocket connection +# Step 5: Setup service for WebSocket connection # Note: The service will automatically send an initial report on startup (see serve.go) -info "๐Ÿ”ง Setting up systemd service..." - -# Stop and disable existing service if it exists -if systemctl is-active --quiet patchmon-agent.service 2>/dev/null; then - warning "โš ๏ธ Stopping existing PatchMon agent service..." - systemctl stop patchmon-agent.service -fi - -if systemctl is-enabled --quiet patchmon-agent.service 2>/dev/null; then - warning "โš ๏ธ Disabling existing PatchMon agent service..." - systemctl disable patchmon-agent.service -fi - -# Create systemd service file -cat > /etc/systemd/system/patchmon-agent.service << EOF +# Detect init system and create appropriate service +if command -v systemctl >/dev/null 2>&1; then + # Systemd is available + info "๐Ÿ”ง Setting up systemd service..." + + # Stop and disable existing service if it exists + if systemctl is-active --quiet patchmon-agent.service 2>/dev/null; then + warning "โš ๏ธ Stopping existing PatchMon agent service..." + systemctl stop patchmon-agent.service + fi + + if systemctl is-enabled --quiet patchmon-agent.service 2>/dev/null; then + warning "โš ๏ธ Disabling existing PatchMon agent service..." + systemctl disable patchmon-agent.service + fi + + # Create systemd service file + cat > /etc/systemd/system/patchmon-agent.service << EOF [Unit] Description=PatchMon Agent Service After=network.target @@ -651,25 +686,105 @@ SyslogIdentifier=patchmon-agent [Install] WantedBy=multi-user.target EOF + + # Clean up old crontab entries if they exist (from previous installations) + if crontab -l 2>/dev/null | grep -q "patchmon-agent"; then + warning "โš ๏ธ Found old crontab entries, removing them..." + crontab -l 2>/dev/null | grep -v "patchmon-agent" | crontab - + info "๐Ÿ“‹ Removed old crontab entries" + fi + + # Reload systemd and enable/start the service + systemctl daemon-reload + systemctl enable patchmon-agent.service + systemctl start patchmon-agent.service + + # Check if service started successfully + if systemctl is-active --quiet patchmon-agent.service; then + success "โœ… PatchMon Agent service started successfully" + info "๐Ÿ”— WebSocket connection established" + else + warning "โš ๏ธ Service may have failed to start. Check status with: systemctl status patchmon-agent" + fi + + SERVICE_TYPE="systemd" +elif [ -d /etc/init.d ] && command -v rc-service >/dev/null 2>&1; then + # OpenRC is available (Alpine Linux) + info "๐Ÿ”ง Setting up OpenRC service..." + + # Stop and disable existing service if it exists + if rc-service patchmon-agent status >/dev/null 2>&1; then + warning "โš ๏ธ Stopping existing PatchMon agent service..." + rc-service patchmon-agent stop + fi + + if rc-update show default 2>/dev/null | grep -q "patchmon-agent"; then + warning "โš ๏ธ Disabling existing PatchMon agent service..." + rc-update del patchmon-agent default + fi + + # Create OpenRC service file + cat > /etc/init.d/patchmon-agent << 'EOF' +#!/sbin/openrc-run -# Clean up old crontab entries if they exist (from previous installations) -if crontab -l 2>/dev/null | grep -q "patchmon-agent"; then - warning "โš ๏ธ Found old crontab entries, removing them..." - crontab -l 2>/dev/null | grep -v "patchmon-agent" | crontab - - info "๐Ÿ“‹ Removed old crontab entries" -fi +name="patchmon-agent" +description="PatchMon Agent Service" +command="/usr/local/bin/patchmon-agent" +command_args="serve" +command_user="root" +pidfile="/var/run/patchmon-agent.pid" +command_background="yes" +working_dir="/etc/patchmon" -# Reload systemd and enable/start the service -systemctl daemon-reload -systemctl enable patchmon-agent.service -systemctl start patchmon-agent.service - -# Check if service started successfully -if systemctl is-active --quiet patchmon-agent.service; then - success "โœ… PatchMon Agent service started successfully" - info "๐Ÿ”— WebSocket connection established" +depend() { + need net + after net +} +EOF + + chmod +x /etc/init.d/patchmon-agent + + # Clean up old crontab entries if they exist (from previous installations) + if crontab -l 2>/dev/null | grep -q "patchmon-agent"; then + warning "โš ๏ธ Found old crontab entries, removing them..." + crontab -l 2>/dev/null | grep -v "patchmon-agent" | crontab - + info "๐Ÿ“‹ Removed old crontab entries" + fi + + # Enable and start the service + rc-update add patchmon-agent default + rc-service patchmon-agent start + + # Check if service started successfully + if rc-service patchmon-agent status >/dev/null 2>&1; then + success "โœ… PatchMon Agent service started successfully" + info "๐Ÿ”— WebSocket connection established" + else + warning "โš ๏ธ Service may have failed to start. Check status with: rc-service patchmon-agent status" + fi + + SERVICE_TYPE="openrc" else - warning "โš ๏ธ Service may have failed to start. Check status with: systemctl status patchmon-agent" + # No init system detected, use crontab as fallback + warning "โš ๏ธ No init system detected (systemd or OpenRC). Using crontab for service management." + + # Clean up old crontab entries if they exist + if crontab -l 2>/dev/null | grep -q "patchmon-agent"; then + warning "โš ๏ธ Found old crontab entries, removing them..." + crontab -l 2>/dev/null | grep -v "patchmon-agent" | crontab - + info "๐Ÿ“‹ Removed old crontab entries" + fi + + # Add crontab entry to run the agent + (crontab -l 2>/dev/null; echo "@reboot /usr/local/bin/patchmon-agent serve >/dev/null 2>&1") | crontab - + info "๐Ÿ“‹ Added crontab entry for PatchMon agent" + + # Start the agent manually + /usr/local/bin/patchmon-agent serve >/dev/null 2>&1 & + success "โœ… PatchMon Agent started in background" + info "๐Ÿ”— WebSocket connection established" + + SERVICE_TYPE="crontab" fi # Installation complete @@ -680,14 +795,20 @@ echo " โ€ข Configuration directory: /etc/patchmon" echo " โ€ข Agent binary installed: /usr/local/bin/patchmon-agent" echo " โ€ข Architecture: $ARCHITECTURE" echo " โ€ข Dependencies installed: jq, curl, bc" -echo " โ€ข Systemd service configured and running" +if [ "$SERVICE_TYPE" = "systemd" ]; then + echo " โ€ข Systemd service configured and running" +elif [ "$SERVICE_TYPE" = "openrc" ]; then + echo " โ€ข OpenRC service configured and running" +else + echo " โ€ข Service configured via crontab" +fi echo " โ€ข API credentials configured and tested" echo " โ€ข WebSocket connection established" echo " โ€ข Logs directory: /etc/patchmon/logs" # Check for moved files and show them MOVED_FILES=$(ls /etc/patchmon/credentials.yml.backup.* /etc/patchmon/config.yml.backup.* /usr/local/bin/patchmon-agent.backup.* /etc/patchmon/logs/patchmon-agent.log.old.* /usr/local/bin/patchmon-agent.sh.backup.* /etc/patchmon/credentials.backup.* 2>/dev/null || true) -if [[ -n "$MOVED_FILES" ]]; then +if [ -n "$MOVED_FILES" ]; then echo "" echo -e "${YELLOW}๐Ÿ“‹ Files Moved for Fresh Installation:${NC}" echo "$MOVED_FILES" | while read -r moved_file; do @@ -702,8 +823,17 @@ echo -e "${BLUE}๐Ÿ”ง Management Commands:${NC}" echo " โ€ข Test connection: /usr/local/bin/patchmon-agent ping" echo " โ€ข Manual report: /usr/local/bin/patchmon-agent report" echo " โ€ข Check status: /usr/local/bin/patchmon-agent diagnostics" -echo " โ€ข Service status: systemctl status patchmon-agent" -echo " โ€ข Service logs: journalctl -u patchmon-agent -f" -echo " โ€ข Restart service: systemctl restart patchmon-agent" +if [ "$SERVICE_TYPE" = "systemd" ]; then + echo " โ€ข Service status: systemctl status patchmon-agent" + echo " โ€ข Service logs: journalctl -u patchmon-agent -f" + echo " โ€ข Restart service: systemctl restart patchmon-agent" +elif [ "$SERVICE_TYPE" = "openrc" ]; then + echo " โ€ข Service status: rc-service patchmon-agent status" + echo " โ€ข Service logs: tail -f /etc/patchmon/logs/patchmon-agent.log" + echo " โ€ข Restart service: rc-service patchmon-agent restart" +else + echo " โ€ข Service logs: tail -f /etc/patchmon/logs/patchmon-agent.log" + echo " โ€ข Restart service: pkill -f 'patchmon-agent serve' && /usr/local/bin/patchmon-agent serve &" +fi echo "" success "โœ… Your system is now being monitored by PatchMon!"