Files
wekan/snap-src/bin/cpu-exec
T
Lauri Ojansivu 794346e396 cpu-exec qemu-user fallback everywhere; tests for all newest fixes.
Fix #6458 completely: the reporter's host runs inside a hypervisor that
masks AVX, and the old per-tool AVX wrappers (amd64-only, PATH-based)
were bypassed by absolute-path mongod invocations.

- New general snap-src/bin/cpu-exec: runs any binary directly when the
  CPU has the features it declares (--features x86_64=avx,aarch64=atomics
  or WEKAN_REQUIRED_CPU_FEATURES) and through same-arch qemu-user when
  not; plain zero-overhead exec with no declared features. Lookup order:
  WEKAN_QEMU_USER, qemu-<arch> next to the script (bundle/Docker),
  $SNAP/qemu-<arch>, the snap's migratemongo/avx one, then system
  qemu-user(-static). Clear error + direct run when no qemu exists, so
  the real SIGILL surfaces instead of hiding.
- Snap: mongodb-control and migration-control run every mongod 7 through
  cpu-exec, so MongoDB works without AVX (slower) and migration can READ
  modern MongoDB data on such CPUs; completed-FerretDB switch and
  migration fallbacks unchanged (switch_to_completed_ferretdb extracted).
- release-all.yml: every Linux bundle .zip ships cpu-exec + this-arch's
  static qemu-user (amd64/arm64/ppc64le/s390x/riscv64; stripped from
  win64/mac bundles), flowing into Docker and Snap automatically;
  Sandstorm .spk gets both via build-deps.sh. Bundle launcher and Docker
  entrypoint route node/ferretdb through cpu-exec.
- Docs: mongodb-avx-qemu.md and mongodb-raspi4-qemu.md document the
  built-in mechanism.

