1477514f
test: the Linux-to-Mac leg pins the port the Mac's daemon actually announced
a73x 2026-09-04 10:16
Commit message
test/xos.sh
| Old | New | ||
|---|---|---|---|
| @@ -50,10 +50,12 @@ LAN_IP=${LAN#*@} | |||
| 50 | REPO="${MUX_MAC_REPO:-code/rad/mux}" | 50 | REPO="${MUX_MAC_REPO:-code/rad/mux}" |
| 51 | BRANCH=mac-journey | 51 | BRANCH=mac-journey |
| 52 | QUIC_PORT=4433 | 52 | QUIC_PORT=4433 |
| 53 | # 4433 as /proc/net/udp writes a wildcard bind, which is the spelling | 53 | # The port as /proc/net/udp writes a wildcard bind, which is the spelling |
| 54 | # os_oracle.sh's udp_local_bound takes on BOTH arms — the Darwin arm | 54 | # os_oracle.sh's udp_local_bound takes on BOTH arms — the Darwin arm converts |
| 55 | # converts it, so a leg never learns which OS it is asking. | 55 | # it, so a leg never learns which OS it is asking. Derived rather than written |
| 56 | QUIC_HEX=00000000:1151 | 56 | # out, because a hand-typed hex goes stale the moment somebody edits the port |
| 57 | # above it; leg 7 spells the same derivation for the port a Mac announces. | ||
| 58 | QUIC_HEX=$(printf '00000000:%04X' "$QUIC_PORT") | ||
| 57 | 59 | ||
| 58 | # BatchMode so a broken auth fails in seconds rather than prompting a | 60 | # BatchMode so a broken auth fails in seconds rather than prompting a |
| 59 | # harness nobody sits in front of; accept-new so the first run records a | 61 | # harness nobody sits in front of; accept-new so the first run records a |
| @@ -61,9 +63,10 @@ QUIC_HEX=00000000:1151 | |||
| 61 | # spawns plain `ssh $HOST` and reads that same file. | 63 | # spawns plain `ssh $HOST` and reads that same file. |
| 62 | SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new" | 64 | SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new" |
| 63 | 65 | ||
| 64 | # now_ms and the pid_*/file_* names the remote legs source for themselves. | 66 | # now_ms for the leg clock, and sha256_of for the two installed-image hashes. |
| 65 | # The harness spells time and paths through the oracle so this script would | 67 | # The pid_*, file_* and udp_* names are sourced by the REMOTE legs from the |
| 66 | # still answer if it were ever run FROM a Mac. | 68 | # copy beside the binary on each box, which is what keeps a leg from having to |
| 69 | # know which OS is answering it. | ||
| 67 | . "$(dirname "$0")/os_oracle.sh" | 70 | . "$(dirname "$0")/os_oracle.sh" |
| 68 | 71 | ||
| 69 | OK_COUNT=0 | 72 | OK_COUNT=0 |
| @@ -343,12 +346,15 @@ LINFO=$(lssh 60 <<'L' | |||
| 343 | . ~/xos/os_oracle.sh | 346 | . ~/xos/os_oracle.sh |
| 344 | echo "ver=$(~/.local/bin/mux --version)" | 347 | echo "ver=$(~/.local/bin/mux --version)" |
| 345 | echo "sha=$(sha256_of ~/.local/bin/mux)" | 348 | echo "sha=$(sha256_of ~/.local/bin/mux)" |
| 349 | echo "exe=$(real_path ~/.local/bin/mux)" | ||
| 346 | echo "rtd=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" | 350 | echo "rtd=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" |
| 347 | L | 351 | L |
| 348 | ) | 352 | ) |
| 349 | LAN_VER=$(printf '%s\n' "$LINFO" | sed -n 's/^ver=//p') | 353 | LAN_VER=$(printf '%s\n' "$LINFO" | sed -n 's/^ver=//p') |
| 350 | LAN_GOT_SHA=$(printf '%s\n' "$LINFO" | sed -n 's/^sha=//p') | 354 | LAN_GOT_SHA=$(printf '%s\n' "$LINFO" | sed -n 's/^sha=//p') |
| 355 | LAN_EXE=$(printf '%s\n' "$LINFO" | sed -n 's/^exe=//p') | ||
| 351 | LAN_RTD=$(printf '%s\n' "$LINFO" | sed -n 's/^rtd=//p') | 356 | LAN_RTD=$(printf '%s\n' "$LINFO" | sed -n 's/^rtd=//p') |
| 357 | [ -n "$LAN_EXE" ] || fail reset "$LAN would not resolve the path of its installed mux" | ||
| 352 | [ "$LAN_VER" = "$WANT_VER" ] || | 358 | [ "$LAN_VER" = "$WANT_VER" ] || |
| 353 | fail reset "$LAN answers '$LAN_VER', this tree built '$WANT_VER'" | 359 | fail reset "$LAN answers '$LAN_VER', this tree built '$WANT_VER'" |
| 354 | [ "$LAN_GOT_SHA" = "$LAN_BIN_SHA" ] || | 360 | [ "$LAN_GOT_SHA" = "$LAN_BIN_SHA" ] || |
| @@ -434,7 +440,14 @@ L | |||
| 434 | # does not hold is invisible from the client — the handshake simply never | 440 | # does not hold is invisible from the client — the handshake simply never |
| 435 | # answers — and leg 5 would then be grading the ssh fallback while reporting | 441 | # answers — and leg 5 would then be grading the ssh fallback while reporting |
| 436 | # QUIC. | 442 | # QUIC. |
| 443 | # | ||
| 444 | # XDG_CONFIG_HOME is UNSET for this one read. The announce that matters is the | ||
| 445 | # one at the far end of the MAC's entry dial, which is a fresh ssh carrying | ||
| 446 | # this box's own environment and so resolves ~/.config/mux/key; asking under | ||
| 447 | # the prelude's isolated config dir would check a sibling file that happens to | ||
| 448 | # hold the same bytes and guard nothing. | ||
| 437 | ANN=$(lssh 30 <<'L' | 449 | ANN=$(lssh 30 <<'L' |
| 450 | unset XDG_CONFIG_HOME | ||
| 438 | ~/.local/bin/mux d endpoint </dev/null | 451 | ~/.local/bin/mux d endpoint </dev/null |
| 439 | L | 452 | L |
| 440 | ) | 453 | ) |
| @@ -447,6 +460,17 @@ pgrep -x mux | head -1 | |||
| 447 | L | 460 | L |
| 448 | ) | 461 | ) |
| 449 | [ -n "$LPID" ] || fail mac-to-linux "no mux process on $LAN after mux d start -d" | 462 | [ -n "$LPID" ] || fail mac-to-linux "no mux process on $LAN after mux d start -d" |
| 463 | # The image this daemon is running, recorded before anything is pushed at it. | ||
| 464 | # Leg 9 reads it again after the refused push: on Linux an upgrade keeps the | ||
| 465 | # pid across the exec, so the pid alone cannot tell an untouched daemon from | ||
| 466 | # one that swallowed a new image, and the exe is what can. | ||
| 467 | LIS=$(lssh 30 <<L | ||
| 468 | . ~/xos/os_oracle.sh | ||
| 469 | pid_exe $LPID | ||
| 470 | L | ||
| 471 | ) | ||
| 472 | [ "$LIS" = "$LAN_EXE" ] || | ||
| 473 | fail mac-to-linux "daemon $LPID on $LAN execs '$LIS', not the installed '$LAN_EXE'" | ||
| 450 | # Bound BEFORE the dial, asked of /proc and not of the daemon: a leg that | 474 | # Bound BEFORE the dial, asked of /proc and not of the daemon: a leg that |
| 451 | # read "no answer" from the Mac could otherwise be blaming the network for a | 475 | # read "no answer" from the Mac could otherwise be blaming the network for a |
| 452 | # listener that was never up. | 476 | # listener that was never up. |
| @@ -598,19 +622,27 @@ mssh 60 >/dev/null <<M || fail linux-to-mac "shell $L2M_PID is not a child of da | |||
| 598 | . \$HOME/xos/os_oracle.sh | 622 | . \$HOME/xos/os_oracle.sh |
| 599 | pid_children $MPID | grep -qx $L2M_PID | 623 | pid_children $MPID | grep -qx $L2M_PID |
| 600 | M | 624 | M |
| 601 | # WHICH transport carried it, read off the announce rather than assumed. The | 625 | # WHICH transport carried it, read off the announce rather than assumed. |
| 602 | # entry dial started this daemon bare — no --quic, no key — so what it says | 626 | # The entry dial starts this daemon BARE — no --quic and no --key on any |
| 603 | # is `endpoint none`, the handoff's explicit "no coordinates", and the | 627 | # command line — and what the Mac does with that is the product's own |
| 604 | # session stays on the ssh pipe. There is no positive line for that case | 628 | # behaviour, not this gate's to arrange: `mux d endpoint` creates the |
| 605 | # (the client only speaks up when QUIC was tried and lost), so the pin is | 629 | # default key file when the box has none and the daemon then binds a QUIC |
| 606 | # three facts: the daemon offers no arm, it binds no UDP port, and the | 630 | # listener lazily, on an EPHEMERAL port rather than 4433, so the announce |
| 607 | # client printed no fallback sentence. If a future daemon does announce an | 631 | # names a port nothing here could have predicted. Whichever of the two |
| 608 | # arm here, this leg says so instead of quietly grading the other transport. | 632 | # answers comes back, the leg asserts the matching OS fact and reports what |
| 633 | # carried the session; what it will not do is grade one transport while | ||
| 634 | # reporting the other. | ||
| 635 | # | ||
| 609 | # An `if` and not `grep ... && fail`: an AND-OR list whose first command | 636 | # An `if` and not `grep ... && fail`: an AND-OR list whose first command |
| 610 | # fails is the ordinary case here, and reading it as the leg's verdict is | 637 | # fails is the ORDINARY case here, and reading that as the leg's verdict is |
| 611 | # the wrong way round. | 638 | # the wrong way round. |
| 612 | case "$MANN" in | 639 | case "$MANN" in |
| 613 | "endpoint none") | 640 | "endpoint none") |
| 641 | # No arm to move onto, so the pipe that carried the announce is the | ||
| 642 | # session. The daemon's own word is what says so — there is no | ||
| 643 | # positive line on the client side, which only speaks up when QUIC | ||
| 644 | # was tried and lost — and the OS fact beside it is that nothing is | ||
| 645 | # bound at the port this gate would otherwise expect. | ||
| 614 | printf '%s\n' "$MSTATE" | grep -qx 'udp=none' || | 646 | printf '%s\n' "$MSTATE" | grep -qx 'udp=none' || |
| 615 | fail linux-to-mac "$MAC announces no endpoint yet binds UDP $QUIC_PORT: $MSTATE" | 647 | fail linux-to-mac "$MAC announces no endpoint yet binds UDP $QUIC_PORT: $MSTATE" |
| 616 | if grep -q 'attaching over ssh' "$SCRATCH/l2m.err"; then | 648 | if grep -q 'attaching over ssh' "$SCRATCH/l2m.err"; then |
| @@ -618,12 +650,23 @@ case "$MANN" in | |||
| 618 | fi | 650 | fi |
| 619 | L2M_WIRE="ssh handoff (the daemon announced 'endpoint none')" ;; | 651 | L2M_WIRE="ssh handoff (the daemon announced 'endpoint none')" ;; |
| 620 | 'endpoint '*) | 652 | 'endpoint '*) |
| 621 | printf '%s\n' "$MSTATE" | grep -qx 'udp=bound' || | 653 | MPORT=${MANN#endpoint } |
| 622 | fail linux-to-mac "$MAC announces '$MANN' but binds no UDP $QUIC_PORT: $MSTATE" | 654 | MPORT=${MPORT%% *} |
| 655 | case "$MPORT" in | ||
| 656 | ''|*[!0-9]*) fail linux-to-mac "$MAC announces no readable port: '$MANN'" ;; | ||
| 657 | esac | ||
| 658 | # The announced port, as /proc/net/udp writes a wildcard bind — the | ||
| 659 | # spelling both arms of the oracle take. Asked of lsof on the Mac, | ||
| 660 | # so the claim "the daemon is listening there" is the OS's and not | ||
| 661 | # the daemon's own. | ||
| 662 | mssh 60 >/dev/null <<M || fail linux-to-mac "$MAC announces '$MANN' but binds no UDP $MPORT" | ||
| 663 | . \$HOME/xos/os_oracle.sh | ||
| 664 | udp_local_bound $(printf '00000000:%04X' "$MPORT") | ||
| 665 | M | ||
| 623 | if grep -q 'attaching over ssh' "$SCRATCH/l2m.err"; then | 666 | if grep -q 'attaching over ssh' "$SCRATCH/l2m.err"; then |
| 624 | fail linux-to-mac "$MAC offered QUIC ('$MANN') and the dial fell back to ssh" | 667 | fail linux-to-mac "$MAC offered QUIC on $MPORT and the dial fell back to ssh" |
| 625 | fi | 668 | fi |
| 626 | L2M_WIRE="QUIC (the daemon announced '$MANN')" ;; | 669 | L2M_WIRE="QUIC on the lazily-bound port $MPORT" ;; |
| 627 | *) | 670 | *) |
| 628 | fail linux-to-mac "$MAC's daemon answers no announce this leg can read: '$MANN'" ;; | 671 | fail linux-to-mac "$MAC's daemon answers no announce this leg can read: '$MANN'" ;; |
| 629 | esac | 672 | esac |
| @@ -775,6 +818,8 @@ printf '%s\n' "$POST" | grep -qx "lanpid=$LPID" || | |||
| 775 | fail upgrade-refused "$LAN's daemon is no longer $LPID: $POST" | 818 | fail upgrade-refused "$LAN's daemon is no longer $LPID: $POST" |
| 776 | printf '%s\n' "$POST" | grep -qx "macexe=$MAC_EXE" || | 819 | printf '%s\n' "$POST" | grep -qx "macexe=$MAC_EXE" || |
| 777 | fail upgrade-refused "$MAC's daemon execs something else now: $POST" | 820 | fail upgrade-refused "$MAC's daemon execs something else now: $POST" |
| 821 | printf '%s\n' "$POST" | grep -qx "lanexe=$LAN_EXE" || | ||
| 822 | fail upgrade-refused "$LAN's daemon execs something else now: $POST" | ||
| 778 | ok upgrade-refused "both pushes refused on uname alone; both images and both daemons untouched" | 823 | ok upgrade-refused "both pushes refused on uname alone; both images and both daemons untouched" |
| 779 | 824 | ||
| 780 | # ---- leg 10: stop — and each box's socket goes with its daemon --------- | 825 | # ---- leg 10: stop — and each box's socket goes with its daemon --------- |