diff options
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/build-amiga-telnet.sh | 26 | ||||
| -rwxr-xr-x | scripts/build-clients.sh | 19 | ||||
| -rwxr-xr-x | scripts/dev-setup.sh | 19 | ||||
| -rwxr-xr-x | scripts/hybbx.sh | 86 | ||||
| -rwxr-xr-x | scripts/mock-ardopc.py | 137 | ||||
| -rwxr-xr-x | scripts/test-ardop-plugin.sh | 89 | ||||
| -rwxr-xr-x | scripts/test-crdop-plugin.sh | 97 |
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}" |