Tests, with negative cases, for the newest features and fixes at
changelog: htmljs array-vs-attributes (#6459), description draft
normalize/reset (#6455), comment draft lifecycle (#5547), attachment
delete guard (#5282), rules move actions + import normalization (#6472),
snap mongod-failure/migration recovery (#6458 #6466), FerretDB polling
defaults (#6467 #6468), UI density guards for every #6465 fix, and a
behavioral cpu-exec test against fake cpuinfo + fake qemu. All wired
into test:unit:node. Go table tests for the FerretDB SQLite filter
pushdown are in the wekan/FerretDB repo.

Updated ChangeLog.

Thanks to xet7.
2026-07-16 17:40:24 +03:00

113 lines
4.9 KiB
Bash
Executable File

#!/bin/bash
# ─────────────────────────────────────────────────────────────────────────────
# cpu-exec: run a binary directly when the CPU has every instruction-set
# feature the binary needs, otherwise transparently re-run it through
# qemu-user emulation (which implements the full modern instruction set).
# General mechanism for ALL scripts, sandboxes, platforms and CPUs (#6458).
#
# Why: MongoDB >= 5.0 x86_64 binaries require AVX — on CPUs without it (old
# Intel/AMD, and hypervisors/sandboxes that mask AVX: some QEMU/KVM/Proxmox
# configurations, which is exactly the reported #6458 environment) mongod dies
# instantly with SIGILL (exit 132). MongoDB >= 5 arm64 similarly requires
# ARMv8.2-A (Raspberry Pi 4 and older are ARMv8.0). The snap's old
# migratemongo/avx wrappers did this per-tool, amd64-only, and only for PATH
# lookups — every script invoking $SNAP/bin/mongod by ABSOLUTE path bypassed
# them. This helper replaces that with one general, explicit mechanism.
#
# Usage:
# cpu-exec [--features <arch>=<f1>[+<f2>...][,<arch>=...]] <binary> [args...]
#
# --features declares the CPU features the binary needs, per architecture:
# cpu-exec --features x86_64=avx,aarch64=atomics mongod --dbpath ...
# Only the entry matching `uname -m` is checked (against /proc/cpuinfo
# flags/Features). No --features, an empty spec, or no entry for the current
# architecture means "no special requirements" — the binary is exec'd
# directly with zero overhead, so it is safe to route EVERY binary through
# cpu-exec. The WEKAN_REQUIRED_CPU_FEATURES environment variable, when set,
# overrides --features (same syntax).
#
# Note: `aarch64=atomics` (LSE, ARMv8.1) is the practical /proc/cpuinfo
# proxy for MongoDB's ARMv8.2-A requirement — Cortex-A53/A72 (Raspberry Pi
# 3/4) lack it, ARMv8.2-A cores have it.
#
# qemu-user lookup order (first executable wins):
# 1. $WEKAN_QEMU_USER (explicit override)
# 2. qemu-<arch> next to this script (bundle .zip / Docker /build)
# 3. $SNAP/qemu-<arch>, $SNAP/bin/qemu-<arch>
# 4. $SNAP/migratemongo/avx/qemu-x86_64 (existing snap amd64 bundle)
# 5. qemu-<arch> / qemu-<arch>-static on PATH
# If none is found, a clear error is printed and the binary runs directly so
# the real failure (SIGILL) surfaces in the logs instead of hiding.
#
# WEKAN_CPUINFO overrides the /proc/cpuinfo path (used by the unit tests).
# ─────────────────────────────────────────────────────────────────────────────
set -u
FEATURES_SPEC=""
if [ "${1:-}" = "--features" ]; then
FEATURES_SPEC="${2:-}"
shift 2
fi
[ -n "${WEKAN_REQUIRED_CPU_FEATURES:-}" ] && FEATURES_SPEC="${WEKAN_REQUIRED_CPU_FEATURES}"
if [ $# -lt 1 ]; then
echo "Usage: cpu-exec [--features arch=feat1+feat2,arch2=...] <binary> [args...]" >&2
exit 64
fi
ARCH="$(uname -m)"
CPUINFO="${WEKAN_CPUINFO:-/proc/cpuinfo}"
# Pick this architecture's '+'-separated feature list out of the spec.
required=""
IFS=',' read -r -a _entries <<< "$FEATURES_SPEC"
for _e in "${_entries[@]:-}"; do
case "$_e" in
"$ARCH"=*) required="${_e#*=}" ;;
esac
done
# Which of the required features is the CPU missing? x86 lists them on
# "flags :" lines, ARM on "Features :" lines.
missing=""
if [ -n "$required" ] && [ -r "$CPUINFO" ]; then
IFS='+' read -r -a _feats <<< "$required"
for _f in "${_feats[@]:-}"; do
[ -z "$_f" ] && continue
grep -Eiq "^(flags|features)[[:space:]]*:.*[[:space:]]${_f}([[:space:]]|$)" "$CPUINFO" \
|| missing="${missing} ${_f}"
done
fi
if [ -z "$missing" ]; then
exec "$@"
fi
# The CPU lacks something this binary needs: find a same-architecture
# qemu-user to emulate the missing instructions.
HERE="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
QEMU=""
for candidate in \
"${WEKAN_QEMU_USER:-}" \
"${HERE:+$HERE/qemu-$ARCH}" \
"${SNAP:+$SNAP/qemu-$ARCH}" \
"${SNAP:+$SNAP/bin/qemu-$ARCH}" \
"${SNAP:+$SNAP/migratemongo/avx/qemu-$ARCH}" \
"$(command -v "qemu-$ARCH" 2>/dev/null)" \
"$(command -v "qemu-$ARCH-static" 2>/dev/null)"; do
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
QEMU="$candidate"
break
fi
done
if [ -n "$QEMU" ]; then
echo "cpu-exec: this CPU lacks${missing}; running $1 through qemu-user emulation ($QEMU)." >&2
echo "cpu-exec: emulation is slower — consider migrating to FerretDB (no special CPU features needed)." >&2
exec "$QEMU" "$@"
fi
echo "cpu-exec: ERROR: this CPU lacks${missing} and no qemu-user (qemu-$ARCH) was found;" >&2
echo "cpu-exec: running $1 directly — expect SIGILL (Illegal instruction). Install qemu-user(-static) or migrate to FerretDB." >&2
exec "$@"