Skip to content
AshahellPublic

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Arospike

Drive an AROS machine from a host — run commands, push and pull files, inject input, take screenshots — over a connection the guest opens outward.

That direction is the whole design. A VM behind QEMU user-mode networking, or a real machine behind NAT, cannot be connected to. So the agent dials the host, the host keeps a persistent server, and everything else rides that one socket.

Two agents, one protocol:

  • ATCPGUI — a MUI window showing connection state and a live log. What you want when a person is watching.
  • ATCPBIN — headless, -nodefaultlibs, no C runtime. What you want when a screen test is running, because the GUI's own window lands in every capture.

Both are plain C against AROS LVOs. Neither depends on the project they were written in; the export that produces this repo fails if a guest source ever includes a Vulkan header.


Start here

  1. QUICKSTART.md — the shortest path to a working link.
  2. INSTALL.md — building, deploying, and the ABI traps.
  3. REMOTE-UI.md — input injection and screen capture.
  4. DESIGN.md — the protocol and the reasoning behind it.
  5. TRANSFER.md — why file transfer ran at ~100 KB/s, and the pacing + chunk-size fixes already shipped.

The network comes first

Nothing works until AROSTCP is up with a default route. The agent dials out, so a missing stack is not a degraded mode — it is silence, and from the host it looks exactly like a broken agent.

It is worse than silence if the route is missing. Without a default route the guest's first TCP send does not fail, it panics the whole machine:

TCP/IP PANIC: tcp_output

tp->t_template cannot be built without a route, and AROSTCP panics rather than returning an error. So guest/net/ is not optional furniture:

file what it does
net/NETUP.AROS brings up AROSTCP only — no agent
net/SPIKE.AROS brings up AROSTCP and launches the headless agent
net/AROSTCP_IF interface config: static IP for QEMU user-mode networking
net/AROSTCP_ROUTES the default gateway, which is what stops the panic
net/INSTALL_AGENT.AROS one-time: install the boot autostart
net/USER_STARTUP.AROS the autostart itself — network, then agent

Why NETUP and SPIKE are separate

They overlap almost entirely, and merging them is the obvious cleanup. Don't.

The server accepts one agent at a time (backlog 1). SPIKE starts the headless agent; if you also want the GUI, running SPIKE first leaves two agents fighting over one slot — the second hangs in recv() and the first stops being serviced, which presents as an agent that connected and then went deaf. NETUP exists so the boot script can bring the network up and then start one agent of its choosing.

Both are safe to run when AROSTCP is already up: WaitForPort AROSTCP doubles as the is-it-running test. Skip it and a second AROSTCP pops a modal "Another TCP/IP stack is running, please quit it first" requester, which on a headless guest is unrecoverable.

The addresses are QEMU's, not yours

QEMU user-mode (SLIRP) networking hands out a fixed subnet, so AROSTCP_IF uses a static IP rather than DHCP — deterministic, and no client timing to race:

guest 10.0.2.15/255.255.255.0
host / gateway 10.0.2.2
DNS 10.0.2.3

On real hardware or bridged networking, edit both files. The stock AROS db/static-routes ships DEFAULT GATEWAY 192.168.0.1, which does not exist under SLIRP — that default is what the override in AROSTCP_ROUTES exists to correct.

Both scripts copy the stock db into RAM:net/db and overlay these two files there, so the disk image's own network config is never modified. Nothing to undo.

Autostart

Execute CD0:INSTALL_AGENT

once per disk image. It first refreshes the on-disk agent binaries (SYS:ATCPBIN, SYS:ATCPGUI) from the CD — so the copy that will win the next boot is the one on the CD, not whatever stale version the disk already held — then copies USER_STARTUP.AROS to SYS:S/User-Startup (existing file preserved as SYS:S/User-Startup.bak). The stock Startup-Sequence already executes SYS:S/User-Startup if present — no system file is edited, and it is undone by deleting one file. The autostart runs NETUP inline (it returns, and the agent must not start before the network is up) and the agent detached with Run <>NIL: (it loops forever, so running it inline would hang the boot before Workbench appears). See scripts/build_atcp_deploy_iso.sh for how a deploy CD bundles these.


Layout

