#!/usr/bin/env bash # # AIS-catcher Installation Script # # Description: # Automated installation script for AIS-catcher on Debian and Ubuntu systems. # Supports both package-based and source-based installations. # # Usage: # sudo ./aiscatcher-install [OPTIONS] # # Options: # -p, --package Install from pre-built package (faster, recommended) # --no-systemd Skip systemd service installation (useful for containers) # -h, --help Display this help message # # Requirements: # - Must be run as root (use sudo) # - Debian-based system (Debian/Ubuntu/Raspbian) # - Internet connection for downloading packages/source # # Author: AIS-catcher Project # License: See LICENSE file # set -euo pipefail # Display usage information usage() { cat << EOF AIS-catcher Installation Script Usage: sudo $0 [OPTIONS] Options: -p, --package Install from pre-built package (faster, recommended) --no-systemd Skip systemd service installation (useful for containers) --no-user Skip user creation and run as root (useful for containers) -h, --help Display this help message Examples: sudo $0 --package # Install from pre-built package sudo $0 # Build and install from source sudo $0 --no-systemd --no-user # Docker/container installation For more information, visit: https://docs.aiscatcher.org/ EOF exit 0 } # Check if running as root if [ "$EUID" -ne 0 ]; then if [[ -t 1 ]]; then echo -e "\033[1;31mError: This script must be run as root\033[0m" >&2 echo -e "\033[1;33mPlease run: sudo $0 $*\033[0m" >&2 else echo "Error: This script must be run as root" >&2 echo "Please run: sudo $0 $*" >&2 fi exit 1 fi # Constants readonly SCRIPT_NAME=$(basename "$0") readonly LOG_FILE="/var/log/${SCRIPT_NAME%.*}.log" readonly TMP_DIR=$(mktemp -d -t AIS-catcher-XXXXXX) readonly CONFIG_DIR="/etc/AIS-catcher" readonly PLUGIN_DIR="${CONFIG_DIR}/plugins" readonly DBMS_DIR="${CONFIG_DIR}/DBMS" readonly README_DIR="${CONFIG_DIR}/README" readonly LIB_DIR="/usr/lib/ais-catcher" readonly CONFIG_FILE="${CONFIG_DIR}/config.json" readonly CMD_FILE="${CONFIG_DIR}/config.cmd" readonly SERVICE_NAME="ais-catcher.service" readonly SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}" # Global variables INSTALL_PACKAGE=false SKIP_SYSTEMD=false SKIP_USER=false SERVICE_STATE="" # Color constants (using ANSI escape codes) if [[ -t 1 ]]; then readonly COLOR_RESET="\033[0m" readonly COLOR_BOLD="\033[1m" readonly COLOR_RED="\033[0;31m" readonly COLOR_GREEN="\033[0;32m" readonly COLOR_YELLOW="\033[0;33m" readonly COLOR_BLUE="\033[0;34m" readonly COLOR_CYAN="\033[0;36m" readonly COLOR_BOLD_RED="\033[1;31m" readonly COLOR_BOLD_GREEN="\033[1;32m" readonly COLOR_BOLD_YELLOW="\033[1;33m" readonly COLOR_BOLD_BLUE="\033[1;34m" else readonly COLOR_RESET="" readonly COLOR_BOLD="" readonly COLOR_RED="" readonly COLOR_GREEN="" readonly COLOR_YELLOW="" readonly COLOR_BLUE="" readonly COLOR_CYAN="" readonly COLOR_BOLD_RED="" readonly COLOR_BOLD_GREEN="" readonly COLOR_BOLD_YELLOW="" readonly COLOR_BOLD_BLUE="" fi # Logger function with color support log() { local level="$1" shift local color="" local timestamp="$(date +'%Y-%m-%d %H:%M:%S')" case "$level" in ERROR) color="${COLOR_BOLD_RED}" ;; WARN) color="${COLOR_BOLD_YELLOW}" ;; INFO) color="${COLOR_BOLD_GREEN}" ;; *) color="${COLOR_RESET}" ;; esac # Print to terminal with color echo -e "${COLOR_CYAN}[${timestamp}]${COLOR_RESET} ${color}[${level}]${COLOR_RESET} $*" # Log to file without color codes echo "[${timestamp}] [${level}] $*" >> "$LOG_FILE" } # Error handler error_exit() { log "ERROR" "$1" # Try to restore service state before exiting if [[ -n "$SERVICE_STATE" ]]; then log "INFO" "Attempting to restore service state before exit" restore_service_state || log "WARN" "Failed to restore service state during error exit" fi exit 1 } # Cleanup function cleanup() { log "INFO" "Cleaning up temporary directory ${TMP_DIR}" rm -rf "$TMP_DIR" } ################################################################################ # UTILITY FUNCTIONS (Used by both package and source installations) ################################################################################ # Check for conflicting existing service check_existing_service() { if systemctl is-enabled aiscatcher.service &>/dev/null; then log "ERROR" "Detected existing aiscatcher.service. Please disable it first with:" log "ERROR" "systemctl disable aiscatcher.service" log "ERROR" "Then run this script again." exit 1 fi } # Detect system information (OS, version, architecture) get_system_info() { if [ -f /etc/os-release ]; then . /etc/os-release if [ "$ID" = "raspbian" ]; then OS="debian" else OS="$ID" fi VERSION="$VERSION_CODENAME" else OS=$(uname -s) VERSION=$(uname -r) fi ARCH=$(dpkg --print-architecture) echo "${OS}_${VERSION}_${ARCH}" } download_webassets_common() { local webassets_url="https://github.com/jvde-github/webassets/archive/refs/heads/main.zip" local temp_zip="/tmp/webassets.zip" log "INFO" "Downloading webassets" if curl -sL "$webassets_url" -o "$temp_zip"; then if [[ -d "$CONFIG_DIR/webassets" ]]; then log "INFO" "Removing old webassets" rm -rf "$CONFIG_DIR/webassets" fi log "INFO" "Extracting webassets" unzip -q "$temp_zip" -d "$CONFIG_DIR" mv "$CONFIG_DIR/webassets-main" "$CONFIG_DIR/webassets" rm "$temp_zip" log "INFO" "Webassets updated successfully" else log "ERROR" "Failed to download webassets" return 1 fi } ################################################################################ # SOURCE INSTALLATION FUNCTIONS ################################################################################ # Install build dependencies for source compilation install_source_build_dependencies() { local deps="git cmake build-essential pkg-config libzmq3-dev libssl-dev zlib1g-dev libpq-dev curl unzip libusb-1.0-0-dev libsqlite3-dev libairspy-dev libairspyhf-dev libhackrf-dev" log "INFO" "Installing build dependencies: $deps" apt-get install -y $deps || error_exit "Failed to install dependencies" } # Install librtlsdr from source install_librtlsdr_from_source() { log "INFO" "Installing librtlsdr from source" git clone https://gitea.osmocom.org/sdr/rtl-sdr --depth 1 || error_exit "Failed to clone rtl-sdr repository" cd rtl-sdr || error_exit "Failed to change directory to rtl-sdr" mkdir build && cd build || error_exit "Failed to create and enter build directory" cmake ../ -DCMAKE_INSTALL_PREFIX="${LIB_DIR}" -DDETACH_KERNEL_DRIVER=ON -DINSTALL_UDEV_RULES=ON || error_exit "Failed to run cmake" make || error_exit "Failed to build rtl-sdr" make install || error_exit "Failed to install rtl-sdr" ldconfig || error_exit "Failed to run ldconfig" cd ../.. || error_exit "Failed to return to parent directory" } # Install libhydrasdr from source install_libhydrasdr_from_source() { log "INFO" "Installing libhydrasdr from source" git clone https://github.com/hydrasdr/rfone_host.git --depth 1 || error_exit "Failed to clone rfone_host repository" cd rfone_host/libhydrasdr || error_exit "Failed to change directory to rfone_host/libhydrasdr" mkdir build && cd build || error_exit "Failed to create and enter build directory" cmake .. -DCMAKE_INSTALL_PREFIX="${LIB_DIR}" -DINSTALL_UDEV_RULES=ON || error_exit "Failed to run cmake" make || error_exit "Failed to build libhydrasdr" make install || error_exit "Failed to install libhydrasdr" ldconfig || error_exit "Failed to run ldconfig" cd ../../.. || error_exit "Failed to return to parent directory" } # Install NMEA2000 library from source install_nmea2000_from_source() { log "INFO" "Building NMEA2000 library from source" git clone https://github.com/jvde-github/NMEA2000.git --depth 1 || error_exit "Failed to clone NMEA2000 repository" cd NMEA2000/src || error_exit "Failed to change directory to NMEA2000/src" # Build as static library log "INFO" "Building NMEA2000 as static library" g++ -O3 -c N2kMsg.cpp N2kStream.cpp N2kMessages.cpp N2kTimer.cpp NMEA2000.cpp N2kGroupFunctionDefaultHandlers.cpp N2kGroupFunction.cpp -I. || error_exit "Failed to compile NMEA2000" ar rcs libnmea2000.a *.o || error_exit "Failed to create NMEA2000 static library" cd ../.. || error_exit "Failed to return to parent directory" } # Install SDR libraries from source (only for source build) install_sdr_libraries_from_source() { # Create library directory mkdir -p "${LIB_DIR}" || error_exit "Failed to create library directory" # Install rtlsdr and hydrasdr from source (need custom cmake flags for udev rules) install_librtlsdr_from_source install_libhydrasdr_from_source # Install NMEA2000 library install_nmea2000_from_source } # Build AIS-catcher from source build_ais_catcher_from_source() { log "INFO" "Building AIS-catcher" git clone https://github.com/jvde-github/AIS-catcher.git --depth 1 || error_exit "Failed to download AIS-catcher" cd AIS-catcher # Set environment variables to find custom libraries export PKG_CONFIG_PATH="${LIB_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH:-}" export CMAKE_PREFIX_PATH="${LIB_DIR}:${CMAKE_PREFIX_PATH:-}" # Build with rpath to custom library directory mkdir -p build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_RPATH="${LIB_DIR}/lib;${LIB_DIR}" \ -DCMAKE_BUILD_RPATH="${LIB_DIR}/lib;${LIB_DIR}" \ -DNMEA2000_INCLUDE="${TMP_DIR}/NMEA2000/src" \ -DNMEA2000_LIB="${TMP_DIR}/NMEA2000/src/libnmea2000.a" || error_exit "Failed to run cmake" make || error_exit "Failed to build AIS-catcher" cd ../.. cp AIS-catcher/build/AIS-catcher /usr/bin || error_exit "Failed to copy AIS-catcher executable" } # Install additional files (plugins, DBMS, README, LICENSE) from source install_source_additional_files() { log "INFO" "Installing plugins" mkdir -p "$PLUGIN_DIR" || error_exit "Failed to create plugin directory" cp AIS-catcher/plugins/* "$PLUGIN_DIR" || error_exit "Failed to copy plugins" log "INFO" "Installing DBMS" mkdir -p "$DBMS_DIR" || error_exit "Failed to create DBMS directory" cp AIS-catcher/Source/DBMS/create.sql "$DBMS_DIR" || error_exit "Failed to copy DBMS" log "INFO" "Installing README" mkdir -p "$README_DIR" || error_exit "Failed to create README directory" cp AIS-catcher/README.md "$README_DIR" || error_exit "Failed to copy README" log "INFO" "Copying LICENSE" cp AIS-catcher/LICENSE /etc/AIS-catcher/LICENSE || error_exit "Failed to copy LICENSE" } ################################################################################ # PACKAGE INSTALLATION FUNCTIONS ################################################################################ # Check if a pre-built package is available for this system # Args: # $1: system_info string (format: os_version_arch) # Returns: # 0 if package found (prints package name), 1 otherwise check_package_availability() { local system_info=$1 local packages=( "ais-catcher_debian_bookworm_amd64.deb" "ais-catcher_debian_bookworm_arm64.deb" "ais-catcher_debian_bookworm_armhf.deb" "ais-catcher_debian_bullseye_amd64.deb" "ais-catcher_debian_bullseye_arm64.deb" "ais-catcher_debian_bullseye_armhf.deb" "ais-catcher_debian_trixie_amd64.deb" "ais-catcher_debian_trixie_arm64.deb" "ais-catcher_debian_trixie_armhf.deb" "ais-catcher_ubuntu_focal_amd64.deb" "ais-catcher_ubuntu_jammy_amd64.deb" "ais-catcher_ubuntu_noble_amd64.deb" "ais-catcher_ubuntu_plucky_amd64.deb" "ais-catcher_ubuntu_questing_amd64.deb" "ais-catcher_ubuntu_resolute_amd64.deb" ) for package in "${packages[@]}"; do if [[ $package == *"$system_info"* ]]; then echo "$package" return 0 fi done return 1 } # Install AIS-catcher from pre-built Debian package install_from_package() { local package=$1 local download_url="https://github.com/jvde-github/AIS-catcher/releases/download/Edge/$package" log "INFO" "Installing AIS-catcher from package: $package" # Create a temporary directory with appropriate permissions local temp_dir=$(mktemp -d) chmod 755 "$temp_dir" # Download the package log "INFO" "Downloading package..." if ! curl -sL -o "$temp_dir/$(basename "$download_url")" "$download_url"; then rm -rf "$temp_dir" error_exit "Failed to download package" fi # Ensure the downloaded file has correct permissions chmod 644 "$temp_dir/$package" # Install the package and dependencies using apt with proper flags log "INFO" "Installing package and dependencies..." if ! apt-get update; then rm -rf "$temp_dir" error_exit "Failed to update package lists" fi # Use --allow-downgrades to handle version conflicts and better error handling local install_success=false if apt-get install -y --allow-downgrades "$temp_dir/$package" 2>/dev/null; then install_success=true log "INFO" "Package installed successfully" else log "WARN" "Standard package installation failed, trying with additional flags..." if apt-get install -y --allow-downgrades --allow-change-held-packages "$temp_dir/$package"; then install_success=true log "INFO" "Package installed successfully with additional flags" fi fi # Clean up temporary directory rm -rf "$temp_dir" || log "WARN" "Failed to remove temporary directory: $temp_dir" if [[ "$install_success" != true ]]; then error_exit "Failed to install package after all attempts" fi log "INFO" "AIS-catcher package installation completed" } ################################################################################ # CONFIGURATION FUNCTIONS (Common to both installation methods) ################################################################################ # Create and configure aiscatcher system user setup_aiscatcher_user() { if [ "$SKIP_USER" = true ]; then log "INFO" "Skipping user creation (--no-user flag specified), will run as root" return 0 fi log "INFO" "Setting up aiscatcher system user" # Create system user if it doesn't exist if ! id -u aiscatcher &>/dev/null; then log "INFO" "Creating aiscatcher system user" useradd --system --no-create-home --shell /usr/sbin/nologin aiscatcher || error_exit "Failed to create aiscatcher user" else log "INFO" "User aiscatcher already exists" fi # Add user to necessary groups for SDR device access for group in plugdev dialout; do if getent group "$group" &>/dev/null; then if ! id -nG aiscatcher | grep -qw "$group"; then log "INFO" "Adding aiscatcher to $group group" usermod -a -G "$group" aiscatcher || log "WARN" "Failed to add aiscatcher to $group group" fi else log "WARN" "Group $group does not exist, skipping" fi done } # Setup configuration setup_configuration() { log "INFO" "Setting up configuration files" mkdir -p "$CONFIG_DIR" || error_exit "Failed to create config directory" download_webassets_common || error_exit "Failed to download webassets" if [[ ! -f "$CONFIG_FILE" ]]; then echo '{"config":"aiscatcher","version":1,"screen":0,"verbose":true,"verbose_time":60,"sharing":true,"udp":[],"tcp":[],"server":{"active":false,"plugin_dir":"/etc/AIS-catcher/plugins","cdn":"/etc/AIS-catcher/webassets","file":"/etc/AIS-catcher/stat.bin","realtime":false,"geojson":false,"prome":false,"port":"8100","backup":"10","context":"settings"},"rtlsdr":{"tuner":"auto","bandwidth":"192K","sample_rate":"1536K","biastee":false,"rtlagc":true},"airspyhf":{"threshold":"low","preamp":false},"rtltcp":{"protocol":"none","sample_rate":"288K","bandwidth":"0","rtlagc":false}}' | tee "$CONFIG_FILE" > /dev/null || error_exit "Failed to create config.json" fi if [[ ! -f "$CMD_FILE" ]]; then cat << EOF | tee "$CMD_FILE" > /dev/null || error_exit "Failed to create config.cmd" # AIS-catcher configuration EOF fi } ################################################################################ # SYSTEMD SERVICE FUNCTIONS (Common to both installation methods) ################################################################################ # Setup systemd service setup_systemd_service() { log "INFO" "Setting up systemd service" # Build service file content conditionally based on SKIP_USER flag local service_content="[Unit] Description=AIS-catcher Service After=network.target [Service]" # Only add User/Group directives if not skipping user creation if [ "$SKIP_USER" = false ]; then service_content+=" User=aiscatcher Group=aiscatcher SupplementaryGroups=plugdev dialout" fi service_content+=" ExecStart=/bin/bash -c '/usr/bin/AIS-catcher -G system on -o 0 -C ${CONFIG_FILE} \$(/bin/grep -v \"^#\" ${CMD_FILE} | /bin/grep -v \"^[[:space:]]*\$\" | tr -d \"\\r\" | /usr/bin/tr \"\\n\" \" \")' Restart=always RestartSec=10 StartLimitBurst=0 [Install] WantedBy=multi-user.target" echo "$service_content" | tee "$SERVICE_FILE" > /dev/null || error_exit "Failed to create service file" systemctl daemon-reload || error_exit "Failed to reload systemd" } # Function to stop the service and save its state stop_disable_and_save_service_state() { log "INFO" "Checking AIS-catcher service state, stopping if active, and disabling" local was_active=false local was_enabled=false # Check if the service is active if systemctl is-active --quiet "$SERVICE_NAME"; then was_active=true log "INFO" "$SERVICE_NAME is active, attempting to stop" if systemctl stop "$SERVICE_NAME"; then log "INFO" "$SERVICE_NAME stopped successfully" else log "WARN" "Failed to stop $SERVICE_NAME" fi else log "INFO" "$SERVICE_NAME is not active, no need to stop" fi # Check if the service is enabled if systemctl is-enabled --quiet "$SERVICE_NAME"; then was_enabled=true log "INFO" "$SERVICE_NAME is enabled, attempting to disable" if systemctl disable "$SERVICE_NAME"; then log "INFO" "$SERVICE_NAME disabled successfully" else log "WARN" "Failed to disable $SERVICE_NAME" fi else log "INFO" "$SERVICE_NAME is not enabled, no need to disable" fi # Save the state to the global variable SERVICE_STATE="${was_active},${was_enabled}" log "INFO" "Service state saved: $SERVICE_STATE" } # FIXED: Improved service state restoration with better error handling restore_service_state() { if [[ -z "$SERVICE_STATE" ]]; then log "INFO" "No service state to restore" return 0 fi log "INFO" "Restoring AIS-catcher service state" IFS=',' read -ra STATE_ARRAY <<< "$SERVICE_STATE" local was_active=${STATE_ARRAY[0]:-false} local was_enabled=${STATE_ARRAY[1]:-false} # Enable first if it was enabled if [[ "$was_enabled" == true ]]; then if systemctl enable "$SERVICE_NAME"; then log "INFO" "$SERVICE_NAME enabled successfully" else log "ERROR" "Failed to enable $SERVICE_NAME" return 1 fi fi # Then start if it was active if [[ "$was_active" == true ]]; then if systemctl start "$SERVICE_NAME"; then log "INFO" "$SERVICE_NAME started successfully" # Give service a moment to start and check status sleep 2 if systemctl is-active --quiet "$SERVICE_NAME"; then log "INFO" "$SERVICE_NAME is running properly" else log "WARN" "$SERVICE_NAME may have failed to start properly" systemctl status "$SERVICE_NAME" --no-pager || true fi else log "ERROR" "Failed to start $SERVICE_NAME" return 1 fi fi # Clear the global state after restore SERVICE_STATE="" log "INFO" "Service state restored and cleared" return 0 } ################################################################################ # INSTALLATION METHOD FUNCTIONS ################################################################################ # Parse command-line arguments parse_arguments() { while [[ $# -gt 0 ]]; do case "$1" in -p|--package) log "INFO" "Package installation requested" INSTALL_PACKAGE=true shift ;; --no-systemd) log "INFO" "Skipping systemd service installation" SKIP_SYSTEMD=true shift ;; --no-user) log "INFO" "Skipping user creation, will run as root" SKIP_USER=true shift ;; -h|--help) usage ;; *) log "WARN" "Unknown option: $1" log "INFO" "Use --help for usage information" shift ;; esac done } # Install from pre-built package install_from_package_method() { local system_info=$(get_system_info) log "INFO" "Detected system: $system_info" local package=$(check_package_availability "$system_info") if [ -z "$package" ]; then error_exit "Package installation requested but no package available for $system_info. Consider building from source: see https://docs.aiscatcher.org/installation/ubuntu-debian/#install-from-source" fi log "INFO" "Package available: $package" # Install minimal dependencies for package installation and webassets download log "INFO" "Installing minimal dependencies for package installation" apt-get install -y curl unzip || error_exit "Failed to install minimal dependencies" install_from_package "$package" log "INFO" "AIS-catcher installation from package completed successfully" } # Install from source install_from_source_method() { log "INFO" "AIS-catcher installation from source" cd "$TMP_DIR" || error_exit "Failed to change to temporary directory" install_source_build_dependencies install_sdr_libraries_from_source build_ais_catcher_from_source install_source_additional_files log "INFO" "AIS-catcher source installation completed successfully" } # Install binary (delegates to package or source method) install_binary() { if [ "$INSTALL_PACKAGE" = true ]; then install_from_package_method else install_from_source_method fi } # Configure installation (user, config, permissions) configure_installation() { setup_aiscatcher_user setup_configuration # Ensure correct ownership after config files are created (skip if running as root) if [ "$SKIP_USER" = false ]; then log "INFO" "Ensuring correct ownership of configuration directory" chown -R aiscatcher:aiscatcher "$CONFIG_DIR" || log "WARN" "Failed to set ownership of config directory" fi } # Main function main() { # Phase 1: Pre-flight checks and setup log "INFO" "Starting AIS-catcher installation script" check_existing_service trap cleanup EXIT # Phase 2: Parse arguments parse_arguments "$@" # Phase 3: System preparation apt-get update || error_exit "Failed to update package lists" # Phase 4: Stop existing service if needed if [ "$SKIP_SYSTEMD" = false ]; then stop_disable_and_save_service_state fi # Phase 5: Install binary (package or source) install_binary # Phase 6: Post-install configuration configure_installation # Phase 7: Setup and start service if [ "$SKIP_SYSTEMD" = false ]; then setup_systemd_service if ! restore_service_state; then log "ERROR" "Failed to restore service state" exit 1 fi else log "INFO" "Skipping systemd service installation (--no-systemd flag specified)" fi # Phase 8: Installation complete log "INFO" "AIS-catcher installation completed successfully" display_installation_summary } # Display installation summary with useful information display_installation_summary() { local divider="═══════════════════════════════════════════════════════════════════════════════" echo -e "\n${COLOR_BOLD_BLUE}${divider}${COLOR_RESET}" echo -e "${COLOR_BOLD_GREEN} AIS-CATCHER INSTALLATION SUMMARY${COLOR_RESET}" echo -e "${COLOR_BOLD_BLUE}${divider}${COLOR_RESET}\n" echo -e "${COLOR_BOLD_GREEN}✓ Installation completed successfully!${COLOR_RESET}\n" echo -e "${COLOR_BOLD}INSTALLED FILES:${COLOR_RESET}" echo " • Executable: /usr/bin/AIS-catcher" echo " • Configuration: ${CONFIG_DIR}/" echo " - Main config: ${CONFIG_FILE}" echo " - Command options: ${CMD_FILE}" echo " - Plugins: ${PLUGIN_DIR}/" echo " - Web assets: ${CONFIG_DIR}/webassets/" echo " - Database schema: ${DBMS_DIR}/create.sql" echo " - Documentation: ${README_DIR}/README.md" echo " - License: ${CONFIG_DIR}/LICENSE" echo " • Service file: ${SERVICE_FILE}" echo " • Log file: ${LOG_FILE}" echo "" if [ "$SKIP_SYSTEMD" = false ]; then echo -e "${COLOR_BOLD}SYSTEMD SERVICE MANAGEMENT:${COLOR_RESET}" echo " • View service status:" echo -e " ${COLOR_YELLOW}systemctl status ${SERVICE_NAME}${COLOR_RESET}" echo "" echo " • Start the service:" echo -e " ${COLOR_YELLOW}systemctl start ${SERVICE_NAME}${COLOR_RESET}" echo "" echo " • Stop the service:" echo -e " ${COLOR_YELLOW}systemctl stop ${SERVICE_NAME}${COLOR_RESET}" echo "" echo " • Restart the service:" echo -e " ${COLOR_YELLOW}systemctl restart ${SERVICE_NAME}${COLOR_RESET}" echo "" echo " • Enable at boot:" echo -e " ${COLOR_YELLOW}systemctl enable ${SERVICE_NAME}${COLOR_RESET}" echo "" echo " • Disable at boot:" echo -e " ${COLOR_YELLOW}systemctl disable ${SERVICE_NAME}${COLOR_RESET}" echo "" echo " • View service logs:" echo -e " ${COLOR_YELLOW}journalctl -u ${SERVICE_NAME} -f${COLOR_RESET}" echo "" fi echo -e "${COLOR_BOLD}CONFIGURATION:${COLOR_RESET}" echo " • Edit main configuration:" echo -e " ${COLOR_YELLOW}nano ${CONFIG_FILE}${COLOR_RESET}" echo "" echo " • Edit command-line options:" echo -e " ${COLOR_YELLOW}nano ${CMD_FILE}${COLOR_RESET}" echo "" echo " • After editing, restart the service:" echo -e " ${COLOR_YELLOW}systemctl restart ${SERVICE_NAME}${COLOR_RESET}" echo "" echo -e "${COLOR_BOLD}WEB INTERFACE:${COLOR_RESET}" echo " • Once running, access the web interface at:" echo -e " ${COLOR_CYAN}http://localhost:8100${COLOR_RESET}" echo " (or use your server's IP address)" echo "" echo " • Enable the web server by editing ${CONFIG_FILE}" echo -e " and setting ${COLOR_YELLOW}\"active\": true${COLOR_RESET} in the \"server\" section" echo "" echo -e "${COLOR_BOLD}USEFUL COMMANDS:${COLOR_RESET}" echo " • Run AIS-catcher directly (for testing):" echo -e " ${COLOR_YELLOW}AIS-catcher -h${COLOR_RESET}" echo "" echo " • View this installation log:" echo -e " ${COLOR_YELLOW}cat ${LOG_FILE}${COLOR_RESET}" echo "" echo -e "${COLOR_BOLD}DOCUMENTATION:${COLOR_RESET}" echo -e " • Online docs: ${COLOR_CYAN}https://docs.aiscatcher.org/${COLOR_RESET}" echo -e " • GitHub repo: ${COLOR_CYAN}https://github.com/jvde-github/AIS-catcher${COLOR_RESET}" echo " • Local README: ${README_DIR}/README.md" echo "" echo -e "${COLOR_BOLD_BLUE}${divider}${COLOR_RESET}\n" } # Run the script main "$@"