A maintainable, extensible BitTorrent client (C11, Linux/io_uring) targeting 10 GbE saturation. All torrent functionality is built from scratch; liburing is the only linked third-party dependency on the data path. Implements Phases 1-7 of the roadmap: - core: page-aligned buffer pool, MPMC/Treiber queues, bitfields, worker pool - crypto: SHA-1/256 (SHA-NI + scalar), Merkle (BEP-52), RC4 (MSE) - bencode/metainfo: zero-copy parser, v1/v2/hybrid .torrent + magnet - peer: sans-IO wire codec, MSE/PE handshake state machine, BEP-10, ut_metadata, PEX - piece/storage: block-level multi-peer engine, rarest-first + endgame, per-file completion events + single-file relocate (move-as-you-finish) - tracker/dht: HTTP + UDP (BEP-15) trackers, BEP-5 KRPC iterative lookup - platform: io_uring reactor (SQPOLL, registered buffers, SEND_ZC) - surface: versioned RPC, native plugin ABI, sandboxed Lua scripting, nautd/nautctl Verified against libtorrent (single/multi/hybrid, MSE, magnet-via-DHT, swarm); unit + interop tests green; ASan/UBSan/TSan clean. Scripting reference in docs/scripting.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
98 lines
2.6 KiB
Bash
98 lines
2.6 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
daemon=$1
|
|
ctl=$2
|
|
plugin=$3
|
|
script=$4
|
|
tmp=$(mktemp -d)
|
|
socket="$tmp/nautd.sock"
|
|
daemon_log="$tmp/nautd.log"
|
|
events_log="$tmp/events.log"
|
|
|
|
cleanup() {
|
|
result=$?
|
|
if [[ -n "${daemon_pid:-}" ]]; then
|
|
kill "$daemon_pid" 2>/dev/null || true
|
|
wait "$daemon_pid" 2>/dev/null || true
|
|
fi
|
|
if [[ -n "${events_pid:-}" ]]; then
|
|
kill "$events_pid" 2>/dev/null || true
|
|
wait "$events_pid" 2>/dev/null || true
|
|
fi
|
|
if [[ "$result" -ne 0 ]]; then
|
|
cat "$daemon_log" >&2 2>/dev/null || true
|
|
cat "$events_log" >&2 2>/dev/null || true
|
|
fi
|
|
rm -rf "$tmp"
|
|
return "$result"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
"$daemon" --socket "$socket" --plugin "$plugin" --script "$script" \
|
|
>"$daemon_log" 2>&1 &
|
|
daemon_pid=$!
|
|
|
|
for _ in $(seq 1 100); do
|
|
[[ -S "$socket" ]] && break
|
|
sleep 0.02
|
|
done
|
|
[[ -S "$socket" ]]
|
|
|
|
"$ctl" --socket "$socket" ping | grep -q '"service": "nautd"'
|
|
"$ctl" --socket "$socket" plugins | grep -q '"memory"'
|
|
|
|
timeout 5 "$ctl" --socket "$socket" events >"$events_log" &
|
|
events_pid=$!
|
|
sleep 0.1
|
|
"$ctl" --socket "$socket" emit \
|
|
'{"type":"torrent_finished","torrent_id":7}' >/dev/null
|
|
|
|
status=
|
|
for _ in $(seq 1 100); do
|
|
status=$("$ctl" --socket "$socket" status)
|
|
if grep -q '"move_commands": 1' <<<"$status" &&
|
|
grep -q '"handled": 1' <<<"$status"; then
|
|
break
|
|
fi
|
|
sleep 0.02
|
|
done
|
|
grep -q '"move_commands": 1' <<<"$status"
|
|
grep -q '"handled": 1' <<<"$status"
|
|
grep -q '"errors": 0' <<<"$status"
|
|
|
|
plugin_status=$("$ctl" --socket "$socket" example.events)
|
|
grep -q '"finished": 1' <<<"$plugin_status"
|
|
|
|
for _ in $(seq 1 100); do
|
|
grep -q '"event": "torrent_finished"' "$events_log" && break
|
|
sleep 0.02
|
|
done
|
|
grep -q '"event": "torrent_finished"' "$events_log"
|
|
|
|
# --- end-to-end move-as-you-finish: register a real torrent's storage, fire a
|
|
# file_complete event, and confirm the script-driven move actually relocates the
|
|
# file on disk (script thread -> bounded queue -> owner thread -> storage). ----
|
|
fixtures=$(dirname "$script")
|
|
root="$tmp/torrent-data"
|
|
"$ctl" --socket "$socket" add_torrent \
|
|
"{\"torrent_id\":42,\"torrent\":\"$fixtures/single_v1.torrent\",\"root\":\"$root\"}" \
|
|
| grep -q '"ok": true'
|
|
|
|
src=$(find "$root" -type f | head -n1)
|
|
[[ -n "$src" ]]
|
|
|
|
"$ctl" --socket "$socket" emit \
|
|
"{\"type\":\"file_complete\",\"torrent_id\":42,\"index\":0,\"path\":\"$src\"}" \
|
|
>/dev/null
|
|
|
|
for _ in $(seq 1 100); do
|
|
[[ -f "$src.moved" ]] && break
|
|
sleep 0.02
|
|
done
|
|
[[ -f "$src.moved" ]]
|
|
[[ ! -f "$src" ]]
|
|
|
|
"$ctl" --socket "$socket" shutdown >/dev/null
|
|
wait "$daemon_pid"
|
|
daemon_pid=
|