path what it is
guest/arostcp_gui.c ATCPGUI: MUI window, protocol handler, main loop
guest/arostcp.c ATCPBIN: headless agent, single translation unit
guest/arostcp_agent.c the agent loop both front-ends share
guest/agent_state.c/.h shared state, telemetry, status rendering
guest/frame_parser.c/.h length-prefixed frame reassembly
guest/arostcp_shared.h socket helpers, run_exec_cmd, base64
guest/arostcp_json.h minimal JSON reader plus frame builders
guest/atcp_dbghook.h captures the kernel bug()/kprintf stream
guest/atcp_ui.c/.h input injection and screen capture
guest/test_ui_geom.c host-runnable: close-gadget geometry and title matching
guest/net/ AROSTCP bring-up and autostart — see above
guest/build.sh cross-build both binaries
host/spike_server.py the control server: serve, submit, status
host/spike_control.py protocol and transfer implementation
host/agent_diag.sh says which layer is broken before you restart anything
host/hello_world.sh proves exec, put and get end to end
host/ui_frames.py ui_input/ui_capture frame builders and reply parsing
host/ui_png.py PNG read/write: Pillow when present, stdlib zlib otherwise
host/ui_sweep.py unattended sweep: launch apps, capture each, report
host/aros_screen_triage.py optional screenshot triage behind --triage (needs a VLM)
host/frame_prefilter.py blank/duplicate frame filter used by the triage pass
host/ui_smoke.sh proves input and capture work, and checks the pixels
host/ui_probe.py asserts a capture is a live screen, not a flat buffer
tools/aros_audit.sh repository invariant checks (run before a push)
tests/ host-side suites for the server, pairs routing and UI helpers — no VM needed
skill/ AROS development reference: cross-compiling, ABI traps, debugging

Protocol

Length-prefixed JSON frames: a 4-byte little-endian length, then the object.

The agent opens with hello, advertising its capabilities; the server then drives with ping, exec_cmd, put_begin/put_chunk/put_end, get_begin/get_chunk/get_end, prepare, run_script, and — only when the hello advertised them — ui_input, ui_capture, ui_windows, ui_close.

File chunks are base64 with a SHA-256 check at put_end. Every reply has the same shape, {"id":N,"type":"result","ok":…}; there is no per-command envelope.

Full detail in DESIGN.md; the UI frames in REMOTE-UI.md.


Remote UI control

ui_input injects keyboard and mouse events through input.device, so Intuition routes focus and qualifiers exactly as it does for real hardware. Posting IntuiMessages at a window's UserPort instead reaches one window and bypasses all of that; it was tried and rejected.

ui_capture returns an RGB24 snapshot of the active screen. ui_windows returns Intuition's window list — which is a far better verdict than a screen diff, because a blank window and a crashed app produce equally valid PNGs.

Both agents have it. atcp_ui.c opens intuition.library and cybergraphics.library itself rather than relying on autoinit, which is what lets the headless binary — linked -nodefaultlibs, no C runtime — share the code.


The debug hook

atcp_dbghook.h installs a SetFunction hook on exec's RawPutChar (LVO 86) and captures the guest's bug()/kprintf stream for retrieval over the link. Dump it with atcpdbg [file].

The hook needs an r12 trampoline: this ABI passes the library base in r12, and plain C reads garbage from RSI.

While the hook is installed, QEMU's -serial log stays empty. That is the hook working, not the guest failing to print — worth knowing before you spend an afternoon on it.


Server notes

  • Persistent. It survives agent restarts and reconnects, and takes work from a job spool rather than argv.
  • One agent at a time (backlog 1). See above — this is load-bearing.
  • It never sends bye on its own; the agent stays connected between jobs.
  • When something stops responding, run host/agent_diag.sh before restarting anything. "The agent died", "the server is wedged" and "the guest is gone" are three different problems with three different fixes, and guessing between them costs VM restarts.

Building

export TOOLCHAIN=/path/to/x86_64-aros-toolchain/bin
export AROS_SDK=/path/to/AROS/Developer/include
export AROS_LIB=/path/to/AROS/Developer/lib
cd guest && ./build.sh

build.sh carries the flags that are not optional and explains each one. The short version: -ffixed-r12 because r12 carries a library base inside AROS's own entry trampolines; -mcmodel=large for ATCPBIN because at small the base-global accesses emit zero relocations, SysBase reads garbage, and OpenLibrary("bsdsocket") returns NULL — it links, it runs, it simply never connects; and -fno-builtin because GCC recognises the shim's byte-copy loops as strlen/memcpy and emits calls into a libc that is not there.

What has to match across machines is the ELF ABI generation, not the compiler version — check with x86_64-aros-readelf -h.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages