mklni/mklni

387 lines
9 KiB
Bash
Executable file

#!/usr/bin/env bash
ALPINE="https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/x86_64/alpine-minirootfs-3.20.3-x86_64.tar.gz"
CHROOTDIR="./alpine_chroot"
QEMU_STATIC_URL="https://github.com/multiarch/qemu-user-static/releases/download/v7.2.0-1/qemu-aarch64-static"
log()
{
case "$2" in
error)
printf "\e[1m\e[31mERROR:\e[0m \e[1m%s\e[0m (host)\n" "$1">&2;;
internal)
printf "\e[1m\e[96m h>\e[0m \e[1m%s\e[0m\n" "$1">&2;;
ierror)
printf "\e[1m\e[31m h>\e[0m \e[1m%s\e[0m\n" "$1">&2;;
*)
printf "\e[1m\e[92mH>>\e[0m \e[1m%s\e[0m\n" "$1">&2;;
esac
}
mkdir_if_not_exists()
{
[ -d "$1" ] && return 0
[ -f "$1" ] && rm -f "$1"
mkdir "$1"
}
umount_if_mouted()
{
[ ! -d "$1" ] && return 0
if grep -qs "$(realpath "$1")" /proc/mounts; then
umount "$1"
return $?
else
return 0
fi
}
require ()
{
which "$1" > /dev/null 2>&1 ||
{
log "$1 not found" error
exit 1
}
}
# shellcheck disable=SC2161
# shellcheck disable=SC2086
arguments() {
while [[ $# -gt 0 ]]; do
opt="$1"
shift
case "$(echo ${opt} | tr '[:upper:]' '[:lower:]')" in
install)
install_chroot
break 2 ;;
remove)
remove_chroot
break 2 ;;
build)
build "$1"
break 2 ;;
chroot)
chroot_into
break 2 ;;
-r | -c | --cleanraw | --cleanup)
run_makelni "$opt"
break ;;
-h | -help | --help)
usage ;;
*)
log "Unknown option: $opt" error
break 2 ;;
esac
done
}
usage() {
log "Usage commands:"
cat <<EOF
# Help screen (this)
$0 --help or $0 -h
# Setup build chroot
$0 install
# Remove build chroot
$0 uninstall
# Make image
$0 build path/to/lnibuild
# Clean raw images
$0 -r or --cleanraw
# Clean build chroots
$0 -c or --cleanup
# Chroot into
$0 chroot
EOF
exit 0
}
# shellcheck disable=SC2317
_shutdown()
{
detach_chroot
if [ -z ${1+x} ]; then
exit 0
else
exit "$1"
fi
}
sigterm_handler()
{
sig=$?
printf "\e[1m\e[31m>>\e[0m \e[1m%s\e[0m\n" "Shutdown signal received."
_shutdown $sig
}
trap 'trap " " SIGINT SIGTERM SIGHUP; kill 0; wait; sigterm_handler' SIGINT SIGTERM SIGHUP
# Chroot functions
require "lsof"
require "tar"
require "gzip"
require "wget"
setup_inet()
{
[ ! -d "$CHROOTDIR" ] && {
log "Rootdir [$CHROOTDIR] does not exists" ierror
return 2
}
echo "nameserver 1.1.1.1" > "$CHROOTDIR/etc/resolv.conf"
echo "makelnichroot" > "$CHROOTDIR/etc/hostname"
echo "127.0.0.1 localhost" > "$CHROOTDIR/etc/hosts"
}
prepare_chroot()
{
[ ! -d "$CHROOTDIR" ] && {
log "Rootdir [$CHROOTDIR] does not exists" ierror
return 2
}
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:\$PATH
mount --bind "$CHROOTDIR" "$CHROOTDIR"
mount -t proc proc "$CHROOTDIR/proc"
mount -t sysfs sysfs "$CHROOTDIR/sys"
mount -t devtmpfs devtmpfs "$CHROOTDIR/dev"
mount -t devpts devpts "$CHROOTDIR/dev/pts"
mount -t tmpfs devshm "$CHROOTDIR/dev/shm"
if uname -m | grep -q aarch64 || [ -f "/proc/sys/fs/binfmt_misc/qemu-aarch64" ]; then
log "Cancel qemu-aarch64-static register"
else
# shellcheck disable=SC2028
echo ':aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:'"$(realpath $CHROOTDIR/bin/qemu-aarch64-static):PF" > /proc/sys/fs/binfmt_misc/register 2> /dev/null || true
# shellcheck disable=SC2028
echo ':aarch64ld:M::\x7fELF\x02\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:'"$(realpath $CHROOTDIR/bin/qemu-aarch64-static):PF" > /proc/sys/fs/binfmt_misc/register 2> /dev/null || true
fi
}
detach_chroot()
{
[ ! -d "$CHROOTDIR" ] && {
log "Rootdir [$CHROOTDIR] does not exists" ierror
return 2
}
umount_if_mouted "$CHROOTDIR/sys"
umount_if_mouted "$CHROOTDIR/dev/pts"
umount_if_mouted "$CHROOTDIR/dev/shm"
umount_if_mouted "$CHROOTDIR/dev"
umount_if_mouted "$CHROOTDIR/proc"
if [ -f "/proc/sys/fs/binfmt_misc/aarch64" ]; then
echo -1 > /proc/sys/fs/binfmt_misc/aarch64 2> /dev/null
fi
if [ -f "/proc/sys/fs/binfmt_misc/aarch64ld" ]; then
echo -1 > /proc/sys/fs/binfmt_misc/aarch64ld 2> /dev/null
fi
umount_if_mouted "$CHROOTDIR"
}
install_chroot()
{
[ -d "$CHROOTDIR" ] && {
log "Chroot already installed" error
exit 1
}
mkdir "$CHROOTDIR"
[ ! -f "./alpine.tar.gz" ] && {
log "Downloading alpine rootfs tarball"
wget -q --show-progress "$ALPINE" -O "./alpine.tar.gz" || {
ec=$?
log "Failed to download rootfs tarball" error
exit "$ec"
}
}
log "Extracting alpine rootfs tarball"
tar -xzf "./alpine.tar.gz" -C "$CHROOTDIR/" || {
log "Failed to extract rootfs" error
}
setup_inet
prepare_chroot || {
log "Failed to setup chroot" error
exit 1
}
log "Updating apk repos"
chroot "$CHROOTDIR" apk update --quiet || {
log "Failed to update apk repos" error
exit 1
}
log "Installing pakages"
chroot "$CHROOTDIR" apk add --quiet shadow fish bash git debootstrap pixz losetup rsync pv wget e2fsprogs e2fsprogs-extra libarchive-tools coreutils go libusb libusb-dev pkgconfig || {
log "Failed to install packages" error
exit 1
}
chroot "$CHROOTDIR" chsh -s /usr/bin/fish root
log "Cloning makelni repo"
chroot "$CHROOTDIR" git clone --quiet "https://git.timoxa0.su/timoxa0/makelni.git" /makelni || {
log "Failed to clone makelni repo" error
exit 1
}
log "Installing qemu-aarch64-static"
{
wget -q --show-progress -N "$QEMU_STATIC_URL" -O "$CHROOTDIR/bin/qemu-aarch64-static" &&
chmod 755 "$CHROOTDIR/bin/qemu-aarch64-static"
} || {
log "Failed to install qemu-aarch64-static" error
exit 1
}
log "Installing lon-tool"
{
chroot "$CHROOTDIR" git clone --quiet https://git.timoxa0.su/timoxa0/lon-tool.git /lon-tool-src &&
chroot "$CHROOTDIR" /bin/bash -c 'cd /lon-tool-src && rev=$(git describe --abbrev=4 --dirty --always --tags) && go get git.timoxa0.su/timoxa0/lon-tool/cmd && go build -ldflags "-X git.timoxa0.su/timoxa0/lon-tool/cmd.version=$rev" -o /bin/lon-tool main.go'
} || {
log "Failed to install lon-tool" error
exit 1
}
detach_chroot || {
log "Failed to detach chroot" error
exit 1
}
log "Done!"
exit 0
}
remove_chroot()
{
[ ! -d "$CHROOTDIR" ] && {
log "Chroot not installed" error
exit 1
}
detach_chroot || {
log "Failed to umount chroot" error
exit 1
}
{
rm -rf "${CHROOTDIR:?}/" &&
log "Done!"
} || {
log "Failed to remove chroot" error
exit 1
}
}
chroot_into()
{
prepare_chroot || {
log "Failed to setup chroot" error
exit 1
}
chroot "$CHROOTDIR" /usr/bin/fish
detach_chroot || {
log "Failed to detach chroot" error
exit 1
}
}
build()
{
[ ! -d "$CHROOTDIR" ] && {
log "Chroot not installed" error
exit 1
}
local lni="$1"
[ ! -f "$lni" ] && {
log "$lni no such file" error
exit 2
}
cp "$lni" "$CHROOTDIR/tmp/lnibuild"
prepare_chroot || {
log "Failed to setup chroot" error
exit 1
}
chroot "$CHROOTDIR" /bin/bash -c "cd /makelni && ./makelni /tmp/lnibuild"
local exitcode=$?
rm "$CHROOTDIR/tmp/lnibuild"
detach_chroot || {
log "Failed to detach chroot" error
exit 1
}
[ "$exitcode" -eq "0" ] || {
log "Failed to build image" error
exit 1
}
source <( grep name "$lni" )
mv "$CHROOTDIR/makelni/out/${name}.lni" "./${name}.lni"
chown "$(stat -c '%U:%G' "$0")" "./${name}.lni"
exit 0
}
run_makelni()
{
[ ! -d "$CHROOTDIR" ] && {
log "Chroot not installed" error
exit 1
}
log "Running makelni $1"
prepare_chroot || {
log "Failed to setup chroot" error
exit 1
}
chroot "$CHROOTDIR" /bin/bash -c "cd /makelni && ./makelni $1"
local exitcode=$?
detach_chroot || {
log "Failed to detach chroot" error
exit 1
}
[ "$exitcode" -eq "0" ] || {
log "Failed to build image" error
}
return "$exitcode"
}
if [ "$(id -u)" != "0" ]; then
log "$0 must be run as root"
exit 3
fi
[[ "$(uname -m)" != "x86_64*" ]] || {
printf "Unsupported CPU arch\n"
exit 1
}
arguments $*