diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:26:40 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:26:40 +0300 |
| commit | 6a34aa4538028e0ee7d7df21b1579e38b2d19c65 (patch) | |
| tree | ba28748a76117746901b4b6b91619428ba289766 /corebinutils | |
| parent | 997dd21c2f45e9a586e9ef459381de1d540a9cfb (diff) | |
| parent | d768fb6b2c853a5658f5e671056e0dc48897f358 (diff) | |
| download | Project-Tick-6a34aa4538028e0ee7d7df21b1579e38b2d19c65.tar.gz Project-Tick-6a34aa4538028e0ee7d7df21b1579e38b2d19c65.zip | |
Add 'corebinutils/freebsd-version/' from commit 'd768fb6b2c853a5658f5e671056e0dc48897f358'
git-subtree-dir: corebinutils/freebsd-version
git-subtree-mainline: 997dd21c2f45e9a586e9ef459381de1d540a9cfb
git-subtree-split: d768fb6b2c853a5658f5e671056e0dc48897f358
Diffstat (limited to 'corebinutils')
| -rw-r--r-- | corebinutils/freebsd-version/.gitignore | 25 | ||||
| -rw-r--r-- | corebinutils/freebsd-version/GNUmakefile | 37 | ||||
| -rw-r--r-- | corebinutils/freebsd-version/LICENSE | 23 | ||||
| -rw-r--r-- | corebinutils/freebsd-version/LICENSES/BSD-2-Clause.txt | 9 | ||||
| -rw-r--r-- | corebinutils/freebsd-version/README.md | 29 | ||||
| -rw-r--r-- | corebinutils/freebsd-version/linux-version.1 | 124 | ||||
| -rw-r--r-- | corebinutils/freebsd-version/linux-version.sh.in | 377 | ||||
| -rw-r--r-- | corebinutils/freebsd-version/tests/test.sh | 125 |
8 files changed, 749 insertions, 0 deletions
diff --git a/corebinutils/freebsd-version/.gitignore b/corebinutils/freebsd-version/.gitignore new file mode 100644 index 0000000000..a74d30b48c --- /dev/null +++ b/corebinutils/freebsd-version/.gitignore @@ -0,0 +1,25 @@ +*.a +*.core +*.lo +*.nossppico +*.o +*.orig +*.pico +*.pieo +*.po +*.rej +*.so +*.so.[0-9]* +*.sw[nop] +*~ +.*DS_Store +.cache +.clangd +.ccls-cache +.depend* +compile_commands.json +compile_commands.events.json +tags +build/ +out/ +.linux-obj/ diff --git a/corebinutils/freebsd-version/GNUmakefile b/corebinutils/freebsd-version/GNUmakefile new file mode 100644 index 0000000000..21357b27a3 --- /dev/null +++ b/corebinutils/freebsd-version/GNUmakefile @@ -0,0 +1,37 @@ +.DEFAULT_GOAL := all + +SED ?= sed +SH ?= sh + +OBJDIR := $(CURDIR)/build +OUTDIR := $(CURDIR)/out +GENERATED := $(OBJDIR)/linux-version +TARGET := $(OUTDIR)/linux-version + +.PHONY: all clean dirs status test + +all: $(TARGET) + +dirs: + @mkdir -p "$(OBJDIR)" "$(OUTDIR)" + +$(GENERATED): $(CURDIR)/linux-version.sh.in | dirs + $(SED) \ + -e 's|@@OS_RELEASE_PRIMARY@@|/etc/os-release|g' \ + -e 's|@@OS_RELEASE_FALLBACK@@|/usr/lib/os-release|g' \ + -e 's|@@PROC_OSRELEASE@@|/proc/sys/kernel/osrelease|g' \ + "$<" >"$@" + @chmod +x "$@" + +$(TARGET): $(GENERATED) | dirs + cp "$(GENERATED)" "$(TARGET)" + @chmod +x "$(TARGET)" + +test: $(TARGET) + LINUX_VERSION_BIN="$(TARGET)" $(SH) "$(CURDIR)/tests/test.sh" + +status: + @printf '%s\n' "$(TARGET)" + +clean: + @rm -rf "$(OBJDIR)" "$(OUTDIR)" diff --git a/corebinutils/freebsd-version/LICENSE b/corebinutils/freebsd-version/LICENSE new file mode 100644 index 0000000000..ef8aba3d05 --- /dev/null +++ b/corebinutils/freebsd-version/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2026 + Project Tick. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/corebinutils/freebsd-version/LICENSES/BSD-2-Clause.txt b/corebinutils/freebsd-version/LICENSES/BSD-2-Clause.txt new file mode 100644 index 0000000000..5f662b354c --- /dev/null +++ b/corebinutils/freebsd-version/LICENSES/BSD-2-Clause.txt @@ -0,0 +1,9 @@ +Copyright (c) <year> <owner> + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/corebinutils/freebsd-version/README.md b/corebinutils/freebsd-version/README.md new file mode 100644 index 0000000000..aeb1d1aca3 --- /dev/null +++ b/corebinutils/freebsd-version/README.md @@ -0,0 +1,29 @@ +# linux-version + +Standalone shell-based Linux port of FreeBSD `freebsd-version`, renamed to `linux-version` for this repository. + +## Build + +```sh +gmake -f GNUmakefile +gmake -f GNUmakefile CC=musl-gcc +``` + +This port is implemented as a POSIX `sh` script, so the `CC=musl-gcc` build is a reproducibility check rather than a separate compilation path. + +## Test + +```sh +gmake -f GNUmakefile test +gmake -f GNUmakefile test CC=musl-gcc +``` + +## Notes + +- Port strategy is Linux-native translation, not preservation of FreeBSD loader, `sysctl`, or jail semantics. +- `-r` reads the running kernel release from `/proc/sys/kernel/osrelease`, with `uname -r` only as a fallback when procfs is unavailable. +- `-u` reads the installed userland version from the target root's `os-release` file. The port prefers `VERSION_ID`, then falls back to `BUILD_ID`, `VERSION`, and `PRETTY_NAME`. +- `-k` does not inspect a FreeBSD kernel binary. On Linux it resolves the default booted kernel via `/vmlinuz` or `/boot/vmlinuz` symlinks when available, then falls back to a unique kernel release under `/lib/modules` or `/usr/lib/modules`. +- `ROOT=/path` is supported for offline inspection of another Linux filesystem tree for `-k` and `-u`. +- Unsupported semantics are explicit: `-j` is rejected because Linux containers and namespaces do not provide a jail-equivalent installed-userland query with compatible semantics. +- The port does not guess when several kernel module trees exist without a default boot symlink; it fails with an explicit ambiguity error instead. diff --git a/corebinutils/freebsd-version/linux-version.1 b/corebinutils/freebsd-version/linux-version.1 new file mode 100644 index 0000000000..97a992586e --- /dev/null +++ b/corebinutils/freebsd-version/linux-version.1 @@ -0,0 +1,124 @@ +.\"- +.\" Copyright (c) 2026 +.\" Project Tick. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd February 28, 2026 +.Dt LINUX-VERSION 1 +.Os +.Sh NAME +.Nm linux-version +.Nd print the installed system and kernel version on Linux +.Sh SYNOPSIS +.Nm +.Op Fl kru +.Op Fl j Ar jail +.Sh DESCRIPTION +The +.Nm +utility is a Linux-native port of +.Xr freebsd-version 1 +for this repository. +It reports the installed kernel version, +the running kernel version, +and the installed userland version. +.Pp +The following options are available: +.Bl -tag -width Fl +.It Fl k +Print the installed kernel version for the target root. +On Linux, this is resolved from the default kernel symlink +.Pa /vmlinuz +or +.Pa /boot/vmlinuz +when available, +otherwise from a unique kernel release under +.Pa /lib/modules +or +.Pa /usr/lib/modules . +If several installed kernels exist and no default boot target can be +determined, the command fails with an explicit error. +.It Fl r +Print the running kernel version from +.Pa /proc/sys/kernel/osrelease . +.It Fl u +Print the installed userland version from +.Pa /etc/os-release +or +.Pa /usr/lib/os-release +under the target root. +The utility prefers +.Ev VERSION_ID , +then falls back to +.Ev BUILD_ID , +.Ev VERSION , +and +.Ev PRETTY_NAME . +.It Fl j Ar jail +Accepted for interface compatibility but not supported on Linux. +The command exits with an explicit error because Linux containers and +namespaces do not provide a jail-equivalent query with compatible semantics. +.El +.Pp +If several supported options are specified, +.Nm +prints the installed kernel version first, +then the running kernel version, +and finally the installed userland version, +each on a separate line. +If no option is specified, +it prints the installed userland version only. +.Sh ENVIRONMENT +.Bl -tag -width ROOT +.It Ev ROOT +Path to the root of the filesystem tree in which to inspect +.Pa os-release , +.Pa /boot/vmlinuz , +and kernel module directories for +.Fl k +and +.Fl u . +This does not affect +.Fl r , +which always reports the current running kernel. +.El +.Sh IMPLEMENTATION NOTES +This port intentionally does not preserve FreeBSD-specific implementation +details such as parsing +.Pa loader.conf , +querying +.Va kern.osrelease +via +.Xr sysctl 8 , +or executing inside jails with +.Xr jexec 8 . +.Pp +The Linux kernel does not provide a syscall or procfs interface that reports +the installed-on-disk kernel version separately from the running kernel. +Therefore +.Nm +uses the default kernel symlink when present and otherwise requires a unique +kernel release under the module tree. +.Sh SEE ALSO +.Xr uname 1 , +.Xr os-release 5 diff --git a/corebinutils/freebsd-version/linux-version.sh.in b/corebinutils/freebsd-version/linux-version.sh.in new file mode 100644 index 0000000000..52cd486f96 --- /dev/null +++ b/corebinutils/freebsd-version/linux-version.sh.in @@ -0,0 +1,377 @@ +#!/bin/sh + +set -eu + +: "${ROOT:=}" +: "${OS_RELEASE_PRIMARY:=@@OS_RELEASE_PRIMARY@@}" +: "${OS_RELEASE_FALLBACK:=@@OS_RELEASE_FALLBACK@@}" +: "${PROC_OSRELEASE:=@@PROC_OSRELEASE@@}" + +progname=${0##*/} + +error() { + printf '%s\n' "$progname: $*" >&2 + exit 1 +} + +usage() { + printf '%s\n' "usage: $progname [-kru] [-j jail]" >&2 + exit 1 +} + +root_path() { + case $1 in + /*) + if [ -n "$ROOT" ]; then + printf '%s%s\n' "$ROOT" "$1" + else + printf '%s\n' "$1" + fi + ;; + *) + error "internal error: expected absolute path, got '$1'" + ;; + esac +} + +path_basename() { + path=$1 + while [ "$path" != "/" ] && [ "${path%/}" != "$path" ]; do + path=${path%/} + done + printf '%s\n' "${path##*/}" +} + +extract_release_from_name() { + case $1 in + vmlinuz-*) + printf '%s\n' "${1#vmlinuz-}" + return 0 + ;; + bzImage-*) + printf '%s\n' "${1#bzImage-}" + return 0 + ;; + kernel-*) + printf '%s\n' "${1#kernel-}" + return 0 + ;; + esac + return 1 +} + +append_unique_candidate() { + candidate=$1 + [ -n "$candidate" ] || return 0 + + if [ -z "${kernel_candidates:-}" ]; then + kernel_candidates=$candidate + return 0 + fi + + if printf '%s\n' "$kernel_candidates" | grep -Fx "$candidate" >/dev/null 2>&1; then + return 0 + fi + + kernel_candidates=$kernel_candidates' +'$candidate +} + +extract_release_from_link() { + link_path=$1 + + [ -L "$link_path" ] || return 1 + target=$(readlink "$link_path" 2>/dev/null) || return 1 + extract_release_from_name "$(path_basename "$target")" +} + +collect_boot_kernel_candidates() { + for absolute in /vmlinuz /boot/vmlinuz; do + link_path=$(root_path "$absolute") + if release=$(extract_release_from_link "$link_path"); then + printf '%s\n' "$release" + return 0 + fi + done + + for absolute in /vmlinuz-* /boot/vmlinuz-* /boot/bzImage-* /boot/kernel-*; do + pattern=$(root_path "$absolute") + for entry in $pattern; do + [ -e "$entry" ] || continue + release=$(extract_release_from_name "$(path_basename "$entry")") || continue + append_unique_candidate "$release" + done + done + + return 1 +} + +collect_module_tree_candidates() { + for absolute in /lib/modules /usr/lib/modules; do + modules_root=$(root_path "$absolute") + [ -d "$modules_root" ] || continue + + for entry in "$modules_root"/*; do + [ -d "$entry" ] || continue + if [ ! -d "$entry/kernel" ] && [ ! -f "$entry/modules.dep" ]; then + continue + fi + append_unique_candidate "$(path_basename "$entry")" + done + done +} + +format_candidate_list() { + printf '%s\n' "$kernel_candidates" | awk ' + NF { + if (count > 0) + printf(", "); + printf("%s", $0); + count++; + } + END { + if (count > 0) + printf("\n"); + } + ' +} + +installed_kernel_version() { + kernel_candidates= + + if release=$(collect_boot_kernel_candidates); then + printf '%s\n' "$release" + return 0 + fi + + collect_module_tree_candidates + count=$(printf '%s\n' "${kernel_candidates:-}" | awk 'NF { count++ } END { print count + 0 }') + + case $count in + 0) + error "unable to determine installed kernel version under '${ROOT:-/}'" + ;; + 1) + printf '%s\n' "$kernel_candidates" + ;; + *) + error "unable to determine installed kernel version under '${ROOT:-/}': multiple kernel releases found ($(format_candidate_list))" + ;; + esac +} + +running_kernel_version() { + if [ -r "$PROC_OSRELEASE" ]; then + release= + IFS= read -r release <"$PROC_OSRELEASE" || true + [ -n "$release" ] || error "unable to determine running kernel version from $PROC_OSRELEASE" + printf '%s\n' "$release" + return 0 + fi + + if command -v uname >/dev/null 2>&1; then + command uname -r + return 0 + fi + + error "unable to determine running kernel version: $PROC_OSRELEASE is unavailable" +} + +extract_os_release_field() { + field=$1 + file=$2 + + if output=$(awk -v want="$field" ' + function ltrim(s) { + sub(/^[[:space:]]+/, "", s) + return s + } + + function rtrim(s) { + sub(/[[:space:]]+$/, "", s) + return s + } + + function decode_quoted(s, out, i, c, esc) { + out = "" + esc = 0 + for (i = 2; i <= length(s); i++) { + c = substr(s, i, 1) + if (esc) { + out = out c + esc = 0 + continue + } + if (c == "\\") { + esc = 1 + continue + } + if (c == "\"") { + if (rtrim(substr(s, i + 1)) != "") + return "__TRAILING__" + return out + } + out = out c + } + return "__UNTERMINATED__" + } + + BEGIN { + found = 0 + parse_error = 0 + } + + /^[[:space:]]*#/ || /^[[:space:]]*$/ { + next + } + + { + line = $0 + sub(/^[[:space:]]+/, "", line) + if (line !~ /^[A-Z0-9_]+=.*$/) + next + + key = line + sub(/=.*/, "", key) + if (key != want) + next + + value = line + sub(/^[^=]*=/, "", value) + value = ltrim(value) + + if (value ~ /^"/) { + value = decode_quoted(value) + if (value == "__TRAILING__" || value == "__UNTERMINATED__") { + parse_error = 1 + exit + } + print value + found = 1 + exit 0 + } + + if (value ~ /[[:space:]]/) { + parse_error = 1 + exit + } + + print rtrim(value) + found = 1 + exit 0 + } + + END { + if (parse_error) + exit 3 + if (!found) + exit 1 + } + ' "$file" 2>&1); then + status=0 + else + status=$? + fi + + case $status in + 0) + [ -n "$output" ] || return 1 + printf '%s\n' "$output" + return 0 + ;; + 1) + return 1 + ;; + 3) + return 2 + ;; + *) + return 3 + ;; + esac +} + +userland_version() { + primary=$(root_path "$OS_RELEASE_PRIMARY") + fallback=$(root_path "$OS_RELEASE_FALLBACK") + + for file in "$primary" "$fallback"; do + [ -r "$file" ] || continue + + for field in VERSION_ID BUILD_ID VERSION PRETTY_NAME; do + if value=$(extract_os_release_field "$field" "$file"); then + printf '%s\n' "$value" + return 0 + else + status=$? + case $status in + 1) + ;; + 2) + error "malformed os-release entry '$field' in $file" + ;; + *) + error "failed to parse $file" + ;; + esac + fi + done + + error "unable to determine userland version from $file" + done + + error "unable to locate os-release under '${ROOT:-/}'" +} + +main() { + opt_k=0 + opt_r=0 + opt_u=0 + opt_j=0 + + OPTIND=1 + while getopts "kruj:" option; do + case $option in + k) + opt_k=1 + ;; + r) + opt_r=1 + ;; + u) + opt_u=1 + ;; + j) + opt_j=1 + ;; + *) + usage + ;; + esac + done + shift $((OPTIND - 1)) + + [ $# -eq 0 ] || usage + + if [ $((opt_k + opt_r + opt_u + opt_j)) -eq 0 ]; then + opt_u=1 + fi + + if [ "$opt_j" -ne 0 ]; then + error "option -j is not supported on Linux" + fi + + if [ "$opt_k" -ne 0 ]; then + installed_kernel_version + fi + + if [ "$opt_r" -ne 0 ]; then + running_kernel_version + fi + + if [ "$opt_u" -ne 0 ]; then + userland_version + fi +} + +main "$@" diff --git a/corebinutils/freebsd-version/tests/test.sh b/corebinutils/freebsd-version/tests/test.sh new file mode 100644 index 0000000000..ea0bccc7d8 --- /dev/null +++ b/corebinutils/freebsd-version/tests/test.sh @@ -0,0 +1,125 @@ +#!/bin/sh +set -eu + +ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +LINUX_VERSION_BIN=${LINUX_VERSION_BIN:-"$ROOT_DIR/out/linux-version"} +TMPDIR=${TMPDIR:-/tmp} +WORKDIR=$(mktemp -d "$TMPDIR/linux-version-test.XXXXXX") +trap 'rm -rf "$WORKDIR"' EXIT INT TERM + +fail() { + printf '%s\n' "FAIL: $1" >&2 + exit 1 +} + +assert_eq() { + name=$1 + expected=$2 + actual=$3 + if [ "$expected" != "$actual" ]; then + printf '%s\n' "FAIL: $name" >&2 + printf '%s\n' "--- expected ---" >&2 + printf '%s\n' "$expected" >&2 + printf '%s\n' "--- actual ---" >&2 + printf '%s\n' "$actual" >&2 + exit 1 + fi +} + +assert_match() { + name=$1 + pattern=$2 + text=$3 + printf '%s\n' "$text" | grep -Eq "$pattern" || fail "$name" +} + +write_os_release() { + root=$1 + content=$2 + mkdir -p "$root/etc" + printf '%s\n' "$content" >"$root/etc/os-release" +} + +[ -x "$LINUX_VERSION_BIN" ] || fail "missing binary: $LINUX_VERSION_BIN" + +usage_output=$("$LINUX_VERSION_BIN" -x 2>&1 || true) +assert_match "invalid option should print usage" '^usage: linux-version ' "$usage_output" + +too_many_output=$("$LINUX_VERSION_BIN" arg1 arg2 2>&1 || true) +assert_match "extra operands should print usage" '^usage: linux-version ' "$too_many_output" + +jail_output=$("$LINUX_VERSION_BIN" -j demo 2>&1 || true) +assert_eq "unsupported jail option" \ + "linux-version: option -j is not supported on Linux" "$jail_output" + +root_default="$WORKDIR/root-default" +write_os_release "$root_default" 'NAME=Test Linux +VERSION_ID=42.3 +PRETTY_NAME="Test Linux 42.3"' + +default_output=$(ROOT="$root_default" "$LINUX_VERSION_BIN") +assert_eq "default output should be userland version" "42.3" "$default_output" + +quoted_root="$WORKDIR/root-quoted" +write_os_release "$quoted_root" 'NAME=Quoted Linux +PRETTY_NAME="Quoted Linux 1.0 LTS"' + +quoted_output=$(ROOT="$quoted_root" "$LINUX_VERSION_BIN" -u) +assert_eq "quoted PRETTY_NAME fallback" "Quoted Linux 1.0 LTS" "$quoted_output" + +malformed_root="$WORKDIR/root-malformed" +write_os_release "$malformed_root" 'NAME=Broken Linux +VERSION_ID="unterminated' + +malformed_output=$(ROOT="$malformed_root" "$LINUX_VERSION_BIN" -u 2>&1 || true) +assert_eq "malformed os-release should fail" \ + "linux-version: malformed os-release entry 'VERSION_ID' in $malformed_root/etc/os-release" \ + "$malformed_output" + +missing_root="$WORKDIR/root-missing" +mkdir -p "$missing_root" +missing_output=$(ROOT="$missing_root" "$LINUX_VERSION_BIN" -u 2>&1 || true) +assert_eq "missing os-release should fail" \ + "linux-version: unable to locate os-release under '$missing_root'" "$missing_output" + +running_expected= +if [ -r /proc/sys/kernel/osrelease ]; then + IFS= read -r running_expected </proc/sys/kernel/osrelease || true +fi +if [ -z "$running_expected" ]; then + running_expected=$(uname -r) +fi +running_output=$("$LINUX_VERSION_BIN" -r) +assert_eq "running kernel release" "$running_expected" "$running_output" + +kernel_link_root="$WORKDIR/root-kernel-link" +mkdir -p "$kernel_link_root/boot" "$kernel_link_root/etc" +ln -s ../images/vmlinuz-6.9.7-port "$kernel_link_root/boot/vmlinuz" +write_os_release "$kernel_link_root" 'VERSION_ID=9.1' + +kernel_link_output=$(ROOT="$kernel_link_root" "$LINUX_VERSION_BIN" -k) +assert_eq "kernel version from default symlink" "6.9.7-port" "$kernel_link_output" + +kernel_modules_root="$WORKDIR/root-kernel-modules" +mkdir -p "$kernel_modules_root/lib/modules/6.8.12-demo/kernel" "$kernel_modules_root/etc" +: >"$kernel_modules_root/lib/modules/6.8.12-demo/modules.dep" +write_os_release "$kernel_modules_root" 'VERSION_ID=6.8-userland' + +kernel_modules_output=$(ROOT="$kernel_modules_root" "$LINUX_VERSION_BIN" -k) +assert_eq "kernel version from unique modules tree" "6.8.12-demo" "$kernel_modules_output" + +ambiguous_root="$WORKDIR/root-ambiguous" +mkdir -p "$ambiguous_root/lib/modules/6.1.1-a/kernel" "$ambiguous_root/lib/modules/6.1.2-b/kernel" +: >"$ambiguous_root/lib/modules/6.1.1-a/modules.dep" +: >"$ambiguous_root/lib/modules/6.1.2-b/modules.dep" + +ambiguous_output=$(ROOT="$ambiguous_root" "$LINUX_VERSION_BIN" -k 2>&1 || true) +assert_match "ambiguous modules tree should fail" \ + "^linux-version: unable to determine installed kernel version under '$ambiguous_root': multiple kernel releases found \\(6\\.1\\.1-a, 6\\.1\\.2-b\\)$" \ + "$ambiguous_output" + +combo_output=$(ROOT="$kernel_link_root" "$LINUX_VERSION_BIN" -kru) +combo_expected=$(printf '%s\n%s\n%s' "6.9.7-port" "$running_expected" "9.1") +assert_eq "combined output order" "$combo_expected" "$combo_output" + +printf '%s\n' "PASS" |
