#!/usr/bin/env bash # Absolute DB v9.5.1 # Copyright (c) 2024-2026 D.H.Maree. All rights reserved. # Author/Creator: David H Maree # Owner: D.H.Maree (ABN 21 498 105 915) — sole IP holder # Licensed to: SupportCALL AU — primary distributor # SPDX-License-Identifier: BSL-1.1 # https://absolutedb.com/ # https://downloads.absolutedb.com/install-ubuntu.sh # Copyright 2024–2026 SupportCALL AU & D.H.Maree — SPDX-License-Identifier: BSL-1.1 # # Supported distros: # Ubuntu 18.04 / 20.04 / 22.04 / 24.04 # Debian 10 (Buster) / 11 (Bullseye) / 12 (Bookworm) # Linux Mint 20/21, Pop!_OS 20.04/22.04, elementary OS 6+ # WSL2 (Ubuntu or Debian image) # # Usage: # bash install-ubuntu.sh [OPTIONS] # # Options: # --no-service Skip systemd service creation # --no-test Skip post-install verification tests # --uninstall Remove Absolute DB from this system # --prefix= Override installation prefix (default: /usr/local) # --branch= Clone a specific git branch (default: main) # --help Show this help message set -euo pipefail # --------------------------------------------------------------------------- # Global constants # --------------------------------------------------------------------------- VERSION="9.5.1" REPO="https://github.com/supportcall/AbsoluteDB.git" INSTALL_DIR="${HOME}/AbsoluteDB" PREFIX="/usr/local" BIN_DIR="${PREFIX}/bin" DATA_DIR="/var/lib/absdb" LOG_DIR="/var/log/absdb" SERVICE_USER="absdb" PG_PORT=5433 REST_PORT=8080 GRPC_PORT=9090 BRANCH="main" TOTAL_STEPS=11 # Flags (may be overridden by CLI args) DO_SERVICE=true DO_TEST=true DO_UNINSTALL=false # --------------------------------------------------------------------------- # Colors and output helpers # --------------------------------------------------------------------------- RED='\033[0;31m' GRN='\033[0;32m' YLW='\033[0;33m' BLU='\033[0;34m' CYN='\033[0;36m' MAG='\033[0;35m' BOLD='\033[1m' DIM='\033[2m' NC='\033[0m' # Disable colors when not a terminal if [ ! -t 1 ]; then RED=''; GRN=''; YLW=''; BLU=''; CYN=''; MAG=''; BOLD=''; DIM=''; NC='' fi info() { echo -e "${BLU}[INFO]${NC} $*"; } ok() { echo -e "${GRN}[ OK ]${NC} $*"; } warn() { echo -e "${YLW}[WARN]${NC} $*"; } step() { echo -e "\n${BOLD}${CYN}━━━ Step ${1}/${TOTAL_STEPS}: ${2} ${NC}"; } hdr() { echo -e "${BOLD}${MAG}$*${NC}"; } die() { echo -e "\n${RED}${BOLD}[FAIL]${NC}${RED} $* ${NC}" >&2 echo -e "\n${BOLD}${YLW}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${BOLD}${YLW}║ TROUBLESHOOTING GUIDE ║${NC}" echo -e "${BOLD}${YLW}╚══════════════════════════════════════════════════════════════╝${NC}" cat >&2 <<'TROUBLE' 1. sudo not found → apt-get install sudo (as root) → Then add yourself: usermod -aG sudo $USER && newgrp sudo 2. gcc not found / GCC too old (need ≥ 9) → sudo apt-get install -y gcc-11 → sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 110 → Ubuntu 18.04: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update && sudo apt-get install -y gcc-11 3. git not found → sudo apt-get install -y git 4. DNS failure / no internet → ping -c1 8.8.8.8 # test network → cat /etc/resolv.conf # check DNS → sudo systemctl restart systemd-resolved 5. SSL certificate error (git clone fails) → sudo apt-get install -y ca-certificates → sudo update-ca-certificates → Or temporarily: git -c http.sslVerify=false clone → Proxy users: export https_proxy=http://proxy.corp:3128/ 6. make not found → sudo apt-get install -y build-essential make 7. Permission denied for /usr/local/bin → Run with sudo or as root → Or use --prefix=$HOME/.local (then add $HOME/.local/bin to PATH) 8. Port 5433 already in use → sudo ss -tlnp | grep 5433 → sudo systemctl stop postgresql # stop conflicting PostgreSQL → Or change PG_PORT by editing this script before running 9. systemd not available (Docker / WSL1) → WSL1: upgrade to WSL2: wsl --set-version Ubuntu 2 → Docker: use --no-service flag; start manually: absdb-server --daemon → Check: [ -d /run/systemd/system ] && echo "systemd running" 10. libm.so.6 not found at runtime → sudo apt-get install -y libc6 → sudo ldconfig 11. Test failures / resource limits → ulimit -n 65536 # increase file descriptor limit → Check: cat /proc/sys/kernel/perf_event_paranoid (should be ≤ 2) → sudo sysctl -w kernel.perf_event_paranoid=1 12. WSL2 specific notes → systemd in WSL2 requires Windows 11 Build 22000+ and WSL 0.67.6+ → Add to /etc/wsl.conf: [boot] systemd=true → Then: wsl --shutdown && re-open terminal → WSL2 IP changes on restart — use 127.0.0.1 for localhost connections 13. Running as root (Docker containers) → The installer supports root — SERVICE_USER will still be created → In Docker: use --no-service and start absdb-server manually 14. absdb command not found after install → Check PATH: echo $PATH | grep -q /usr/local/bin || echo "MISSING" → Add to shell: echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.bashrc → source ~/.bashrc → Or use full path: /usr/local/bin/absdb --version 15. Disk space issues → df -h /usr/local /var /tmp # check available space → Minimum required: 500 MB free → du -sh /var/lib/absdb # check data directory size 16. Proxy environment → export http_proxy=http://proxy.corp:3128/ → export https_proxy=http://proxy.corp:3128/ → export no_proxy=localhost,127.0.0.1 → For apt: sudo -E apt-get install ... (passes env through) → For git: git config --global http.proxy http://proxy.corp:3128/ TROUBLE echo -e "${DIM}Full log: /tmp/absdb-install-$$.log${NC}" >&2 echo -e "${DIM}Support: https://absolutedb.com/support | community@absolutedb.com${NC}" >&2 exit 1 } # Redirect all output to log file as well exec > >(tee -a /tmp/absdb-install-$$.log) 2>&1 # --------------------------------------------------------------------------- # Banner # --------------------------------------------------------------------------- print_banner() { echo -e "${BOLD}${CYN}" cat <<'BANNER' █████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗ ██╗████████╗███████╗ ██████╗ ██████╗ ██╔══██╗██╔══██╗██╔════╝██╔═══██╗██║ ██║ ██║╚══██╔══╝██╔════╝ ██╔══██╗██╔══██╗ ███████║██████╔╝███████╗██║ ██║██║ ██║ ██║ ██║ █████╗ ██║ ██║██████╔╝ ██╔══██║██╔══██╗╚════██║██║ ██║██║ ██║ ██║ ██║ ██╔══╝ ██║ ██║██╔══██╗ ██║ ██║██████╔╝███████║╚██████╔╝███████╗╚██████╔╝ ██║ ███████╗ ██████╔╝██████╔╝ ╚═╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚══════╝ ╚═════╝ ╚═════╝ BANNER echo -e "${NC}" echo -e "${BOLD} Absolute DB v${VERSION} — Ubuntu / Debian Installer${NC}" echo -e "${DIM} Unified AI-native database platform | https://absolutedb.com${NC}" echo -e "${DIM} Copyright 2024–2026 SupportCALL AU & D.H.Maree | BSL-1.1${NC}" echo -e "" echo -e "${DIM} Install log: /tmp/absdb-install-$$.log${NC}" echo "" } # --------------------------------------------------------------------------- # Argument parsing # --------------------------------------------------------------------------- parse_args() { for arg in "$@"; do case "$arg" in --no-service) DO_SERVICE=false ;; --no-test) DO_TEST=false ;; --uninstall) DO_UNINSTALL=true ;; --prefix=*) PREFIX="${arg#*=}"; BIN_DIR="${PREFIX}/bin" ;; --branch=*) BRANCH="${arg#*=}" ;; --help|-h) echo "Usage: bash install-ubuntu.sh [OPTIONS]" echo "" echo "Options:" echo " --no-service Skip systemd service creation" echo " --no-test Skip post-install verification" echo " --uninstall Remove Absolute DB from this system" echo " --prefix= Override install prefix (default: /usr/local)" echo " --branch= Clone specific git branch (default: main)" echo " --help Show this help" exit 0 ;; *) warn "Unknown argument: $arg (ignored)" ;; esac done } # --------------------------------------------------------------------------- # Sudo detection and helper # --------------------------------------------------------------------------- SUDO="" setup_sudo() { if [ "$(id -u)" -eq 0 ]; then SUDO="" info "Running as root — sudo not required." return fi if ! command -v sudo >/dev/null 2>&1; then die "sudo is not installed. Install it as root: apt-get install sudo && usermod -aG sudo $USER" fi # Validate sudo access if ! sudo -n true 2>/dev/null; then info "This installer requires sudo privileges. You may be prompted for your password." sudo -v || die "sudo authentication failed. Ensure your user is in the sudo group." fi SUDO="sudo" ok "sudo access confirmed." } # --------------------------------------------------------------------------- # WSL2 detection # --------------------------------------------------------------------------- IS_WSL=false IS_WSL2=false detect_wsl() { if grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then IS_WSL=true if grep -qi 'WSL2' /proc/version 2>/dev/null || \ [ -f /proc/sys/fs/binfmt_misc/WSLInterop ]; then IS_WSL2=true warn "WSL2 detected. systemd may require extra setup (see troubleshooting)." else warn "WSL1 detected. Recommend upgrading to WSL2 for full systemd support." warn "Run from PowerShell: wsl --set-version Ubuntu 2" DO_SERVICE=false fi fi } # --------------------------------------------------------------------------- # Systemd availability check # --------------------------------------------------------------------------- SYSTEMD_AVAILABLE=false check_systemd() { if [ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1; then # Accept both "running" and "degraded" — degraded systemd is fully functional # for starting new services; only missing/failed/offline means truly unavailable. local _state _state=$(systemctl is-system-running 2>/dev/null || echo "unknown") case "${_state}" in running|degraded) SYSTEMD_AVAILABLE=true ;; esac fi if [ "$SYSTEMD_AVAILABLE" = false ]; then warn "systemd is not active on this system." warn "Service will not be installed. Start manually with: absdb-server --daemon" DO_SERVICE=false fi } # --------------------------------------------------------------------------- # Distro detection # --------------------------------------------------------------------------- DISTRO_ID="" DISTRO_VERSION="" DISTRO_CODENAME="" detect_distro() { if [ -f /etc/os-release ]; then # shellcheck disable=SC1091 . /etc/os-release DISTRO_ID="${ID:-unknown}" DISTRO_VERSION="${VERSION_ID:-0}" DISTRO_CODENAME="${VERSION_CODENAME:-}" # Normalize derivatives case "${ID_LIKE:-}" in *debian*|*ubuntu*) DISTRO_ID="ubuntu" ;; esac case "$DISTRO_ID" in linuxmint|pop|elementary|zorin) DISTRO_ID="ubuntu" ;; debian) : ;; # keep as-is ubuntu) : ;; *) warn "Unrecognised distro '${ID:-unknown}' — treating as Ubuntu-compatible." ; DISTRO_ID="ubuntu" ;; esac else warn "/etc/os-release not found — assuming Debian-compatible." DISTRO_ID="ubuntu" fi info "Distro: ${BOLD}${ID:-$DISTRO_ID} ${DISTRO_VERSION}${NC} (${DISTRO_CODENAME:-n/a})" } # --------------------------------------------------------------------------- # Disk space check # --------------------------------------------------------------------------- check_disk_space() { local required_kb=1572864 # 1.5 GB (full build + obj files) local available_kb available_kb=$(df -k "${HOME}" 2>/dev/null | awk 'NR==2{print $4}' || echo 0) if [ "$available_kb" -lt "$required_kb" ]; then die "Insufficient disk space. Need ≥ 512 MB free in ${HOME}. Have: $(( available_kb / 1024 )) MB." fi ok "Disk space: $(( available_kb / 1024 )) MB available — sufficient." } # --------------------------------------------------------------------------- # Network check (non-fatal — just warn) # --------------------------------------------------------------------------- check_network() { if ! ping -c1 -W3 8.8.8.8 >/dev/null 2>&1 && \ ! curl -s --connect-timeout 5 https://github.com >/dev/null 2>&1; then warn "Cannot reach the internet. Check connectivity or proxy settings." warn "Set: export https_proxy=http://proxy.corp:3128/" else ok "Network connectivity confirmed." fi } # --------------------------------------------------------------------------- # Step 1 — System update and prerequisites # --------------------------------------------------------------------------- install_prerequisites() { step 1 "Installing prerequisites (apt)" # apt-get may not be available on exotic installs — check if ! command -v apt-get >/dev/null 2>&1; then die "apt-get not found. This installer requires a Debian/Ubuntu-based system." fi info "Updating package lists..." $SUDO apt-get update -qq || warn "apt-get update failed — continuing with cached lists." local pkgs=( build-essential gcc g++ make git curl wget ca-certificates lsb-release gnupg software-properties-common coreutils util-linux procps netcat-openbsd ) # Note: libm-dev does not exist on Debian/Ubuntu — libm is part of libc6-dev # which is pulled in by build-essential. Do not add it here. info "Installing packages: ${pkgs[*]}" $SUDO apt-get install -y --no-install-recommends "${pkgs[@]}" \ || die "Package installation failed. Check apt output above." ok "Base packages installed." } # --------------------------------------------------------------------------- # Step 2 — Ensure GCC ≥ 9 # --------------------------------------------------------------------------- ensure_gcc() { step 2 "Verifying GCC version (need ≥ 9)" local gcc_cmd="gcc" local gcc_ver=0 if command -v gcc >/dev/null 2>&1; then gcc_ver=$(gcc -dumpversion 2>/dev/null | cut -d. -f1 || echo 0) fi # Guard against non-numeric version (e.g. empty string or "unknown") if ! echo "${gcc_ver}" | grep -qE '^[0-9]+$'; then warn "Could not determine GCC version ('${gcc_ver}') — forcing install of GCC 11" gcc_ver=0 fi if [ "${gcc_ver}" -lt 9 ] 2>/dev/null; then warn "GCC $gcc_ver is too old (need ≥ 9). Installing GCC 11 from toolchain PPA..." # Ubuntu 18.04 / 20.04 need the PPA; 22.04+ have gcc-11 natively local ubuntu_ver ubuntu_ver=$(lsb_release -rs 2>/dev/null | cut -d. -f1 || echo 0) if [ "$ubuntu_ver" -lt 22 ] 2>/dev/null; then info "Adding ubuntu-toolchain-r/test PPA for GCC 11..." $SUDO add-apt-repository -y ppa:ubuntu-toolchain-r/test \ || die "Failed to add toolchain PPA. Check internet/proxy access." $SUDO apt-get update -qq fi $SUDO apt-get install -y gcc-11 g++-11 \ || die "Failed to install GCC 11." $SUDO update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 110 \ --slave /usr/bin/g++ g++ /usr/bin/g++-11 2>/dev/null || true gcc_cmd="gcc-11" fi # Verify command -v gcc >/dev/null 2>&1 || die "gcc not found after installation." gcc_ver=$(gcc -dumpversion | cut -d. -f1) ok "GCC ${gcc_ver} ready." } # --------------------------------------------------------------------------- # Step 3 — Verify all tools are present # --------------------------------------------------------------------------- verify_tools() { step 3 "Verifying build tools" local tools=(gcc make git curl) for tool in "${tools[@]}"; do if ! command -v "$tool" >/dev/null 2>&1; then die "$tool is not available after installation. See troubleshooting item for $tool." fi ok "$tool: $(command -v "$tool")" done } # --------------------------------------------------------------------------- # Branch detection: prefer 'main', fall back to 'master' # --------------------------------------------------------------------------- _detect_branch() { local remote="${1:-origin}" if git ls-remote --heads "${remote}" main 2>/dev/null | grep -q "refs/heads/main"; then echo "main" elif git ls-remote --heads "${remote}" master 2>/dev/null | grep -q "refs/heads/master"; then echo "master" else echo "main" fi } # Git clone with 3 attempts and exponential backoff (4s, 8s) # Removes partial clone dirs before retry to prevent "already exists" errors. _git_clone_with_retry() { local branch="$1" dest="$2" repo="$3" local attempt=1 delay=4 while [ "${attempt}" -le 3 ]; do # Remove partial clone (no .git) from a previous failed attempt if [ -d "${dest}" ] && [ ! -d "${dest}/.git" ]; then rm -rf "${dest}" fi if git clone --depth=1 --branch="${branch}" "${repo}" "${dest}" 2>&1; then return 0 fi if [ "${attempt}" -lt 3 ]; then warn "Clone attempt ${attempt}/3 failed — retrying in ${delay}s..." sleep "${delay}"; delay=$(( delay * 2 )) fi attempt=$(( attempt + 1 )) done die "git clone failed after 3 attempts. Check: internet, DNS, firewall, proxy settings." } # --------------------------------------------------------------------------- # Step 4 — Clone or update repository # --------------------------------------------------------------------------- clone_repository() { step 4 "Cloning Absolute DB v${VERSION} repository" # If --branch was not explicitly overridden via CLI, detect from remote if [ "${BRANCH}" = "main" ]; then local detected detected="$( _detect_branch "${REPO}" )" if [ "${detected}" != "${BRANCH}" ]; then info "Remote branch detected: ${detected}" BRANCH="${detected}" fi fi if [ -d "$INSTALL_DIR/.git" ]; then info "Existing installation found at ${INSTALL_DIR} — updating..." cd "${INSTALL_DIR}" info "Fetching latest source (branch: ${BRANCH})..." if git fetch --depth=1 origin "${BRANCH}" 2>&1; then git reset --hard FETCH_HEAD ok "Updated to latest ${BRANCH}" else warn "git fetch failed — removing and recloning..." cd /; rm -rf "${INSTALL_DIR}" _git_clone_with_retry "${BRANCH}" "${INSTALL_DIR}" "${REPO}" ok "Recloned to ${INSTALL_DIR}" fi cd "${INSTALL_DIR}" else if [ -d "$INSTALL_DIR" ]; then warn "Directory ${INSTALL_DIR} exists but is not a git repo — removing." rm -rf "$INSTALL_DIR" || die "Failed to remove existing directory: $INSTALL_DIR" fi info "Cloning ${REPO} (branch: ${BRANCH}) → ${INSTALL_DIR}" _git_clone_with_retry "${BRANCH}" "${INSTALL_DIR}" "${REPO}" ok "Repository cloned to ${INSTALL_DIR}." fi # Verify that the cloned repository matches the expected version local repo_ver repo_ver=$(grep -o '"[0-9]\+\.[0-9]\+\.[0-9]\+"' "${INSTALL_DIR}/include/adb_types.h" 2>/dev/null | tr -d '"' | head -1) if [ -z "$repo_ver" ]; then warn "Could not read version from cloned repository — proceeding anyway." elif [ "$repo_ver" != "$VERSION" ]; then die "Repository version ${repo_ver} does not match expected ${VERSION}. Check BRANCH setting." fi } # --------------------------------------------------------------------------- # Step 5 — Build # --------------------------------------------------------------------------- build_absdb() { step 5 "Building Absolute DB v${VERSION}" cd "$INSTALL_DIR" || die "Cannot enter ${INSTALL_DIR}" # Check virtual memory (RAM + swap) — compiler needs at least 1 GB to avoid OOM kill local _total_mb _total_mb=$(free -m 2>/dev/null | awk '/^Mem:/{m=$2} /^Swap:/{s=$2} END{print m+s}' || echo 9999) if [ "${_total_mb}" -lt 1024 ] 2>/dev/null; then warn "Low virtual memory: ${_total_mb} MB (RAM + swap). Build may fail with OOM." warn "Limiting to 1 parallel compile job to reduce peak memory usage." export MAKEFLAGS="-j1" fi info "Running make clean..." make clean 2>&1 || warn "make clean failed — continuing anyway." info "Running make all..." make release 2>&1 || die "Build failed. Review errors above." # Verify mandatory binaries were produced (absdb + absdb-server required) for bin in absdb absdb-server; do local found found=$(find "$INSTALL_DIR" -maxdepth 3 -name "$bin" -type f 2>/dev/null | head -1) if [ -z "$found" ]; then die "Binary '${bin}' not found after build. Build may have failed silently." fi done # Optional binaries — warn but don't abort for bin in absdb-bench adb_admin; do local found found=$(find "$INSTALL_DIR" -maxdepth 3 -name "$bin" -type f 2>/dev/null | head -1) [ -z "$found" ] && warn "Optional binary '${bin}' not found — protocol wizard unavailable." done ok "Build completed successfully." } # --------------------------------------------------------------------------- # Step 6 — Install binaries # --------------------------------------------------------------------------- install_binaries() { step 6 "Installing binaries to ${BIN_DIR}" $SUDO mkdir -p "$BIN_DIR" || die "Cannot create ${BIN_DIR}" # Find and install each binary for bin in absdb absdb-server absdb-bench adb_admin; do local src="" # Look in common build output locations for loc in \ "${INSTALL_DIR}/${bin}" \ "${INSTALL_DIR}/build/${bin}" \ "${INSTALL_DIR}/out/${bin}" \ "${INSTALL_DIR}/dist/${bin}"; do if [ -f "$loc" ] && [ -x "$loc" ]; then src="$loc" break fi done if [ -z "$src" ]; then warn "Binary '${bin}' not found — skipping. (It may not be built on this platform.)" continue fi $SUDO install -m 755 "$src" "${BIN_DIR}/${bin}" \ || die "Failed to install ${bin} to ${BIN_DIR}" ok "Installed: ${BIN_DIR}/${bin}" done # Ensure BIN_DIR is on PATH if ! echo "$PATH" | grep -q "${BIN_DIR}"; then warn "${BIN_DIR} is not in PATH. Adding to ~/.bashrc..." if ! grep -q "${BIN_DIR}" "${HOME}/.bashrc" 2>/dev/null; then echo "" >> "${HOME}/.bashrc" echo "# Absolute DB — added by installer" >> "${HOME}/.bashrc" echo "export PATH=\"${BIN_DIR}:\$PATH\"" >> "${HOME}/.bashrc" fi export PATH="${BIN_DIR}:${PATH}" ok "Added ${BIN_DIR} to PATH (effective after: source ~/.bashrc)" fi } # --------------------------------------------------------------------------- # Step 7 — Create data and log directories, service user # --------------------------------------------------------------------------- setup_directories_and_user() { step 7 "Creating data directories and service user" # Create service user (system account, no login shell) if ! id "$SERVICE_USER" >/dev/null 2>&1; then $SUDO useradd \ --system \ --no-create-home \ --shell /usr/sbin/nologin \ --comment "Absolute DB service account" \ "$SERVICE_USER" \ || die "Failed to create service user '${SERVICE_USER}'." ok "Service user '${SERVICE_USER}' created." else ok "Service user '${SERVICE_USER}' already exists." fi # Create directories for dir in "$DATA_DIR" "$LOG_DIR"; do $SUDO mkdir -p "$dir" || die "Cannot create directory: $dir" $SUDO chown "${SERVICE_USER}:${SERVICE_USER}" "$dir" \ || warn "Cannot chown ${dir} — permissions may be incorrect." $SUDO chmod 750 "$dir" ok "Directory: $dir" done } # --------------------------------------------------------------------------- # Step 8 — Systemd service # --------------------------------------------------------------------------- install_service() { step 8 "Installing systemd service" if [ "$DO_SERVICE" = false ]; then info "Skipping service installation (--no-service or systemd unavailable)." return fi if [ "$SYSTEMD_AVAILABLE" = false ]; then warn "systemd is not running — skipping service installation." info "To start manually: ${BIN_DIR}/absdb-server --data-dir=${DATA_DIR} --log-dir=${LOG_DIR} --daemon" return fi local service_file="/etc/systemd/system/absdb.service" $SUDO tee "$service_file" > /dev/null </dev/null | grep -qE ":${_port}[[:space:]]|:${_port}$") 2>/dev/null || \ (netstat -tlnp 2>/dev/null | grep -qE ":${_port}[[:space:]]|:${_port}$") 2>/dev/null; then warn "Port ${_port} is already in use by another process." warn "The service may fail to start. Check with: sudo ss -tlnp | grep :${_port}" fi done # Start the service info "Starting absdb.service..." if $SUDO systemctl start absdb.service 2>&1; then ok "absdb.service started." else warn "Service failed to start immediately. Check logs:" warn " journalctl -u absdb.service -n 50" warn " cat ${LOG_DIR}/absdb-error.log" fi # Check for immediate crash (start + instant fail pattern) sleep 2 if $SUDO systemctl is-failed absdb.service --quiet 2>/dev/null; then warn "absdb.service has failed immediately after start. Last 20 log lines:" $SUDO journalctl -u absdb.service -n 20 --no-pager 2>/dev/null || true fi } # --------------------------------------------------------------------------- # Step 9 — Protocol Selection Wizard (least-privilege surface control) # --------------------------------------------------------------------------- prompt_protocol_selection() { # Skip if non-interactive or --preset was already passed [ -t 0 ] && [ -t 1 ] || return 0 step 9 "Protocol Selection" echo "" echo -e " ${BOLD}Absolute DB — Adaptive Protocol Manager${NC}" echo -e " ${DIM}Only the ports you enable will be opened.${NC}" echo -e " ${DIM}All others remain closed (principle of least-protocol).${NC}" echo "" echo -e " ${BOLD}${BLU}┌──────────────────────────────────────────────────────────────────┐${NC}" echo -e " ${BOLD}${BLU}│${NC} ${CYN}1)${NC} ${BOLD}Minimal${NC} — PostgreSQL wire + REST only ${DIM}(default)${NC} ${BOLD}${BLU}│${NC}" echo -e " ${BOLD}${BLU}│${NC} ${CYN}2)${NC} ${BOLD}Web App${NC} — PostgreSQL + REST + Redis ${BOLD}${BLU}│${NC}" echo -e " ${BOLD}${BLU}│${NC} ${CYN}3)${NC} ${BOLD}Microservices${NC} — PostgreSQL + REST + gRPC ${BOLD}${BLU}│${NC}" echo -e " ${BOLD}${BLU}│${NC} ${CYN}4)${NC} ${BOLD}Enterprise${NC} — All protocols ${BOLD}${BLU}│${NC}" echo -e " ${BOLD}${BLU}│${NC} ${CYN}5)${NC} ${BOLD}Custom${NC} — Choose individually ${BOLD}${BLU}│${NC}" echo -e " ${BOLD}${BLU}└──────────────────────────────────────────────────────────────────┘${NC}" echo "" local PROTO_CHOICE="" while true; do read -rp " Enter choice [1-5] (default: 1): " _raw _raw="${_raw:-1}" case "${_raw}" in 1|minimal) PROTO_CHOICE="minimal"; break ;; 2|webapp) PROTO_CHOICE="webapp"; break ;; 3|microservices) PROTO_CHOICE="microservices"; break ;; 4|enterprise) PROTO_CHOICE="enterprise"; break ;; 5|custom) PROTO_CHOICE="custom"; break ;; *) warn "Enter 1–5." ;; esac done local CONF_DIR="/etc/absdb" local CONF_FILE="${CONF_DIR}/absdb.conf" $SUDO mkdir -p "${CONF_DIR}" 2>/dev/null || true if [ "${PROTO_CHOICE}" != "custom" ]; then # Apply preset via adb_admin if command -v adb_admin >/dev/null 2>&1; then $SUDO adb_admin --protocol preset "${PROTO_CHOICE}" -c "${CONF_FILE}" \ 2>/dev/null || true else # Write config directly if adb_admin not yet on PATH { echo "[protocols]" echo "# Absolute DB v${VERSION} — generated by installer" echo "shadow = on" case "${PROTO_CHOICE}" in minimal) echo "pg_wire = on ; port 5433" echo "rest = on ; port 8080" echo "grpc = off" echo "redis = off" echo "prometheus = off" echo "cluster = off" echo "craid = off" ;; webapp) echo "pg_wire = on ; port 5433" echo "rest = on ; port 8080" echo "grpc = off" echo "redis = on ; port 6379" echo "prometheus = off" echo "cluster = off" echo "craid = off" ;; microservices) echo "pg_wire = on ; port 5433" echo "rest = on ; port 8080" echo "grpc = on ; port 9090" echo "redis = off" echo "prometheus = off" echo "cluster = off" echo "craid = off" ;; enterprise) echo "pg_wire = on ; port 5433" echo "rest = on ; port 8080" echo "grpc = on ; port 9090" echo "redis = on ; port 6379" echo "prometheus = on ; port 9093" echo "cluster = on ; port 9091" echo "craid = on ; port 9092" ;; esac } | $SUDO tee "${CONF_FILE}" > /dev/null fi ok "Protocol preset '${PROTO_CHOICE}' configured in ${CONF_FILE}" else # Custom: prompt per-protocol echo "" echo -e " ${BOLD}Custom protocol selection (y/n for each):${NC}" echo "" local CONF_LINES="[protocols]\n# Absolute DB v${VERSION} — generated by installer\nshadow = on\n" for PROTO in "pg_wire:5433:PostgreSQL wire v3 (psql, ORMs, JDBC)" \ "rest:8080:REST API + Web Console" \ "grpc:9090:gRPC / HTTP2 (microservices)" \ "redis:6379:Redis RESP3 (cache, pub/sub)" \ "prometheus:9093:Prometheus /metrics (observability)" \ "cluster:9091:Raft consensus (multi-node HA)" \ "craid:9092:C-RAID replication (distributed storage)"; do local NAME="${PROTO%%:*}" local REST="${PROTO#*:}" local PORT="${REST%%:*}" local DESC="${REST#*:}" read -rp " Enable ${NAME} (port ${PORT}) — ${DESC}? [y/N]: " _ans _ans="${_ans:-n}" if [[ "${_ans,,}" == "y" || "${_ans,,}" == "yes" ]]; then CONF_LINES+="${NAME} = on ; port ${PORT}\n" else CONF_LINES+="${NAME} = off\n" fi done printf '%b' "${CONF_LINES}" | $SUDO tee "${CONF_FILE}" > /dev/null ok "Custom protocol selection saved to ${CONF_FILE}" fi echo "" echo -e " ${DIM}Change anytime: adb_admin --protocol enable ${NC}" echo -e " ${DIM} adb_admin --protocol status${NC}" echo -e " ${DIM} adb_admin --protocol-wizard${NC}" echo "" } # --------------------------------------------------------------------------- # Step 10 — Verify installation # --------------------------------------------------------------------------- # ── Production vs Testing wizard ───────────────────────────────────────────── prompt_install_type() { # Only prompt in an interactive terminal [ -t 0 ] && [ -t 1 ] || return 0 step 9 "Installation Type" echo "" echo -e " ${BOLD}Is this installation for Production or Testing/Evaluation?${NC}" echo "" echo -e " ${BOLD}${RED}┌─────────────────────────────────────────────────────────────────┐${NC}" echo -e " ${BOLD}${RED}│${NC} ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${CYN}1)${NC} ${BOLD}Production${NC} — clean start, no sample data ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${CYN}2)${NC} ${BOLD}Testing${NC} — optionally load tier-sized sample data ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}└─────────────────────────────────────────────────────────────────┘${NC}" echo "" local INSTALL_TYPE="" while true; do read -rp " Enter choice [1/2] (default: 1): " _raw _raw="${_raw:-1}" case "${_raw}" in 1|production|prod) INSTALL_TYPE="production"; break ;; 2|testing|test) INSTALL_TYPE="testing"; break ;; *) warn "Enter 1 (Production) or 2 (Testing)." ;; esac done if [ "${INSTALL_TYPE}" = "production" ]; then info "Production installation — server starts clean." info "To generate test data later:" info " bash ${INSTALL_DIR}/tools/gen_test_data.sh" return 0 fi # ── Testing branch ──────────────────────────────────────────────────────── echo "" info "Testing installation selected." echo "" read -rp " Generate sample/test data now? [y/N]: " _want _want="${_want:-n}" if [[ "${_want,,}" != "y" && "${_want,,}" != "yes" ]]; then info "Skipping sample data. Run later:" info " bash ${INSTALL_DIR}/tools/gen_test_data.sh" return 0 fi echo "" echo -e " ${BOLD}Select the tier size for test data:${NC}" echo "" echo -e " ${BOLD}${RED}┌─────────────────────────────────────────────────────────────────┐${NC}" echo -e " ${BOLD}${RED}│${NC} ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${CYN}1)${NC} ${BOLD}Community${NC} — ${YLW}2 GB${NC} 10 connections · 10 GB limit ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${CYN}2)${NC} ${BOLD}SME${NC} — ${YLW}3 GB${NC} 500 connections · 5 GB limit ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${CYN}3)${NC} ${BOLD}Professional${NC} — ${YLW}10 GB${NC} Unlimited connections ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${CYN}4)${NC} ${BOLD}Enterprise${NC} — ${YLW}20 GB${NC} Unlimited + cluster + C-RAID ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${DIM}Fixed seed — same data every run, ±0.5% of target size ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}│${NC} ${BOLD}${RED}│${NC}" echo -e " ${BOLD}${RED}└─────────────────────────────────────────────────────────────────┘${NC}" echo "" local DATA_TIER="" while true; do read -rp " Enter tier [1-4 or name]: " _t _t="${_t,,}" case "${_t}" in 1|community) DATA_TIER="community"; break ;; 2|sme) DATA_TIER="sme"; break ;; 3|pro|professional) DATA_TIER="pro"; break ;; 4|enterprise) DATA_TIER="enterprise"; break ;; *) warn "Enter 1–4 or community/sme/pro/enterprise." ;; esac done # Confirm disk space warning case "${DATA_TIER}" in enterprise) warn "Enterprise tier: 20 GB will be generated. Ensure sufficient disk space." ;; pro) warn "Professional tier: 10 GB will be generated. Ensure sufficient disk space." ;; esac echo "" info "Generating ${DATA_TIER} test data — this may take several minutes..." info "Data is deterministic: same dataset is produced every run." echo "" local GEN_SH="${INSTALL_DIR}/tools/gen_test_data.sh" local GEN_PY="${INSTALL_DIR}/tools/gen_test_data.py" if [ -f "${GEN_SH}" ]; then bash "${GEN_SH}" \ --tier "${DATA_TIER}" \ --host "127.0.0.1" \ --port "${PG_PORT}" \ --dbname "absdb" \ --drop-existing \ || warn "Sample data generation encountered errors — check output above." elif [ -f "${GEN_PY}" ] && command -v python3 &>/dev/null; then # Install psycopg2 if needed python3 -c "import psycopg2" &>/dev/null 2>&1 \ || python3 -m pip install --quiet psycopg2-binary \ || warn "pip install psycopg2-binary failed — install manually and rerun." python3 "${GEN_PY}" \ --tier "${DATA_TIER}" \ --host "127.0.0.1" \ --port "${PG_PORT}" \ --dbname "absdb" \ --drop-existing \ || warn "Sample data generation encountered errors — check output above." else warn "gen_test_data.sh/py not found at ${INSTALL_DIR}/tools/" warn "Run manually after install:" warn " bash ${INSTALL_DIR}/tools/gen_test_data.sh --tier ${DATA_TIER}" fi } verify_install() { step 9 "Verifying installation" if [ "$DO_TEST" = false ]; then info "Skipping verification (--no-test)." return fi # Verify CLI binary if command -v absdb >/dev/null 2>&1; then local ver_out ver_out=$(absdb --version 2>&1 || true) ok "absdb CLI: ${ver_out}" else warn "absdb not found in PATH. You may need to: source ~/.bashrc" fi # Verify server binary if command -v absdb-server >/dev/null 2>&1; then local sver_out sver_out=$(absdb-server --version 2>&1 || true) ok "absdb-server: ${sver_out}" else warn "absdb-server not found in PATH." fi # Check if server is listening (if service was started) if [ "$DO_SERVICE" = true ] && [ "$SYSTEMD_AVAILABLE" = true ]; then local attempts=6 local port_open=false info "Waiting for server to accept connections on port ${PG_PORT}..." for i in $(seq 1 $attempts); do if nc -z 127.0.0.1 "$PG_PORT" 2>/dev/null; then port_open=true break fi sleep 2 done if [ "$port_open" = true ]; then ok "Server is accepting connections on port ${PG_PORT}." else warn "Server does not appear to be listening on port ${PG_PORT} yet." warn "Check with: sudo systemctl status absdb && journalctl -u absdb -n 30" warn "Port conflict: sudo ss -tlnp | grep ${PG_PORT}" fi fi } # --------------------------------------------------------------------------- # Step 10 — Final summary # --------------------------------------------------------------------------- print_summary() { step 10 "Installation complete" echo "" echo -e "${BOLD}${GRN}╔══════════════════════════════════════════════════════════════════════╗${NC}" echo -e "${BOLD}${GRN}║ Absolute DB v${VERSION} — Successfully Installed! ║${NC}" echo -e "${BOLD}${GRN}╚══════════════════════════════════════════════════════════════════════╝${NC}" echo "" hdr " Installation Summary" echo -e " ${DIM}Binary (CLI): ${NC}${BIN_DIR}/absdb" echo -e " ${DIM}Binary (Server):${NC}${BIN_DIR}/absdb-server" echo -e " ${DIM}Data directory: ${NC}${DATA_DIR}" echo -e " ${DIM}Log directory: ${NC}${LOG_DIR}" echo -e " ${DIM}Service user: ${NC}${SERVICE_USER}" echo -e " ${DIM}PostgreSQL port:${NC}${PG_PORT}" echo -e " ${DIM}REST API port: ${NC}${REST_PORT}" echo -e " ${DIM}gRPC port: ${NC}${GRPC_PORT}" echo "" hdr " Quick-Start Commands" echo -e " ${CYN}# Connect with psql${NC}" echo -e " psql -h 127.0.0.1 -p ${PG_PORT} -U absdb" echo "" echo -e " ${CYN}# Run an inline SQL query${NC}" echo -e " absdb --exec \"SELECT version();\"" echo "" echo -e " ${CYN}# REST API${NC}" echo -e " curl http://127.0.0.1:${REST_PORT}/api/v1/query -d '{\"sql\":\"SELECT 1\"}'" echo "" echo -e " ${CYN}# Web management console${NC}" echo -e " open http://127.0.0.1:${REST_PORT}/console" echo "" if [ "$DO_SERVICE" = true ] && [ "$SYSTEMD_AVAILABLE" = true ]; then hdr " Service Management" echo -e " ${CYN}sudo systemctl start absdb${NC} # start" echo -e " ${CYN}sudo systemctl stop absdb${NC} # stop" echo -e " ${CYN}sudo systemctl restart absdb${NC} # restart" echo -e " ${CYN}sudo systemctl status absdb${NC} # status" echo -e " ${CYN}journalctl -u absdb -f ${NC} # follow logs" echo "" else hdr " Manual Start" echo -e " ${CYN}absdb-server --data-dir=${DATA_DIR} --log-dir=${LOG_DIR} --daemon${NC}" echo "" fi hdr " Next Steps" echo -e " ${YLW}1. Open firewall ports for remote access:${NC}" echo -e " ${CYN}sudo ufw allow ${PG_PORT}/tcp ${NC}${DIM}# PostgreSQL wire protocol${NC}" echo -e " ${CYN}sudo ufw allow ${REST_PORT}/tcp ${NC}${DIM}# REST API + Web console${NC}" echo -e " ${CYN}sudo ufw allow ${GRPC_PORT}/tcp ${NC}${DIM}# gRPC / HTTP/2${NC}" echo -e " ${CYN}sudo ufw allow 6379/tcp ${NC}${DIM}# Redis RESP3 (if enabled)${NC}" echo "" echo -e " ${YLW}2. Open the web management console:${NC}" echo -e " ${CYN}http://localhost:${REST_PORT}/console${NC}" echo "" echo -e " ${YLW}3. Enable TLS for production:${NC}" echo -e " See ${DIM}https://absolutedb.com/docs/tls${NC}" echo "" hdr " Documentation & Support" echo -e " ${DIM}Docs: ${NC}https://absolutedb.com/docs" echo -e " ${DIM}Discord: ${NC}https://discord.gg/absolutedb" echo -e " ${DIM}Support: ${NC}community@absolutedb.com" echo -e " ${DIM}GitHub: ${NC}${REPO}" echo "" if ! echo "$PATH" | grep -q "${BIN_DIR}"; then echo -e " ${YLW}NOTE: Run${NC} source ~/.bashrc ${YLW}to update your PATH in this shell session.${NC}" echo "" fi echo -e "${BOLD}${GRN} Enjoy Absolute DB v${VERSION}!${NC}" echo "" } # --------------------------------------------------------------------------- # Uninstall # --------------------------------------------------------------------------- do_uninstall() { hdr "Uninstalling Absolute DB v${VERSION}..." if [ "$SYSTEMD_AVAILABLE" = true ] && \ systemctl is-active --quiet absdb.service 2>/dev/null; then $SUDO systemctl stop absdb.service || true $SUDO systemctl disable absdb.service || true $SUDO rm -f /etc/systemd/system/absdb.service $SUDO systemctl daemon-reload || true ok "systemd service removed." fi for bin in absdb absdb-server absdb-bench adb_admin; do if [ -f "${BIN_DIR}/${bin}" ]; then $SUDO rm -f "${BIN_DIR}/${bin}" ok "Removed ${BIN_DIR}/${bin}" fi done if [ -d "$INSTALL_DIR" ]; then rm -rf "$INSTALL_DIR" ok "Removed ${INSTALL_DIR}" fi warn "Data and logs NOT removed. To also remove them:" warn " sudo rm -rf ${DATA_DIR} ${LOG_DIR}" warn " sudo userdel ${SERVICE_USER}" ok "Uninstall complete." exit 0 } # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- main() { parse_args "$@" print_banner if [ "$DO_UNINSTALL" = true ]; then setup_sudo detect_wsl check_systemd do_uninstall fi info "Starting Absolute DB v${VERSION} installation..." info "Install dir: ${INSTALL_DIR}" info "Prefix: ${PREFIX}" info "Branch: ${BRANCH}" info "Service: ${DO_SERVICE}" echo "" detect_distro detect_wsl check_systemd check_disk_space check_network setup_sudo install_prerequisites ensure_gcc verify_tools clone_repository build_absdb install_binaries setup_directories_and_user prompt_protocol_selection install_service verify_install prompt_install_type print_summary } main "$@"