# BST-TIMEOUT: 900 # three fresh guest processes (lsusb, testlibusb, the probe), # # each a statically linked wasm module carrying libusb-compat # # AND libusb-1.0 AND glibc. The probe alone is 3.5 MB. The # # work is trivial; the process creation is not, and a trip # # costs the whole run's ==BST-END== sentinel. # # THE MARKER IS ON LINE 1 because the runner reads only the first 20 lines for it. # # libusb-compat-0.1 0.1.8 (meta-oe recipes-support/libusb) -- BRINTOS-1892. # The libusb-0.1 compatibility layer over libusb-1.0: one translation unit # (libusb/core.c) that reimplements the old synchronous API on top of the new # one, and PROVIDES virtual/libusb0 so a later round can take up openobex and # the other libusb-0.1 consumers. # # THIS PORT SHIPS SOMETHING THAT RUNS, WHICH IS UNUSUAL FOR A LIBRARY HERE. The # recipe inherits ptest and meta-oe packages upstream's two example programs -- # examples/lsusb and examples/testlibusb -- as libusb-compat-ptest. This distro # sets DISTRO_FEATURES without "ptest" so that package is not built by default; # libusb-compat_%.bbappend turns PTEST_ENABLED on for this one recipe precisely # so the suite can run them (§2 there). # # --------------------------------------------------------------------------- # WHAT THE LIBRARY DOES ON THIS MACHINE, AND WHY THAT IS A RESULT AND NOT A GAP # # usb_init() is `void`. It calls libusb_init() and, on failure, logs through # usbi_err and returns with the static ctx left NULL. libusb-0.1 has no way to # report that, so usb_find_busses() and usb_find_devices() each open with an # explicit `if (!ctx) return 0;` -- upstream's own comment calls it "trap that # situation here". ZERO BUSSES AND ZERO DEVICES IS THEREFORE THE DEFINED ANSWER # on a machine where libusb-1.0 could not initialise, not an accident and not a # degradation. # # And libusb-1.0 cannot initialise here. This image's libusb1 is built # --disable-udev (BRINTOS-1576), which compiles out the assume-/dev/bus/usb # fallback at the end of find_usbfs_path(); the kernel has a USB core but no host # controller, so /dev/bus/usb and /proc/bus/usb are both absent and op_init() # refuses with LIBUSB_ERROR_OTHER (-99). 99-libusb1.test establishes that half # and skips its own runtime cases for want of a consumer; this suite IS that # consumer, so what libusb1's suite could only reason about is measured here. # # THAT IS EXACTLY WHY THE ptest ON ITS OWN IS NOT ENOUGH. lsusb and testlibusb # both call usb_init/usb_find_busses/usb_find_devices, print one line per device # found, find none, and exit 0 -- and they would exit 0 just the same if the # library had never initialised. Their PASS says the API is callable and does not # crash; it cannot say which of the two worlds produced it. data/libusb-compat-probe # is what separates them: it sets usb_set_debug(3) BEFORE usb_init, which is what # makes usbi_log emit at all, and the failing usb_init then writes # # libusb-compat error: usb_init: initialization failed! # # to stderr. That line is the one directly observable statement that the -99 path # was taken rather than an empty-but-live bus, and the suite reads it. # # THE SUITE MUST NOT RED ON A HEALTHIER MACHINE. A runtime that grew a host # controller would make libusb_init succeed, and the enumeration cases would then # be reporting a real bus. So the probe's device-count assertions are written # against the bus state the suite reads for itself (the same /dev/bus/usb, # /proc/bus/usb, /sys/bus/usb probe 99-libusb1.test uses), and where a bus exists # they become skips naming what was found instead of reds. # # --------------------------------------------------------------------------- # WHY usb_open(NULL) IS NOT ASSERTED # # The obvious negative case -- does the API reject a null device rather than # crash -- is deliberately absent. Upstream's usb_open() is # # usb_dev_handle *udev = malloc(sizeof(*udev)); # if (!udev) return NULL; # r = libusb_open((libusb_device *) dev->dev, &udev->handle); # # so it dereferences its argument before any null check, on every platform. # libusb-0.1 never contracted null-safety there: every caller passes a device the # enumeration handed it. Asserting a rejection would assert a contract that does # not exist, and running it would trap the guest -- and a suite that wedges the # machine is quarantined and costs its siblings the boot, which is a far worse # outcome than a case not written. The null-handling that IS contracted, the # `if (!ctx)` trap, is exercised instead, twice over (see probe/find-* below). # # --------------------------------------------------------------------------- # THE PACKAGING HALF IS NOT DECORATION. Every ptest binary links libusb.a at # build time, so the whole ptest would pass unchanged on an image where the # shipped archive, the header and the pkg-config file were all missing. The # ptest tests the code; §1 below is the only thing that tests the port. . "$BST_LIB" PT=/usr/lib/libusb-compat/ptest INC=/usr/include/usb.h PC=/usr/lib/pkgconfig/libusb.pc LIB=/usr/lib/libusb.a CFG=/usr/bin/libusb-config TD=${TMPDIR:-/tmp} nl=' ' # hasfile -- ok iff is a non-empty regular file. # nofile -- ok iff the (already globbed) path does not exist. # has -- ok iff contains the literal . # hasline -- ok iff carries as a WHOLE line. hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "absent or empty: $2"; fi; } has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not present: $2" ;; esac; } nofile() { _id=$1; shift; if [ ! -e "$1" ]; then bst_ok "$_id"; else bst_notok "$_id" "present: $1"; fi; } # --------------------------------------------------------------------------- # 1. What the port ships. # --------------------------------------------------------------------------- hasfile libusb-compat/header-present "$INC" hasfile libusb-compat/pc-present "$PC" hasfile libusb-compat/archive-present "$LIB" hasfile libusb-compat/config-present "$CFG" # THE ARCHIVE IS IN /usr/lib HERE AND IN /lib FOR libusb1, and the difference is # real rather than cosmetic: poky's libusb1 configures --libdir=${base_libdir} # and meta-oe's libusb-compat does not. There is no usrmerge on this distro (not # a DISTRO_FEATURE), so the two directories are genuinely different, and a # consumer that assumed one layout for both would find only one of them. nofile libusb-compat/archive-not-in-base-lib /lib/libusb.a # dpkg's own record of what each package placed, read from a tmpfs copy rather # than in place. Three packages are produced and no main package is -- with # --disable-shared there is no shared object for FILES:${PN} to match, the same # shape libusb1 has -- so the -dev/-staticdev/-ptest split IS the deliverable and # is asserted as such. W=$TD/bst-libusb-compat.$$ mkdir -p "$W" 2>/dev/null for p in dev staticdev ptest; do cat "/var/lib/dpkg/info/libusb-compat-$p.list" > "$W/$p.list" 2>/dev/null || : > "$W/$p.list" done nomain=1; [ -f /var/lib/dpkg/info/libusb-compat.list ] && nomain=0 check_eq libusb-compat/dpkg-no-main-package 1 "$nomain" if [ -s "$W/dev.list" ]; then bst_ok libusb-compat/dpkg-dev-listed dl=$(cat "$W/dev.list") hasline libusb-compat/dpkg-dev-header "$INC" "$dl" hasline libusb-compat/dpkg-dev-pc "$PC" "$dl" hasline libusb-compat/dpkg-dev-config "$CFG" "$dl" else bst_notok libusb-compat/dpkg-dev-listed "dpkg has no file list for libusb-compat-dev" fi if [ -s "$W/staticdev.list" ]; then bst_ok libusb-compat/dpkg-staticdev-listed hasline libusb-compat/dpkg-staticdev-archive "$LIB" "$(cat "$W/staticdev.list")" else bst_notok libusb-compat/dpkg-staticdev-listed "dpkg has no file list for libusb-compat-staticdev" fi if [ -s "$W/ptest.list" ]; then bst_ok libusb-compat/dpkg-ptest-listed pl=$(cat "$W/ptest.list") for f in run-ptest lsusb testlibusb; do hasline "libusb-compat/dpkg-ptest-$f" "$PT/$f" "$pl" done else bst_notok libusb-compat/dpkg-ptest-listed "dpkg has no file list for libusb-compat-ptest" fi # The distro appends --disable-shared at class-target scope and there is no # dynamic loader on this arm, so a .so or a libtool archive here would mean a # recipe reached configure by some other route. set -- /usr/lib/libusb.so*; nofile libusb-compat/no-shared "$1" set -- /usr/lib/libusb.la; nofile libusb-compat/no-libtool "$1" # do_install_ptest MOVES lsusb and testlibusb out of ${bindir} into PTEST_PATH, # upstream's stated reason being that lsusb would otherwise clash with a "real" # lsusb. This image has usbutils' name space to protect, so the move is asserted # from both ends: present under ptest (below) and absent from /usr/bin. nofile libusb-compat/no-bindir-lsusb /usr/bin/lsusb nofile libusb-compat/no-bindir-testlibusb /usr/bin/testlibusb # --- the installed header --------------------------------------------------- # # One batched grep, then fork-free matching against its result. hn=$W/hneedles printf '%s\n' \ 'void usb_init(void);' \ 'void usb_set_debug(int level);' \ 'int usb_find_busses(void);' \ 'int usb_find_devices(void);' \ 'struct usb_bus *usb_get_busses(void);' \ 'char *usb_strerror(void);' \ 'usb_dev_handle *usb_open(struct usb_device *dev);' \ 'int usb_close(usb_dev_handle *dev);' \ 'extern struct usb_bus *usb_busses;' \ 'struct usb_device {' \ 'struct usb_bus {' \ 'struct usb_device_descriptor {' \ 'struct usb_config_descriptor {' \ 'struct usb_interface_descriptor {' \ 'struct usb_endpoint_descriptor {' \ 'struct usb_ctrl_setup {' \ 'typedef struct usb_dev_handle usb_dev_handle;' \ > "$hn" hf=$(grep -oFf "$hn" "$INC" 2>/dev/null | sort -u) # The six entry points this port's own probe and both ptest binaries call. A # header that lost one of these would still compile a trivial consumer. hasline libusb-compat/hdr-decl-init 'void usb_init(void);' "$hf" hasline libusb-compat/hdr-decl-setdebug 'void usb_set_debug(int level);' "$hf" hasline libusb-compat/hdr-decl-findbusses 'int usb_find_busses(void);' "$hf" hasline libusb-compat/hdr-decl-finddevs 'int usb_find_devices(void);' "$hf" hasline libusb-compat/hdr-decl-getbusses 'struct usb_bus *usb_get_busses(void);' "$hf" hasline libusb-compat/hdr-decl-strerror 'char *usb_strerror(void);' "$hf" hasline libusb-compat/hdr-decl-open 'usb_dev_handle *usb_open(struct usb_device *dev);' "$hf" hasline libusb-compat/hdr-decl-close 'int usb_close(usb_dev_handle *dev);' "$hf" # usb_busses is EXPORTED AS A GLOBAL as well as reachable through the accessor, # and both spellings are in use on this image: examples/testlibusb.c walks the # global directly while examples/lsusb.c goes through usb_get_busses(). A port # that shipped only one of them would break one of the two binaries. hasline libusb-compat/hdr-global-busses 'extern struct usb_bus *usb_busses;' "$hf" # The structs that ARE the libusb-0.1 API surface. usb_ctrl_setup is the USB wire # format's control-transfer header as libusb-0.1 spells it. for s in 'struct usb_device {' 'struct usb_bus {' 'struct usb_device_descriptor {' \ 'struct usb_config_descriptor {' 'struct usb_interface_descriptor {' \ 'struct usb_endpoint_descriptor {' 'struct usb_ctrl_setup {'; do n=$(printf '%s' "$s" | sed 's/^struct usb_//; s/ {$//') hasline "libusb-compat/hdr-struct-$n" "$s" "$hf" done hasline libusb-compat/hdr-typedef-handle 'typedef struct usb_dev_handle usb_dev_handle;' "$hf" # --- pkg-config ------------------------------------------------------------- pct=$(cat "$PC" 2>/dev/null) has libusb-compat/pc-name 'Name: libusb' "$pct" # 0.1.12, not 0.1.8. libusb.pc.in carries the libusb-0.1 API version the shim # emulates, which is deliberately NOT the compat layer's own release number -- # a consumer's `pkg-config --atleast-version` is asking about the API. has libusb-compat/pc-version 'Version: 0.1.12' "$pct" has libusb-compat/pc-libdir 'libdir=/usr/lib' "$pct" has libusb-compat/pc-libs 'Libs: -L${libdir} -lusb' "$pct" has libusb-compat/pc-cflags 'Cflags: -I${includedir}' "$pct" # The two lines that say this package IS a shim rather than an implementation. # Requires.private is what a STATIC consumer follows to libusb-1.0, and on this # static-only distro Libs.private/Requires.private are not advisory -- they are # the link line pkg-config hands a consumer. has libusb-compat/pc-emulated 'emulated=1' "$pct" has libusb-compat/pc-emulated-by 'emulated_by=libusb-1.0' "$pct" has libusb-compat/pc-requires-private 'Requires.private: libusb-1.0' "$pct" # --- libusb-config, which is a DISABLED STUB and must stay one ---------------- # # binconfig-disabled replaces upstream's libusb-config with a script that prints # an error and exits 1. That IS the contract on this distro -- a *-config script # hard-codes the build host's paths and is wrong the moment it is cross-compiled # -- so the stub is asserted as behaviour rather than merely as a file, and the # non-zero exit is asserted with it. A libusb-config that started answering would # be a regression, not an improvement. cfgout=$("$CFG" --libs 2>&1); cfgrc=$? check_eq libusb-compat/config-stub-rc 1 "$cfgrc" has libusb-compat/config-stub-message 'should not be used' "$cfgout" has libusb-compat/config-stub-advice 'pkg-config' "$cfgout" # --- the archive: every declared entry point is compiled in ------------------- # # libusb.a is an ar archive of LLVM IR BITCODE, so its string table carries # identifiers but not C string literals. Flatten the printable runs to one token # per line first: a raw grep over binary data reports "Binary file matches" # instead of the matches, which would silently under-count and read as a partial # compile. The octal range rather than [:print:] is the formulation both greps # and both trs on this image agree on (derived in 69-libb64.test). check_eq libusb-compat/archive-format '!' "$(head -c 7 "$LIB" 2>/dev/null)" flat=$W/flat ( tr -c '\40-\176\n' '\n' < "$LIB" > "$flat" 2>/dev/null ) # The names are read out of the INSTALLED HEADER rather than listed here, so the # assertion tracks the header across a version bump instead of a list somebody # has to remember to edit. Every declaration in usb.h is a plain # ` [*]usb_(` at the start of a line; the sed takes the LAST usb_ # token before the paren, because the return type of usb_open is itself # `usb_dev_handle *`. namef=$W/names grep -oE '^[A-Za-z_][A-Za-z0-9_ ]*\**usb_[A-Za-z0-9_]+\(' "$INC" 2>/dev/null \ | sed -E 's/^.*[^A-Za-z0-9_](usb_[A-Za-z0-9_]+)\($/\1/' \ | sort -u > "$namef" # -owFf: -F because a name is a literal, -f for ONE batched pass instead of a # fork per symbol, and -w because without it a name that is a PREFIX of another # is reported as found -- usb_open sits inside nothing here, but usb_reset is a # prefix of usb_resetep and usb_get_string of usb_get_string_simple, so getting # this wrong inflates the count in the direction that reads as success. found=$(grep -owFf "$namef" "$flat" 2>/dev/null | sort -u) ndecl=$(grep -c . "$namef"); nfound=$(printf '%s' "$found" | grep -c .) # The floor guards the check itself: a header that lost its declarations, or an # extraction that silently matched nothing, would otherwise pass vacuously with # zero names to look up. 0.1.8 declares 27. if [ "$ndecl" -ge 25 ] && [ "$nfound" -eq "$ndecl" ]; then bst_ok libusb-compat/sym-complete else bst_notok libusb-compat/sym-complete "declared=$ndecl found=$nfound"; fi # A named sample as well, so a failure names the layer that broke rather than # only reporting a count: the lifecycle, the enumeration, the handle and one # transfer entry point from each of the four transfer types. for s in usb_init usb_set_debug usb_find_busses usb_find_devices usb_get_busses \ usb_open usb_close usb_strerror usb_control_msg usb_bulk_read \ usb_bulk_write usb_interrupt_read usb_interrupt_write \ usb_claim_interface usb_release_interface usb_set_configuration \ usb_get_descriptor usb_detach_kernel_driver_np; do hasline "libusb-compat/sym-$s" "$s" "$found" done # THE SHIM'S OTHER HALF: the compiled archive must carry references INTO # libusb-1.0, because a libusb-0.1 API that resolved without them would not be a # shim at all. core.c is the only translation unit, and these are the libusb-1.0 # entry points it names. # # -oF HERE, NOT -owF, AND THE DIFFERENCE IS FORCED BY THE FILE. The names above # are DEFINED symbols and each gets its own string-table entry, so a word-bounded # match works on them. These are UNDEFINED references, and this bitcode's string # table stores them run together with the identifiers around them -- # `libusb_exitusb_initllvm`, `libusb_openlibusb_closelibusb_set_configuration...` # are single entries in it, verbatim. A -w match finds none of them (measured: 0 # of 10), which would have read as a shim that calls nothing. # # WHAT THAT COSTS, stated rather than glossed: a substring match cannot tell # `libusb_init` from a hypothetical `libusb_init_context` reference, so each case # is weaker than its defined-symbol counterpart above. It is kept because the set # is broad and because the cost is bounded -- and because the probe in §4 links # and RUNS against libusb-1.0, which is a far stronger statement about the shim # than any token match. These cases exist to localise a break to the archive. for s in libusb_init libusb_exit libusb_get_device_list libusb_free_device_list \ libusb_open libusb_close libusb_control_transfer libusb_bulk_transfer \ libusb_interrupt_transfer libusb_get_device_descriptor \ libusb_get_config_descriptor libusb_free_config_descriptor \ libusb_claim_interface libusb_detach_kernel_driver; do if grep -qoF "$s" "$flat" 2>/dev/null; then bst_ok "libusb-compat/uses-$s" else bst_notok "libusb-compat/uses-$s" "the archive references no $s"; fi done # --------------------------------------------------------------------------- # 2. The bus state this machine actually has, read once and reused. # --------------------------------------------------------------------------- # # Every runtime expectation below is conditioned on this rather than asserted # flat, so the suite states a fact about a device-less machine without going red # the day the runtime grows a host controller. A machine WITH USB is a healthier # machine and a suite that reds on it is wrong. devtmpfs=absent; [ -d /dev/bus/usb ] && devtmpfs=present procusb=absent; [ -d /proc/bus/usb ] && procusb=present usbcore=absent; [ -d /sys/bus/usb ] && usbcore=present ndev=0 if [ -d /sys/bus/usb/devices ]; then for d in /sys/bus/usb/devices/*; do [ -e "$d" ] && ndev=$((ndev + 1)); done fi probe_state="usbfs:/dev/bus/usb=$devtmpfs /proc/bus/usb=$procusb sysfs:/sys/bus/usb=$usbcore devices=$ndev" nobus=0 [ "$devtmpfs" = absent ] && [ "$procusb" = absent ] && nobus=1 # --------------------------------------------------------------------------- # 3. Upstream's own ptest, run unmodified. # --------------------------------------------------------------------------- check libusb-compat/ptest-payload test -x "$PT/run-ptest" nbin=0 for t in lsusb testlibusb; do [ -x "$PT/$t" ] && nbin=$((nbin+1)); done check_eq libusb-compat/ptest-binaries 2 "$nbin" if ! command -v ptest-runner >/dev/null 2>&1; then bst_skip libusb-compat/ptest-runner "ptest-runner not installed on this image" else bst_ok libusb-compat/ptest-runner # STAGED TO tmpfs RATHER THAN RUN IN PLACE, for the reason 79-neon.test # records: ptest-runner chdir()s into the ptest directory, and chdir(2) on # the virtiofs root intermittently returns the raw kernel restart sentinel # 513 (brintos/linux#73). run-ptest then invokes each binary as `./$TEST`, so # a failed chdir is not a slow path, it is the whole suite scoring zero. # Unlike neon's, this run-ptest has no `set -e` and no ulimit calls, so the # staging is the only accommodation needed and the SCRIPT ITSELF is shipped # and run byte-identical to meta-oe's. # # BRINTOS-WORKAROUND: brintos/linux#73 remove -- when chdir(2) stops # returning the restart sentinel on the virtiofs root, this staging goes and # `ptest-runner libusb-compat` runs in place. # # The binaries are symlinked rather than copied; nothing reads them through # their own path. A symlink that does not resolve executable is replaced by a # copy, so a guest that refuses them still runs the suite. PTRUN=$W/ptest OUTF=$W/ptest.out mkdir -p "$PTRUN/libusb-compat/ptest" 2>/dev/null cp "$PT/run-ptest" "$PTRUN/libusb-compat/ptest/" 2>/dev/null chmod 755 "$PTRUN/libusb-compat/ptest/run-ptest" 2>/dev/null staged=0 for t in lsusb testlibusb; do ln -s "$PT/$t" "$PTRUN/libusb-compat/ptest/$t" 2>/dev/null if [ -x "$PTRUN/libusb-compat/ptest/$t" ]; then staged=$((staged+1)) else rm -f "$PTRUN/libusb-compat/ptest/$t" 2>/dev/null cp "$PT/$t" "$PTRUN/libusb-compat/ptest/$t" 2>/dev/null \ && chmod 755 "$PTRUN/libusb-compat/ptest/$t" 2>/dev/null \ && staged=$((staged+1)) fi done check_eq libusb-compat/ptest-staged 2 "$staged" # A pipe rather than a redirect, in both directions -- the read side never # performs a stdin redirect, which is the other half of brintos/linux#73. ptest-runner -d "$PTRUN" libusb-compat 2>&1 | tr '\r' '\n' > "$OUTF" # A RUN THAT REPORTED NOTHING CARRIES ITS OWN FIRST DIAGNOSTIC INTO THE # RESULT, because $OUTF is deleted at the end and would otherwise take the # one line naming the cause with it. if ! grep -qE '^(PASS|FAIL):' "$OUTF"; then bst_notok libusb-compat/ptest-verdicts \ "no PASS/FAIL from ptest-runner: $(_bst_1line "$(grep -vE '^(START|BEGIN|DURATION|END|STOP|[0-9]{4}-)' "$OUTF" | head -3 | tr '\n' ' ')")" else bst_ok libusb-compat/ptest-verdicts fi # EVERY CASE ACCOUNTED, BY NAME AND BY COUNT. run-ptest emits `PASS:$TEST` # with no space after the colon, so the stream is normalised into one shape # once, here, rather than at each reader. sed -n 's/^\(PASS\|FAIL\):[ ]*\([^ ]*\)$/\1: \2/p' "$OUTF" > "$OUTF.bin" nverd=$(grep -cE '^(PASS|FAIL): ' "$OUTF.bin") check_eq libusb-compat/ptest-verdict-count 2 "$nverd" for t in lsusb testlibusb; do if grep -qxF "PASS: $t" "$OUTF.bin"; then bst_ok "libusb-compat/ptest-$t" elif grep -qxF "FAIL: $t" "$OUTF.bin"; then bst_notok "libusb-compat/ptest-$t" "upstream's $t exited non-zero" else bst_notok "libusb-compat/ptest-$t" "no verdict emitted for $t" fi done # ptest-runner reached the end of the suite rather than dying inside it. if grep -q '^END:' "$OUTF" || grep -q 'END: ' "$OUTF"; then bst_ok libusb-compat/ptest-end else bst_notok libusb-compat/ptest-end "ptest-runner emitted no END marker" fi # BOTH BINARIES PRINT ONE LINE PER DEVICE FOUND AND NOTHING ELSE, so on a # bus-less machine their combined device output is empty. That is a weak # statement on its own -- §4 is what makes it mean something -- but it is the # one thing the ptest itself can say about enumeration, and it is asserted # rather than left implied. lsusb prints `VVVV:PPPP`; testlibusb prints an # indented `Device: ...` tree. ndevlines=$(grep -cE '^[0-9a-f]{4}:[0-9a-f]{4}$|^ *Device:' "$OUTF" 2>/dev/null) if [ "$nobus" = 1 ]; then check_eq libusb-compat/ptest-no-devices-printed 0 "$ndevlines" else bst_skip libusb-compat/ptest-no-devices-printed \ "a usbfs tree exists, so a non-empty listing is the correct answer -- $probe_state, printed=$ndevlines" fi fi # --------------------------------------------------------------------------- # 4. The probe: what the ptest structurally cannot say. # --------------------------------------------------------------------------- # # data/libusb-compat-probe is cross-linked with wasmcc-glibc against THE VERY # ARCHIVE THIS PACKAGE SHIPS (image/usr/lib/libusb.a) plus libusb1's # libusb-1.0.a. Source and rationale: src/libusb_compat_probe.c. # # Never `test -x` the shipped payload: do_install installs data/ with mode 0644 # and the rootfs is read-only at browser runtime, so it arrives non-executable by # construction. Asserting -x would pass against a hand-placed copy and fail # against every image that ever built it. Assert -f, and let the cp/chmod carry # the executable question. check libusb-compat/probe-shipped test -f "$BST_DATA/libusb-compat-probe" PROBE=$W/probe if cp "$BST_DATA/libusb-compat-probe" "$PROBE" 2>/dev/null && chmod +x "$PROBE" 2>/dev/null; then bst_ok libusb-compat/probe-stage "$PROBE" > "$W/probe.out" 2> "$W/probe.err" prc=$? check_eq libusb-compat/probe-rc 0 "$prc" pout=$(cat "$W/probe.out" 2>/dev/null) perr=$(cat "$W/probe.err" 2>/dev/null) # The completion sentinel first: a probe that died part-way through would # otherwise have its missing lines read as individual failures rather than as # one truncation. hasline libusb-compat/probe-complete 'probe-complete=1' "$pout" # THE ENUMERATION CONTRACT. Both entry points return the number of CHANGES, # so 0 is "nothing appeared or disappeared since the last call" -- which on a # machine with no context is what the `if (!ctx)` trap returns, and on a # machine with a live but empty bus is what a real enumeration returns. The # two are separated by probe-init-refused below, not by these. # # Called twice, because a library that had partially initialised would report # a change on the second call as its lists settled. Both must be 0. if [ "$nobus" = 1 ]; then hasline libusb-compat/probe-find-busses-1 'find-busses-1=0' "$pout" hasline libusb-compat/probe-find-devices-1 'find-devices-1=0' "$pout" hasline libusb-compat/probe-find-busses-2 'find-busses-2=0' "$pout" hasline libusb-compat/probe-find-devices-2 'find-devices-2=0' "$pout" hasline libusb-compat/probe-busses-null 'get-busses-null=1' "$pout" hasline libusb-compat/probe-walk-busses 'walk-busses=0' "$pout" hasline libusb-compat/probe-walk-devices 'walk-devices=0' "$pout" hasline libusb-compat/probe-walk-roots 'walk-roots=0' "$pout" else for c in find-busses-1 find-devices-1 find-busses-2 find-devices-2 \ busses-null walk-busses walk-devices walk-roots; do bst_skip "libusb-compat/probe-$c" \ "a usbfs tree exists, so a non-empty enumeration is the correct answer -- $probe_state" done fi # THE ACCESSOR AND THE GLOBAL MUST AGREE, whatever the bus state. lsusb.c # reads one and testlibusb.c the other, so this holds on every machine and is # asserted unconditionally. hasline libusb-compat/probe-accessor-agrees 'accessor-agrees=1' "$pout" # usb_strerror() is strerror(errno) and callers print it unconditionally, so # it must return a usable string whatever errno holds. Also machine- # independent. hasline libusb-compat/probe-strerror-nonnull 'strerror-nonnull=1' "$pout" hasline libusb-compat/probe-strerror-nonempty 'strerror-nonempty=1' "$pout" # THE ONE CASE THAT SEPARATES THE TWO WORLDS. With usb_set_debug(3) called # before usb_init, usbi_log emits, and a libusb_init that refused writes # # libusb-compat error: usb_init: initialization failed! # # to stderr. On a machine with a usbfs tree libusb_init succeeds and the line # is absent -- so the assertion is conditioned rather than flat, and its # inverse is asserted on the other arm. if [ "$nobus" = 1 ]; then has libusb-compat/probe-init-refused 'initialization failed!' "$perr" else case "$perr" in *'initialization failed!'*) bst_notok libusb-compat/probe-init-refused \ "a usbfs tree exists but libusb_init still refused -- $probe_state" ;; *) bst_ok libusb-compat/probe-init-refused ;; esac fi else bst_notok libusb-compat/probe-stage "cannot stage $BST_DATA/libusb-compat-probe" fi rm -rf "$W"