From e4a7b96d726ce59ede67a324267937dc5569b4d9 Mon Sep 17 00:00:00 2001 From: Isak-JO Date: Sun, 20 Sep 2026 11:22:28 +0200 Subject: [PATCH 1/6] Added the -L argument for legacy installation. Implementing the -L (--legacy) argument to the install-script. This option utilizes source code from a previous commit when building from source. The -L argument must be followed by a commit ID (SHA/hash given to each commit by GitHub) that allows users to define from what occasion in history the source code should be cloned. --- scripts/aiscatcher-install | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/scripts/aiscatcher-install b/scripts/aiscatcher-install index 54c5cd2ff9..eb627f0577 100755 --- a/scripts/aiscatcher-install +++ b/scripts/aiscatcher-install @@ -38,6 +38,7 @@ Options: -p, --package Install from pre-built package (faster, recommended) -i, --interactive Source build with interactive feature selection (forces source path) -b, --branch Specify git branch to clone (default: main) + -L, --legacy Use older source code dated by a given commit (the argument must be followed by a commit ID to determine the timestamp) -M, --managed Run the service in managed mode (-E ${MANAGED_CONFIG_FILE} ${MANAGED_BIND}) --no-systemd Skip systemd service installation (useful for containers) --no-user Skip user creation and run as root (useful for containers) @@ -122,6 +123,8 @@ SET_AUTO_RESTART="" INSTALL_REQUESTED=false TMP_DIR="" DEB_TMP_DIR="" +OPTION_LEGACY=OFF +COMMIT_ID="" # User-facing CMake feature toggles (defaults mirror CMakeLists.txt option() lines). # Infrastructure libs (SOXR, SAMPLERATE, ZLIB, OPENSSL) and link-style toggles @@ -566,8 +569,18 @@ install_sdr_libraries_from_source() { build_ais_catcher_from_source() { log "INFO" "Building AIS-catcher from branch: ${BRANCH}" - git clone https://github.com/jvde-github/AIS-catcher.git --depth 1 --branch "${BRANCH}" || error_exit "Failed to download AIS-catcher (branch: ${BRANCH})" - cd AIS-catcher + if [[ ${OPTION_LEGACY} == ON ]]; then + # Selects the source code version detemined by the time of a choosen commit. + log "INFO" "Cloning AIS-catcher repository dated at commit ID: ${COMMIT_ID}" + git clone https://github.com/jvde-github/AIS-catcher.git --branch "${BRANCH}" || error_exit "Failed to download AIS-catcher (branch: ${BRANCH})" + cd AIS-catcher + git checkout "${COMMIT_ID}" || error_exit "Failed to set git version. The given commit ID ("${COMMIT_ID}") was not found." + else + # Defaults to clone the latest commit from "EDGE" realese. + log "INFO" "Cloning AIS-catcher reposertory of the latest realese ("EDGE")" + git clone https://github.com/jvde-github/AIS-catcher.git --depth 1 --branch "${BRANCH}" || error_exit "Failed to download AIS-catcher (branch: ${BRANCH})" + cd AIS-catcher + fi # Set environment variables to find custom libraries export PKG_CONFIG_PATH="${LIB_DIR}/lib/pkgconfig:${PKG_CONFIG_PATH:-}" @@ -1103,6 +1116,17 @@ parse_arguments() { error_exit "Option -b/--branch requires a branch name argument" fi ;; + -L|--legacy) + if [[ -n "${2:-}" && "${2:0:1}" != "-" ]]; then + OPTION_LEGACY=ON + COMMIT_ID="$2" + INSTALL_REQUESTED=true + log "INFO" "Legacy installation requested, building with source code by the time of commit: ${COMMIT_ID}" + shift 2 + else + error_exit "Option -L/--legacy requires a commit ID argument to select a version (-L must be followed by the SHA/hash identifier for a chosen commit)" + fi + ;; -M|--managed) log "INFO" "Managed mode service requested (-E ${MANAGED_CONFIG_FILE} ${MANAGED_BIND})" MANAGED=true @@ -1163,6 +1187,11 @@ parse_arguments() { log "WARN" "--branch is ignored when --package is used (pre-built packages are always Edge)" fi + # Warn if --legacy is ignored because --package was also specified + if [[ "$INSTALL_PACKAGE" == true && "$OPTION_LEGACY" != OFF ]]; then + log "WARN" "--legacy is ignored when --package is specified (Legacy installation of a given commit ID requires building from source)" + fi + # --interactive only makes sense for source builds; the .deb is pre-baked. if [[ "$INTERACTIVE" == true && "$INSTALL_PACKAGE" == true ]]; then log "WARN" "--interactive overrides --package (pre-built packages can't be customised)" From e7fdca0abea87d189f7b2e4db107d15c69f146ce Mon Sep 17 00:00:00 2001 From: Isak-JO Date: Sun, 20 Sep 2026 12:29:44 +0200 Subject: [PATCH 2/6] Adding the -M and -L arguments as options in the interactive feature selection. -M (Managed Mode) and -L (Legacy) may now be configured either as arguments to the script or by toggling the options with YES/NO answers in the interactive feature selection prompts. --- scripts/aiscatcher-install | 69 +++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/scripts/aiscatcher-install b/scripts/aiscatcher-install index eb627f0577..ae9196a26a 100755 --- a/scripts/aiscatcher-install +++ b/scripts/aiscatcher-install @@ -112,7 +112,6 @@ INSTALL_PACKAGE=false INTERACTIVE=false SKIP_SYSTEMD=false SKIP_USER=false -MANAGED=false BRANCH="main" SERVICE_STATE="" SERVICE_START_FAILED=false @@ -123,9 +122,15 @@ SET_AUTO_RESTART="" INSTALL_REQUESTED=false TMP_DIR="" DEB_TMP_DIR="" + +# User-facing installation option toggles. +OPTION_MANAGED=OFF OPTION_LEGACY=OFF COMMIT_ID="" +# Options that may be toggled in the interactive feature selection (does not affect CMakelists.txt). +readonly OPTIONS=(MANAGED LEGACY) + # User-facing CMake feature toggles (defaults mirror CMakeLists.txt option() lines). # Infrastructure libs (SOXR, SAMPLERATE, ZLIB, OPENSSL) and link-style toggles # (RTLSDR_STATIC, HYDRASDR_STATIC, ARMV6, RBIT) are intentionally not exposed @@ -143,6 +148,7 @@ FEATURE_PSQL=ON FEATURE_SQLITE=ON FEATURE_WEBVIEWER=ON +# Features that may be added to CMakelists.txt readonly FEATURES=(RTLSDR AIRSPY AIRSPYHF HACKRF HYDRASDR SDRPLAY SOAPYSDR ZMQ NMEA2000 PSQL SQLITE WEBVIEWER) # Color constants (using ANSI escape codes) @@ -239,8 +245,27 @@ prompt_yes_no() { done } +# Prompt for an answer specifying the commit ID with a default; echoes COMMIT_ID. +# Args: $1 label, $2 default (if COMMIT_ID was given as an argument) +prompt_commit_id() { + local label="$1" default="$2" + local suffix + suffix="["$default"]" + + while true; do + local answer + read -r -p "$(printf "${COLOR_BOLD}%s${COLOR_RESET} %s: " "$label" "$suffix")" answer + # shellcheck disable=SC2155 # locale lowercasing needs ${var,,} + local lower="${answer,,}" + case "${lower:-${default,,}}" in + "") echo "Commit ID must not be empty." >&2 ;; + *) echo "${lower:-${default,,}}"; return 0 ;; + esac + done +} + # Walk the user through per-feature toggles. -# Sets the FEATURE_* globals from current defaults, and aborts if the user +# Sets the FEATURE_* and OPTIONS_* globals from current defaults, and aborts if the user # rejects the summary. Requires a TTY. interactive_feature_select() { [[ -t 0 ]] || error_exit "Interactive mode requires a TTY (stdin is not a terminal)" @@ -267,8 +292,16 @@ interactive_feature_select() { FEATURE_SQLITE=$(prompt_yes_no " SQLite (offline map tiles and -D output)" "$FEATURE_SQLITE") echo - echo -e "${COLOR_BOLD}Other${COLOR_RESET}" + echo -e "${COLOR_BOLD}User interfaces${COLOR_RESET}" FEATURE_WEBVIEWER=$(prompt_yes_no " Embedded web UI (-N)" "$FEATURE_WEBVIEWER") + OPTION_MANAGED=$(prompt_yes_no " Managed Mode" "$OPTION_MANAGED") + + echo + echo -e "${COLOR_BOLD}Source-code version${COLOR_RESET}" + OPTION_LEGACY=$(prompt_yes_no " Build AIS-catcher from legacy source-code" "$OPTION_LEGACY") + if [[ "${OPTION_LEGACY}" == ON ]]; then + COMMIT_ID=$(prompt_commit_id " Commit ID (SHA from a github commit)" "$COMMIT_ID") + fi echo echo -e "${COLOR_BOLD_BLUE}Selected features:${COLOR_RESET}" @@ -277,6 +310,16 @@ interactive_feature_select() { vname="FEATURE_$f" printf " %-12s %s\n" "$f" "${!vname}" done + + for f in "${OPTIONS[@]}"; do + vname="OPTION_$f" + printf " %-12s %s\n" "$f" "${!vname}" + done + + if [[ "${OPTION_LEGACY}" == ON ]]; then + printf " %-12s %s\n" "COMMIT ID:" "${COMMIT_ID}" + fi + echo local confirm read -r -p "Proceed with these settings? [Y/n]: " confirm @@ -813,7 +856,7 @@ setup_aiscatcher_user() { setup_configuration() { log "INFO" "Setting up configuration files" mkdir -p "$CONFIG_DIR" || error_exit "Failed to create config directory" - if [[ "$MANAGED" == true ]]; then + if [[ "$OPTION_MANAGED" == ON ]]; then mkdir -p "$CONFIG_DIR/places" || error_exit "Failed to create places directory" chmod 750 "$CONFIG_DIR/places" fi @@ -822,7 +865,7 @@ setup_configuration() { # managed config with its path so the setup wizard can offer to import # the configured output channels. Must run before the legacy default # config is (re)created below, so a fresh install does not qualify. - if [[ "$MANAGED" == true && ! -f "$MANAGED_CONFIG_FILE" && -f "$CONFIG_FILE" ]]; then + if [[ "$OPTION_MANAGED" == ON && ! -f "$MANAGED_CONFIG_FILE" && -f "$CONFIG_FILE" ]]; then log "INFO" "Existing config.json found; the setup wizard will offer to import its outputs" cat << EOF | tee "$MANAGED_CONFIG_FILE" > /dev/null || error_exit "Failed to create aiscatcher.json" { @@ -956,7 +999,7 @@ SupplementaryGroups=plugdev dialout" fi local exec_start="/usr/bin/AIS-catcher -G level debug -o 0 -C ${CONFIG_FILE} @${CMD_FILE}" - if [ "$MANAGED" = true ]; then + if [ "$OPTION_MANAGED" = ON ]; then exec_start="/usr/bin/AIS-catcher -E ${MANAGED_CONFIG_FILE} ${MANAGED_BIND}" fi @@ -1032,7 +1075,7 @@ verify_service_active() { log "ERROR" "$SERVICE_NAME did not stay running after start" systemctl status "$SERVICE_NAME" --no-pager -l 2>/dev/null | tail -n 15 || true journalctl -u "$SERVICE_NAME" -n 20 --no-pager 2>/dev/null || true - if [[ "$MANAGED" == true ]]; then + if [[ "$OPTION_MANAGED" == ON ]]; then local ctrl_port="${MANAGED_BIND##*:}" log "ERROR" "Managed mode could not start — control port ${ctrl_port} or viewer port $((ctrl_port + 1)) may already be in use" log "ERROR" "Free the port(s), then run: systemctl restart ${SERVICE_NAME}" @@ -1071,7 +1114,7 @@ restore_service_state() { log "INFO" "$SERVICE_NAME start command issued" # Only verify the managed default-start (the script's own decision); # a plain restore of the user's prior state is taken at face value. - if [[ "$MANAGED" == true ]]; then + if [[ "$OPTION_MANAGED" == ON ]]; then verify_service_active || SERVICE_START_FAILED=true fi else @@ -1129,7 +1172,7 @@ parse_arguments() { ;; -M|--managed) log "INFO" "Managed mode service requested (-E ${MANAGED_CONFIG_FILE} ${MANAGED_BIND})" - MANAGED=true + OPTION_MANAGED=ON INSTALL_REQUESTED=true shift ;; @@ -1262,7 +1305,7 @@ display_installation_summary() { if [ "$SERVICE_START_FAILED" = true ]; then echo "" echo -e " ${COLOR_YELLOW}⚠ The service was started but did not stay running.${COLOR_RESET}" - if [ "$MANAGED" = true ]; then + if [ "$OPTION_MANAGED" = ON ]; then local status_port="${MANAGED_BIND##*:}" echo -e " Control port ${status_port} or viewer port $((status_port + 1)) may already be in use." fi @@ -1338,7 +1381,7 @@ display_installation_summary() { fi echo -e "${COLOR_BOLD}CONFIGURATION:${COLOR_RESET}" - if [ "$MANAGED" = true ]; then + if [ "$OPTION_MANAGED" = ON ]; then echo " • Managed mode: configure everything from the hub, not by hand" echo -e " config file: ${COLOR_YELLOW}${MANAGED_CONFIG_FILE}${COLOR_RESET}" echo "" @@ -1356,7 +1399,7 @@ display_installation_summary() { echo "" echo -e "${COLOR_BOLD}WEB INTERFACE:${COLOR_RESET}" - if [ "$MANAGED" = true ]; then + if [ "$OPTION_MANAGED" = ON ]; then local ctrl_port="${MANAGED_BIND##*:}" echo " • Control hub (configuration and control):" echo -e " ${COLOR_CYAN}http://localhost:${ctrl_port}${COLOR_RESET}" @@ -1454,7 +1497,7 @@ main() { setup_systemd_service # Managed installs (-M) are enabled and started by default, regardless of prior state. - if [ "$MANAGED" = true ]; then + if [ "$OPTION_MANAGED" = ON ]; then log "INFO" "Managed mode: enabling and starting ${SERVICE_NAME} by default" SERVICE_STATE="true,true" fi From 57fb2b15d6aa1d6d8eb1dc7cac6a75012875482c Mon Sep 17 00:00:00 2001 From: Isak-JO Date: Sun, 20 Sep 2026 14:15:34 +0200 Subject: [PATCH 3/6] Ensures that Managed Mode calls for webviewer to be enabled. Validates user inputs to avoid installing AIS-catcher in managed mode if the the necessary vebviewer feature is disabled. --- scripts/aiscatcher-install | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/aiscatcher-install b/scripts/aiscatcher-install index ae9196a26a..c5f9658cc8 100755 --- a/scripts/aiscatcher-install +++ b/scripts/aiscatcher-install @@ -304,6 +304,13 @@ interactive_feature_select() { fi echo + # MANAGED must override WEBVIEWER if the necessary feature is disabled. + if [[ "${OPTION_MANAGED}" == ON && "${FEATURE_WEBVIEWER}" != ON ]]; then + FEATURE_WEBVIEWER=ON + log "INFO" "Managed Mode requires Embedded web UI. The feature WEBVIEWER was enabled automatically." + fi + + echo -e "${COLOR_BOLD_BLUE}Selected features:${COLOR_RESET}" local f vname for f in "${FEATURES[@]}"; do From a424d7ee46d1b750f0ce3fb491e906a9ff6a788c Mon Sep 17 00:00:00 2001 From: Isak-JO Date: Sun, 20 Sep 2026 19:05:39 +0200 Subject: [PATCH 4/6] bug fix when switching between source-build and package Adressing incompability that may occur when alternating between a pre-packaged and source-built installations. Removing a potential ais-catcher package after installing a source build ensures apt install does not have any ais-catcher package when the installation has been replaced by an source-build (-which may be either a newer or older version) and thereby ensures furure package installation is not disrupted by seeing a preexisting package. --- scripts/aiscatcher-install | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/aiscatcher-install b/scripts/aiscatcher-install index c5f9658cc8..c8f4428601 100755 --- a/scripts/aiscatcher-install +++ b/scripts/aiscatcher-install @@ -665,6 +665,10 @@ build_ais_catcher_from_source() { cd ../.. install -m 755 AIS-catcher/build/AIS-catcher /usr/bin/AIS-catcher || error_exit "Failed to install AIS-catcher executable" + + # If a package installation has been present, it should be removed after building from source. + # This ensures compatiblity when switching between pre-packaged installation and source builds. Otherwise apt install may be confused why the ais-catcher package exist but has been replaced by an installation which is built from source (-which may be either a newer or older version). + apt remove -y ais-catcher || error_exit "Failed to remove preexisting AIS-catcher package" } # Install additional files (plugins, DBMS, README, LICENSE) from source From d7f14ffe800e8e5d7d081316efaa945b5c9b4b9d Mon Sep 17 00:00:00 2001 From: Isak-JO Date: Mon, 21 Sep 2026 21:27:02 +0200 Subject: [PATCH 5/6] Moved the "apt remove" to before installation. Oversight in the previous commit caused new build to be uninstalled (apt remove) immediatly after installation. The script now removes previous a package installation before installing a new, To ensure compatiblity when switching between pre-packaged installation and source builds. (+ updated the path in makefile, this is an issue only if the user has builds from souce manually with the makefile) --- Makefile | 2 +- scripts/aiscatcher-install | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 3a360db7d3..f73e886d8d 100644 --- a/Makefile +++ b/Makefile @@ -139,4 +139,4 @@ clean: rm -f AIS-catcher install: - cp AIS-catcher /usr/local/bin/AIS-catcher + cp AIS-catcher /usr/bin/AIS-catcher diff --git a/scripts/aiscatcher-install b/scripts/aiscatcher-install index c8f4428601..c632dea4b0 100755 --- a/scripts/aiscatcher-install +++ b/scripts/aiscatcher-install @@ -664,11 +664,11 @@ build_ais_catcher_from_source() { make -j"$MAKE_JOBS" || error_exit "Failed to build AIS-catcher" cd ../.. + + delete_previous_binary + install -m 755 AIS-catcher/build/AIS-catcher /usr/bin/AIS-catcher || error_exit "Failed to install AIS-catcher executable" - # If a package installation has been present, it should be removed after building from source. - # This ensures compatiblity when switching between pre-packaged installation and source builds. Otherwise apt install may be confused why the ais-catcher package exist but has been replaced by an installation which is built from source (-which may be either a newer or older version). - apt remove -y ais-catcher || error_exit "Failed to remove preexisting AIS-catcher package" } # Install additional files (plugins, DBMS, README, LICENSE) from source @@ -1253,7 +1253,11 @@ parse_arguments() { fi } - +delete_previous_binary() { + # If a package installation has been present, it should be removed after building from source. + # This ensures compatiblity when switching between pre-packaged installation and source builds. Otherwise apt install may be confused why the ais-catcher package exist but has been replaced by an installation which is built from source (-which may be either a newer or older version). + apt remove -y ais-catcher || error_exit "Failed to remove preexisting AIS-catcher package" +} # Install from source install_from_source_method() { From a401d89afda7470e9841113519cfc8c682895540 Mon Sep 17 00:00:00 2001 From: Isak-JO Date: Mon, 21 Sep 2026 22:36:25 +0200 Subject: [PATCH 6/6] Added log entry when removing apt-package. Informing users that any previous pre-packaged installation will be removed when building from source (to avoid duplicate installations). --- scripts/aiscatcher-install | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/aiscatcher-install b/scripts/aiscatcher-install index c632dea4b0..a13a96e8d5 100755 --- a/scripts/aiscatcher-install +++ b/scripts/aiscatcher-install @@ -1256,6 +1256,7 @@ parse_arguments() { delete_previous_binary() { # If a package installation has been present, it should be removed after building from source. # This ensures compatiblity when switching between pre-packaged installation and source builds. Otherwise apt install may be confused why the ais-catcher package exist but has been replaced by an installation which is built from source (-which may be either a newer or older version). + log "INFO" "A pre-built AIS-catcher package is no longer used and will be removed (if present)" apt remove -y ais-catcher || error_exit "Failed to remove preexisting AIS-catcher package" }