. "$BST_LIB" # BST-TIMEOUT: 600 # socat 1.8.0.0 -- second half: loopback TCP/UDP, a pty, the file addresses and # the companion tools. Split from 88-socat.test so each half gets its own guest; # the reason, and the header describing the suite as a whole, are in that file. require socat socat/present D=${TMPDIR:-/tmp}/bst-socat.$$ rm -rf "$D"; mkdir -p "$D" || { bst_notok socat/workdir "cannot create $D"; exit 0; } # Bound every server. busybox and coreutils both provide timeout; if neither # does, say so once and let the cases run unbounded rather than skipping the # ticket's whole criterion -- an unbounded run is still a result, and the # runner's own per-suite bound is the backstop. T=""; TC="" if command -v timeout >/dev/null 2>&1; then T="timeout 20"; TC="timeout 6"; else bst_notok socat/timeout-available "no timeout(1) on this image; server cases run unbounded under the suite bound alone" fi # A listener with no filesystem artifact (TCP, UDP, the abstract namespace) # cannot be waited on the way a socket node or FIFO can, so those clients retry. # The retry budget is deliberately small and the per-attempt bound short: this is # a product, and 40 attempts at 20 s apiece is 13 minutes PER CASE on the failure # path -- enough for four such cases to blow the whole suite's bound and report a # timeout instead of the four reds that would have named the cause. TRIES=10 # wait_for -- bounded poll on a listener's own artifact. wait_for() { _n=0 while [ "$_n" -lt 40 ]; do [ "$1" "$2" ] && return 0 _n=$((_n+1)); sleep 1 done return 1 } # gap -- the red for a failure this port has already traced # to a platform gap, named at the case rather than left for a reader to # re-derive. An unrecognised failure falls through to a plain red on purpose: # that is what a regression in socat itself should look like, and it must not be # absorbed by an attribution written for a different cause. gap() { case "$2" in *"Cannot allocate memory"*) bst_notok "$1" "$BST_UNRUN_TAG ($BST_UNRUN_ISSUE): a 51 MiB socat image could not be staged this late in the run, so the assertion never ran -- $(_bst_1line "$2")" ;; *"system("*"returned with status"*) bst_notok "$1" "brintos/linux#14 (system(3) ENOSYS): socat's SYSTEM: address goes through system(3), which this kernel does not wire -- $(_bst_1line "$2")" ;; *) bst_notok "$1" "$(_bst_1line "$2")" ;; esac } # relayed -- the byte-for-byte verdict this suite exists # for. Split from the rc case deliberately: a relay that exits 0 having moved # nothing is the failure mode a status check cannot see. relayed() { if [ ! -e "$3" ]; then gap "$1" "no output produced -- $4" elif cmp -s "$2" "$3"; then bst_ok "$1" else gap "$1" "$(wc -c < "$2") bytes in, $(wc -c < "$3" 2>/dev/null) out -- $4"; fi } # --- the payloads ------------------------------------------------------------- # 48000 bytes of fixed text: six transfer blocks, so a short relay is visible. awk 'BEGIN{for(n=1;n<=800;n++) printf "%04d the quick brown fox jumps over the lazy dog 0123456789\n", n}' > "$D/txt.src" # Every byte value 0..255, so a relay that is not eight-bit clean is visible. # # ONE PROCESS, AND THE LOCALE IS PINNED -- both matter. The shell idiom for this # (`printf "\NNN"` in a loop over a command substitution) forks 256 subshells, # and fork is expensive enough on this guest that it took longer than every # assertion in this file combined, with the kernel's per-exit chunk census # flooding the console the whole time. LC_ALL=C is the other half: under a UTF-8 # locale awk's %c emits the UTF-8 ENCODING of code points 128-255, so the # "256-byte" payload is silently 384 bytes and no longer covers the high half at # all. LC_ALL=C awk 'BEGIN{for(i=0;i<256;i++) printf "%c", i}' > "$D/bin.src" # The payloads are rebuilt here because this half relays them too, but their two # assertions are NOT re-emitted: a case id must be unique across all suites, and # 88-socat.test already owns socat/payload-text and socat/payload-binary. # --- loopback: a real socket stack, no route off the machine ------------------- $T socat -u FILE:"$D/txt.src" TCP4-LISTEN:22345,bind=127.0.0.1,reuseaddr 2>"$D/tl.err" & lpid=$! i=0; trc=1 while [ $i -lt $TRIES ]; do $TC socat -u TCP4-CONNECT:127.0.0.1:22345 FILE:"$D/tcp.out",create 2>>"$D/tl.err" && { trc=0; break; } i=$((i+1)); sleep 1 done if [ "$trc" -eq 0 ]; then relayed socat/tcp-loopback "$D/txt.src" "$D/tcp.out" "$(_bst_1line "$(cat "$D/tl.err" 2>/dev/null)")" else gap socat/tcp-loopback "never connected on 127.0.0.1:22345: $(cat "$D/tl.err" 2>/dev/null)"; fi $T socat TCP4-LISTEN:22346,bind=127.0.0.1,reuseaddr EXEC:cat 2>"$D/te.err" & spid=$! i=0; erc=1 while [ $i -lt $TRIES ]; do $TC socat -t2 - TCP4-CONNECT:127.0.0.1:22346 < "$D/txt.src" > "$D/tcpecho.out" 2>>"$D/te.err" && { erc=0; break; } i=$((i+1)); sleep 1 done kill $spid 2>/dev/null if [ "$erc" -eq 0 ]; then relayed socat/tcp-echo-roundtrip "$D/txt.src" "$D/tcpecho.out" "$(_bst_1line "$(cat "$D/te.err" 2>/dev/null)")" else gap socat/tcp-echo-roundtrip "never completed on 127.0.0.1:22346: $(cat "$D/te.err" 2>/dev/null)"; fi $T socat -u UDP4-RECVFROM:22347,bind=127.0.0.1 FILE:"$D/udp.out",create 2>"$D/ud.err" & rpid=$! i=0 while [ $i -lt $TRIES ]; do [ -s "$D/udp.out" ] && break printf 'socat-udp-marker\n' | $TC socat -u - UDP4-SENDTO:127.0.0.1:22347 2>>"$D/ud.err" i=$((i+1)); sleep 1 done # NOT check_eq: it compares the VALUE, so a receiver that could not be staged # leaves the file empty and the case reports `got:''` -- a red with no cause on # it, which is the one thing a partially-red suite may not ship. Compare by hand # and hand the diagnostic to gap(), so this case attributes itself exactly like # the other exec-staging casualties. udp_got=$(cat "$D/udp.out" 2>/dev/null) if [ "$udp_got" = "socat-udp-marker" ]; then bst_ok socat/udp-loopback else gap socat/udp-loopback "want:'socat-udp-marker' got:'$udp_got' -- $(cat "$D/ud.err" 2>/dev/null)"; fi # --- pty, file addresses and the companions ----------------------------------- # A pty is what makes socat useful as a serial/terminal bridge, and it is the # one address here that needs the kernel's pty layer rather than its socket one. $T socat -u PTY,link="$D/pty.link",raw,echo=0 /dev/null 2>"$D/pty.err" & ppid=$! if wait_for -L "$D/pty.link"; then bst_ok socat/pty-link else bst_notok socat/pty-link "PTY,link= created no symlink: $(_bst_1line "$(cat "$D/pty.err" 2>/dev/null)")"; fi kill $ppid 2>/dev/null $T socat -u OPEN:"$D/txt.src",rdonly FILE:"$D/open.out",create 2>"$D/op.err" relayed socat/open-rdonly "$D/txt.src" "$D/open.out" "$(_bst_1line "$(cat "$D/op.err" 2>/dev/null)")" $T socat -u FILE:"$D/txt.src" CREATE:"$D/create.out" 2>"$D/cr.err" relayed socat/create-address "$D/txt.src" "$D/create.out" "$(_bst_1line "$(cat "$D/cr.err" 2>/dev/null)")" # ,append must preserve what was already there -- the option is the assertion, # so a plain byte comparison against the source would pass a build that ignored # it. printf 'PREFIX-KEPT\n' > "$D/app.out" $T socat -u FILE:"$D/txt.src" FILE:"$D/app.out",append 2>"$D/ap.err" check_eq socat/append-keeps-prefix "PREFIX-KEPT" "$(head -1 "$D/app.out" 2>/dev/null)" check_eq socat/append-adds-payload 48012 "$(wc -c < "$D/app.out" 2>/dev/null | tr -d ' ')" # -x dumps the traffic in hex on stderr: three bytes in, three bytes named. check_out socat/hexdump "41 42 0a" sh -c "printf 'AB\n' | socat -u -x - /dev/null 2>&1" check_out socat/procan "process id" sh -c "procan 2>&1" check_out socat/filan "cloexec" sh -c "filan -f '$D/txt.src' 2>&1" # procan reports the compiler that built it, and Makefile.in fed that macro the # compiler's full path -- so a cross-built procan shipped an absolute path into # somebody's build tree, and two engineers building the same revision got # different bytes. The port takes the basename; this asserts it on the target, # where a reproducibility regression would otherwise be a build-host warning # nobody reads. The name must still be there: an empty CC line would pass a # "contains no slash" test while telling us nothing. cc=$(procan 2>/dev/null | sed -n 's|^// CC: *||p' | head -1) case "$cc" in "") bst_notok socat/procan-no-buildpath "procan printed no '// CC:' line at all" ;; */*) bst_notok socat/procan-no-buildpath "compiler path baked into the shipped binary: $cc" ;; *) bst_ok socat/procan-no-buildpath ;; esac rm -rf "$D"