summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorinfo@mode42.com <info@mode42.com>2026-08-08 03:54:55 +0000
committerinfo@mode42.com <info@mode42.com>2026-08-08 03:54:55 +0000
commit20cb29c2f8c5c87bc590896854a20b1473ceb358 (patch)
tree2857f41513a56ad41af97b57362639298aa7f033 /scripts
#2
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/build-amiga-telnet.sh26
-rwxr-xr-xscripts/build-clients.sh19
-rwxr-xr-xscripts/dev-setup.sh19
-rwxr-xr-xscripts/hybbx.sh86
-rwxr-xr-xscripts/mock-ardopc.py137
-rwxr-xr-xscripts/test-ardop-plugin.sh89
-rwxr-xr-xscripts/test-crdop-plugin.sh97
7 files changed, 473 insertions, 0 deletions
diff --git a/scripts/build-amiga-telnet.sh b/scripts/build-amiga-telnet.sh
new file mode 100755
index 0000000..b423923
--- /dev/null
+++ b/scripts/build-amiga-telnet.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+# Cross-build hybbx-telnet for AmigaOS 3.9+ (bebbo-gcc / clib2).
+set -eu
+
+ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"
+BUILD="${1:-$ROOT/build-amiga}"
+SDK="${AMIGA_SDK_PATH:-/opt/amiga}"
+
+shift 2>/dev/null || true
+
+if [ ! -x "$SDK/bin/m68k-amigaos-gcc" ]; then
+ echo "Amiga SDK not found at $SDK (set AMIGA_SDK_PATH)" >&2
+ exit 1
+fi
+
+cmake -B "$BUILD" \
+ -DCMAKE_TOOLCHAIN_FILE="$ROOT/cmake/Toolchain-AmigaOS.cmake" \
+ -DAMIGA_SDK_PATH="$SDK" \
+ -DHYBBX_CLIENTS_ONLY=ON \
+ -DCMAKE_BUILD_TYPE=Release \
+ "$@"
+
+cmake --build "$BUILD" --target hybbx-telnet
+
+echo "AmigaOS client:"
+echo " $BUILD/src/clients/hybbx-telnet"
diff --git a/scripts/build-clients.sh b/scripts/build-clients.sh
new file mode 100755
index 0000000..3e909f9
--- /dev/null
+++ b/scripts/build-clients.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env sh
+# Build hybbx-telnet, hybbx-terminal, and hybbx-ssh (when libssh is available)
+# without the hybbx server or plugins.
+set -eu
+
+ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"
+BUILD="${1:-$ROOT/build-clients}"
+
+shift 2>/dev/null || true
+
+cmake -B "$BUILD" -DHYBBX_CLIENTS_ONLY=ON "$@"
+cmake --build "$BUILD"
+
+echo "Clients:"
+echo " $BUILD/src/clients/hybbx-telnet"
+echo " $BUILD/src/clients/hybbx-terminal"
+if [ -x "$BUILD/src/clients/hybbx-ssh" ]; then
+ echo " $BUILD/src/clients/hybbx-ssh"
+fi
diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh
new file mode 100755
index 0000000..69929d3
--- /dev/null
+++ b/scripts/dev-setup.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+# One-shot dev environment: configure, build, IDE compile_commands symlink.
+set -e
+
+root="$(cd "$(dirname "$0")/.." && pwd)"
+
+cmake -B "$root/build" \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+
+cmake --build "$root/build"
+
+if [ -f "$root/build/compile_commands.json" ]; then
+ ln -sf build/compile_commands.json "$root/compile_commands.json"
+ echo "Linked compile_commands.json -> build/compile_commands.json"
+fi
+
+echo "Ready. Install (default → ./bin): cmake --install $root/build"
+echo "Start: $root/bin/hybbx-start or $root/scripts/hybbx.sh"
diff --git a/scripts/hybbx.sh b/scripts/hybbx.sh
new file mode 100755
index 0000000..9652654
--- /dev/null
+++ b/scripts/hybbx.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+# Start hybbxd only. Build and install with CMake (see README).
+#
+# Default install (source tree): ./bin/hybbx-start
+# Prefix install: /usr/local/hybbx/hybbx-start (or $HOME/hybbx/…)
+# Dev (no install): ./scripts/hybbx.sh
+#
+# Detached: hybbx-start --screen | --tmux [--attach]
+set -e
+
+self="${0#./}"
+case "$self" in
+ /*) script_path="$self" ;;
+ *) script_path="$(pwd)/$self" ;;
+esac
+script_dir="$(cd "$(dirname "$script_path")" && pwd)"
+script_name="$(basename "$script_path")"
+
+# Installed layout: <tree>/hybbx-start + <tree>/hybbxd (./bin or <prefix>/hybbx)
+if [ "$script_name" = "hybbx-start" ] && [ -x "$script_dir/hybbxd" ]; then
+ prefix="$script_dir"
+ config="${HYBBX_CONFIG:-$prefix/hybbx.ini}"
+ binary="$script_dir/hybbxd"
+ export HYBBX_ROOT="${HYBBX_ROOT:-$prefix}"
+ cd "$prefix"
+ if [ ! -f "$config" ]; then
+ echo "Config not found: $config" >&2
+ exit 1
+ fi
+ if [ "$#" -eq 0 ]; then
+ exec "$binary" -c "$config"
+ fi
+ exec "$binary" -c "$config" "$@"
+fi
+
+# Explicit or known install roots
+root="$(cd "$script_dir/.." && pwd)"
+if [ -n "$HYBBX_ROOT" ]; then
+ prefix="$HYBBX_ROOT"
+elif [ -x "$root/bin/hybbxd" ]; then
+ prefix="$root/bin"
+elif [ -x "/usr/local/hybbx/hybbxd" ]; then
+ prefix="/usr/local/hybbx"
+elif [ -n "$HOME" ] && [ -x "$HOME/hybbx/hybbxd" ]; then
+ prefix="$HOME/hybbx"
+else
+ prefix=""
+fi
+
+if [ -n "$prefix" ]; then
+ config="${HYBBX_CONFIG:-$prefix/hybbx.ini}"
+ binary="$prefix/hybbxd"
+ if [ -x "$binary" ] && [ -f "$config" ]; then
+ export HYBBX_ROOT="${HYBBX_ROOT:-$prefix}"
+ cd "$prefix"
+ if [ "$#" -eq 0 ]; then
+ exec "$binary" -c "$config"
+ fi
+ exec "$binary" -c "$config" "$@"
+ fi
+fi
+
+# Development tree (repo checkout, no cmake --install yet)
+config="${HYBBX_CONFIG:-$root/local/hybbx.ini}"
+build="$root/build/src/hybbxd"
+
+if [ -f "$config" ] && [ -x "$build" ]; then
+ export HYBBX_ROOT="${HYBBX_ROOT:-$root}"
+ cd "$root"
+ if [ "$#" -eq 0 ]; then
+ exec "$build" -c "$config"
+ fi
+ exec "$build" -c "$config" "$@"
+fi
+
+echo "HyBBX not found." >&2
+echo "Build and install (default → ./bin):" >&2
+echo " cmake -B build -DCMAKE_BUILD_TYPE=Release" >&2
+echo " cmake --build build" >&2
+echo " cmake --install build" >&2
+echo " ./bin/hybbx-start" >&2
+echo "System prefix:" >&2
+echo " cmake -B build -DCMAKE_INSTALL_PREFIX=/usr/local" >&2
+echo " cmake --build build && sudo cmake --install build" >&2
+echo " /usr/local/hybbx/hybbx-start" >&2
+exit 1
diff --git a/scripts/mock-ardopc.py b/scripts/mock-ardopc.py
new file mode 100755
index 0000000..0a2e110
--- /dev/null
+++ b/scripts/mock-ardopc.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+"""
+Minimal ARDOPC TCP mock for HyBBX plugin host-TCP local tests.
+Control port N, data port N+1. CRC-16 poly 0x8810 (ARDOP host family).
+"""
+from __future__ import annotations
+
+import socket
+import struct
+import sys
+import threading
+import time
+
+INT_POLY = 0x8810
+
+
+def crc16(data: bytes) -> int:
+ reg = 0xFFFF
+ for byte in data:
+ mask = 0x80
+ for _ in range(8):
+ bit = byte & mask
+ mask >>= 1
+ if reg & 0x8000:
+ if bit:
+ reg = (1 + (reg << 1)) & 0xFFFF
+ else:
+ reg = (reg << 1) & 0xFFFF
+ reg ^= INT_POLY
+ else:
+ if bit:
+ reg = (1 + (reg << 1)) & 0xFFFF
+ else:
+ reg = (reg << 1) & 0xFFFF
+ return reg
+
+
+def frame_cmd(text: str) -> bytes:
+ body = (text + "\r").encode("ascii")
+ c = crc16(body)
+ return body + struct.pack(">H", c)
+
+
+def parse_frames(buf: bytearray) -> list[bytes]:
+ out: list[bytes] = []
+ while True:
+ if b"\r" not in buf:
+ break
+ cr = buf.index(0x0D)
+ need = cr + 1 + 2
+ if len(buf) < need:
+ break
+ frame = bytes(buf[:need])
+ del buf[:need]
+ if crc16(frame[:-2]) == struct.unpack(">H", frame[-2:])[0]:
+ out.append(frame[:-2])
+ return out
+
+
+def serve_ctrl(port: int, log: list[str]) -> None:
+ srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ srv.bind(("127.0.0.1", port))
+ srv.listen(1)
+ srv.settimeout(8.0)
+ try:
+ conn, _ = srv.accept()
+ except socket.timeout:
+ srv.close()
+ return
+ conn.settimeout(2.0)
+ conn.sendall(frame_cmd("VERSION crdopc_1.0.0"))
+ buf = bytearray()
+ try:
+ while True:
+ try:
+ chunk = conn.recv(4096)
+ except TimeoutError:
+ break
+ if not chunk:
+ break
+ buf.extend(chunk)
+ for frame in parse_frames(buf):
+ cmd = frame.decode("ascii", errors="replace").strip()
+ log.append(f"ctrl<= {cmd}")
+ if cmd.startswith("INITIALIZE"):
+ conn.sendall(frame_cmd("RDY"))
+ elif cmd == "RDY":
+ pass
+ else:
+ conn.sendall(frame_cmd("RDY"))
+ finally:
+ conn.close()
+ srv.close()
+
+
+def serve_data(port: int, log: list[str]) -> None:
+ srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ srv.bind(("127.0.0.1", port))
+ srv.listen(1)
+ srv.settimeout(8.0)
+ try:
+ conn, _ = srv.accept()
+ except socket.timeout:
+ srv.close()
+ return
+ conn.settimeout(1.0)
+ try:
+ while True:
+ try:
+ chunk = conn.recv(4096)
+ except socket.timeout:
+ continue
+ if not chunk:
+ break
+ log.append(f"data<= {len(chunk)} bytes")
+ finally:
+ conn.close()
+ srv.close()
+
+
+def main() -> int:
+ port = int(sys.argv[1]) if len(sys.argv) > 1 else 18515
+ log: list[str] = []
+ t1 = threading.Thread(target=serve_ctrl, args=(port, log), daemon=True)
+ t2 = threading.Thread(target=serve_data, args=(port + 1, log), daemon=True)
+ t1.start()
+ t2.start()
+ time.sleep(6)
+ for line in log:
+ print(line)
+ return 0 if any("INITIALIZE" in x for x in log) else 1
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/scripts/test-ardop-plugin.sh b/scripts/test-ardop-plugin.sh
new file mode 100755
index 0000000..67900f1
--- /dev/null
+++ b/scripts/test-ardop-plugin.sh
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+# Local test: mock ARDOPC + HyBBX ardop plugin (no Main circuit required).
+set -euo pipefail
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+BUILD="${ROOT}/build"
+HYBBX="${BUILD}/src/hybbxd"
+MOCK_PORT=18515
+TMPDIR="${ROOT}/local/test-ardop-$$"
+mkdir -p "${TMPDIR}"
+
+cleanup() {
+ kill "${MOCK_PID:-}" 2>/dev/null || true
+ kill "${CIRCUIT_PID:-}" 2>/dev/null || true
+ kill "${HYBBX_PID:-}" 2>/dev/null || true
+ rm -rf "${TMPDIR}"
+}
+trap cleanup EXIT
+
+if [[ ! -x "${HYBBX}" ]]; then
+ echo "Build hybbx first: cmake --build build" >&2
+ exit 1
+fi
+
+cat > "${TMPDIR}/hybbx.ini" <<EOF
+[service]
+name = test-ardop
+
+[storage]
+path = ${TMPDIR}/data
+
+[auth]
+auto_login = no
+
+[networks]
+ax25 = no
+ardop = yes
+circuit = no
+
+[transport.telnet]
+enabled = no
+
+[transport.ardop]
+enabled = yes
+ardop_host = 127.0.0.1
+ardop_port = ${MOCK_PORT}
+mycall = TEST-0
+radio_profile = cb
+arq_bandwidth = 500MAX
+listen = yes
+circuit_host = 127.0.0.1
+circuit_port = 17323
+link_id = test-ardop
+EOF
+
+python3 "${ROOT}/scripts/mock-ardopc.py" "${MOCK_PORT}" &
+MOCK_PID=$!
+sleep 0.3
+
+# Minimal TCP acceptor so circuit connect does not block startup (no HBX handshake).
+nc -l 127.0.0.1 17323 >/dev/null 2>&1 &
+CIRCUIT_PID=$!
+sleep 0.2
+
+LOG="${TMPDIR}/hybbx.log"
+stdbuf -oL timeout 5 "${HYBBX}" -c "${TMPDIR}/hybbx.ini" >"${LOG}" 2>&1 &
+HYBBX_PID=$!
+sleep 3
+kill "${HYBBX_PID}" 2>/dev/null || true
+wait "${HYBBX_PID}" 2>/dev/null || true
+kill "${CIRCUIT_PID}" 2>/dev/null || true
+
+grep -q "connected to external ARDOPC" "${LOG}" || {
+ echo "FAIL: no ARDOPC connect log" >&2
+ cat "${LOG}" >&2
+ exit 1
+}
+grep -q "host init sent" "${LOG}" || {
+ echo "FAIL: no host init log" >&2
+ cat "${LOG}" >&2
+ exit 1
+}
+grep -q "CB half-duplex profile" "${LOG}" || {
+ echo "FAIL: no CB profile log" >&2
+ cat "${LOG}" >&2
+ exit 1
+}
+
+echo "OK: ardop plugin local test passed"
+cat "${LOG}" | grep '\[ardop\]'
diff --git a/scripts/test-crdop-plugin.sh b/scripts/test-crdop-plugin.sh
new file mode 100755
index 0000000..aec10e1
--- /dev/null
+++ b/scripts/test-crdop-plugin.sh
@@ -0,0 +1,97 @@
+#!/usr/bin/env bash
+# Local test: mock CRDOPC + HyBBX crdop plugin (CB defaults, no Main circuit required).
+set -euo pipefail
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+BUILD="${ROOT}/build"
+HYBBX="${BUILD}/src/hybbxd"
+MOCK_PORT=18517
+TMPDIR="${ROOT}/local/test-crdop-$$"
+mkdir -p "${TMPDIR}"
+
+cleanup() {
+ kill "${MOCK_PID:-}" 2>/dev/null || true
+ kill "${CIRCUIT_PID:-}" 2>/dev/null || true
+ kill "${HYBBX_PID:-}" 2>/dev/null || true
+ rm -rf "${TMPDIR}"
+}
+trap cleanup EXIT
+
+if [[ ! -x "${HYBBX}" ]]; then
+ echo "Build hybbx first: cmake --build build" >&2
+ exit 1
+fi
+
+cat > "${TMPDIR}/hybbx.ini" <<EOF
+[service]
+name = test-crdop
+
+[storage]
+path = ${TMPDIR}/data
+
+[auth]
+auto_login = no
+
+[networks]
+ax25 = no
+ardop = no
+crdop = yes
+circuit = no
+
+[transport.telnet]
+enabled = no
+
+[transport.crdop]
+enabled = yes
+modem_host = 127.0.0.1
+modem_port = ${MOCK_PORT}
+mycall = CB-0
+listen = yes
+circuit_host = 127.0.0.1
+circuit_port = 17325
+link_id = test-crdop
+EOF
+
+python3 "${ROOT}/scripts/mock-ardopc.py" "${MOCK_PORT}" &
+MOCK_PID=$!
+sleep 0.3
+
+nc -l 127.0.0.1 17325 >/dev/null 2>&1 &
+CIRCUIT_PID=$!
+sleep 0.2
+
+LOG="${TMPDIR}/hybbx.log"
+stdbuf -oL timeout 5 "${HYBBX}" -c "${TMPDIR}/hybbx.ini" >"${LOG}" 2>&1 &
+HYBBX_PID=$!
+sleep 3
+kill "${HYBBX_PID}" 2>/dev/null || true
+wait "${HYBBX_PID}" 2>/dev/null || true
+kill "${CIRCUIT_PID}" 2>/dev/null || true
+
+grep -q '\[crdop\] connected to external CRDOPC' "${LOG}" || {
+ echo "FAIL: no CRDOP CRDOPC connect log" >&2
+ cat "${LOG}" >&2
+ exit 1
+}
+grep -q '\[crdop\] modem VERSION' "${LOG}" || {
+ echo "FAIL: no CRDOP VERSION log" >&2
+ cat "${LOG}" >&2
+ exit 1
+}
+grep -q '\[crdop\] host init sent' "${LOG}" || {
+ echo "FAIL: no CRDOP host init log" >&2
+ cat "${LOG}" >&2
+ exit 1
+}
+grep -q '\[crdop\] CB half-duplex profile' "${LOG}" || {
+ echo "FAIL: no CRDOP CB profile log" >&2
+ cat "${LOG}" >&2
+ exit 1
+}
+grep -q 'profile=cb' "${LOG}" || {
+ echo "FAIL: crdop profile not cb" >&2
+ cat "${LOG}" >&2
+ exit 1
+}
+
+echo "OK: crdop plugin local test passed"
+grep '\[crdop\]' "${LOG}"
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com