From ada078f5b33658d77d5b96bee1227a52aaabc911 Mon Sep 17 00:00:00 2001 From: cx-git-agent Date: Sun, 26 Apr 2026 16:35:08 +0000 Subject: [PATCH] vendor: update cxos-vendor-busybox --- PINNED.json | 16 ++++++++ README.md | 3 -- build.sh | 85 ++++++++++++++++++++++++++++++++++++++++ cxos.config.fragment | 93 ++++++++++++++++++++++++++++++++++++++++++++ fetch.sh | 86 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 280 insertions(+), 3 deletions(-) create mode 100644 PINNED.json delete mode 100644 README.md create mode 100755 build.sh create mode 100644 cxos.config.fragment create mode 100755 fetch.sh diff --git a/PINNED.json b/PINNED.json new file mode 100644 index 0000000..e039a07 --- /dev/null +++ b/PINNED.json @@ -0,0 +1,16 @@ +{ + "schema": "cxos.vendor.busybox/v1", + "version": "1.36.1", + "tarball_url": "https://busybox.net/downloads/busybox-1.36.1.tar.bz2", + "signature_url": "https://busybox.net/downloads/busybox-1.36.1.tar.bz2.sig", + "sha256": "b8cc24c9574d809e7279c3be349795c5d5ceb6fdf19ca709f80cde50e47de314", + "gpg_signing_keys": [ + { + "name": "Denys Vlasenko", + "email": "vda.linux@googlemail.com", + "fingerprint": "C9E9416F76E610DBD09D040F47B70C55ACC9965B" + } + ], + "extracted_dir": "busybox-1.36.1", + "notes": "CxOS Tier-0 userland — single static binary providing /sbin/init, getty, login, sh, ls, ps, cat, mount, ip, ifconfig, dhcpc, plus ~300 more applets. Built with --static to avoid libc. Update version + sha256 together; verify via 'shasum -a 256 busybox-1.36.1.tar.bz2'." +} diff --git a/README.md b/README.md deleted file mode 100644 index 9cc4320..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# cxos-vendor-busybox - -CxOS vendor: busybox \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..9c65ada --- /dev/null +++ b/build.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# cxos/vendor/busybox/build.sh — build a static busybox + install into rootfs. +# +# Usage: +# build.sh # x86_64, install into dist/cxos/rootfs +# build.sh --arch aarch64 +# build.sh --rootfs /path/to/rootfs +# build.sh --dry +# +# After install: +# /bin/busybox static binary (~1 MB) +# /bin/* ./sbin/* symlinks to busybox (one per applet) +set -euo pipefail + +ARCH="x86_64" +DRY=0 +ROOTFS="" +for ((i=1; i<=$#; i++)); do + case "${!i}" in + --arch) ((i++)); ARCH="${!i}" ;; + --rootfs) ((i++)); ROOTFS="${!i}" ;; + --dry) DRY=1 ;; + -h|--help) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) echo "build.sh: unknown arg: ${!i}" >&2; exit 2 ;; + esac +done + +HERE="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$HERE/../../.." && pwd)" +PINNED="$HERE/PINNED.json" +FRAGMENT="$HERE/cxos.config.fragment" + +VERSION="$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1]))["version"])' "$PINNED")" +EXTRACTED_DIR="$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1]))["extracted_dir"])' "$PINNED")" +SRC="$HERE/src/$EXTRACTED_DIR" +ROOTFS="${ROOTFS:-$REPO_ROOT/dist/cxos/rootfs}" + +case "$ARCH" in + x86_64) CROSS="" ;; + aarch64) CROSS="${CROSS_COMPILE:-aarch64-linux-gnu-}" ;; + *) echo "build.sh: unsupported arch: $ARCH" >&2; exit 2 ;; +esac + +JOBS="${JOBS:-$(nproc 2>/dev/null || echo 2)}" + +echo "==> busybox ${VERSION} (${ARCH}) -> ${ROOTFS}" +echo " src : ${SRC}" +echo " fragment: ${FRAGMENT}" +echo " cross : ${CROSS:-}" +echo " jobs : ${JOBS}" + +if [[ "$DRY" == "1" ]]; then + echo "==> dry-run; not compiling" + exit 0 +fi + +[[ -d "$SRC" ]] || { echo "build.sh: source not extracted; run fetch.sh first" >&2; exit 1; } + +cd "$SRC" +make CROSS_COMPILE="$CROSS" defconfig + +# Apply fragment overrides line-by-line. Lines that start with '#' or are +# blank are skipped. KEY=VALUE lines replace any existing KEY= line, or are +# appended if absent. +while IFS= read -r line; do + [[ -z "$line" || "$line" == \#* ]] && continue + key="${line%%=*}" + if grep -qE "^# ${key} is not set$|^${key}=" .config; then + sed -i -E "s|^# ${key} is not set\$|${line}|; s|^${key}=.*|${line}|" .config + else + printf '%s\n' "$line" >> .config + fi +done < "$FRAGMENT" + +make CROSS_COMPILE="$CROSS" oldconfig +make CROSS_COMPILE="$CROSS" -j"$JOBS" busybox + +mkdir -p "$ROOTFS" +make CROSS_COMPILE="$CROSS" CONFIG_PREFIX="$ROOTFS" install + +# busybox install creates /linuxrc symlink — pointless on initramfs (we have +# our own /init). Drop it. +rm -f "$ROOTFS/linuxrc" + +echo "==> installed: $(find "$ROOTFS/bin" "$ROOTFS/sbin" -maxdepth 1 -mindepth 1 2>/dev/null | wc -l) applets" diff --git a/cxos.config.fragment b/cxos.config.fragment new file mode 100644 index 0000000..4c35227 --- /dev/null +++ b/cxos.config.fragment @@ -0,0 +1,93 @@ +# cxos.config.fragment — busybox additions on top of `defconfig`. +# +# Statically linked, no debug, sufficient applets to bring up CxOS Tier-0. +# Merged via plain `sed -i` overrides in build.sh (busybox uses Kconfig but +# its config files are flat KEY=VALUE; we apply each line as 'set or add'). + +CONFIG_STATIC=y +CONFIG_INSTALL_NO_USR=n + +# --- Init / login / shell --- +CONFIG_INIT=y +CONFIG_GETTY=y +CONFIG_LOGIN=y +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_SHADOW=y +CONFIG_PASSWD=y +CONFIG_ASH=y +CONFIG_FEATURE_SH_IS_ASH=y + +# --- coreutils --- +CONFIG_LS=y +CONFIG_CAT=y +CONFIG_CP=y +CONFIG_MV=y +CONFIG_RM=y +CONFIG_MKDIR=y +CONFIG_RMDIR=y +CONFIG_LN=y +CONFIG_PWD=y +CONFIG_ECHO=y +CONFIG_PRINTF=y +CONFIG_TRUE=y +CONFIG_FALSE=y +CONFIG_SLEEP=y +CONFIG_DATE=y +CONFIG_UNAME=y +CONFIG_HOSTNAME=y +CONFIG_TOUCH=y +CONFIG_HEAD=y +CONFIG_TAIL=y +CONFIG_GREP=y +CONFIG_SED=y +CONFIG_AWK=y +CONFIG_FIND=y +CONFIG_TR=y +CONFIG_SORT=y +CONFIG_UNIQ=y +CONFIG_WC=y +CONFIG_TEE=y +CONFIG_DD=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +CONFIG_SHA256SUM=y +CONFIG_MD5SUM=y + +# --- Process / system --- +CONFIG_PS=y +CONFIG_TOP=y +CONFIG_KILL=y +CONFIG_PIDOF=y +CONFIG_FREE=y +CONFIG_DMESG=y +CONFIG_MOUNT=y +CONFIG_UMOUNT=y +CONFIG_DF=y +CONFIG_DU=y +CONFIG_LSMOD=y +CONFIG_INSMOD=y +CONFIG_RMMOD=y +CONFIG_REBOOT=y +CONFIG_HALT=y +CONFIG_POWEROFF=y +CONFIG_SWITCH_ROOT=y + +# --- Networking --- +CONFIG_IP=y +CONFIG_IPADDR=y +CONFIG_IPLINK=y +CONFIG_IPROUTE=y +CONFIG_IFCONFIG=y +CONFIG_ROUTE=y +CONFIG_PING=y +CONFIG_NSLOOKUP=y +CONFIG_UDHCPC=y +CONFIG_WGET=y + +# --- Misc --- +CONFIG_VI=y +CONFIG_LESS=y +CONFIG_TAR=y +CONFIG_GZIP=y +CONFIG_GUNZIP=y +CONFIG_XZ=n diff --git a/fetch.sh b/fetch.sh new file mode 100755 index 0000000..17b7b43 --- /dev/null +++ b/fetch.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# cxos/vendor/busybox/fetch.sh — download + verify + extract busybox tarball. +# Mirrors cxos/vendor/linux/fetch.sh shape (PINNED.json driven, --dry, --gpg). +set -euo pipefail + +DRY=0 +GPG=0 +for arg in "$@"; do + case "$arg" in + --dry) DRY=1 ;; + --gpg) GPG=1 ;; + -h|--help) sed -n '2,4p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) echo "fetch.sh: unknown arg: $arg" >&2; exit 2 ;; + esac +done + +HERE="$(cd "$(dirname "$0")" && pwd)" +PINNED="$HERE/PINNED.json" +[[ -f "$PINNED" ]] || { echo "fetch.sh: missing $PINNED" >&2; exit 1; } + +read -r VERSION TARBALL_URL SIG_URL SHA256 EXTRACTED_DIR < <( + python3 - "$PINNED" <<'PY' +import json, sys +d = json.load(open(sys.argv[1])) +print(d["version"], d["tarball_url"], d["signature_url"], + d["sha256"], d["extracted_dir"]) +PY +) + +TARBALL="$HERE/busybox-${VERSION}.tar.bz2" +SIGNATURE="$HERE/busybox-${VERSION}.tar.bz2.sig" +SRC_DIR="$HERE/src" + +echo "==> busybox ${VERSION}" +echo " tarball: ${TARBALL_URL}" +echo " sha256 : ${SHA256}" +echo " extract: ${SRC_DIR}/${EXTRACTED_DIR}" + +if [[ "$DRY" == "1" ]]; then + echo "==> dry-run; nothing fetched" + exit 0 +fi + +mkdir -p "$SRC_DIR" + +if [[ ! -f "$TARBALL" ]]; then + echo "==> downloading" + curl -fsSL --retry 3 -o "$TARBALL" "$TARBALL_URL" +fi + +echo "==> verifying sha256" +ACTUAL_SHA="$(sha256sum "$TARBALL" | awk '{print $1}')" +if [[ "$ACTUAL_SHA" != "$SHA256" ]]; then + echo "fetch.sh: SHA-256 mismatch" >&2 + echo " expected: $SHA256" >&2 + echo " actual : $ACTUAL_SHA" >&2 + rm -f "$TARBALL" + exit 1 +fi +echo " ok" + +if [[ "$GPG" == "1" ]]; then + command -v gpg >/dev/null 2>&1 || { echo "fetch.sh: --gpg requested but gpg not installed" >&2; exit 1; } + [[ -f "$SIGNATURE" ]] || curl -fsSL --retry 3 -o "$SIGNATURE" "$SIG_URL" + FINGERPRINTS="$(python3 -c ' +import json,sys +for k in json.load(open(sys.argv[1]))["gpg_signing_keys"]: + print(k["fingerprint"])' "$PINNED")" + for fp in $FINGERPRINTS; do + gpg --keyserver hkps://keys.openpgp.org --recv-keys "$fp" >/dev/null 2>&1 || true + done + echo "==> verifying gpg signature" + if ! gpg --verify "$SIGNATURE" "$TARBALL" 2>&1 \ + | grep -qE 'Good signature.*('"$(echo "$FINGERPRINTS" | tr '\n' '|' | sed 's/|$//')"')'; then + echo "fetch.sh: GPG verification failed" >&2 + exit 1 + fi + echo " ok" +fi + +if [[ ! -d "$SRC_DIR/$EXTRACTED_DIR" ]]; then + echo "==> extracting" + tar -xjf "$TARBALL" -C "$SRC_DIR" +fi + +echo "==> busybox source ready: $SRC_DIR/$EXTRACTED_DIR"