SPS · log · files · refs · tags · releases · people · languages · GitHub · file
bin/mkiso
73283 bytes at 2909919 · GitHub
#!/bin/sh
# mkiso builds SPS Linux live ISOs (informally Splux). Maintainer tool;
# not a desktop installer front-end. SPS the package system does not
# require these images; they exist so Splux can be installed from USB.
#
# Default image: hybrid ISO, setup(1) on PATH, gettys on tty1 and ttyS0.
# Flash it, boot it, run setup. Guided partitioning is in setup, not here.
#
# --session plasma copies Plasma/Wayland from this host. Qt is not compiled
# during the ISO build. If startplasma-wayland is missing, the disc still
# boots to a TTY. NVIDIA on the live image is nouveau; proprietary nvidia*
# modules are blacklisted so they cannot load next to KWin. From Plasma,
# open Konsole and run setup.
#
# --with-firmware copies all of /lib/firmware (often over a gigabyte).
# Plasma sessions also copy nvidia/nouveau firmware without that flag;
# TU-class cards need those blobs for nouveau.
#
# BusyBox --install -s writes host-absolute applet links. Those break
# inside the initramfs. mkiso uses relative `ln -sf busybox applet`.
#
# grub-install is never run against a disk. The GRUB payload is El Torito
# plus an ESP for this ISO only. grub-mkrescue wants unicode.pf2, which
# this host does not ship, so the image is built with grub-mkimage and
# xorriso.
#
# Seed toolchain (--no-seed to skip): gcc, headers, cmake, ninja, python
# stdlib, Meson (mesonbuild), Perl library. Host /usr/bin/meson is a
# setuptools wrapper; mesonbuild lives under site-packages, not next
# to the Python stdlib, so copying python3.12 is not enough.
# Pass --core/--extra so the live image can build packages without cloning
# the recipe repos. --with-go copies the official Go tree; gccgo on PATH
# is too old for the Go recipes in extra.
sps_prog=mkiso
sps_bin_dir=$(CDPATH= cd -P "$(dirname "$0")" 2>/dev/null && pwd) || exit 1
if [ -z "${SPS_LIBDIR+x}" ]; then
if [ -r "$sps_bin_dir/../lib/common.sh" ]; then
SPS_LIBDIR=$sps_bin_dir/../lib
else
SPS_LIBDIR=$sps_bin_dir/../lib/sps
fi
fi
if ! . "$SPS_LIBDIR/common.sh"; then
printf '%s\n' "mkiso: cannot load $SPS_LIBDIR/common.sh" >&2
exit 1
fi
mkiso_usage()
{
cat <<'EOF'
usage: mkiso --output FILE.iso [options]
mkiso --layout DIR [options]
Build a live ISO (or a layout directory) for SPS Linux, informally Splux.
Flash it, boot it, run setup(1) on a spare disk.
Sessions:
tty root shell on tty1 and ttyS0 (default; always works)
plasma copy host Plasma/Wayland into the squashfs; Konsole -> setup
NVIDIA live path is nouveau. Falls back to a TTY if Plasma
is missing on the build host.
options:
--output FILE write a hybrid ISO
--layout DIR populate a live root and stop (tests, inspection)
--session NAME tty or plasma (default: tty)
--init NAME systemd or openrc (default: systemd)
--work DIR working directory (default: a temp dir)
--kernel FILE vmlinuz (default: /boot/vmlinuz)
--modules DIR kernel modules (default: /lib/modules/RELEASE)
--busybox FILE static busybox for the initramfs
--core DIR sps-core recipes (copied to /usr/src/sps/core)
--extra DIR sps-extra recipes (copied to /usr/src/sps/extra)
--with-rust copy rustc and cargo from the host
--with-go copy the host Go toolchain (not gccgo)
--with-firmware copy all of /lib/firmware (large)
--no-modules do not copy /lib/modules (smaller, less hardware)
--no-seed do not copy host compiler tools (layout/tests)
-h, --help
Makefile wrappers: make iso, make iso-slim, make iso-plasma.
EOF
}
mkiso_output=
mkiso_layout=
mkiso_work=
mkiso_work_created=0
mkiso_kernel=
mkiso_modules=
mkiso_busybox=
mkiso_core=
mkiso_extra=
mkiso_with_rust=0
mkiso_with_go=0
mkiso_with_firmware=0
mkiso_copy_modules=1
mkiso_copy_seed=1
mkiso_session=tty
mkiso_init=systemd
mkiso_libdir=$SPS_LIBDIR/mkiso
mkiso_need_value()
{
[ "$#" -ge 2 ] && [ -n "${2-}" ] ||
sps_die "$SPS_EX_USAGE" "option '$1' requires an argument"
}
while [ "$#" -gt 0 ]; do
case $1 in
-h|--help)
mkiso_usage
exit 0
;;
--output)
mkiso_need_value "$@"
mkiso_output=$2
shift 2
;;
--layout)
mkiso_need_value "$@"
mkiso_layout=$2
shift 2
;;
--work)
mkiso_need_value "$@"
mkiso_work=$2
shift 2
;;
--kernel)
mkiso_need_value "$@"
mkiso_kernel=$2
shift 2
;;
--modules)
mkiso_need_value "$@"
mkiso_modules=$2
shift 2
;;
--busybox)
mkiso_need_value "$@"
mkiso_busybox=$2
shift 2
;;
--core)
mkiso_need_value "$@"
mkiso_core=$2
shift 2
;;
--extra)
mkiso_need_value "$@"
mkiso_extra=$2
shift 2
;;
--with-rust) mkiso_with_rust=1; shift ;;
--with-go) mkiso_with_go=1; shift ;;
--with-firmware) mkiso_with_firmware=1; shift ;;
--no-modules) mkiso_copy_modules=0; shift ;;
--no-seed) mkiso_copy_seed=0; shift ;;
--session)
mkiso_need_value "$@"
mkiso_session=$2
shift 2
;;
--init)
mkiso_need_value "$@"
mkiso_init=$2
shift 2
;;
-*)
sps_die "$SPS_EX_USAGE" "unknown option: $1"
;;
*)
sps_die "$SPS_EX_USAGE" "unexpected argument: $1"
;;
esac
done
if [ -z "$mkiso_output" ] && [ -z "$mkiso_layout" ]; then
sps_die "$SPS_EX_USAGE" "pass --output FILE.iso or --layout DIR"
fi
case $mkiso_session in
tty|plasma) ;;
*) sps_die "$SPS_EX_USAGE" "session must be tty or plasma" ;;
esac
case $mkiso_init in
systemd|openrc|s6|runit|dinit|shepherd|sysvinit) ;;
*) sps_die "$SPS_EX_USAGE" "init must be systemd, openrc, s6, runit, dinit, shepherd, or sysvinit" ;;
esac
mkiso_cleanup()
{
if [ "$mkiso_work_created" -eq 1 ] && [ -n "$mkiso_work" ] &&
[ -d "$mkiso_work" ]; then
rm -rf "$mkiso_work"
fi
}
if [ -z "$mkiso_work" ]; then
mkiso_work=$(sps_mktemp_dir "${TMPDIR:-/tmp}" mkiso) || exit $?
mkiso_work_created=1
trap mkiso_cleanup 0 HUP INT TERM
else
case $mkiso_work in
/*) ;;
*) sps_die "$SPS_EX_USAGE" "work directory must be absolute" ;;
esac
mkdir -p "$mkiso_work" ||
sps_die "$SPS_EX_PERMISSION" "cannot create $mkiso_work"
fi
if [ -n "$mkiso_layout" ]; then
case $mkiso_layout in
/*) ;;
*) sps_die "$SPS_EX_USAGE" "layout directory must be absolute" ;;
esac
mkiso_live=$mkiso_layout
else
mkiso_live=$mkiso_work/live-root
fi
mkdir -p "$mkiso_live" ||
sps_die "$SPS_EX_PERMISSION" "cannot create live root"
# --- copy helpers -------------------------------------------------------
# Copy a host path into the live root. Directories are merged (src/. into
# dest/) so a second copy of /usr/lib64/qt6 cannot nest as qt6/qt6.
# Symlinks are copied as links, then the chain is followed so sonames like
# libtinfo.so.6 are not left dangling.
mkiso_copy_path()
{
mkiso_src=$1
mkiso_depth=${2:-0}
[ "$mkiso_depth" -lt 16 ] || return 0
[ -e "$mkiso_src" ] || [ -L "$mkiso_src" ] || return 0
mkiso_dest=$mkiso_live$mkiso_src
mkdir -p "$(dirname "$mkiso_dest")" || return 0
if [ -d "$mkiso_src" ] && [ ! -L "$mkiso_src" ]; then
mkdir -p "$mkiso_dest"
cp -a --reflink=auto "$mkiso_src"/. "$mkiso_dest"/ 2>/dev/null ||
cp -a "$mkiso_src"/. "$mkiso_dest"/ 2>/dev/null ||
cp -R "$mkiso_src"/. "$mkiso_dest"/ 2>/dev/null || :
else
# usr-merge puts busybox applets in usr/bin. cp onto a
# symlink follows it and overwrites busybox itself.
if [ -L "$mkiso_dest" ]; then
rm -f "$mkiso_dest"
fi
cp -a --reflink=auto "$mkiso_src" "$mkiso_dest" 2>/dev/null ||
cp -a "$mkiso_src" "$mkiso_dest" 2>/dev/null ||
cp "$mkiso_src" "$mkiso_dest" 2>/dev/null || :
if [ -L "$mkiso_src" ]; then
mkiso_link=$(readlink "$mkiso_src") || return 0
case $mkiso_link in
/*) mkiso_next=$mkiso_link ;;
*) mkiso_next=$(dirname "$mkiso_src")/$mkiso_link ;;
esac
mkiso_copy_path "$mkiso_next" $((mkiso_depth + 1))
fi
fi
}
mkiso_copy_libs()
{
mkiso_bin=$1
[ -f "$mkiso_bin" ] || [ -L "$mkiso_bin" ] || return 0
command -v ldd >/dev/null 2>&1 || return 0
ldd "$mkiso_bin" 2>/dev/null | while IFS= read -r mkiso_line; do
mkiso_lib=
case $mkiso_line in
*linux-vdso*|*'not a dynamic'*) continue ;;
esac
# "libx.so => /lib64/libx.so (0x...)"
mkiso_lib=${mkiso_line#*=> }
mkiso_lib=${mkiso_lib%% (*}
mkiso_lib=${mkiso_lib## }
case $mkiso_lib in
/*) mkiso_copy_path "$mkiso_lib" ;;
esac
# interpreter-only lines: "/lib64/ld-linux-x86-64.so.2 (0x...)"
case $mkiso_line in
" /"*|" /"*)
mkiso_interp=${mkiso_line%% (*}
mkiso_interp=${mkiso_interp## }
mkiso_interp=${mkiso_interp# }
case $mkiso_interp in
/*) mkiso_copy_path "$mkiso_interp" ;;
esac
;;
esac
# An ldd line reads "WANTED => RESOLVED". For a library, WANTED is
# a soname the loader searches for, and copying RESOLVED is right.
# For the program interpreter, WANTED is the absolute path baked
# into the ELF, and the kernel opens exactly that. When the two
# differ because the host reaches the loader through a symlinked
# lib64, copying only RESOLVED leaves the interpreter missing at
# the one path that matters, and exec fails with ENOENT. The
# kernel then reports it as "can't execute /sbin/init".
case $mkiso_line in
*" => "*)
mkiso_wanted=${mkiso_line%% => *}
mkiso_wanted=${mkiso_wanted## }
mkiso_wanted=${mkiso_wanted# }
case $mkiso_wanted in
/*)
[ "$mkiso_wanted" = "$mkiso_lib" ] ||
mkiso_copy_as "$mkiso_lib" "$mkiso_wanted"
;;
esac
;;
esac
done
}
# Place the contents of an existing host file at a specific path in the live
# root, which may differ from where it lives on the host.
mkiso_copy_as()
{
mkiso_src=$1
mkiso_dst=$2
[ -n "$mkiso_src" ] && [ -e "$mkiso_src" ] || return 0
[ -e "$mkiso_live$mkiso_dst" ] && return 0
mkdir -p "$mkiso_live${mkiso_dst%/*}" || return 0
cp -a "$mkiso_src" "$mkiso_live$mkiso_dst" 2>/dev/null || return 0
}
# close_libs only walks bin/sbin. cc1 (libmpc/libisl) and Mesa DRI
# (libSPIRV-Tools) are dlopened helpers, so a seeded disc can have gcc
# and still fail "C compiler cannot create executables", and Plasma can
# have /dev/dri/card0 and still report "No suitable DRM devices".
mkiso_copy_so_deps()
{
mkiso_so=$1
[ -e "$mkiso_so" ] || [ -L "$mkiso_so" ] || return 0
case $mkiso_so in
*.a|*-rust*|*nvidia*) return 0 ;;
esac
mkiso_copy_path "$mkiso_so"
mkiso_copy_libs "$mkiso_so"
}
mkiso_copy_gcc_deps()
{
command -v find >/dev/null 2>&1 || return 0
printf '%s: resolving gcc cc1 libraries\n' "$sps_prog" >&2
for mkiso_d in /usr/libexec/gcc /usr/lib64/gcc /usr/lib/gcc
do
[ -d "$mkiso_d" ] || continue
find "$mkiso_d" -type f \( \
-name cc1 -o -name cc1plus -o -name collect2 -o \
-name lto-wrapper -o -name lto1 -o \
-name liblto_plugin.so -o -name 'liblto_plugin.so.*' \
\) -print 2>/dev/null |
while IFS= read -r mkiso_elf || [ -n "$mkiso_elf" ]; do
[ -n "$mkiso_elf" ] || continue
mkiso_copy_so_deps "$mkiso_elf"
done
done
}
# Python's stdlib is under lib64; Meson is a pure-Python package under
# /usr/lib/pythonX.Y/site-packages. Copying the interpreter and
# lib64/pythonX.Y is not enough: that tree's site-packages is PyQt and
# friends, not mesonbuild. The setuptools /usr/bin/meson then dies with
# ModuleNotFoundError, which looks like a Python error at the first
# meson recipe (often libxml2).
# The live root's interpreter is the /usr/bin/python3 that seed-bins
# copies, so seed the stdlib and Meson for that same interpreter. A PATH
# that puts a relocated python (Linuxbrew, pyenv) first must not drag the
# seed onto a different minor version.
mkiso_seed_python()
{
for mkiso_probe in /usr/bin/python3 /usr/bin/python3.12 python3
do
mkiso_probe_cmd=$(command -v "$mkiso_probe" 2>/dev/null) || continue
[ -x "$mkiso_probe_cmd" ] || continue
printf '%s\n' "$mkiso_probe_cmd"
return 0
done
return 1
}
mkiso_python_version()
{
mkiso_py_probe=$(mkiso_seed_python) ||
mkiso_py_probe=/usr/bin/python3
"$mkiso_py_probe" -c \
'import sys; print("%d.%d" % sys.version_info[:2])' \
2>/dev/null || printf '%s\n' 3.12
}
mkiso_copy_python_stdlib()
{
mkiso_pyver=$(mkiso_python_version)
mkiso_seeded_pylib=
for mkiso_pylib in /usr/lib64/python$mkiso_pyver \
/usr/lib/python$mkiso_pyver
do
[ -d "$mkiso_pylib" ] || continue
if [ -d "$mkiso_pylib/encodings" ]; then
mkiso_seeded_pylib=$mkiso_pylib
fi
printf '%s: copying Python %s stdlib from %s\n' \
"$sps_prog" "$mkiso_pyver" "$mkiso_pylib" >&2
for mkiso_pyent in "$mkiso_pylib"/*; do
[ -e "$mkiso_pyent" ] || continue
mkiso_pybase=${mkiso_pyent##*/}
case $mkiso_pybase in
test|idlelib|ensurepip|turtledemo|site-packages)
continue
;;
esac
mkiso_copy_path "$mkiso_pyent"
done
if [ -d "$mkiso_pylib/lib-dynload" ]; then
for mkiso_pyso in "$mkiso_pylib/lib-dynload"/*.so \
"$mkiso_pylib/lib-dynload"/*.so.*
do
[ -e "$mkiso_pyso" ] || continue
mkiso_copy_libs "$mkiso_pyso"
done
fi
done
# Relocated pythons (Linuxbrew, pyenv) keep stdlib beside the
# interpreter, not under /usr/lib. Map it onto the standard live
# path so the seeded interpreter finds encodings and modules.
if [ -z "$mkiso_seeded_pylib" ]; then
mkiso_py_probe=$(mkiso_seed_python) ||
mkiso_py_probe=/usr/bin/python3
mkiso_pysrc=$("$mkiso_py_probe" -c \
'import sys, os; print(os.path.join(sys.base_prefix, "lib", "python%d.%d" % sys.version_info[:2]))' \
2>/dev/null) || mkiso_pysrc=
if [ -n "$mkiso_pysrc" ] && [ -d "$mkiso_pysrc" ] &&
[ -d "$mkiso_pysrc/encodings" ]
then
printf '%s: copying Python %s stdlib from %s\n' \
"$sps_prog" "$mkiso_pyver" "$mkiso_pysrc" >&2
mkdir -p "$mkiso_live/usr/lib/python$mkiso_pyver"
for mkiso_pyent in "$mkiso_pysrc"/*; do
[ -e "$mkiso_pyent" ] || continue
mkiso_pybase=${mkiso_pyent##*/}
case $mkiso_pybase in
test|idlelib|ensurepip|turtledemo|site-packages)
continue
;;
esac
cp -a --reflink=auto "$mkiso_pyent" \
"$mkiso_live/usr/lib/python$mkiso_pyver/" \
2>/dev/null ||
cp -a "$mkiso_pyent" \
"$mkiso_live/usr/lib/python$mkiso_pyver/" \
2>/dev/null ||
cp -R "$mkiso_pyent" \
"$mkiso_live/usr/lib/python$mkiso_pyver/" \
2>/dev/null || :
done
if [ -d "$mkiso_pysrc/lib-dynload" ]; then
for mkiso_pyso in "$mkiso_pysrc/lib-dynload"/*.so \
"$mkiso_pysrc/lib-dynload"/*.so.*
do
[ -e "$mkiso_pyso" ] || continue
mkiso_copy_libs "$mkiso_pyso"
done
fi
fi
fi
}
# Install Meson as meson.py next to mesonbuild so it does not need
# importlib.metadata or a setuptools egg-info (the host /usr/bin/meson
# does). Same layout the meson recipe ships.
mkiso_install_meson()
{
mkiso_mod=
mkiso_py_probe=$(mkiso_seed_python) ||
mkiso_py_probe=/usr/bin/python3
mkiso_mod=$("$mkiso_py_probe" -c \
'import mesonbuild, os; print(os.path.dirname(mesonbuild.__file__))' \
2>/dev/null) || mkiso_mod=
if [ -z "$mkiso_mod" ] || [ ! -d "$mkiso_mod" ]; then
for mkiso_cand in \
/usr/lib/python*/site-packages/mesonbuild \
/usr/lib64/python*/site-packages/mesonbuild
do
[ -d "$mkiso_cand" ] || continue
mkiso_mod=$mkiso_cand
break
done
fi
[ -n "$mkiso_mod" ] && [ -d "$mkiso_mod" ] || return 0
printf '%s: copying Meson from %s\n' "$sps_prog" "$mkiso_mod" >&2
mkdir -p "$mkiso_live/usr/share/meson" "$mkiso_live/usr/bin"
rm -rf "$mkiso_live/usr/share/meson/mesonbuild"
# Homebrew and other foreign layouts symlink module files into a
# per-version Cellar tree. cp -a preserves those links, and a link
# whose target points outside the live root dangles once packed.
# Dereference so the seeded mesonbuild is self-contained.
cp -aL --reflink=auto "$mkiso_mod" \
"$mkiso_live/usr/share/meson/mesonbuild" 2>/dev/null ||
cp -aL "$mkiso_mod" "$mkiso_live/usr/share/meson/mesonbuild" 2>/dev/null ||
cp -LR "$mkiso_mod" "$mkiso_live/usr/share/meson/mesonbuild"
cat >"$mkiso_live/usr/share/meson/meson.py" <<'EOF'
#!/usr/bin/python3
import sys
from pathlib import Path
meson_exe = Path(sys.argv[0]).resolve()
if (meson_exe.parent / 'mesonbuild').is_dir():
sys.path.insert(0, str(meson_exe.parent))
from mesonbuild import mesonmain
if __name__ == '__main__':
sys.exit(mesonmain.main())
EOF
chmod 755 "$mkiso_live/usr/share/meson/meson.py"
rm -f "$mkiso_live/usr/bin/meson"
ln -s ../share/meson/meson.py "$mkiso_live/usr/bin/meson"
}
mkiso_copy_cmake_data()
{
printf '%s: copying CMake modules\n' "$sps_prog" >&2
for mkiso_cmake in /usr/share/cmake /usr/share/cmake-*
do
[ -e "$mkiso_cmake" ] || continue
mkiso_copy_path "$mkiso_cmake"
done
}
mkiso_copy_perl_lib()
{
printf '%s: copying Perl library\n' "$sps_prog" >&2
for mkiso_perl in /usr/lib64/perl5 /usr/lib/perl5 \
/usr/share/perl5 /usr/share/perl
do
[ -e "$mkiso_perl" ] || continue
mkiso_copy_path "$mkiso_perl"
done
}
# Host /usr/include is copied so gcc can compile against libc. Optional
# third-party headers on that tree make configure detect libraries the
# disc does not ship (alsa-utils vs samplerate.h) or compile a recipe
# against a different upstream version (host libxml2 vs the tarball).
mkiso_prune_seed_headers()
{
rm -f "$mkiso_live/usr/include/samplerate.h"
rm -rf "$mkiso_live/usr/include/libxml2"
}
mkiso_copy_mesa_deps()
{
printf '%s: resolving Mesa DRI/GBM libraries\n' "$sps_prog" >&2
for mkiso_d in /usr/lib64/dri /usr/lib/dri /usr/lib64/gbm /usr/lib/gbm
do
[ -d "$mkiso_d" ] || continue
find "$mkiso_d" -type f -name '*.so*' -print 2>/dev/null |
while IFS= read -r mkiso_so || [ -n "$mkiso_so" ]; do
[ -n "$mkiso_so" ] || continue
mkiso_copy_so_deps "$mkiso_so"
done
done
for mkiso_so in /usr/lib64/libgallium*.so /usr/lib64/libgallium*.so.* \
/usr/lib64/libEGL_mesa.so /usr/lib64/libEGL_mesa.so.* \
/usr/lib64/libGLX_mesa.so /usr/lib64/libGLX_mesa.so.* \
/usr/lib64/libgbm.so /usr/lib64/libgbm.so.* \
/usr/lib64/libglapi.so /usr/lib64/libglapi.so.* \
/usr/lib64/libSPIRV-Tools.so \
/usr/lib64/qt6/plugins/platforms/libqwayland.so \
/usr/lib64/qt6/plugins/platforms/libqxcb.so \
/usr/lib64/qt6/plugins/wayland-shell-integration/libxdg-shell.so \
/usr/bin/kwin_wayland /usr/bin/kwin_wayland_wrapper \
/usr/bin/Xwayland
do
[ -e "$mkiso_so" ] || [ -L "$mkiso_so" ] || continue
mkiso_copy_so_deps "$mkiso_so"
done
# Live uses nouveau. A dangling nvidia GBM backend makes libgbm
# probe a proprietary stack that is not on the disc.
rm -f "$mkiso_live/usr/lib64/gbm/nvidia-drm_gbm.so" \
"$mkiso_live/usr/lib/gbm/nvidia-drm_gbm.so"
}
# Session helpers and Plasma's own QML/applet plugins. Tree copies leave the
# plugin .so files in place while their non-Qt libraries (taskmanager,
# KExiv2, notificationmanager, processcore, and others) stay on the host.
mkiso_copy_plasma_runtime_deps()
{
printf '%s: resolving Plasma session libraries\n' "$sps_prog" >&2
mkiso_plasma_runtime=$mkiso_work/plasma-runtime-elfs
: >"$mkiso_plasma_runtime"
for mkiso_so in \
/usr/libexec/kactivitymanagerd \
/usr/libexec/kscreenlocker_greet \
/usr/bin/plasmashell \
/usr/bin/plasma_session \
/usr/bin/kwin_wayland \
/usr/bin/startplasma-wayland \
/usr/bin/xkbcomp \
/usr/lib64/libseat.so \
/usr/lib64/libseat.so.1 \
/usr/lib64/qt6/plugins/wayland-shell-integration/liblayer-shell.so \
/usr/lib64/qt6/plugins/styles/breeze6.so \
/usr/lib64/qt6/plugins/platforms/libqwayland.so \
/usr/lib64/qt6/plugins/sqldrivers/libqsqlite.so \
/usr/lib64/qt6/qml/org/kde/kirigami/libKirigamiplugin.so \
/usr/lib64/qt6/plugins/kf6/kauth/backend/kauth_backend_plugin.so \
/usr/lib64/qt6/plugins/kwin/plugins/eis.so \
/usr/lib64/libtaskmanager.so.6 \
/usr/lib64/libKirigami.so.6 \
/usr/lib64/libKirigamiPlatform.so.6 \
/usr/lib64/firefox/firefox-bin \
/usr/lib64/firefox/libxul.so \
/usr/lib/qt6/plugins/sqldrivers/libqsqlite.so \
/usr/lib/qt6/qml/org/kde/kirigami/libKirigamiplugin.so \
/usr/lib/qt6/plugins/kf6/kauth/backend/kauth_backend_plugin.so \
/usr/lib/qt6/plugins/kwin/plugins/eis.so
do
printf '%s\n' "$mkiso_so" >>"$mkiso_plasma_runtime"
done
# org/kde covers Kirigami, activityswitcher, and the rest of the
# desktop QML graph. plugins/plasma is applets; plugins/kwin is
# compositor helpers such as eis.so.
for mkiso_d in \
/usr/lib64/qt6/qml/org/kde \
/usr/lib64/qt6/plugins/plasma \
/usr/lib64/qt6/plugins/kwin \
/usr/lib64/qt6/plugins/kf6 \
/usr/lib/qt6/qml/org/kde \
/usr/lib/qt6/plugins/plasma \
/usr/lib/qt6/plugins/kwin \
/usr/lib/qt6/plugins/kf6
do
[ -d "$mkiso_d" ] || continue
find "$mkiso_d" -type f -name '*.so' -print 2>/dev/null \
>>"$mkiso_plasma_runtime"
done
LC_ALL=C sort -u "$mkiso_plasma_runtime" -o "$mkiso_plasma_runtime"
while IFS= read -r mkiso_so || [ -n "$mkiso_so" ]
do
[ -e "$mkiso_so" ] || [ -L "$mkiso_so" ] || continue
mkiso_copy_so_deps "$mkiso_so"
done <"$mkiso_plasma_runtime"
}
# ldd a host ELF and require each resolved library to exist in the live
# root. Used so we refuse to ship gcc without libmpc or Mesa without SPIRV.
mkiso_verify_elf_in_live()
{
mkiso_host=$1
[ -e "$mkiso_host" ] || return 0
command -v ldd >/dev/null 2>&1 || return 0
mkiso_ldd=$(ldd "$mkiso_host" 2>/dev/null) || return 0
mkiso_oldifs=$IFS
IFS='
'
for mkiso_line in $mkiso_ldd
do
IFS=$mkiso_oldifs
case $mkiso_line in
*linux-vdso*|*'not a dynamic'*|*'not found'*) continue ;;
esac
mkiso_lib=${mkiso_line#*=> }
mkiso_lib=${mkiso_lib%% (*}
mkiso_lib=${mkiso_lib## }
case $mkiso_lib in
/*)
if [ ! -e "$mkiso_live$mkiso_lib" ] &&
[ ! -L "$mkiso_live$mkiso_lib" ]
then
sps_die "$SPS_EX_PACKAGE" \
"live image missing $mkiso_lib (needed by $mkiso_host)"
fi
;;
esac
done
IFS=$mkiso_oldifs
}
# Copy a host command by name. live-bins lists common paths, but util-linux
# puts lsblk in /bin on this builder and /usr/bin on others. Silent skips
# of a missing path are why live setup printed "no disks found".
mkiso_copy_host_cmd()
{
mkiso_cmd=$1
mkiso_old_path=$PATH
PATH=/sbin:/usr/sbin:/bin:/usr/bin:$PATH
mkiso_found=$(command -v "$mkiso_cmd" 2>/dev/null) || mkiso_found=
PATH=$mkiso_old_path
[ -n "$mkiso_found" ] && [ -e "$mkiso_found" ] || return 1
mkiso_copy_path "$mkiso_found"
mkiso_copy_libs "$mkiso_found"
return 0
}
# glibc, libtinfo, and the loader. Seed and Plasma both need these; a
# missing libtinfo.so.6 (the real file behind the soname symlink) is what
# made getty respawn `/bin/sh` forever on the Plasma ISO.
mkiso_copy_runtime_libs()
{
for mkiso_lib in \
/lib64/ld-linux-x86-64.so.2 \
/lib/ld-linux-x86-64.so.2 \
/lib64/libc.so.6 \
/lib64/libm.so.6 \
/lib64/libdl.so.2 \
/lib64/libpthread.so.0 \
/lib64/librt.so.1 \
/lib64/libutil.so.1 \
/lib64/libresolv.so.2 \
/lib64/libnss_files.so.2 \
/lib64/libnss_dns.so.2 \
/lib64/libtinfo.so.6 \
/lib64/libncursesw.so.6 \
/lib64/libncurses.so.6 \
/lib64/libreadline.so.8 \
/lib64/libgcc_s.so.1 \
/usr/lib64/libgcc_s.so.1 \
/usr/lib64/libstdc++.so.6
do
[ -e "$mkiso_lib" ] || [ -L "$mkiso_lib" ] || continue
mkiso_copy_path "$mkiso_lib"
done
for mkiso_lib in /lib64/libtinfo.so.* /usr/lib64/libtinfo.so.* \
/lib64/libncursesw.so.* /lib64/libc.so.* \
/lib64/ld-linux-x86-64.so.*
do
[ -e "$mkiso_lib" ] || [ -L "$mkiso_lib" ] || continue
mkiso_copy_path "$mkiso_lib"
done
mkiso_copy_path /etc/ld.so.conf
mkiso_copy_path /etc/ld.so.conf.d
if [ ! -f "$mkiso_live/etc/ld.so.conf" ]; then
mkdir -p "$mkiso_live/etc"
printf '%s\n' '/lib64' '/usr/lib64' '/lib' '/usr/lib' \
>"$mkiso_live/etc/ld.so.conf"
fi
mkiso_copy_path /usr/share/terminfo
mkiso_copy_path /etc/terminfo
mkiso_copy_path /lib/terminfo
}
# ldd live *binaries* so their DT_NEEDED closure is on the disc.
# Do not walk lib directories: a Plasma tree has tens of thousands of
# .so files, and ldd-per-file never finishes (the ISO looks hung after
# "live PID 1 is busybox init"). Plugins are already copied as trees;
# session binaries are ldd'd from plasma-bins.
mkiso_close_libs()
{
command -v ldd >/dev/null 2>&1 || return 0
printf '%s: resolving shared libraries for live binaries\n' \
"$sps_prog" >&2
mkiso_find=
for mkiso_d in bin sbin usr/bin usr/sbin
do
[ -d "$mkiso_live/$mkiso_d" ] || continue
mkiso_find="$mkiso_find $mkiso_live/$mkiso_d"
done
[ -n "$mkiso_find" ] || return 0
# shellcheck disable=SC2086
find $mkiso_find -type f \( -perm -0100 -o -perm -0010 -o -perm -0001 \) \
-print 2>/dev/null |
while IFS= read -r mkiso_elf || [ -n "$mkiso_elf" ]; do
[ -n "$mkiso_elf" ] || continue
# Scripts and data are not DT_NEEDED sources.
mkiso_magic=$(dd if="$mkiso_elf" bs=4 count=1 2>/dev/null) || continue
case $mkiso_magic in
*$'\x7fELF'*) mkiso_copy_libs "$mkiso_elf" ;;
esac
done
}
mkiso_ldconfig_live()
{
command -v ldconfig >/dev/null 2>&1 || return 0
ldconfig -r "$mkiso_live" 2>/dev/null || :
}
# Getty and live-rc exec /bin/sh. That must be static busybox, never bash.
mkiso_force_busybox_sh()
{
[ -x "$mkiso_live/bin/busybox" ] || return 0
ln -sf busybox "$mkiso_live/bin/sh"
}
mkiso_stamp_os_release()
{
mkdir -p "$mkiso_live/etc"
if [ -n "$mkiso_extra" ] &&
[ -f "$mkiso_extra/base/splux-base/files/os-release" ]
then
cp "$mkiso_extra/base/splux-base/files/os-release" \
"$mkiso_live/etc/os-release"
return 0
fi
printf '%s\n' \
'NAME="Splux"' \
'PRETTY_NAME="Splux Linux"' \
'ID=splux' \
'VERSION="1.0"' \
'VERSION_ID="1.0"' \
'LOGO=splux' \
'ANSI_COLOR="1;33"' \
'HOME_URL="https://splux.robertflexx.dev"' \
'DOCUMENTATION_URL="https://splux.robertflexx.dev/docs/"' \
'SUPPORT_URL="https://github.com/RobertFlexx/SPS"' \
'BUG_REPORT_URL="https://github.com/RobertFlexx/SPS/issues"' \
>"$mkiso_live/etc/os-release"
}
# Overlay extra's Distro PNG and a hardcoded plasma-welcome intro so the
# live disc does not read /etc/os-release for the first Welcome paragraph.
mkiso_stamp_distro_branding()
{
mkiso_brand=
if [ -n "$mkiso_extra" ] &&
[ -d "$mkiso_extra/base/splux-base/files" ]
then
mkiso_brand=$mkiso_extra/base/splux-base/files
fi
mkdir -p "$mkiso_live/usr/share/pixmaps" \
"$mkiso_live/usr/share/icons/hicolor/512x512/apps" \
"$mkiso_live/etc/xdg" \
"$mkiso_live/usr/share/plasma/plasma-welcome"
if [ -n "$mkiso_brand" ] && [ -f "$mkiso_brand/sps.png" ]; then
cp "$mkiso_brand/sps.png" "$mkiso_live/usr/share/pixmaps/splux.png"
cp "$mkiso_brand/sps.png" "$mkiso_live/usr/share/pixmaps/sps.png"
cp "$mkiso_brand/sps.png" \
"$mkiso_live/usr/share/icons/hicolor/512x512/apps/splux.png"
fi
if [ -n "$mkiso_brand" ] && [ -f "$mkiso_brand/kcm-about-distrorc" ]; then
cp "$mkiso_brand/kcm-about-distrorc" \
"$mkiso_live/etc/xdg/kcm-about-distrorc"
fi
if [ -n "$mkiso_brand" ] && [ -f "$mkiso_brand/os-release" ]; then
cp "$mkiso_brand/os-release" "$mkiso_live/etc/os-release"
fi
# Hardcoded. plasma-welcome prepends this Name; it must not fall
# back to PRETTY_NAME from os-release.
cat >"$mkiso_live/usr/share/plasma/plasma-welcome/intro-customization.desktop" <<'EOF'
[Desktop Entry]
Type=Application
Name=Welcome to the SPS Linux operating system running KDE Plasma
Comment=SPS Linux
Icon=file:/usr/share/pixmaps/sps.png
URL=https://splux.robertflexx.dev
EOF
}
# Fail the ISO build rather than ship a disc that respawns getty with
# "error while loading shared libraries: libtinfo.so.6".
mkiso_verify_live()
{
[ -x "$mkiso_live/bin/busybox" ] || return 0
if [ -L "$mkiso_live/bin/sh" ]; then
mkiso_sh=$(readlink "$mkiso_live/bin/sh")
case $mkiso_sh in
busybox|./busybox|/bin/busybox) ;;
*)
sps_die "$SPS_EX_BUILD" \
"live /bin/sh must be busybox (got $mkiso_sh)"
;;
esac
else
sps_die "$SPS_EX_BUILD" "live /bin/sh is not a busybox symlink"
fi
mkiso_interp=
for mkiso_p in "$mkiso_live/lib64/ld-linux-x86-64.so.2" \
"$mkiso_live/lib/ld-linux-x86-64.so.2" \
"$mkiso_live/usr/lib64/ld-linux-x86-64.so.2"
do
if [ -e "$mkiso_p" ]; then
mkiso_interp=$mkiso_p
break
fi
done
mkiso_lp=$mkiso_live/lib64:$mkiso_live/usr/lib64:$mkiso_live/lib:$mkiso_live/usr/lib
if [ -x "$mkiso_live/bin/bash" ] && [ ! -L "$mkiso_live/bin/bash" ]; then
if [ ! -e "$mkiso_live/lib64/libtinfo.so.6" ] &&
[ ! -e "$mkiso_live/usr/lib64/libtinfo.so.6" ]; then
sps_die "$SPS_EX_BUILD" \
"live bash is present but libtinfo.so.6 is not"
fi
if [ -n "$mkiso_interp" ]; then
if ! "$mkiso_interp" --library-path "$mkiso_lp" \
"$mkiso_live/bin/bash" -c ':' >/dev/null 2>&1; then
sps_die "$SPS_EX_BUILD" \
"live /bin/bash cannot execute (missing libraries)"
fi
fi
fi
# chroot needs privileges; skip it when the kernel refuses the
# syscall (user namespaces, unprivileged root in tests).
if [ "$(id -u 2>/dev/null)" = 0 ]; then
mkiso_chroot_st=0
mkiso_chroot_err=$("$mkiso_live/bin/busybox" chroot \
"$mkiso_live" /bin/sh -c ':' 2>&1) || mkiso_chroot_st=$?
if [ "$mkiso_chroot_st" -ne 0 ]; then
case $mkiso_chroot_err in
*'Operation not permitted'*|*'not permitted'*) ;;
*)
sps_die "$SPS_EX_BUILD" \
"live /bin/sh cannot execute inside the image"
;;
esac
fi
fi
if [ -e /usr/sbin/efibootmgr ] || [ -L /usr/sbin/efibootmgr ] ||
[ -e /usr/bin/efibootmgr ] || [ -L /usr/bin/efibootmgr ]
then
if [ ! -e "$mkiso_live/usr/sbin/efibootmgr" ] &&
[ ! -L "$mkiso_live/usr/sbin/efibootmgr" ] &&
[ ! -e "$mkiso_live/usr/bin/efibootmgr" ] &&
[ ! -L "$mkiso_live/usr/bin/efibootmgr" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded live image is missing efibootmgr"
fi
fi
mkiso_verify_compiler
mkiso_verify_plasma
mkiso_verify_setup_catalog
}
# Seeded images ship gcc. The driver DT_NEEDs only libc; cc1 needs
# libmpc/libisl. Refuse to ship a disc whose first autoconf check dies.
mkiso_verify_compiler()
{
if [ ! -e "$mkiso_live/usr/bin/gcc" ] &&
[ ! -L "$mkiso_live/usr/bin/gcc" ]
then
return 0
fi
if [ ! -e "$mkiso_live/usr/lib64/crt1.o" ] &&
[ ! -e "$mkiso_live/lib64/crt1.o" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded live gcc is missing crt1.o"
fi
if [ ! -e "$mkiso_live/usr/lib64/libc.so" ] &&
[ ! -L "$mkiso_live/usr/lib64/libc.so" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded live gcc is missing the libc.so linker script"
fi
# glibc 2.34 folded pthread into libc. On such hosts -lpthread is
# provided by a tiny compatibility archive rather than libpthread.so.
# Autoconf's ALSA probe still passes -lpthread explicitly.
if [ -e /usr/lib64/libpthread.a ] &&
[ ! -e "$mkiso_live/usr/lib64/libpthread.a" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded live gcc is missing the libpthread.a linker archive"
fi
if [ -e /usr/bin/msgfmt ] &&
[ ! -e "$mkiso_live/usr/bin/msgfmt" ] &&
[ ! -L "$mkiso_live/usr/bin/msgfmt" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded live gcc is missing msgfmt"
fi
if [ ! -e "$mkiso_live/usr/bin/as" ] &&
[ ! -L "$mkiso_live/usr/bin/as" ]
then
sps_die "$SPS_EX_PACKAGE" "seeded live gcc is missing as"
fi
if [ ! -e "$mkiso_live/usr/bin/ld" ] &&
[ ! -L "$mkiso_live/usr/bin/ld" ]
then
sps_die "$SPS_EX_PACKAGE" "seeded live gcc is missing ld"
fi
mkiso_cc1=
for mkiso_d in "$mkiso_live/usr/libexec/gcc" "$mkiso_live/usr/lib64/gcc"
do
[ -d "$mkiso_d" ] || continue
mkiso_cc1=$(find "$mkiso_d" -name cc1 -type f -print 2>/dev/null |
head -1) || mkiso_cc1=
[ -n "$mkiso_cc1" ] && break
done
[ -n "$mkiso_cc1" ] ||
sps_die "$SPS_EX_PACKAGE" "seeded live gcc is missing cc1"
mkiso_verify_elf_in_live "${mkiso_cc1#"$mkiso_live"}"
mkiso_verify_elf_in_live /usr/bin/as
if [ -e /usr/bin/ld.bfd ]; then
mkiso_verify_elf_in_live /usr/bin/ld.bfd
elif [ -e /usr/bin/ld ]; then
mkiso_verify_elf_in_live /usr/bin/ld
fi
# Meson is a Python program. The host driver is a setuptools
# wrapper; without mesonbuild and encodings, the first meson
# recipe dies with a traceback (libxml2's -Dpython= option is a
# second, separate failure).
if [ -e /usr/bin/meson ] || [ -L /usr/bin/meson ]; then
if [ ! -e "$mkiso_live/usr/share/meson/mesonbuild/mesonmain.py" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded meson is missing mesonbuild"
fi
if [ ! -e "$mkiso_live/usr/bin/meson" ] &&
[ ! -L "$mkiso_live/usr/bin/meson" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded meson wrapper is missing"
fi
mkiso_pyver=$(mkiso_python_version)
if [ ! -e "$mkiso_live/usr/lib64/python$mkiso_pyver/encodings/__init__.py" ] &&
[ ! -e "$mkiso_live/usr/lib/python$mkiso_pyver/encodings/__init__.py" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded python is missing the encodings stdlib module"
fi
fi
if [ -e /usr/bin/pod2man ] || [ -L /usr/bin/pod2man ]; then
if [ ! -e "$mkiso_live/usr/bin/pod2man" ] &&
[ ! -L "$mkiso_live/usr/bin/pod2man" ]
then
sps_die "$SPS_EX_PACKAGE" \
"seeded live gcc is missing pod2man"
fi
fi
}
mkiso_verify_plasma()
{
[ "$mkiso_session" = plasma ] || return 0
mkiso_miss=
for mkiso_need in usr/bin/kwin_wayland usr/bin/kwin_wayland_wrapper \
usr/bin/startplasma-wayland usr/bin/plasmashell usr/bin/xkbcomp \
usr/libexec/kactivitymanagerd usr/libexec/kscreenlocker_greet \
usr/lib64/gbm/dri_gbm.so usr/lib64/libxcb-cursor.so.0 \
usr/lib64/libtaskmanager.so.6 usr/lib64/libKirigami.so.6 \
usr/lib64/qt6/plugins/sqldrivers/libqsqlite.so \
usr/lib64/qt6/qml/org/kde/kirigami/libKirigamiplugin.so \
usr/share/xkeyboard-config-2/rules/evdev \
usr/share/libinput/10-generic-keyboard.quirks \
usr/share/icons/breeze_cursors/index.theme \
usr/share/systemsettings/categories \
usr/share/glib-2.0/schemas/gschemas.compiled \
usr/lib/polkit-1/polkitd \
etc/xdg/kscreenlockerrc
do
if [ ! -e "$mkiso_live/$mkiso_need" ] &&
[ ! -L "$mkiso_live/$mkiso_need" ]
then
mkiso_miss="$mkiso_miss $mkiso_need"
fi
done
if [ -e /usr/bin/firefox ]; then
if [ ! -e "$mkiso_live/usr/bin/firefox" ] &&
[ ! -L "$mkiso_live/usr/bin/firefox" ]
then
mkiso_miss="$mkiso_miss usr/bin/firefox"
fi
if [ ! -e "$mkiso_live/usr/lib64/firefox/firefox-bin" ] &&
[ ! -L "$mkiso_live/usr/lib64/firefox/firefox-bin" ]
then
mkiso_miss="$mkiso_miss usr/lib64/firefox/firefox-bin"
fi
fi
if ! ls "$mkiso_live"/usr/lib64/libgallium*.so* >/dev/null 2>&1
then
mkiso_miss="$mkiso_miss libgallium"
fi
if [ -e /usr/lib64/libSPIRV-Tools.so ] ||
[ -L /usr/lib64/libSPIRV-Tools.so ]
then
if ! ls "$mkiso_live"/usr/lib64/libSPIRV-Tools.so* >/dev/null 2>&1
then
mkiso_miss="$mkiso_miss libSPIRV-Tools.so"
fi
fi
if [ -e /usr/bin/Xwayland ] &&
[ ! -e "$mkiso_live/usr/bin/Xwayland" ] &&
[ ! -L "$mkiso_live/usr/bin/Xwayland" ]
then
mkiso_miss="$mkiso_miss usr/bin/Xwayland"
fi
if ! find "$mkiso_live/usr/share/fonts" -type f -name '*.ttf' \
-print -quit 2>/dev/null | grep -q .
then
mkiso_miss="$mkiso_miss TrueType fonts"
fi
[ -z "$mkiso_miss" ] ||
sps_die "$SPS_EX_PACKAGE" "plasma image missing:$mkiso_miss"
for mkiso_g in /usr/lib64/libgallium*.so
do
[ -e "$mkiso_g" ] || continue
mkiso_verify_elf_in_live "$mkiso_g"
break
done
if [ -f "$mkiso_work/plasma-runtime-elfs" ]; then
while IFS= read -r mkiso_plugin || [ -n "$mkiso_plugin" ]
do
[ -e "$mkiso_plugin" ] || [ -L "$mkiso_plugin" ] || continue
mkiso_verify_elf_in_live "$mkiso_plugin"
done <"$mkiso_work/plasma-runtime-elfs"
fi
}
# setup extras/sets/profiles may only name packages that exist in the
# recipe trees on this image. An extras checklist entry with no matching
# recipe is how live setup died at "package not found".
mkiso_verify_setup_catalog()
{
mkiso_setupdir=$SPS_LIBDIR/setup
mkiso_extras=$mkiso_setupdir/extras
[ -f "$mkiso_extras" ] ||
sps_die "$SPS_EX_PACKAGE" "setup extras file is missing"
LC_ALL=C awk '
BEGIN { bad = 0; count = 0 }
/^[[:space:]]*$/ || /^#/ { next }
{
tab = index($0, "\t")
if (tab == 0) {
printf "mkiso: extras line has no tab: %s\n", $0
bad = 1
next
}
name = substr($0, 1, tab - 1)
if (name !~ /^[A-Za-z0-9][A-Za-z0-9+_.-]*$/) {
printf "mkiso: extras bad name: %s\n", name
bad = 1
}
if (name in seen) {
printf "mkiso: extras duplicate: %s\n", name
bad = 1
}
seen[name] = 1
count++
names[count] = name
}
END {
for (i = 1; i < count; i++)
if (names[i] > names[i + 1]) {
printf "mkiso: extras not sorted: %s before %s\n", \
names[i], names[i + 1]
bad = 1
}
exit bad
}
' "$mkiso_extras" ||
sps_die "$SPS_EX_PACKAGE" \
"setup extras must be unique, tab-separated, and sorted"
mkiso_recipes=$mkiso_work/catalog-recipes
: >"$mkiso_recipes"
for mkiso_tree in \
"$mkiso_live/usr/src/sps/core" \
"$mkiso_live/usr/src/sps/extra"
do
[ -d "$mkiso_tree" ] || continue
find "$mkiso_tree" \( -type d \( -name .git -o -name files -o \
-name patches -o -name hooks \) -prune \) -o \
-type f -name recipe -print
done | while IFS= read -r mkiso_rfile || [ -n "$mkiso_rfile" ]
do
[ -n "$mkiso_rfile" ] || continue
awk '/^name[[:space:]]/ { print $2; exit }' "$mkiso_rfile"
done | LC_ALL=C sort -u >"$mkiso_recipes"
[ -s "$mkiso_recipes" ] || return 0
mkiso_wanted=$mkiso_work/catalog-wanted
: >"$mkiso_wanted"
awk -F '\t' '/^[[:space:]]*$/ || /^#/ { next } { print $1 }' \
"$mkiso_extras" >>"$mkiso_wanted"
for mkiso_f in "$mkiso_setupdir"/sets/*.set \
"$mkiso_setupdir"/profiles/*.profile
do
[ -f "$mkiso_f" ] || continue
awk '/^package[[:space:]]/ { print $2 }' "$mkiso_f" \
>>"$mkiso_wanted"
done
if [ -f "$mkiso_setupdir/hwdetect.sh" ]; then
awk '
/printf '\''%s\\n'\'' [a-z]/ && $0 !~ /\$/ && $0 !~ /\|/ {
for (i = 3; i <= NF; i++) {
gsub(/;/, "", $i)
if ($i ~ /^[a-z][a-z0-9+-]*$/)
print $i
}
}
' "$mkiso_setupdir/hwdetect.sh" >>"$mkiso_wanted"
fi
LC_ALL=C sort -u "$mkiso_wanted" -o "$mkiso_wanted"
mkiso_miss=
while IFS= read -r mkiso_pkg || [ -n "$mkiso_pkg" ]
do
[ -n "$mkiso_pkg" ] || continue
grep -qx "$mkiso_pkg" "$mkiso_recipes" && continue
mkiso_miss="$mkiso_miss $mkiso_pkg"
done <"$mkiso_wanted"
[ -z "$mkiso_miss" ] ||
sps_die "$SPS_EX_PACKAGE" \
"setup names packages with no recipe:$mkiso_miss"
}
# BusyBox --install -s writes absolute symlinks to the host path. Relative
# links keep applets working inside the squashfs and initramfs.
mkiso_busybox_links()
{
mkiso_bbdir=$1
[ -x "$mkiso_bbdir/busybox" ] || return 1
"$mkiso_bbdir/busybox" --list | while IFS= read -r mkiso_ap || [ -n "$mkiso_ap" ]
do
[ -n "$mkiso_ap" ] || continue
[ "$mkiso_ap" = busybox ] && continue
if [ -e "$mkiso_bbdir/$mkiso_ap" ] &&
[ ! -L "$mkiso_bbdir/$mkiso_ap" ]
then
continue
fi
ln -sf busybox "$mkiso_bbdir/$mkiso_ap"
done
ln -sf busybox "$mkiso_bbdir/sh"
}
# filesystem ships /bin -> usr/bin. A real /bin directory on the live
# root makes pkin refuse the package, so sget install dies immediately.
mkiso_ensure_usr_merge()
{
mkdir -p "$mkiso_live/usr/bin" "$mkiso_live/usr/sbin" \
"$mkiso_live/usr/lib"
for mkiso_merge in bin:usr/bin sbin:usr/sbin lib:usr/lib
do
mkiso_merge_name=${mkiso_merge%%:*}
mkiso_merge_target=${mkiso_merge#*:}
mkiso_merge_path=$mkiso_live/$mkiso_merge_name
if [ -L "$mkiso_merge_path" ]; then
continue
fi
if [ -e "$mkiso_merge_path" ]; then
sps_die "$SPS_EX_BUILD" \
"live /$mkiso_merge_name exists and is not a usr-merge symlink"
fi
ln -s "$mkiso_merge_target" "$mkiso_merge_path"
done
}
# Own the usr-merge links and static files so later packages that depend
# on filesystem can install on the live image itself.
mkiso_install_base_filesystem()
{
mkiso_fs_recipe=
if [ -n "${mkiso_core:-}" ] &&
[ -f "$mkiso_core/base/filesystem/recipe" ]
then
mkiso_fs_recipe=$mkiso_core/base/filesystem/recipe
elif [ -f "$mkiso_libdir/filesystem.recipe" ]; then
mkiso_fs_recipe=$mkiso_libdir/filesystem.recipe
fi
[ -n "$mkiso_fs_recipe" ] || return 0
printf '%s: installing filesystem into the live root\n' \
"$sps_prog" >&2
mkiso_fs_out=$(mktemp -d "${TMPDIR:-/tmp}/.sps-mkiso-fs.XXXXXX") ||
sps_die "$SPS_EX_BUILD" "cannot create filesystem package workspace"
mkiso_fs_st=0
if ! (
unset SPS_ROOT SPS_DB SPS_CACHE SPS_BUILD SPS_CONFIG \
SPS_REPOS_CONFIG SPS_COMPRESSION
SPS_ROOT=$mkiso_live
SPS_DB=$mkiso_live/var/lib/sps
SPS_CACHE=$mkiso_live/var/cache/sps
SPS_BUILD=$mkiso_live/var/tmp/sps
SPS_CONFIG=/dev/null
SPS_REPOS_CONFIG=/dev/null
SPS_COMPRESSION=none
PATH=$sps_bin_dir:$PATH
export SPS_ROOT SPS_DB SPS_CACHE SPS_BUILD SPS_CONFIG \
SPS_REPOS_CONFIG SPS_COMPRESSION SPS_LIBDIR PATH
mkdir -p "$SPS_DB" "$SPS_CACHE" "$SPS_BUILD"
mkpkg --output "$mkiso_fs_out" "$mkiso_fs_recipe" || exit 1
mkiso_fs_pkg=
for mkiso_fs_cand in "$mkiso_fs_out"/filesystem-*.pkg.tar \
"$mkiso_fs_out"/filesystem-*.pkg.tar.gz \
"$mkiso_fs_out"/filesystem-*.pkg.tar.xz \
"$mkiso_fs_out"/filesystem-*.pkg.tar.zst
do
[ -f "$mkiso_fs_cand" ] || continue
mkiso_fs_pkg=$mkiso_fs_cand
break
done
[ -n "$mkiso_fs_pkg" ] || exit 1
pkin --dependency "$mkiso_fs_pkg"
)
then
mkiso_fs_st=1
fi
rm -rf "$mkiso_fs_out"
[ "$mkiso_fs_st" -eq 0 ] ||
sps_die "$SPS_EX_BUILD" \
"failed to install filesystem into the live root"
}
mkiso_add_group()
{
mkiso_gname=$1
mkiso_gid=$2
mkiso_gmembers=${3-}
mkiso_gfile=$mkiso_live/etc/group
[ -f "$mkiso_gfile" ] || printf '%s\n' 'root:x:0:' >"$mkiso_gfile"
grep -q "^${mkiso_gname}:" "$mkiso_gfile" && return 0
if [ -n "$mkiso_gmembers" ]; then
printf '%s:x:%s:%s\n' "$mkiso_gname" "$mkiso_gid" \
"$mkiso_gmembers" >>"$mkiso_gfile"
else
printf '%s:x:%s:\n' "$mkiso_gname" "$mkiso_gid" >>"$mkiso_gfile"
fi
}
mkiso_add_user()
{
mkiso_uname=$1
mkiso_uid=$2
mkiso_ugid=$3
mkiso_ugeos=$4
mkiso_uhome=$5
mkiso_pfile=$mkiso_live/etc/passwd
mkiso_sfile=$mkiso_live/etc/shadow
[ -f "$mkiso_pfile" ] ||
printf '%s\n' 'root:x:0:0:root:/root:/bin/sh' >"$mkiso_pfile"
grep -q "^${mkiso_uname}:" "$mkiso_pfile" && return 0
printf '%s:x:%s:%s:%s:%s:/usr/bin/nologin\n' \
"$mkiso_uname" "$mkiso_uid" "$mkiso_ugid" "$mkiso_ugeos" \
"$mkiso_uhome" >>"$mkiso_pfile"
if [ -f "$mkiso_sfile" ] && ! grep -q "^${mkiso_uname}:" "$mkiso_sfile"
then
printf '%s:!:0:0:99999:7:::\n' "$mkiso_uname" >>"$mkiso_sfile"
fi
}
mkiso_write_live_accounts()
{
mkdir -p "$mkiso_live/etc" "$mkiso_live/var/lib/polkit" \
"$mkiso_live/run/dbus"
if [ ! -s "$mkiso_live/etc/passwd" ]; then
printf '%s\n' 'root:x:0:0:root:/root:/bin/sh' \
>"$mkiso_live/etc/passwd"
fi
if [ ! -s "$mkiso_live/etc/group" ]; then
printf '%s\n' 'root:x:0:' >"$mkiso_live/etc/group"
fi
mkiso_add_group tty 5
mkiso_add_group disk 6
mkiso_add_group audio 17
mkiso_add_group video 18 root
mkiso_add_group input 71 root
mkiso_add_group render 107 root
mkiso_add_group messagebus 81
mkiso_add_group polkitd 87
mkiso_add_user messagebus 81 81 "User for D-BUS" /run/dbus
mkiso_add_user polkitd 87 87 "PolicyKit daemon owner" /var/lib/polkit
if [ ! -s "$mkiso_live/etc/hosts" ]; then
printf '%s\n' '127.0.0.1 localhost' '::1 localhost' \
>"$mkiso_live/etc/hosts"
fi
if ! grep -q splux-live "$mkiso_live/etc/hosts"; then
mkiso_hosts=$mkiso_live/etc/hosts
mkiso_hosts_tmp=$(mktemp "${TMPDIR:-/tmp}/.sps-hosts.XXXXXX") ||
sps_die "$SPS_EX_BUILD" "cannot stage live hosts"
awk '{
if ($1 == "127.0.0.1" && $0 !~ /splux-live/)
print $0 " splux-live"
else if ($1 == "::1" && $0 !~ /splux-live/)
print $0 " splux-live"
else
print
}' "$mkiso_hosts" >"$mkiso_hosts_tmp"
cat "$mkiso_hosts_tmp" >"$mkiso_hosts"
rm -f "$mkiso_hosts_tmp"
fi
if [ ! -s "$mkiso_live/etc/nsswitch.conf" ]; then
printf '%s\n' \
'passwd: files' \
'group: files' \
'shadow: files' \
'hosts: files dns' \
'networks: files' \
'protocols: files' \
'services: files' \
'ethers: files' \
'rpc: files' \
>"$mkiso_live/etc/nsswitch.conf"
fi
if [ ! -s "$mkiso_live/etc/shadow" ]; then
printf '%s\n' 'root::0:0:99999:7:::' \
>"$mkiso_live/etc/shadow"
else
mkiso_shadow_tmp=$(mktemp "${TMPDIR:-/tmp}/.sps-shadow.XXXXXX") ||
sps_die "$SPS_EX_BUILD" "cannot stage live shadow"
awk -F: 'BEGIN { OFS=":" }
$1 == "root" { $2 = ""; print; next }
{ print }' "$mkiso_live/etc/shadow" >"$mkiso_shadow_tmp"
cat "$mkiso_shadow_tmp" >"$mkiso_live/etc/shadow"
rm -f "$mkiso_shadow_tmp"
fi
chmod 600 "$mkiso_live/etc/shadow"
if [ -f "$mkiso_live/etc/gshadow" ]; then
chmod 600 "$mkiso_live/etc/gshadow"
fi
}
# Copy a host Plasma/Wayland stack. mkiso does not compile Qt.
mkiso_copy_plasma()
{
printf '%s: copying host Plasma/Wayland into the live root\n' \
"$sps_prog" >&2
if [ -f "$mkiso_libdir/plasma-bins" ]; then
while IFS= read -r mkiso_bin || [ -n "$mkiso_bin" ]; do
case $mkiso_bin in ''|\#*) continue ;; esac
[ -e "$mkiso_bin" ] || continue
mkiso_copy_path "$mkiso_bin"
mkiso_copy_libs "$mkiso_bin"
done <"$mkiso_libdir/plasma-bins"
fi
if [ -f "$mkiso_libdir/plasma-trees" ]; then
while IFS= read -r mkiso_tree || [ -n "$mkiso_tree" ]; do
case $mkiso_tree in ''|\#*) continue ;; esac
[ -e "$mkiso_tree" ] || continue
printf '%s: copying %s\n' "$sps_prog" "$mkiso_tree" >&2
mkiso_copy_path "$mkiso_tree"
done <"$mkiso_libdir/plasma-trees"
fi
# Shared objects the session links by soname. Copy the files; do not
# ldd each one (this host has hundreds of Qt/KF libraries, and that
# is what made plasma ISO creation look hung after the tree copies).
# plasma-bins and close_libs still pull DT_NEEDED closures.
printf '%s: copying Qt/KF shared libraries\n' "$sps_prog" >&2
for mkiso_pat in /usr/lib64/libQt6*.so* /usr/lib64/libKF6*.so* \
/usr/lib64/libplasma*.so* /usr/lib64/libkwin*.so* \
/usr/lib64/libwayland*.so* /usr/lib64/libGLESv2.so* \
/usr/lib64/libEGL.so* /usr/lib64/libGL.so* \
/usr/lib64/libEGL_mesa.so* /usr/lib64/libGLX_mesa.so* \
/usr/lib64/libgallium*.so* /usr/lib64/dri/libdril_dri.so \
/usr/lib64/libLLVM.so /usr/lib64/libLLVM.so.[0-9]* \
/usr/lib64/libOpenGL.so* /usr/lib64/libxcb-cursor.so* \
/usr/lib64/libgbm.so* /usr/lib64/libglapi.so* \
/usr/lib64/libSPIRV-Tools.so
do
for mkiso_so in $mkiso_pat; do
[ -e "$mkiso_so" ] || continue
# Skip the rust-wrapped LLVM; the Mesa DRI stack uses libLLVM.so.*
case $mkiso_so in
*-rust*) continue ;;
esac
mkiso_copy_path "$mkiso_so"
done
done
# Host fontconfig prefers Liberation Sans. DejaVu alone leaves
# fc-match pointing at a file that is not on the disc.
for mkiso_font in /usr/share/fonts/TTF/DejaVu*.ttf \
/usr/share/fonts/TTF/Liberation*.ttf \
/usr/share/fonts/X11/dejavu/DejaVu*.ttf \
/usr/share/fonts/X11/liberation-fonts/Liberation*.ttf
do
[ -e "$mkiso_font" ] || continue
mkiso_copy_path "$mkiso_font"
done
mkdir -p "$mkiso_live/etc/xdg"
cat >"$mkiso_live/etc/xdg/kscreenlockerrc" <<'EOF'
[Daemon]
Autolock=false
LockOnResume=false
LockOnStart=false
Timeout=0
EOF
mkdir -p "$mkiso_live/root/.config"
cat >"$mkiso_live/root/.config/startkderc" <<'EOF'
[General]
systemdBoot=false
EOF
# Host /etc/dbus-1 may already contain system-local.conf. live-plasma
# used to skip the root-user override when that file existed, which
# left dbus-daemon --system looking for a messagebus account the live
# passwd does not have.
mkdir -p "$mkiso_live/etc/dbus-1"
cat >"$mkiso_live/etc/dbus-1/system-local.conf" <<'EOF'
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<user>root</user>
</busconfig>
EOF
for mkiso_cmd in dbus-daemon dbus-run-session dbus-launch dbus-uuidgen \
dbus-send
do
mkiso_copy_host_cmd "$mkiso_cmd" || :
done
mkiso_copy_path /usr/libexec/dbus-daemon-launch-helper
# startplasmacompositor looks up `qdbus` on PATH. This host ships qdbus6.
if [ -e "$mkiso_live/usr/bin/qdbus6" ] &&
[ ! -e "$mkiso_live/usr/bin/qdbus" ]
then
ln -sf qdbus6 "$mkiso_live/usr/bin/qdbus"
fi
mkdir -p "$mkiso_live/etc/systemd/system"
ln -sf /dev/null "$mkiso_live/etc/systemd/system/dbus.service"
ln -sf /dev/null "$mkiso_live/etc/systemd/system/dbus.socket"
mkiso_copy_plasma_runtime_deps
mkiso_copy_mesa_deps
}
# PID 1 must be an ELF. A shell script that execs getty/setsid is what
# panicked the previous live TTY (init exited 127).
mkiso_install_pid1()
{
mkdir -p "$mkiso_live/sbin" "$mkiso_live/etc/sps" \
"$mkiso_live/etc/systemd/system" \
"$mkiso_live/etc/runlevels/default" \
"$mkiso_live/etc/runlevels/sysinit" \
"$mkiso_live/etc/modprobe.d"
install -m 0755 "$mkiso_libdir/live-rc" "$mkiso_live/etc/sps/live-rc"
install -m 0755 "$mkiso_libdir/live-plasma" "$mkiso_live/etc/sps/live-plasma"
if [ -f "$mkiso_libdir/live-plasma-session" ]; then
install -m 0755 "$mkiso_libdir/live-plasma-session" \
"$mkiso_live/etc/sps/live-plasma-session"
fi
printf '%s\n' "$mkiso_init" >"$mkiso_live/etc/sps/init"
printf '%s\n' "$mkiso_session" >"$mkiso_live/etc/sps/live-session"
mkiso_systemd=
for mkiso_p in /usr/lib/systemd/systemd /lib/systemd/systemd \
/usr/lib64/systemd/systemd "$mkiso_live/usr/lib/systemd/systemd"
do
if [ -x "$mkiso_p" ]; then
mkiso_systemd=$mkiso_p
break
fi
done
mkiso_openrc=
for mkiso_p in /sbin/openrc-init /usr/sbin/openrc-init \
"$mkiso_live/sbin/openrc-init"
do
if [ -x "$mkiso_p" ]; then
mkiso_openrc=$mkiso_p
break
fi
done
# Always plant busybox init + inittab so the disc boots even when
# the preferred init is not on the build host.
if [ -n "$mkiso_busybox" ] && [ -x "$mkiso_busybox" ]; then
cp -a "$mkiso_busybox" "$mkiso_live/bin/busybox"
chmod 755 "$mkiso_live/bin/busybox"
mkiso_busybox_links "$mkiso_live/bin"
mkiso_force_busybox_sh
cp -a "$mkiso_live/bin/busybox" "$mkiso_live/sbin/init"
chmod 755 "$mkiso_live/sbin/init"
fi
if [ "$mkiso_session" = plasma ]; then
install -m 0644 "$mkiso_libdir/inittab-plasma" \
"$mkiso_live/etc/inittab"
else
install -m 0644 "$mkiso_libdir/inittab" "$mkiso_live/etc/inittab"
fi
if [ "$mkiso_init" = systemd ] && [ -n "$mkiso_systemd" ]; then
printf '%s: live PID 1 is systemd (%s)\n' \
"$sps_prog" "$mkiso_systemd" >&2
mkiso_copy_path "$mkiso_systemd"
mkiso_copy_libs "$mkiso_systemd"
mkiso_copy_path /usr/bin/systemctl
mkiso_copy_libs /usr/bin/systemctl
mkiso_copy_path /usr/lib/systemd
mkiso_copy_path /lib/systemd
mkdir -p "$mkiso_live/etc/systemd/system/multi-user.target.wants" \
"$mkiso_live/etc/systemd/system/graphical.target.wants" \
"$mkiso_live/etc/systemd/system/getty.target.wants"
install -m 0644 "$mkiso_libdir/systemd/sps-live-rc.service" \
"$mkiso_live/etc/systemd/system/"
install -m 0644 "$mkiso_libdir/systemd/sps-live-serial.service" \
"$mkiso_live/etc/systemd/system/"
ln -sf /etc/systemd/system/sps-live-rc.service \
"$mkiso_live/etc/systemd/system/multi-user.target.wants/"
ln -sf /etc/systemd/system/sps-live-serial.service \
"$mkiso_live/etc/systemd/system/multi-user.target.wants/"
if [ "$mkiso_session" = plasma ]; then
install -m 0644 "$mkiso_libdir/systemd/sps-live-plasma.service" \
"$mkiso_live/etc/systemd/system/"
ln -sf /etc/systemd/system/sps-live-plasma.service \
"$mkiso_live/etc/systemd/system/graphical.target.wants/"
ln -sf /usr/lib/systemd/system/graphical.target \
"$mkiso_live/etc/systemd/system/default.target"
else
ln -sf /usr/lib/systemd/system/multi-user.target \
"$mkiso_live/etc/systemd/system/default.target"
ln -sf /usr/lib/systemd/system/getty@.service \
"$mkiso_live/etc/systemd/system/getty.target.wants/getty@tty1.service"
fi
# systemd as PID 1: kernel execs this ELF, not a shell.
mkiso_sys_dest=$mkiso_live$mkiso_systemd
if [ -x "$mkiso_sys_dest" ]; then
ln -sf "$mkiso_systemd" "$mkiso_live/sbin/init"
fi
elif [ "$mkiso_init" = openrc ] && [ -n "$mkiso_openrc" ]; then
printf '%s: live PID 1 is OpenRC (%s)\n' \
"$sps_prog" "$mkiso_openrc" >&2
mkiso_copy_path "$mkiso_openrc"
mkiso_copy_libs "$mkiso_openrc"
ln -sf "$mkiso_openrc" "$mkiso_live/sbin/init"
elif [ "$mkiso_init" = systemd ] || [ "$mkiso_init" = openrc ]; then
# Falling back silently ships a disc that says systemd in
# /etc/sps/init and on the kernel command line while PID 1 is
# really busybox. Every service the image declares is then
# quietly dead and the cause is invisible from inside the
# booted system. Refuse instead.
sps_die "$SPS_EX_PACKAGE" \
"--init $mkiso_init was requested but this host has no $mkiso_init binaries to copy"
else
printf '%s: live PID 1 is busybox init (host has no %s binaries)\n' \
"$sps_prog" "$mkiso_init" >&2
fi
if [ ! -x "$mkiso_live/sbin/init" ]; then
install -m 0755 "$mkiso_libdir/live-init" "$mkiso_live/sbin/init"
fi
[ -x "$mkiso_live/sbin/init" ] ||
sps_die "$SPS_EX_PACKAGE" "failed to install /sbin/init"
mkiso_verify_init
}
# /sbin/init existing is not the same as /sbin/init being runnable. A dangling
# symlink, a missing program interpreter, or a missing shared library all make
# the kernel fail switch_root with ENOENT, which it reports as "can't execute
# /sbin/init: No such file or directory". That panic is only visible after the
# disc is written and booted, so check it here while it is still cheap.
mkiso_verify_init()
{
mkiso_init_target=$mkiso_live/sbin/init
mkiso_init_hops=0
while [ -L "$mkiso_init_target" ]; do
mkiso_init_hops=$((mkiso_init_hops + 1))
[ "$mkiso_init_hops" -lt 10 ] ||
sps_die "$SPS_EX_PACKAGE" "/sbin/init is a symlink loop"
mkiso_init_link=$(readlink "$mkiso_init_target")
case $mkiso_init_link in
/*) mkiso_init_target=$mkiso_live$mkiso_init_link ;;
*) mkiso_init_target=${mkiso_init_target%/*}/$mkiso_init_link ;;
esac
done
[ -f "$mkiso_init_target" ] ||
sps_die "$SPS_EX_PACKAGE" \
"/sbin/init resolves to ${mkiso_init_target#$mkiso_live}, which is not in the live root"
[ -x "$mkiso_init_target" ] ||
sps_die "$SPS_EX_PACKAGE" "/sbin/init is not executable"
command -v readelf >/dev/null 2>&1 || return 0
# readelf prints " [ 0] /lib64/ld-linux-x86-64.so.2"; take the
# whole path after the offset field rather than matching on "ld-",
# which a greedy expression would clip to the last path component.
mkiso_init_interp=$(readelf -p .interp "$mkiso_init_target" 2>/dev/null |
sed -n 's/^[[:space:]]*\[[^]]*\][[:space:]]*\(\/.*\)$/\1/p' |
head -n 1)
if [ -n "$mkiso_init_interp" ] &&
[ ! -e "$mkiso_live$mkiso_init_interp" ]
then
sps_die "$SPS_EX_PACKAGE" \
"PID 1 needs interpreter $mkiso_init_interp, which is not in the live root"
fi
command -v ldd >/dev/null 2>&1 || return 0
mkiso_init_missing=$(ldd "$mkiso_init_target" 2>/dev/null |
sed -n 's/^[[:space:]]*\([^ ]*\) => not found.*/\1/p')
[ -z "$mkiso_init_missing" ] ||
printf '%s: warning: PID 1 has unresolved libraries on the host: %s\n' \
"$sps_prog" "$(printf '%s' "$mkiso_init_missing" | tr '\n' ' ')" >&2
}
# --- live root contents -------------------------------------------------
printf '%s\n' "mkiso: installing SPS tools into the live root" >&2
make -C "$sps_bin_dir/.." \
DESTDIR="$mkiso_live" PREFIX=/usr \
install install-config >/dev/null ||
sps_die "$SPS_EX_BUILD" "make install into the live root failed"
mkdir -p "$mkiso_live/usr/bin" "$mkiso_live/usr/sbin" "$mkiso_live/root" \
"$mkiso_live/mnt" "$mkiso_live/proc" "$mkiso_live/sys" \
"$mkiso_live/dev" "$mkiso_live/tmp" "$mkiso_live/run" \
"$mkiso_live/etc/sps" "$mkiso_live/usr/src/sps" \
"$mkiso_live/var/cache/sps" "$mkiso_live/var/lib/sps" \
"$mkiso_live/var/tmp/sps"
mkiso_install_base_filesystem
mkiso_ensure_usr_merge
if [ -z "$mkiso_busybox" ]; then
for mkiso_bb in /tmp/sps-wave/busybox.static \
"$mkiso_libdir/busybox" /usr/bin/busybox /bin/busybox
do
if [ -x "$mkiso_bb" ]; then
mkiso_busybox=$mkiso_bb
break
fi
done
fi
[ -n "$mkiso_busybox" ] && [ -x "$mkiso_busybox" ] || {
if [ -n "$mkiso_output" ]; then
sps_die "$SPS_EX_PACKAGE" \
"busybox is required for an ISO (pass --busybox FILE)"
fi
}
if [ -n "$mkiso_busybox" ] && [ -x "$mkiso_busybox" ]; then
cp -a "$mkiso_busybox" "$mkiso_live/bin/busybox"
chmod 755 "$mkiso_live/bin/busybox"
mkiso_busybox_links "$mkiso_live/bin"
mkiso_force_busybox_sh
ln -sf ../bin/busybox "$mkiso_live/sbin/busybox" 2>/dev/null || :
fi
install -m 0644 "$mkiso_libdir/motd" "$mkiso_live/etc/motd"
install -m 0644 "$mkiso_libdir/issue" "$mkiso_live/etc/issue"
printf '%s\n' live >"$mkiso_live/etc/sps/live"
printf '%s\n' splux-live >"$mkiso_live/etc/hostname"
mkiso_stamp_os_release
mkiso_write_live_accounts
# Do not copy the host nsswitch.conf: this builder uses mdns modules the
# live disc does not ship. Without a file, glibc still looks at 127.0.0.1
# when resolv.conf is missing, so live DNS looks like it "does not exist".
if [ ! -s "$mkiso_live/etc/nsswitch.conf" ]; then
printf '%s\n' \
'passwd: files' \
'group: files' \
'shadow: files' \
'hosts: files dns' \
'networks: files' \
'protocols: files' \
'services: files' \
'ethers: files' \
'rpc: files' \
>"$mkiso_live/etc/nsswitch.conf"
fi
printf '%s\n' \
'nameserver 1.1.1.1' \
'nameserver 8.8.8.8' \
>"$mkiso_live/etc/resolv.conf"
if [ -f "$mkiso_libdir/udhcpc-script" ]; then
install -m 0755 "$mkiso_libdir/udhcpc-script" \
"$mkiso_live/etc/sps/udhcpc-script"
fi
sps_print_default_conf >"$mkiso_live/etc/sps/sps.conf"
mkiso_repos=$mkiso_live/etc/sps/repos.conf
: >"$mkiso_repos"
if [ -n "$mkiso_core" ] && [ -d "$mkiso_core" ]; then
printf '%s: copying sps-core recipes\n' "$sps_prog" >&2
rm -rf "$mkiso_live/usr/src/sps/core"
mkdir -p "$mkiso_live/usr/src/sps/core"
cp -a "$mkiso_core"/. "$mkiso_live/usr/src/sps/core"/
rm -rf "$mkiso_live/usr/src/sps/core/.git"
printf 'dir core /usr/src/sps/core 100\n' >>"$mkiso_repos"
fi
if [ -n "$mkiso_extra" ] && [ -d "$mkiso_extra" ]; then
printf '%s: copying sps-extra recipes\n' "$sps_prog" >&2
rm -rf "$mkiso_live/usr/src/sps/extra"
mkdir -p "$mkiso_live/usr/src/sps/extra"
cp -a "$mkiso_extra"/. "$mkiso_live/usr/src/sps/extra"/
rm -rf "$mkiso_live/usr/src/sps/extra/.git"
printf 'dir extra /usr/src/sps/extra 80\n' >>"$mkiso_repos"
fi
if [ ! -s "$mkiso_repos" ]; then
cat >>"$mkiso_repos" <<'EOF'
git core https://github.com/RobertFlexx/sps-core.git 100
git extra https://github.com/RobertFlexx/sps-extra.git 80
EOF
fi
sps_print_community_repos_hint >>"$mkiso_repos"
# dialog and glibc/libtinfo on every image, including --no-seed.
if [ -f "$mkiso_libdir/live-bins" ]; then
while IFS= read -r mkiso_bin || [ -n "$mkiso_bin" ]; do
case $mkiso_bin in ''|\#*) continue ;; esac
[ -e "$mkiso_bin" ] || continue
mkiso_copy_path "$mkiso_bin"
mkiso_copy_libs "$mkiso_bin"
done <"$mkiso_libdir/live-bins"
fi
printf '%s: copying disk tools for setup\n' "$sps_prog" >&2
for mkiso_cmd in lsblk blkid sfdisk cfdisk fdisk wipefs mkfs.ext4 mke2fs \
mkfs.vfat mkswap partprobe udevadm udevd grub-install grub-mkconfig \
efibootmgr dhcpcd
do
mkiso_copy_host_cmd "$mkiso_cmd" || :
done
mkiso_copy_runtime_libs
mkiso_force_busybox_sh
if [ "$mkiso_copy_seed" -eq 1 ] && [ -f "$mkiso_libdir/seed-bins" ]; then
printf '%s: copying host seed tools\n' "$sps_prog" >&2
while IFS= read -r mkiso_bin || [ -n "$mkiso_bin" ]; do
case $mkiso_bin in ''|\#*) continue ;; esac
[ -e "$mkiso_bin" ] || continue
mkiso_copy_path "$mkiso_bin"
mkiso_copy_libs "$mkiso_bin"
done <"$mkiso_libdir/seed-bins"
# Compiler support files.
mkiso_copy_path /usr/libexec/gcc
mkiso_copy_path /usr/lib64/gcc
mkiso_copy_path /usr/lib/gcc
mkiso_copy_path /usr/include
mkiso_prune_seed_headers
# gcc and ld without glibc startfiles fail the first autoconf check
# with "C compiler cannot create executables". Runtime libc.so.6 is
# not enough; the linker needs crt1.o and the libc.so script.
printf '%s: copying glibc startfiles for the seed compiler\n' \
"$sps_prog" >&2
for mkiso_crt in /usr/lib64/crt1.o /usr/lib64/crti.o /usr/lib64/crtn.o \
/usr/lib64/Scrt1.o /usr/lib64/gcrt1.o /usr/lib64/Mcrt1.o \
/lib64/crt1.o /lib64/crti.o /lib64/crtn.o \
/usr/lib64/libc.so /usr/lib64/libm.so /usr/lib64/libmvec.so \
/usr/lib64/libpthread.so /usr/lib64/libdl.so \
/usr/lib64/librt.so /usr/lib64/libutil.so \
/usr/lib64/libresolv.so /usr/lib64/libanl.so \
/usr/lib64/libthread_db.so /usr/lib64/libgcc_s.so \
/usr/lib64/libstdc++.so /usr/lib64/libc_nonshared.a \
/usr/lib64/libpthread.a /usr/lib64/libdl.a \
/usr/lib64/librt.a /usr/lib64/libutil.a \
/usr/lib64/libanl.a \
/usr/lib64/libpthread_nonshared.a \
/usr/lib64/libmvec_nonshared.a \
/usr/lib64/rcrt1.o /usr/lib64/grcrt1.o \
/lib64/libc.so.6 /usr/lib64/libc.so.6 \
/lib64/libm.so.6 /usr/lib64/libm.so.6 \
/lib64/libmvec.so.1 /usr/lib64/libmvec.so.1 \
/lib64/ld-linux-x86-64.so.2 /usr/lib64/ld-linux-x86-64.so.2
do
[ -e "$mkiso_crt" ] || [ -L "$mkiso_crt" ] || continue
mkiso_copy_path "$mkiso_crt"
done
# cc1 DT_NEEDs libmpc/libisl. ldd of the gcc driver does not.
mkiso_copy_gcc_deps
# file(1) is in seed-bins. Autoconf calls it during configure, and
# without the magic database it prints "could not find any valid
# magic files" and some probes then mis-detect object types.
printf '%s: copying file(1) magic database\n' "$sps_prog" >&2
for mkiso_magic in /etc/file /usr/share/file /usr/share/misc/magic \
/usr/share/misc/magic.mgc /usr/share/file/magic.mgc
do
[ -e "$mkiso_magic" ] || [ -L "$mkiso_magic" ] || continue
mkiso_copy_path "$mkiso_magic"
done
for mkiso_cmd in gcc g++ cc c++ as ld ld.bfd
do
mkiso_copy_host_cmd "$mkiso_cmd" || :
done
mkiso_copy_python_stdlib
mkiso_install_meson
mkiso_copy_cmake_data
mkiso_copy_perl_lib
mkiso_copy_path /usr/share/terminfo
mkiso_copy_path /usr/share/zoneinfo
mkiso_copy_path /etc/ssl
mkiso_copy_path /etc/ca-certificates
mkiso_copy_path /usr/share/ca-certificates
mkiso_copy_path /etc/ld.so.conf
mkiso_copy_path /etc/ld.so.conf.d
mkiso_copy_path /usr/lib64/grub
mkiso_copy_path /usr/lib/grub
mkiso_copy_path /usr/share/grub
if [ "$mkiso_with_firmware" -eq 1 ] && [ -d /lib/firmware ]; then
printf '%s: copying /lib/firmware\n' "$sps_prog" >&2
mkiso_copy_path /lib/firmware
fi
if [ "$mkiso_with_rust" -eq 1 ]; then
mkiso_copy_path /usr/lib64/rustlib
mkiso_copy_path /usr/bin/rustc
mkiso_copy_path /usr/bin/cargo
mkiso_copy_libs /usr/bin/rustc
mkiso_copy_libs /usr/bin/cargo
fi
if [ "$mkiso_with_go" -eq 1 ]; then
mkiso_copy_path /usr/lib64/go1.27.0
fi
fi
if [ "$mkiso_session" = plasma ]; then
mkiso_copy_plasma
if [ -f "$mkiso_libdir/plasma-motd" ]; then
install -m 0644 "$mkiso_libdir/plasma-motd" "$mkiso_live/etc/motd"
fi
fi
mkiso_stamp_distro_branding
# Initramfs modules are required to mount the ISO even when the live
# squashfs does not ship the full /lib/modules tree.
if [ -z "$mkiso_modules" ]; then
mkiso_krel=$(uname -r)
mkiso_modules=/lib/modules/$mkiso_krel
fi
if [ "$mkiso_copy_modules" -eq 1 ] && [ -d "$mkiso_modules" ]; then
printf '%s: copying kernel modules from %s\n' \
"$sps_prog" "$mkiso_modules" >&2
mkdir -p "$mkiso_live/lib/modules"
cp -a "$mkiso_modules" "$mkiso_live/lib/modules/"
fi
# PID 1 last, after seed, so a copied /sbin/init cannot stick around.
mkiso_install_pid1
mkiso_copy_runtime_libs
mkiso_close_libs
mkiso_force_busybox_sh
mkiso_ldconfig_live
mkiso_verify_live
if [ -n "$mkiso_layout" ] && [ -z "$mkiso_output" ]; then
printf '%s\n' "$mkiso_live"
exit 0
fi
# --- squashfs + initrd + ISO --------------------------------------------
command -v mksquashfs >/dev/null 2>&1 ||
sps_die "$SPS_EX_PACKAGE" "mksquashfs is required"
command -v xorriso >/dev/null 2>&1 ||
sps_die "$SPS_EX_PACKAGE" "xorriso is required"
if [ -z "$mkiso_kernel" ]; then
if [ -f /boot/vmlinuz-$(uname -r) ]; then
mkiso_kernel=/boot/vmlinuz-$(uname -r)
elif [ -f /boot/vmlinuz ]; then
mkiso_kernel=/boot/vmlinuz
fi
fi
# Follow a single symlink so the ISO contains a real kernel, not a dangling link.
if [ -L "$mkiso_kernel" ]; then
mkiso_ktarget=$(readlink "$mkiso_kernel")
case $mkiso_ktarget in
/*) mkiso_kernel=$mkiso_ktarget ;;
*) mkiso_kernel=$(dirname "$mkiso_kernel")/$mkiso_ktarget ;;
esac
fi
[ -f "$mkiso_kernel" ] ||
sps_die "$SPS_EX_NOTFOUND" "kernel not found: $mkiso_kernel"
mkiso_iso=$mkiso_work/iso
mkdir -p "$mkiso_iso/live" "$mkiso_iso/boot/grub"
cp -a "$mkiso_kernel" "$mkiso_iso/live/vmlinuz"
printf '%s: writing squashfs (zstd)\n' "$sps_prog" >&2
# zstd mounts in seconds; xz is what made QEMU sit on loop0 while the
# 4G Plasma root decompressed. Level 3 is a live-ISO default: fast
# to build, fast to boot, still much smaller than gzip.
mkiso_squash_jobs=$(nproc 2>/dev/null || printf 2)
case $mkiso_squash_jobs in
''|*[!0-9]*|0) mkiso_squash_jobs=2 ;;
esac
# -all-root: everything in a live root belongs to root. Without it an
# unprivileged build bakes the builder's uid into every file, and the booted
# system finds /usr owned by a user that does not exist on it. systemd
# refuses unit files it does not trust and sudo refuses to run at all.
if ! mksquashfs "$mkiso_live" "$mkiso_iso/live/filesystem.squashfs" \
-comp zstd -Xcompression-level 3 -b 262144 -noappend -no-progress \
-all-root -processors "$mkiso_squash_jobs" \
-e boot tmp var/tmp var/cache
then
printf '%s: zstd squashfs failed, trying xz\n' "$sps_prog" >&2
if ! mksquashfs "$mkiso_live" "$mkiso_iso/live/filesystem.squashfs" \
-comp xz -noappend -no-progress -all-root \
-processors "$mkiso_squash_jobs" \
-e boot tmp var/tmp var/cache
then
printf '%s: xz squashfs failed, trying gzip\n' "$sps_prog" >&2
mksquashfs "$mkiso_live" "$mkiso_iso/live/filesystem.squashfs" \
-comp gzip -noappend -no-progress -all-root \
-processors "$mkiso_squash_jobs" \
-e boot tmp var/tmp var/cache ||
sps_die "$SPS_EX_BUILD" "mksquashfs failed"
fi
fi
# Initramfs: busybox + init + filesystem modules.
mkiso_ird=$mkiso_work/initrd-root
rm -rf "$mkiso_ird"
mkdir -p "$mkiso_ird/bin" "$mkiso_ird/sbin" "$mkiso_ird/lib/modules" \
"$mkiso_ird/proc" "$mkiso_ird/sys" "$mkiso_ird/dev" "$mkiso_ird/mnt"
cp -a "$mkiso_busybox" "$mkiso_ird/bin/busybox"
chmod 755 "$mkiso_ird/bin/busybox"
mkiso_busybox_links "$mkiso_ird/bin"
install -m 0755 "$mkiso_libdir/initrd-init" "$mkiso_ird/init"
if [ -d "$mkiso_modules" ]; then
mkiso_kver=$(basename "$mkiso_modules")
mkdir -p "$mkiso_ird/lib/modules/$mkiso_kver"
# Copy only what the initramfs needs to find and mount the ISO.
# Follow host module dependencies so overlay/loop are complete.
mkiso_copy_one_mod()
{
mkiso_mp=$1
[ -f "$mkiso_mp" ] || return 0
mkiso_rel=${mkiso_mp#"$mkiso_modules"/}
mkdir -p "$mkiso_ird/lib/modules/$mkiso_kver/$(dirname "$mkiso_rel")"
case $mkiso_mp in
*.ko.xz)
xz -dc "$mkiso_mp" >"$mkiso_ird/lib/modules/$mkiso_kver/${mkiso_rel%.xz}"
;;
*.ko.zst)
zstd -q -dc "$mkiso_mp" >"$mkiso_ird/lib/modules/$mkiso_kver/${mkiso_rel%.zst}"
;;
*)
cp -a "$mkiso_mp" \
"$mkiso_ird/lib/modules/$mkiso_kver/$mkiso_rel"
;;
esac
}
for mkiso_mod in squashfs overlay loop isofs cdrom sr_mod sd_mod \
ahci nvme usb-storage xhci-pci ehci-hcd uhci-hcd \
virtio_blk virtio_pci virtio_scsi virtio_net \
nls_cp437 nls_iso8859-1 vfat fat crc32c libcrc32c \
drm drm_kms_helper drm_shmem_helper drm_ttm_helper ttm \
virtio_dma_buf virtio_gpu bochs simpledrm
do
if command -v modprobe >/dev/null 2>&1; then
modprobe -D "$mkiso_mod" 2>/dev/null | while IFS= read -r mkiso_line
do
mkiso_cmd=${mkiso_line%% *}
mkiso_mp=${mkiso_line#* }
mkiso_mp=${mkiso_mp%% *}
[ "$mkiso_cmd" = insmod ] || continue
mkiso_copy_one_mod "$mkiso_mp"
done
fi
find "$mkiso_modules" -name "${mkiso_mod}.ko*" 2>/dev/null |
while IFS= read -r mkiso_mp; do
mkiso_copy_one_mod "$mkiso_mp"
done
done
cp -a "$mkiso_modules"/modules.dep \
"$mkiso_ird/lib/modules/$mkiso_kver"/ 2>/dev/null || :
cp -a "$mkiso_modules"/modules.alias \
"$mkiso_ird/lib/modules/$mkiso_kver"/ 2>/dev/null || :
cp -a "$mkiso_modules"/modules.builtin \
"$mkiso_ird/lib/modules/$mkiso_kver"/ 2>/dev/null || :
fi
printf '%s: packing initramfs\n' "$sps_prog" >&2
# --owner root:root for the same reason as -all-root above: PID 1 runs out of
# this archive, and cpio would otherwise record the building user's uid.
mkiso_cpio_owner=--owner=root:root
printf '' | cpio -o -H newc --owner=root:root >/dev/null 2>&1 ||
mkiso_cpio_owner=-R0:0
(
cd "$mkiso_ird" || exit 1
find . | cpio -o -H newc "$mkiso_cpio_owner" 2>/dev/null
) | gzip -n -6 >"$mkiso_iso/live/initrd.img" ||
sps_die "$SPS_EX_BUILD" "initramfs cpio failed"
mkiso_title="SPS Linux live (Splux)"
[ "$mkiso_session" = plasma ] && mkiso_title="SPS Linux live Plasma (Splux)"
cat >"$mkiso_iso/boot/grub/grub.cfg" <<EOF
set timeout=8
set default=0
serial --unit=0 --speed=115200
terminal_input serial console
terminal_output serial console
menuentry "$mkiso_title" {
linux /live/vmlinuz boot=live sps.init=$mkiso_init console=tty0 console=ttyS0,115200n8
initrd /live/initrd.img
}
menuentry "$mkiso_title (run setup)" {
linux /live/vmlinuz boot=live sps.init=$mkiso_init sps=setup console=tty0 console=ttyS0,115200n8
initrd /live/initrd.img
}
EOF
# BIOS + UEFI hybrid ISO. grub-mkrescue on this host requires
# /usr/share/grub/unicode.pf2, which is not shipped, so mkiso builds
# the image with grub-mkimage and xorriso instead. It never runs
# grub-install on a disk.
command -v grub-mkimage >/dev/null 2>&1 ||
sps_die "$SPS_EX_PACKAGE" "grub-mkimage is required"
mkiso_grub_pc_cmd=$(command -v grub-mkimage)
mkiso_grub_efi_cmd=$mkiso_grub_pc_cmd
mkiso_pc=
mkiso_efi=
for mkiso_gdir in /usr/lib64/grub /usr/lib/grub
do
[ -d "$mkiso_gdir/i386-pc" ] && mkiso_pc=$mkiso_gdir/i386-pc
[ -d "$mkiso_gdir/x86_64-efi" ] && mkiso_efi=$mkiso_gdir/x86_64-efi
done
if command -v brew >/dev/null 2>&1; then
mkiso_brew_pc=$(brew --prefix i686-elf-grub 2>/dev/null || :)
if [ -d "$mkiso_brew_pc/lib/i686-elf/grub/i386-pc" ]; then
mkiso_pc=$mkiso_brew_pc/lib/i686-elf/grub/i386-pc
if command -v i686-elf-grub-mkimage >/dev/null 2>&1; then
mkiso_grub_pc_cmd=$(command -v i686-elf-grub-mkimage)
fi
fi
mkiso_brew_efi=$(brew --prefix x86_64-elf-grub 2>/dev/null || :)
if [ -d "$mkiso_brew_efi/lib/x86_64-elf/grub/x86_64-efi" ]; then
mkiso_efi=$mkiso_brew_efi/lib/x86_64-elf/grub/x86_64-efi
if command -v x86_64-elf-grub-mkimage >/dev/null 2>&1; then
mkiso_grub_efi_cmd=$(command -v x86_64-elf-grub-mkimage)
fi
fi
fi
[ -n "$mkiso_pc" ] ||
sps_die "$SPS_EX_PACKAGE" "GRUB i386-pc modules not found"
mkiso_grub_mods='iso9660 linux normal configfile echo search fat part_gpt part_msdos ext2 serial'
"$mkiso_grub_pc_cmd" -d "$mkiso_pc" -O i386-pc \
-o "$mkiso_work/core.img" -p /boot/grub \
biosdisk $mkiso_grub_mods ||
sps_die "$SPS_EX_BUILD" "grub-mkimage (i386-pc) failed"
cat "$mkiso_pc/cdboot.img" "$mkiso_work/core.img" >"$mkiso_iso/boot/grub/bios.img"
mkdir -p "$mkiso_iso/EFI/BOOT"
if [ -n "$mkiso_efi" ]; then
"$mkiso_grub_efi_cmd" -d "$mkiso_efi" -O x86_64-efi \
-o "$mkiso_iso/EFI/BOOT/BOOTX64.EFI" \
-p /boot/grub $mkiso_grub_mods ||
sps_die "$SPS_EX_BUILD" "grub-mkimage (x86_64-efi) failed"
truncate -s 8M "$mkiso_iso/boot/grub/efi.img"
if command -v mformat >/dev/null 2>&1 && command -v mcopy >/dev/null 2>&1; then
mformat -v SPLUX_EFI -i "$mkiso_iso/boot/grub/efi.img" ::
mmd -i "$mkiso_iso/boot/grub/efi.img" ::EFI ::EFI/BOOT
mcopy -i "$mkiso_iso/boot/grub/efi.img" \
"$mkiso_iso/EFI/BOOT/BOOTX64.EFI" ::EFI/BOOT/BOOTX64.EFI
elif command -v mkfs.vfat >/dev/null 2>&1 || [ -x /sbin/mkfs.vfat ]; then
mkiso_mkfs=$(command -v mkfs.vfat 2>/dev/null || printf /sbin/mkfs.vfat)
"$mkiso_mkfs" "$mkiso_iso/boot/grub/efi.img" >/dev/null
mmd -i "$mkiso_iso/boot/grub/efi.img" ::EFI ::EFI/BOOT
mcopy -i "$mkiso_iso/boot/grub/efi.img" \
"$mkiso_iso/EFI/BOOT/BOOTX64.EFI" ::EFI/BOOT/BOOTX64.EFI
fi
fi
printf '%s: writing ISO\n' "$sps_prog" >&2
case $mkiso_output in
/*) ;;
*) mkiso_output=$(CDPATH= cd -P "$(pwd)" && printf '%s/%s' "$(pwd)" "$mkiso_output") ;;
esac
mkdir -p "$(dirname "$mkiso_output")"
mkiso_xorriso_log=$mkiso_work/xorriso.log
if [ -f "$mkiso_iso/boot/grub/efi.img" ]; then
xorriso -as mkisofs -r -J -V SPLUX_LIVE \
-o "$mkiso_output" \
-b boot/grub/bios.img -no-emul-boot -boot-load-size 4 \
-boot-info-table --grub2-boot-info \
--grub2-mbr "$mkiso_pc/boot_hybrid.img" \
-append_partition 2 0xef "$mkiso_iso/boot/grub/efi.img" \
-eltorito-alt-boot \
-e --interval:appended_partition_2:all:: \
-no-emul-boot \
--mbr-force-bootable \
"$mkiso_iso" >"$mkiso_xorriso_log" 2>&1 ||
sps_die "$SPS_EX_BUILD" "xorriso failed (see $mkiso_xorriso_log)"
else
xorriso -as mkisofs -r -J -V SPLUX_LIVE \
-o "$mkiso_output" \
-b boot/grub/bios.img -no-emul-boot -boot-load-size 4 \
-boot-info-table --grub2-boot-info \
--grub2-mbr "$mkiso_pc/boot_hybrid.img" \
--mbr-force-bootable \
"$mkiso_iso" >"$mkiso_xorriso_log" 2>&1 ||
sps_die "$SPS_EX_BUILD" "xorriso failed (see $mkiso_xorriso_log)"
fi
if [ -f "$mkiso_iso/boot/grub/efi.img" ] &&
command -v fdisk >/dev/null 2>&1
then
if ! fdisk -l "$mkiso_output" 2>/dev/null |
grep -qE '(^|[[:space:]])(ef|EFI)'
then
sps_die "$SPS_EX_BUILD" \
"generated ISO has no USB-visible EFI partition"
fi
fi
printf '%s\n' "$mkiso_output"
ls -lh "$mkiso_output" >&2
exit 0