Boot mode names are: - bios_syslinux.mbr: SYSLINUX in MBR - bios.syslinux.eltorito: SYSLINUX (ISOLINUX) via El Torito - uefi-x64.systemd-boot.esp: systemd-boot on ESP in MBR - uefi-x64.systemd-boot.eltorito: systemd-boot on ESP via El Torito It is not yet possible to create an ISO with only El Torito or only MBR boot modes!
810 lines
27 KiB
Bash
Executable file
810 lines
27 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
set -e -u
|
|
|
|
# Control the environment
|
|
umask 0022
|
|
export LANG="C"
|
|
export SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-"$(date +%s)"}"
|
|
|
|
# mkarchiso defaults
|
|
app_name="${0##*/}"
|
|
pkg_list=()
|
|
run_cmd=""
|
|
quiet="y"
|
|
work_dir="work"
|
|
out_dir="out"
|
|
img_name="${app_name}.iso"
|
|
sfs_mode="sfs"
|
|
sfs_comp="xz"
|
|
gpg_key=""
|
|
|
|
# profile defaults
|
|
profile=""
|
|
iso_name="${app_name}"
|
|
iso_label="${app_name^^}"
|
|
iso_publisher="${app_name}"
|
|
iso_application="${app_name} iso"
|
|
iso_version=""
|
|
install_dir="${app_name}"
|
|
arch="$(uname -m)"
|
|
pacman_conf="/etc/pacman.conf"
|
|
bootmodes=()
|
|
|
|
|
|
# Show an INFO message
|
|
# $1: message string
|
|
_msg_info() {
|
|
local _msg="${1}"
|
|
[[ "${quiet}" == "y" ]] || printf '[%s] INFO: %s\n' "${app_name}" "${_msg}"
|
|
|
|
}
|
|
|
|
# Show a WARNING message
|
|
# $1: message string
|
|
_msg_warning() {
|
|
local _msg="${1}"
|
|
printf '\n[%s] WARNING: %s\n\n' "${app_name}" "${_msg}" >&2
|
|
}
|
|
|
|
# Show an ERROR message then exit with status
|
|
# $1: message string
|
|
# $2: exit code number (with 0 does not exit)
|
|
_msg_error() {
|
|
local _msg="${1}"
|
|
local _error=${2}
|
|
printf '\n[%s] ERROR: %s\n\n' "${app_name}" "${_msg}" >&2
|
|
if (( _error > 0 )); then
|
|
exit "${_error}"
|
|
fi
|
|
}
|
|
|
|
_chroot_init() {
|
|
mkdir -p -- "${airootfs_dir}"
|
|
_pacman base syslinux
|
|
}
|
|
|
|
_chroot_run() {
|
|
eval -- arch-chroot "${airootfs_dir}" "${run_cmd}"
|
|
}
|
|
|
|
_mount_airootfs() {
|
|
trap "_umount_airootfs" EXIT HUP INT TERM
|
|
mkdir -p -- "${work_dir}/mnt/airootfs"
|
|
_msg_info "Mounting '${airootfs_dir}.img' on '${work_dir}/mnt/airootfs'"
|
|
mount -- "${airootfs_dir}.img" "${work_dir}/mnt/airootfs"
|
|
_msg_info "Done!"
|
|
}
|
|
|
|
_umount_airootfs() {
|
|
_msg_info "Unmounting '${work_dir}/mnt/airootfs'"
|
|
umount -d -- "${work_dir}/mnt/airootfs"
|
|
_msg_info "Done!"
|
|
rmdir -- "${work_dir}/mnt/airootfs"
|
|
trap - EXIT HUP INT TERM
|
|
}
|
|
|
|
# Show help usage, with an exit status.
|
|
# $1: exit status number.
|
|
_usage () {
|
|
IFS='' read -r -d '' usagetext <<ENDUSAGETEXT || true
|
|
usage ${app_name} [options] command <command options>
|
|
general options:
|
|
-p PACKAGE(S) Package(s) to install, can be used multiple times
|
|
-r <command> Run <command> inside airootfs
|
|
-C <file> Config file for pacman.
|
|
Default: '${pacman_conf}'
|
|
-L <label> Set a label for the disk
|
|
Default: '${iso_label}'
|
|
-P <publisher> Set a publisher for the disk
|
|
Default: '${iso_publisher}'
|
|
-A <application> Set an application name for the disk
|
|
Default: '${iso_application}'
|
|
-D <install_dir> Set an install_dir. All files will by located here.
|
|
Default: '${install_dir}'
|
|
NOTE: Max 8 characters, use only [a-z0-9]
|
|
-w <work_dir> Set the working directory
|
|
Default: '${work_dir}'
|
|
-o <out_dir> Set the output directory
|
|
Default: '${out_dir}'
|
|
-s <sfs_mode> Set SquashFS image mode (img or sfs)
|
|
img: prepare airootfs.sfs for dm-snapshot usage
|
|
sfs: prepare airootfs.sfs for overlayfs usage
|
|
Default: '${sfs_mode}'
|
|
-c <comp_type> Set SquashFS compression type (gzip, lzma, lzo, xz, zstd)
|
|
Default: '${sfs_comp}'
|
|
-v Enable verbose output
|
|
-h This message
|
|
commands:
|
|
init
|
|
Make base layout and install base group
|
|
install
|
|
Install all specified packages (-p)
|
|
run
|
|
run command specified by -r
|
|
prepare
|
|
build all images
|
|
pkglist
|
|
make a pkglist.txt of packages installed on airootfs
|
|
iso <image name>
|
|
build an iso image from the working dir
|
|
ENDUSAGETEXT
|
|
printf '%s\n' "${usagetext}"
|
|
exit "${1}"
|
|
}
|
|
|
|
# Shows configuration according to command mode.
|
|
# $1: init | install | run | prepare | iso
|
|
_show_config () {
|
|
local _mode="$1"
|
|
printf '\n'
|
|
_msg_info "Configuration settings"
|
|
_msg_info " Command: ${command_name}"
|
|
_msg_info " Architecture: ${arch}"
|
|
_msg_info " Working directory: ${work_dir}"
|
|
_msg_info " Installation directory: ${install_dir}"
|
|
case "${_mode}" in
|
|
init)
|
|
_msg_info " Pacman config file: ${pacman_conf}"
|
|
;;
|
|
install)
|
|
_msg_info " Pacman config file: ${pacman_conf}"
|
|
_msg_info " Packages: ${pkg_list[*]}"
|
|
;;
|
|
run)
|
|
_msg_info " Run command: ${run_cmd}"
|
|
;;
|
|
prepare)
|
|
;;
|
|
pkglist)
|
|
;;
|
|
iso)
|
|
_msg_info " Image name: ${img_name}"
|
|
_msg_info " Disk label: ${iso_label}"
|
|
_msg_info " Disk publisher: ${iso_publisher}"
|
|
_msg_info " Disk application: ${iso_application}"
|
|
;;
|
|
esac
|
|
printf '\n'
|
|
}
|
|
|
|
# Install desired packages to airootfs
|
|
_pacman () {
|
|
_msg_info "Installing packages to '${airootfs_dir}/'..."
|
|
|
|
if [[ "${quiet}" = "y" ]]; then
|
|
pacstrap -C "${pacman_conf}" -c -G -M -- "${airootfs_dir}" "$@" &> /dev/null
|
|
else
|
|
pacstrap -C "${pacman_conf}" -c -G -M -- "${airootfs_dir}" "$@"
|
|
fi
|
|
|
|
_msg_info "Packages installed successfully!"
|
|
}
|
|
|
|
# Cleanup airootfs
|
|
_cleanup () {
|
|
_msg_info "Cleaning up what we can on airootfs..."
|
|
|
|
# Delete initcpio image(s)
|
|
if [[ -d "${airootfs_dir}/boot" ]]; then
|
|
find "${airootfs_dir}/boot" -type f -name '*.img' -delete
|
|
fi
|
|
# Delete kernel(s)
|
|
if [[ -d "${airootfs_dir}/boot" ]]; then
|
|
find "${airootfs_dir}/boot" -type f -name 'vmlinuz*' -delete
|
|
fi
|
|
# Delete pacman database sync cache files (*.tar.gz)
|
|
if [[ -d "${airootfs_dir}/var/lib/pacman" ]]; then
|
|
find "${airootfs_dir}/var/lib/pacman" -maxdepth 1 -type f -delete
|
|
fi
|
|
# Delete pacman database sync cache
|
|
if [[ -d "${airootfs_dir}/var/lib/pacman/sync" ]]; then
|
|
find "${airootfs_dir}/var/lib/pacman/sync" -delete
|
|
fi
|
|
# Delete pacman package cache
|
|
if [[ -d "${airootfs_dir}/var/cache/pacman/pkg" ]]; then
|
|
find "${airootfs_dir}/var/cache/pacman/pkg" -type f -delete
|
|
fi
|
|
# Delete all log files, keeps empty dirs.
|
|
if [[ -d "${airootfs_dir}/var/log" ]]; then
|
|
find "${airootfs_dir}/var/log" -type f -delete
|
|
fi
|
|
# Delete all temporary files and dirs
|
|
if [[ -d "${airootfs_dir}/var/tmp" ]]; then
|
|
find "${airootfs_dir}/var/tmp" -mindepth 1 -delete
|
|
fi
|
|
# Delete package pacman related files.
|
|
find "${work_dir}" \( -name '*.pacnew' -o -name '*.pacsave' -o -name '*.pacorig' \) -delete
|
|
_msg_info "Done!"
|
|
}
|
|
|
|
# Makes a ext4 filesystem inside a SquashFS from a source directory.
|
|
_mkairootfs_img () {
|
|
if [[ ! -e "${airootfs_dir}" ]]; then
|
|
_msg_error "The path '${airootfs_dir}' does not exist" 1
|
|
fi
|
|
|
|
_msg_info "Creating ext4 image of 32GiB..."
|
|
truncate -s 32G -- "${airootfs_dir}.img"
|
|
if [[ "${quiet}" == "y" ]]; then
|
|
mkfs.ext4 -q -O '^has_journal,^resize_inode' -E 'lazy_itable_init=0' -m 0 -F -- "${airootfs_dir}.img"
|
|
else
|
|
mkfs.ext4 -O '^has_journal,^resize_inode' -E 'lazy_itable_init=0' -m 0 -F -- "${airootfs_dir}.img"
|
|
fi
|
|
tune2fs -c 0 -i 0 -- "${airootfs_dir}.img" &> /dev/null
|
|
_msg_info "Done!"
|
|
_mount_airootfs
|
|
_msg_info "Copying '${airootfs_dir}/' to '${work_dir}/mnt/airootfs/'..."
|
|
cp -aT -- "${airootfs_dir}/" "${work_dir}/mnt/airootfs/"
|
|
chown root:root -- "${work_dir}/mnt/airootfs/"
|
|
_msg_info "Done!"
|
|
_umount_airootfs
|
|
mkdir -p -- "${isofs_dir}/${install_dir}/${arch}"
|
|
_msg_info "Creating SquashFS image, this may take some time..."
|
|
if [[ "${quiet}" = "y" ]]; then
|
|
mksquashfs "${airootfs_dir}.img" "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" -noappend \
|
|
-comp "${sfs_comp}" -no-progress &> /dev/null
|
|
else
|
|
mksquashfs "${airootfs_dir}.img" "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" -noappend \
|
|
-comp "${sfs_comp}"
|
|
fi
|
|
_msg_info "Done!"
|
|
rm -- "${airootfs_dir}.img"
|
|
}
|
|
|
|
# Makes a SquashFS filesystem from a source directory.
|
|
_mkairootfs_sfs () {
|
|
if [[ ! -e "${airootfs_dir}" ]]; then
|
|
_msg_error "The path '${airootfs_dir}' does not exist" 1
|
|
fi
|
|
|
|
mkdir -p -- "${isofs_dir}/${install_dir}/${arch}"
|
|
_msg_info "Creating SquashFS image, this may take some time..."
|
|
if [[ "${quiet}" = "y" ]]; then
|
|
mksquashfs "${airootfs_dir}" "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" -noappend \
|
|
-comp "${sfs_comp}" -no-progress &> /dev/null
|
|
else
|
|
mksquashfs "${airootfs_dir}" "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" -noappend \
|
|
-comp "${sfs_comp}"
|
|
fi
|
|
_msg_info "Done!"
|
|
}
|
|
|
|
_mkchecksum () {
|
|
_msg_info "Creating checksum file for self-test..."
|
|
cd -- "${isofs_dir}/${install_dir}/${arch}"
|
|
sha512sum airootfs.sfs > airootfs.sha512
|
|
cd -- "${OLDPWD}"
|
|
_msg_info "Done!"
|
|
}
|
|
|
|
_mksignature () {
|
|
_msg_info "Creating signature file..."
|
|
cd -- "${isofs_dir}/${install_dir}/${arch}"
|
|
gpg --detach-sign --default-key "${gpg_key}" airootfs.sfs
|
|
cd -- "${OLDPWD}"
|
|
_msg_info "Done!"
|
|
}
|
|
|
|
# Helper function to run functions only one time.
|
|
_run_once() {
|
|
if [[ ! -e "${work_dir}/build.${1}" ]]; then
|
|
"$1"
|
|
touch "${work_dir}/build.${1}"
|
|
fi
|
|
}
|
|
|
|
# Set up custom pacman.conf with current cache directories.
|
|
_make_pacman_conf() {
|
|
local _cache_dirs
|
|
_cache_dirs="$(pacman-conf CacheDir)"
|
|
sed -r "s|^#?\\s*CacheDir.+|CacheDir = ${_cache_dirs[*]//$'\n'/ }|g" \
|
|
"${pacman_conf}" > "${work_dir}/pacman.conf"
|
|
}
|
|
|
|
# Prepare working directory and copy custom airootfs files (airootfs)
|
|
_make_custom_airootfs() {
|
|
mkdir -m 755 -- "${airootfs_dir}"
|
|
|
|
local passwd=()
|
|
if [[ -d "${profile}/airootfs" ]]; then
|
|
cp -af --no-preserve=ownership -- "${profile}/airootfs/." "${airootfs_dir}"
|
|
|
|
[[ -e "${airootfs_dir}/etc/shadow" ]] && chmod -f 0400 -- "${airootfs_dir}/etc/shadow"
|
|
[[ -e "${airootfs_dir}/etc/gshadow" ]] && chmod -f 0400 -- "${airootfs_dir}/etc/gshadow"
|
|
|
|
# Set up user home directories and permissions
|
|
if [[ -e "${airootfs_dir}/etc/passwd" ]]; then
|
|
while IFS=':' read -a passwd -r; do
|
|
[[ "${passwd[5]}" == '/' ]] && continue
|
|
[[ -z "${passwd[5]}" ]] && continue
|
|
|
|
if [[ -d "${airootfs_dir}${passwd[5]}" ]]; then
|
|
chown -hR -- "${passwd[2]}:${passwd[3]}" "${airootfs_dir}${passwd[5]}"
|
|
chmod -f 0750 -- "${airootfs_dir}${passwd[5]}"
|
|
else
|
|
install -d -m 0750 -o "${passwd[2]}" -g "${passwd[3]}" -- "${airootfs_dir}${passwd[5]}"
|
|
fi
|
|
done < "${airootfs_dir}/etc/passwd"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Packages (airootfs)
|
|
_make_packages() {
|
|
if [[ -n "${gpg_key}" ]]; then
|
|
exec {ARCHISO_GNUPG_FD}<>"${work_dir}/pubkey.gpg"
|
|
export ARCHISO_GNUPG_FD
|
|
fi
|
|
_pacman "${pkg_list[@]}"
|
|
if [[ -n "${gpg_key}" ]]; then
|
|
exec {ARCHISO_GNUPG_FD}<&-
|
|
unset ARCHISO_GNUPG_FD
|
|
fi
|
|
}
|
|
|
|
# Customize installation (airootfs)
|
|
_make_customize_airootfs() {
|
|
local passwd=()
|
|
if [[ -e "${profile}/airootfs/etc/passwd" ]]; then
|
|
while IFS=':' read -a passwd -r; do
|
|
if [[ "${passwd[5]}" == '/' ]]; then
|
|
continue
|
|
fi
|
|
cp -RdT --preserve=mode,timestamps,links -- "${airootfs_dir}/etc/skel" "${airootfs_dir}${passwd[5]}"
|
|
chown -hR -- "${passwd[2]}:${passwd[3]}" "${airootfs_dir}${passwd[5]}"
|
|
|
|
done < "${profile}/airootfs/etc/passwd"
|
|
fi
|
|
|
|
if [[ -e "${airootfs_dir}/root/customize_airootfs.sh" ]]; then
|
|
_msg_warning "customize_airootfs.sh is deprecated! Support for it will be removed in a future archiso version."
|
|
local run_cmd="/root/customize_airootfs.sh"
|
|
local work_dir="${work_dir}/${arch}"
|
|
command_run
|
|
rm -- "${airootfs_dir}/root/customize_airootfs.sh"
|
|
fi
|
|
}
|
|
|
|
# Set up boot loaders
|
|
_make_bootmodes() {
|
|
local bootmode
|
|
for bootmode in "${bootmodes[@]}"; do
|
|
if typeset -f "_make_boot_${bootmode}" &> /dev/null; then
|
|
_run_once "_make_boot_${bootmode}"
|
|
else
|
|
_msg_error "${bootmode} is not a valid boot mode" 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Prepare kernel/initramfs ${install_dir}/boot/
|
|
_make_boot_on_iso() {
|
|
mkdir -p -- "${isofs_dir}/${install_dir}/boot/${arch}"
|
|
install -m 0644 -- "${airootfs_dir}/boot/archiso.img" "${isofs_dir}/${install_dir}/boot/${arch}/"
|
|
install -m 0644 -- "${airootfs_dir}/boot/vmlinuz-linux" "${isofs_dir}/${install_dir}/boot/${arch}/"
|
|
if [[ -e "${airootfs_dir}/boot/intel-ucode.img" ]]; then
|
|
install -m 0644 -- "${airootfs_dir}/boot/intel-ucode.img" "${isofs_dir}/${install_dir}/boot/"
|
|
mkdir -p "${isofs_dir}/${install_dir}/boot/licenses/intel-ucode/"
|
|
install -m 0644 -- "${airootfs_dir}/usr/share/licenses/intel-ucode/"* \
|
|
"${isofs_dir}/${install_dir}/boot/licenses/intel-ucode/"
|
|
fi
|
|
if [[ -e "${airootfs_dir}/boot/amd-ucode.img" ]]; then
|
|
install -m 0644 -- "${airootfs_dir}/boot/amd-ucode.img" "${isofs_dir}/${install_dir}/boot/"
|
|
mkdir -p "${isofs_dir}/${install_dir}/boot/licenses/amd-ucode/"
|
|
install -m 0644 -- "${airootfs_dir}/usr/share/licenses/amd-ucode/"* \
|
|
"${isofs_dir}/${install_dir}/boot/licenses/amd-ucode/"
|
|
fi
|
|
}
|
|
|
|
# Prepare /${install_dir}/boot/syslinux
|
|
_make_boot_bios.syslinux.mbr() {
|
|
mkdir -p "${isofs_dir}/${install_dir}/boot/syslinux"
|
|
for _cfg in "${profile}/syslinux/"*.cfg; do
|
|
sed "s|%ARCHISO_LABEL%|${iso_label}|g;
|
|
s|%INSTALL_DIR%|${install_dir}|g;
|
|
s|%ARCH%|${arch}|g" \
|
|
"${_cfg}" > "${isofs_dir}/${install_dir}/boot/syslinux/${_cfg##*/}"
|
|
done
|
|
if [[ -e "${profile}/syslinux/splash.png" ]]; then
|
|
install -m 0644 -- "${profile}/syslinux/splash.png" "${isofs_dir}/${install_dir}/boot/syslinux/"
|
|
fi
|
|
install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/"*.c32 "${isofs_dir}/${install_dir}/boot/syslinux/"
|
|
install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/lpxelinux.0" "${isofs_dir}/${install_dir}/boot/syslinux/"
|
|
install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/memdisk" "${isofs_dir}/${install_dir}/boot/syslinux/"
|
|
|
|
_make_boot_on_iso
|
|
_uname_r=$(file -b "${isofs_dir}/${install_dir}/boot/${arch}/vmlinuz-linux" | awk 'f{print;f=0} /version/{f=1}' RS=' ')
|
|
|
|
mkdir -p "${isofs_dir}/${install_dir}/boot/syslinux/hdt"
|
|
gzip -c -9 "${airootfs_dir}/usr/share/hwdata/pci.ids" > \
|
|
"${isofs_dir}/${install_dir}/boot/syslinux/hdt/pciids.gz"
|
|
gzip -c -9 "${airootfs_dir}/usr/lib/modules/${_uname_r}/modules.alias" > \
|
|
"${isofs_dir}/${install_dir}/boot/syslinux/hdt/modalias.gz"
|
|
|
|
# Add other aditional/extra files to ${install_dir}/boot/
|
|
if [[ -e "${airootfs_dir}/boot/memtest86+/memtest.bin" ]]; then
|
|
# rename for PXE: https://wiki.archlinux.org/index.php/Syslinux#Using_memtest
|
|
install -m 0644 -- "${airootfs_dir}/boot/memtest86+/memtest.bin" "${isofs_dir}/${install_dir}/boot/memtest"
|
|
mkdir -p "${isofs_dir}/${install_dir}/boot/licenses/memtest86+/"
|
|
install -m 0644 -- "${airootfs_dir}/usr/share/licenses/common/GPL2/license.txt" \
|
|
"${isofs_dir}/${install_dir}/boot/licenses/memtest86+/"
|
|
fi
|
|
}
|
|
|
|
# Prepare /isolinux
|
|
_make_boot_bios.syslinux.eltorito() {
|
|
mkdir -p "${isofs_dir}/isolinux"
|
|
sed "s|%ARCHISO_LABEL%|${iso_label}|g;
|
|
s|%INSTALL_DIR%|${install_dir}|g;
|
|
s|%ARCH%|${arch}|g" \
|
|
"${profile}/isolinux/isolinux.cfg" > "${isofs_dir}/isolinux/isolinux.cfg"
|
|
install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/isolinux.bin" "${isofs_dir}/isolinux/"
|
|
install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/isohdpfx.bin" "${isofs_dir}/isolinux/"
|
|
install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/ldlinux.c32" "${isofs_dir}/isolinux/"
|
|
|
|
# isolinux.cfg loads syslinux.cfg
|
|
_run_once _make_boot_bios.syslinux.mbr
|
|
}
|
|
|
|
# Prepare /EFI on ISO-9660
|
|
_make_efi() {
|
|
mkdir -p "${isofs_dir}/EFI/BOOT"
|
|
install -m 0644 -- "${airootfs_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \
|
|
"${isofs_dir}/EFI/BOOT/BOOTx64.EFI"
|
|
|
|
mkdir -p "${isofs_dir}/loader/entries"
|
|
install -m 0644 -- "${profile}/efiboot/loader/loader.conf" "${isofs_dir}/loader/"
|
|
|
|
sed "s|%ARCHISO_LABEL%|${iso_label}|g;
|
|
s|%INSTALL_DIR%|${install_dir}|g;
|
|
s|%ARCH%|${arch}|g" \
|
|
"${profile}/efiboot/loader/entries/archiso-x86_64-usb.conf" > \
|
|
"${isofs_dir}/loader/entries/archiso-x86_64.conf"
|
|
|
|
# edk2-shell based UEFI shell
|
|
# shellx64.efi is picked up automatically when on /
|
|
if [[ -e "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then
|
|
install -m 0644 -- "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${isofs_dir}/shellx64.efi"
|
|
fi
|
|
}
|
|
|
|
# Prepare kernel/initramfs on efiboot.img
|
|
_make_boot_on_fat() {
|
|
mkdir -p "${work_dir}/efiboot/EFI/archiso"
|
|
install -m 0644 -- "${airootfs_dir}/boot/vmlinuz-linux" "${work_dir}/efiboot/EFI/archiso/"
|
|
install -m 0644 -- "${isofs_dir}/${install_dir}/boot/${arch}/archiso.img" "${work_dir}/efiboot/EFI/archiso/"
|
|
if [[ -e "${airootfs_dir}/boot/intel-ucode.img" ]]; then
|
|
install -m 0644 -- "${airootfs_dir}/boot/intel-ucode.img" "${work_dir}/efiboot/EFI/archiso/"
|
|
fi
|
|
if [[ -e "${airootfs_dir}/boot/amd-ucode.img" ]]; then
|
|
install -m 0644 -- "${airootfs_dir}/boot/amd-ucode.img" "${work_dir}/efiboot/EFI/archiso/"
|
|
fi
|
|
}
|
|
|
|
# Prepare efiboot.img::/EFI for EFI boot mode
|
|
_make_boot_uefi-x64.systemd-boot.esp() {
|
|
mkdir -p "${isofs_dir}/EFI/archiso"
|
|
mkfs.fat -C -n ARCHISO_EFI "${isofs_dir}/EFI/archiso/efiboot.img" 65536
|
|
|
|
mkdir -p "${work_dir}/efiboot"
|
|
mount "${isofs_dir}/EFI/archiso/efiboot.img" "${work_dir}/efiboot"
|
|
|
|
mkdir -p "${work_dir}/efiboot/EFI/BOOT"
|
|
install -m 0644 -- "${airootfs_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \
|
|
"${work_dir}/efiboot/EFI/BOOT/BOOTx64.EFI"
|
|
|
|
mkdir -p "${work_dir}/efiboot/loader/entries"
|
|
install -m 0644 -- "${profile}/efiboot/loader/loader.conf" "${work_dir}/efiboot/loader/"
|
|
|
|
sed "s|%ARCHISO_LABEL%|${iso_label}|g;
|
|
s|%INSTALL_DIR%|${install_dir}|g;
|
|
s|%ARCH%|${arch}|g" \
|
|
"${profile}/efiboot/loader/entries/archiso-x86_64-cd.conf" > \
|
|
"${work_dir}/efiboot/loader/entries/archiso-x86_64.conf"
|
|
|
|
# shellx64.efi is picked up automatically when on /
|
|
if [[ -e "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then
|
|
install -m 0644 -- "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${work_dir}/efiboot/shellx64.efi"
|
|
fi
|
|
|
|
# Copy kernel and initramfs
|
|
_make_boot_on_fat
|
|
|
|
umount -d "${work_dir}/efiboot"
|
|
}
|
|
|
|
# Prepare efiboot.img::/EFI for "El Torito" EFI boot mode
|
|
_make_boot_uefi-x64.systemd-boot.eltorito() {
|
|
_run_once _make_boot_uefi-x64.systemd-boot.esp
|
|
# Set up /EFI on ISO-9660
|
|
_run_once _make_efi
|
|
}
|
|
|
|
# Build airootfs filesystem image
|
|
_make_prepare() {
|
|
if [[ "${sfs_mode}" == "sfs" ]]; then
|
|
_mkairootfs_sfs
|
|
else
|
|
_mkairootfs_img
|
|
fi
|
|
_mkchecksum
|
|
if [[ "${gpg_key}" ]]; then
|
|
_mksignature
|
|
fi
|
|
}
|
|
|
|
# Build ISO
|
|
_make_iso() {
|
|
local xorrisofs_options=()
|
|
|
|
if [[ "${quiet}" == "y" ]]; then
|
|
xorrisofs_options+=('-quiet')
|
|
fi
|
|
# shellcheck disable=SC2076
|
|
if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.' ]]; then
|
|
if [[ ! -f "${isofs_dir}/isolinux/isolinux.bin" ]]; then
|
|
_msg_error "The file '${isofs_dir}/isolinux/isolinux.bin' does not exist." 1
|
|
fi
|
|
if [[ ! -f "${isofs_dir}/isolinux/isohdpfx.bin" ]]; then
|
|
_msg_error "The file '${isofs_dir}/isolinux/isohdpfx.bin' does not exist." 1
|
|
fi
|
|
xorrisofs_options+=(
|
|
'-eltorito-boot' 'isolinux/isolinux.bin'
|
|
'-eltorito-catalog' 'isolinux/boot.cat'
|
|
'-no-emul-boot' '-boot-load-size' '4' '-boot-info-table'
|
|
'-isohybrid-mbr' "${isofs_dir}/isolinux/isohdpfx.bin"
|
|
)
|
|
fi
|
|
# shellcheck disable=SC2076
|
|
if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.' ]]; then
|
|
xorrisofs_options+=(
|
|
'-eltorito-alt-boot'
|
|
'-e' 'EFI/archiso/efiboot.img'
|
|
'-no-emul-boot'
|
|
'-isohybrid-gpt-basdat'
|
|
)
|
|
fi
|
|
|
|
_msg_info "Creating ISO image..."
|
|
xorriso -as mkisofs \
|
|
-iso-level 3 \
|
|
-full-iso9660-filenames \
|
|
-rational-rock \
|
|
-volid "${iso_label}" \
|
|
-appid "${iso_application}" \
|
|
-publisher "${iso_publisher}" \
|
|
-preparer "prepared by ${app_name}" \
|
|
"${xorrisofs_options[@]}" \
|
|
-output "${out_dir}/${img_name}" \
|
|
"${isofs_dir}/"
|
|
_msg_info "Done! | $(du -h -- "${out_dir}/${img_name}")"
|
|
}
|
|
|
|
# Read profile's values from profiledef.sh
|
|
_read_profile () {
|
|
if [[ -z "${profile}" ]]; then
|
|
_msg_error "No profile specified!" 1
|
|
fi
|
|
if [[ ! -d "${profile}" ]]; then
|
|
_msg_error "Profile '${profile}' does not exist!" 1
|
|
elif [[ ! -e "${profile}/profiledef.sh" ]]; then
|
|
_msg_error "Profile '${profile}' is missing 'profiledef.sh'!" 1
|
|
else
|
|
# Source profile's variables
|
|
# shellcheck source=configs/releng/profiledef.sh
|
|
. "${profile}/profiledef.sh"
|
|
cd -- "${profile}"
|
|
|
|
# Resolve paths
|
|
packages="$(realpath -- "${profile}/packages.${arch}")"
|
|
pacman_conf="$(realpath -- "${pacman_conf}")"
|
|
|
|
# Enumerate packages
|
|
[[ -e "${packages}" ]] || _msg_error "File '${packages}' does not exist!" 1
|
|
mapfile -t pkg_list < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}")
|
|
if (( ${#pkg_list[@]} == 0 )); then
|
|
_msg_error "'${packages}' does not list any packages!" 1
|
|
fi
|
|
|
|
cd -- "${OLDPWD}"
|
|
fi
|
|
}
|
|
|
|
_set_up_directories() {
|
|
local directory
|
|
for directory in "${work_dir}" "${out_dir}" "${work_dir}/${arch}" "${isofs_dir}" "${isofs_dir}/${install_dir}"; do
|
|
[[ -d "${directory}" ]] || mkdir -m 0755 -- "${directory}"
|
|
done
|
|
}
|
|
|
|
_print_settings() {
|
|
_msg_info "${app_name} configuration settings"
|
|
_msg_info " Command: ${command_name}"
|
|
_msg_info " Working directory: ${work_dir}"
|
|
_msg_info " Output directory: ${out_dir}"
|
|
_msg_info " GPG key: ${gpg_key:-None}"
|
|
_msg_info "Profile configuration settings"
|
|
_msg_info " Profile: ${profile}"
|
|
_msg_info " Architecture: ${arch}"
|
|
_msg_info " Image name: ${img_name}"
|
|
_msg_info " Disk label: ${iso_label}"
|
|
_msg_info " Disk publisher: ${iso_publisher}"
|
|
_msg_info " Disk application: ${iso_application}"
|
|
_msg_info " Installation directory: ${install_dir}"
|
|
_msg_info " Pacman config file: ${pacman_conf}"
|
|
_msg_info " Packages: ${pkg_list[*]}"
|
|
_msg_info " Boot modes: ${bootmodes[*]}"
|
|
}
|
|
|
|
_export_gpg_publickey() {
|
|
if [[ -n "${gpg_key}" ]]; then
|
|
gpg --batch --output "${work_dir}/pubkey.gpg" --export "${gpg_key}"
|
|
fi
|
|
}
|
|
|
|
|
|
_make_pkglist() {
|
|
_msg_info "Creating a list of installed packages on live-enviroment..."
|
|
pacman -Q --sysroot "${airootfs_dir}" > "${isofs_dir}/${install_dir}/pkglist.${arch}.txt"
|
|
_msg_info "Done!"
|
|
}
|
|
|
|
command_pkglist () {
|
|
_show_config pkglist
|
|
_make_pkglist
|
|
}
|
|
|
|
# Create an ISO9660 filesystem from "iso" directory.
|
|
command_iso () {
|
|
bootmodes=('bios.syslinux.mbr' 'bios.syslinux.eltorito')
|
|
|
|
# If exists, add an EFI "El Torito" boot image (FAT filesystem) to ISO-9660 image.
|
|
if [[ -f "${isofs_dir}/EFI/archiso/efiboot.img" ]]; then
|
|
bootmodes+=('uefi-x64.systemd-boot.esp' 'uefi-x64.systemd-boot.eltorito')
|
|
fi
|
|
|
|
_show_config iso
|
|
mkdir -p -- "${out_dir}"
|
|
_make_iso
|
|
}
|
|
|
|
# create airootfs.sfs filesystem, and push it in "iso" directory.
|
|
command_prepare () {
|
|
_show_config prepare
|
|
|
|
_cleanup
|
|
_make_prepare
|
|
}
|
|
|
|
# Install packages on airootfs.
|
|
# A basic check to avoid double execution/reinstallation is done via hashing package names.
|
|
command_install () {
|
|
if [[ ! -f "${pacman_conf}" ]]; then
|
|
_msg_error "Pacman config file '${pacman_conf}' does not exist" 1
|
|
fi
|
|
|
|
if (( ${#pkg_list[@]} == 0 )); then
|
|
_msg_error "Packages must be specified" 0
|
|
_usage 1
|
|
fi
|
|
|
|
_show_config install
|
|
|
|
_make_packages
|
|
}
|
|
|
|
command_init() {
|
|
_show_config init
|
|
_chroot_init
|
|
}
|
|
|
|
command_run() {
|
|
_show_config run
|
|
_chroot_run
|
|
}
|
|
|
|
command_build_profile() {
|
|
_msg_warning "The ${FUNCNAME[0]#command_} command is not fully implemented yet :("
|
|
# Set up essential directory paths
|
|
airootfs_dir="${work_dir}/${arch}/airootfs"
|
|
isofs_dir="${work_dir}/iso"
|
|
# Set ISO file name
|
|
img_name="${iso_name}-${iso_version}-${arch}.iso"
|
|
|
|
_print_settings
|
|
_run_once _set_up_directories
|
|
_run_once _make_pacman_conf
|
|
_run_once _export_gpg_publickey
|
|
_run_once _make_custom_airootfs
|
|
_run_once _make_packages
|
|
_run_once _make_customize_airootfs
|
|
_run_once _make_pkglist
|
|
_make_bootmodes
|
|
_run_once _cleanup
|
|
_run_once _make_prepare
|
|
_run_once _make_iso
|
|
}
|
|
|
|
while getopts 'B:p:r:C:L:P:A:D:w:o:s:c:g:vh' arg; do
|
|
case "${arg}" in
|
|
B)
|
|
profile="$(realpath -- "${OPTARG}")"
|
|
_read_profile
|
|
;;
|
|
p)
|
|
read -r -a opt_pkg_list <<< "${OPTARG}"
|
|
pkg_list+=("${opt_pkg_list[@]}")
|
|
;;
|
|
r) run_cmd="${OPTARG}" ;;
|
|
C) pacman_conf="$(realpath -- "${OPTARG}")" ;;
|
|
L) iso_label="${OPTARG}" ;;
|
|
P) iso_publisher="${OPTARG}" ;;
|
|
A) iso_application="${OPTARG}" ;;
|
|
D) install_dir="${OPTARG}" ;;
|
|
w) work_dir="$(realpath -- "${OPTARG}")" ;;
|
|
o) out_dir="$(realpath -- "${OPTARG}")" ;;
|
|
s) sfs_mode="${OPTARG}" ;;
|
|
c) sfs_comp="${OPTARG}" ;;
|
|
g) gpg_key="${OPTARG}" ;;
|
|
v) quiet="n" ;;
|
|
h|?) _usage 0 ;;
|
|
*)
|
|
_msg_error "Invalid argument '${arg}'" 0
|
|
_usage 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if (( EUID != 0 )); then
|
|
_msg_error "${app_name} must be run as root." 1
|
|
fi
|
|
|
|
shift $((OPTIND - 1))
|
|
|
|
if (( $# < 1 )); then
|
|
_msg_error "No command specified" 0
|
|
_usage 1
|
|
fi
|
|
command_name="${1}"
|
|
|
|
# Set directory path defaults
|
|
airootfs_dir="${work_dir}/airootfs"
|
|
isofs_dir="${work_dir}/iso"
|
|
|
|
case "${command_name}" in
|
|
init)
|
|
command_init
|
|
;;
|
|
install)
|
|
command_install
|
|
;;
|
|
run)
|
|
command_run
|
|
;;
|
|
prepare)
|
|
command_prepare
|
|
;;
|
|
pkglist)
|
|
command_pkglist
|
|
;;
|
|
iso)
|
|
if (( $# < 2 )); then
|
|
_msg_error "No image specified" 0
|
|
_usage 1
|
|
fi
|
|
img_name="${2}"
|
|
command_iso
|
|
;;
|
|
build_profile)
|
|
command_build_profile
|
|
;;
|
|
*)
|
|
_msg_error "Invalid command name '${command_name}'" 0
|
|
_usage 1
|
|
;;
|
|
esac
|
|
|
|
# vim:ts=4:sw=4:et:
|