. "$BST_LIB" # BST-TIMEOUT: 400 # measured: this suite runs 129s on a host that was # simultaneously running another engineer's bitbake, so ~3x that. It needs a # marker at all only because sections 9's two signal bounds (30s + 75s) are # wall-clock waits for a delivery that never comes; the other 63 cases together # cost about 25s. # lockfile-progs 0.1.19 (Debian; meta-oe recipes-extended) -- the command-line # wrappers around liblockfile's advisory dot-locking. Seven binaries, all one # program dispatching on argv[0]: lockfile-create/-remove/-touch/-check and # mail-lock/-unlock/-touchlock. Upstream hard-links them; in the packaged image # they arrive as seven independent 3 100 887-byte copies, each with a link count # of 1 (measured in the staged rootfs). That is why section 1 checks each name # for itself and section 2 checks each one's usage text -- neither an inode # comparison nor a single "the file exists" case would say anything here. # # --------------------------------------------------------------------------- # WHAT THIS PORT ACTUALLY PUTS AT RISK # # lockfile-progs is ~400 lines that parse options and call four liblockfile # entry points. It adds no locking logic of its own, so what this suite measures # is not "does the C compile" but whether the two INDEPENDENT liveness rules # liblockfile uses to decide "is this lock still valid?" survive on this arm: # # 1. The AGE rule (a lock with no pid in it). lockfile_check() reads the # lockfile's mtime and calls the lock valid iff it is newer than 5 minutes. # This is the default -- `lockfile-create f` writes "0" -- and it is what # lockfile-touch exists to refresh. # # 2. The PID rule (a lock created with --use-pid). The lockfile holds the # caller's PARENT pid (L_PPID, not L_PID -- see cmd_lock()), and # lockfile_check() asks kill(pid, 0) whether that process is still alive. # # The two rules are not redundant: the pid branch SHORT-CIRCUITS the age rule, # so if kill(pid,0) lies about liveness, a --use-pid lock is never reclaimed no # matter how old it gets, while a plain lock is unaffected. Splitting the suite # along that seam is what makes the reds below attributable to the kernel rather # than to this port. # # The underlying create primitive is a hard link: lockfile_create() opens a # uniquely-named temp file, link()s it to the lock name, then lstat()s BOTH and # claims the lock iff (st_rdev, st_ino) match. link-primitive below measures that # substrate directly, so a collapse there reads as the filesystem's rather than # this port's. (82-liblockfile owns the exhaustive syscall-level version of that # probe; this one case is here so a red in THIS suite is diagnosable without # cross-referencing.) # # --------------------------------------------------------------------------- # EXPECTED REDS ON THIS PLATFORM TODAY -- four, two causes, both already tracked # # lockfile-progs/use-pid-stale-detect lockfile-check --use-pid must report a # lock whose recorded owner is gone. # lockfile-progs/use-pid-stale-break lockfile-create must then reclaim it. # # Both fail for one reason: brintos/linux#41 -- wasm32_sys_kill returns 0 for # every pid when sig == 0, without looking the pid up, so kill(pid, 0) reports # EVERY process alive (including a pid far above pid_max). lockfile_check() # therefore never sees a dead owner. BRINTOS-259 filed and root-caused this at # the library level; these two cases are the same defect reached through the # shipped user-facing tools. # # lockfile-progs/sigkill-substrate SIGKILL must stop a child. # lockfile-progs/touch-sigterm lockfile-touch must exit 0 on SIGTERM. # # Both fail for a second, INDEPENDENT reason: brintos/linux#21 -- no signal is # delivered to anything, SIGKILL included, while kill(2) still reports success. # #41 is the sig==0 existence probe lying; #21 is delivery not happening at all. # The two are separable and are separated here, so each pair flips green on its # own issue: sigkill-substrate answers "does ANY signal arrive" before # touch-sigterm asks "does lockfile-touch handle one correctly", which is why a # red in the second is attributable without reading another suite. # # None of the four is skipped or relaxed: each flips green on its own the day # its kernel arm is fixed, and that is the regression signal this suite exists # to buy. Every age-rule case beside them is expected GREEN, and that split is # the evidence that the port itself is sound. # --------------------------------------------------------------------------- require lockfile-create lockfile-progs/present bst_ok lockfile-progs/present w=${TMPDIR:-/tmp}/.bst-lockfile-progs.$$ rm -rf "$w"; mkdir -p "$w" || { bst_notok lockfile-progs/workdir "cannot mkdir $w"; exit 0; } # rc_out -- assert BOTH the exit status and a # substring of the combined output. The stock helpers cannot express this pair: # check_out demands rc 0, and every usage path here exits 1 while printing the # text that proves WHICH of the seven argv[0] personalities ran. rc_out() { _id=$1; _want=$2; _needle=$3; shift 3 _o=$("$@" 2>&1); _rc=$? case "$_o" in *"$_needle"*) _h=1 ;; *) _h=0 ;; esac if [ "$_rc" -eq "$_want" ] && [ "$_h" -eq 1 ]; then bst_ok "$_id" elif bst_execfail "$_o"; then bst_unrun "$_id" "$_o" else bst_notok "$_id" "rc=$_rc want=$_want want:'$_needle' got:'$(_bst_1line "$_o")'"; fi } # first line of a file, using the shell builtin -- these run once per case and # on this runtime the fork is the cost, not the work. # BST-REDIRECT-OK: called only inside $( ), which forks; the argument is a .lock under $TMPDIR. firstline() { _l=''; [ -r "$1" ] && read _l < "$1" 2>/dev/null; printf '%s' "$_l"; } isfile() { if [ -f "$1" ]; then bst_ok "$2"; else bst_notok "$2" "missing: $1"; fi; } gone() { if [ -e "$1" ]; then bst_notok "$2" "still present: $1"; else bst_ok "$2"; fi; } # --- 1. the shipped payload: seven binaries, one program ----------------------- for b in lockfile-create lockfile-remove lockfile-touch lockfile-check \ mail-lock mail-unlock mail-touchlock; do if [ -x "/usr/bin/$b" ]; then bst_ok "lockfile-progs/bin-$b" else bst_notok "lockfile-progs/bin-$b" "not installed or not executable: /usr/bin/$b"; fi done # --- 2. argv[0] dispatch ------------------------------------------------------- # Each name must print ITS OWN usage. This is the only thing that proves the # hard links / copies survived packaging as distinct personalities rather than # collapsing to one name -- a failure mode no "the file exists" case can see. rc_out lockfile-progs/usage-create 1 "usage: lockfile-create" lockfile-create rc_out lockfile-progs/usage-remove 1 "usage: lockfile-remove" lockfile-remove rc_out lockfile-progs/usage-touch 1 "usage: lockfile-touch" lockfile-touch rc_out lockfile-progs/usage-check 1 "usage: lockfile-check" lockfile-check rc_out lockfile-progs/usage-mail-lock 1 "usage: mail-lock" mail-lock --lock-name rc_out lockfile-progs/usage-mail-unlock 1 "usage: mail-unlock" mail-unlock extra rc_out lockfile-progs/usage-mail-touchlock 1 "usage: mail-touchlock" mail-touchlock extra # --- 3. core lifecycle (the age rule, default) --------------------------------- f=$w/f; touch "$f" check lockfile-progs/create lockfile-create "$f" isfile "$f.lock" lockfile-progs/lock-created check_eq lockfile-progs/lock-content-zero "0" "$(firstline "$f.lock")" check lockfile-progs/check-held lockfile-check "$f" check lockfile-progs/touch-oneshot lockfile-touch --oneshot "$f" check lockfile-progs/check-after-touch lockfile-check "$f" check lockfile-progs/remove lockfile-remove "$f" gone "$f.lock" lockfile-progs/lock-removed # an absent lock is "not locked": liblockfile returns -1, which main() hands to # exit() as 255. Asserting the exact status keeps a future rc-mapping change # visible -- callers script on this. check_rc lockfile-progs/check-after-remove 255 lockfile-check "$f" # no temp-file residue: lockfile_create() unlinks its .lk