summaryrefslogtreecommitdiff
path: root/parabolaiso/mkparabolaiso
diff options
context:
space:
mode:
Diffstat (limited to 'parabolaiso/mkparabolaiso')
-rwxr-xr-xparabolaiso/mkparabolaiso810
1 files changed, 631 insertions, 179 deletions
diff --git a/parabolaiso/mkparabolaiso b/parabolaiso/mkparabolaiso
index f7fae78..51251f5 100755
--- a/parabolaiso/mkparabolaiso
+++ b/parabolaiso/mkparabolaiso
@@ -4,29 +4,49 @@
set -e -u
-export LANG=C
+# Control the environment
+umask 0022
+export LANG="C"
+export SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-"$(date +%s)"}"
-app_name=${0##*/}
-arch=${arch:-$(uname -m)}
+# mkparabolaiso defaults
+app_name="${0##*/}"
pkg_list=()
run_cmd=""
quiet="y"
-pacman_conf="/etc/pacman.conf"
-iso_label="PARA_$(date +%Y%m)"
-iso_publisher="Parabola GNU/Linux-libre <https://www.parabola.nu>"
-iso_application="Parabola GNU/Linux-libre Live/Rescue CD"
-install_dir="parabola"
work_dir="work"
out_dir="out"
+img_name="${app_name}.iso"
sfs_mode="sfs"
sfs_comp="xz"
-gpg_key=
+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="${arch:-$(uname -m)}"
+pacman_conf="/etc/pacman.conf"
+bootmodes=()
+
# Show an INFO message
# $1: message string
_msg_info() {
local _msg="${1}"
- echo "[mkparabolaiso] INFO: ${_msg}"
+ [[ "${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
@@ -35,83 +55,73 @@ _msg_info() {
_msg_error() {
local _msg="${1}"
local _error=${2}
- echo
- echo "[mkparabolaiso] ERROR: ${_msg}"
- echo
- if [[ ${_error} -gt 0 ]]; then
- "exit ${_error}"
+ printf '\n[%s] ERROR: %s\n\n' "${app_name}" "${_msg}" >&2
+ if (( _error > 0 )); then
+ exit "${_error}"
fi
}
_chroot_init() {
- mkdir -p ${work_dir}/airootfs
+ mkdir -p -- "${airootfs_dir}"
_pacman base syslinux
}
_chroot_run() {
- eval arch-chroot ${work_dir}/airootfs "${run_cmd}"
+ 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 '${work_dir}/airootfs.img' on '${work_dir}/mnt/airootfs'"
- mount "${work_dir}/airootfs.img" "${work_dir}/mnt/airootfs"
+ 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"
+ umount -d -- "${work_dir}/mnt/airootfs"
_msg_info "Done!"
- rmdir "${work_dir}/mnt/airootfs"
+ rmdir -- "${work_dir}/mnt/airootfs"
trap - EXIT HUP INT TERM
}
# Show help usage, with an exit status.
# $1: exit status number.
-_usage ()
-{
- echo "usage ${app_name} [options] command <command options>"
- echo " general options:"
- echo " -p PACKAGE(S) Package(s) to install, can be used multiple times"
- echo " -r <command> Run <command> inside airootfs"
- echo " -C <file> Config file for pacman."
- echo " Default: '${pacman_conf}'"
- echo " -L <label> Set a label for the disk"
- echo " Default: '${iso_label}'"
- echo " -P <publisher> Set a publisher for the disk"
- echo " Default: '${iso_publisher}'"
- echo " -A <application> Set an application name for the disk"
- echo " Default: '${iso_application}'"
- echo " -D <install_dir> Set an install_dir. All files will by located here."
- echo " Default: '${install_dir}'"
- echo " NOTE: Max 8 characters, use only [a-z0-9]"
- echo " -w <work_dir> Set the working directory"
- echo " Default: '${work_dir}'"
- echo " -o <out_dir> Set the output directory"
- echo " Default: '${out_dir}'"
- echo " -s <sfs_mode> Set SquashFS image mode (img or sfs)"
- echo " img: prepare airootfs.sfs for dm-snapshot usage"
- echo " sfs: prepare airootfs.sfs for overlayfs usage"
- echo " Default: ${sfs_mode}"
- echo " -c <comp_type> Set SquashFS compression type (gzip, lzma, lzo, xz, zstd)"
- echo " Default: '${sfs_comp}'"
- echo " -v Enable verbose output"
- echo " -h This message"
- echo " commands:"
- echo " init"
- echo " Make base layout and install base group"
- echo " install"
- echo " Install all specified packages (-p)"
- echo " run"
- echo " run command specified by -r"
- echo " prepare"
- echo " build all images"
- echo " pkglist"
- echo " make a pkglist.txt of packages installed on airootfs"
- echo " iso <image name>"
- echo " build an iso image from the working dir"
+_usage () {
+ IFS='' read -r -d '' usagetext <<ENDUSAGETEXT || true
+usage ${app_name} [options] command <command options>
+ general options:
+ -B <profile_dir> Directory of the parabolaiso profile to build
+ -p PACKAGE(S) Package(s) to install, can be used multiple times
+ -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:
+ build_profile
+ build an iso image from a profile
+ENDUSAGETEXT
+ printf '%s\n' "${usagetext}"
exit "${1}"
}
@@ -119,7 +129,7 @@ _usage ()
# $1: init | install | run | prepare | iso
_show_config () {
local _mode="$1"
- echo
+ printf '\n'
_msg_info "Configuration settings"
_msg_info " Command: ${command_name}"
_msg_info " Architecture: ${arch}"
@@ -147,18 +157,17 @@ _show_config () {
_msg_info " Disk application: ${iso_application}"
;;
esac
- echo
+ printf '\n'
}
# Install desired packages to airootfs
-_pacman ()
-{
- _msg_info "Installing packages to '${work_dir}/airootfs/'..."
+_pacman () {
+ _msg_info "Installing packages to '${airootfs_dir}/'..."
if [[ "${quiet}" = "y" ]]; then
- pacstrap -C "${pacman_conf}" -c -G -M "${work_dir}/airootfs" "$@" &> /dev/null
+ pacstrap -C "${pacman_conf}" -c -G -M -- "${airootfs_dir}" --arch "${arch}" "$@" &> /dev/null
else
- pacstrap -C "${pacman_conf}" -c -G -M "${work_dir}/airootfs" "$@"
+ pacstrap -C "${pacman_conf}" -c -G -M -- "${airootfs_dir}" --arch "${arch}" "$@"
fi
_msg_info "Packages installed successfully!"
@@ -168,86 +177,84 @@ _pacman ()
_cleanup () {
_msg_info "Cleaning up what we can on airootfs..."
- # Delete initcpio image(s)
- if [[ -d "${work_dir}/airootfs/boot" ]]; then
- find "${work_dir}/airootfs/boot" -type f -name '*.img' -delete
- fi
- # Delete kernel(s)
- if [[ -d "${work_dir}/airootfs/boot" ]]; then
- find "${work_dir}/airootfs/boot" -type f -name 'vmlinuz*' -delete
+ # Delete all files in /boot
+ if [[ -d "${airootfs_dir}/boot" ]]; then
+ find "${airootfs_dir}/boot" -mindepth 1 -delete
fi
# Delete pacman database sync cache files (*.tar.gz)
- if [[ -d "${work_dir}/airootfs/var/lib/pacman" ]]; then
- find "${work_dir}/airootfs/var/lib/pacman" -maxdepth 1 -type f -delete
+ 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 "${work_dir}/airootfs/var/lib/pacman/sync" ]]; then
- find "${work_dir}/airootfs/var/lib/pacman/sync" -delete
+ if [[ -d "${airootfs_dir}/var/lib/pacman/sync" ]]; then
+ find "${airootfs_dir}/var/lib/pacman/sync" -delete
fi
# Delete pacman package cache
- if [[ -d "${work_dir}/airootfs/var/cache/pacman/pkg" ]]; then
- find "${work_dir}/airootfs/var/cache/pacman/pkg" -type f -delete
+ 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 "${work_dir}/airootfs/var/log" ]]; then
- find "${work_dir}/airootfs/var/log" -type f -delete
+ if [[ -d "${airootfs_dir}/var/log" ]]; then
+ find "${airootfs_dir}/var/log" -type f -delete
fi
# Delete all temporary files and dirs
- if [[ -d "${work_dir}/airootfs/var/tmp" ]]; then
- find "${work_dir}/airootfs/var/tmp" -mindepth 1 -delete
+ 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
+ find "${work_dir}" \( -name '*.pacnew' -o -name '*.pacsave' -o -name '*.pacorig' \) -delete
_msg_info "Done!"
+ # Create an empty /etc/machine-id
+ printf '' > "${airootfs_dir}/etc/machine-id"
}
# Makes a ext4 filesystem inside a SquashFS from a source directory.
_mkairootfs_img () {
- if [[ ! -e "${work_dir}/airootfs" ]]; then
- _msg_error "The path '${work_dir}/airootfs' does not exist" 1
+ 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 "${work_dir}/airootfs.img"
- local _qflag=""
+ truncate -s 32G -- "${airootfs_dir}.img"
if [[ "${quiet}" == "y" ]]; then
- _qflag="-q"
+ 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
- mkfs.ext4 ${_qflag} -O ^has_journal,^resize_inode -E lazy_itable_init=0 -m 0 -F "${work_dir}/airootfs.img"
- tune2fs -c 0 -i 0 "${work_dir}/airootfs.img" &> /dev/null
+ tune2fs -c 0 -i 0 -- "${airootfs_dir}.img" &> /dev/null
_msg_info "Done!"
_mount_airootfs
- _msg_info "Copying '${work_dir}/airootfs/' to '${work_dir}/mnt/airootfs/'..."
- cp -aT "${work_dir}/airootfs/" "${work_dir}/mnt/airootfs/"
- chown root:root "${work_dir}/mnt/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 "${work_dir}/iso/${install_dir}/${arch}"
+ mkdir -p -- "${isofs_dir}/${install_dir}/${arch}"
_msg_info "Creating SquashFS image, this may take some time..."
if [[ "${quiet}" = "y" ]]; then
- mksquashfs "${work_dir}/airootfs.img" "${work_dir}/iso/${install_dir}/${arch}/airootfs.sfs" -noappend \
+ mksquashfs "${airootfs_dir}.img" "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" -noappend \
-comp "${sfs_comp}" -no-progress &> /dev/null
else
- mksquashfs "${work_dir}/airootfs.img" "${work_dir}/iso/${install_dir}/${arch}/airootfs.sfs" -noappend \
+ mksquashfs "${airootfs_dir}.img" "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" -noappend \
-comp "${sfs_comp}"
fi
_msg_info "Done!"
- rm "${work_dir}/airootfs.img"
+ rm -- "${airootfs_dir}.img"
}
# Makes a SquashFS filesystem from a source directory.
_mkairootfs_sfs () {
- if [[ ! -e "${work_dir}/airootfs" ]]; then
- _msg_error "The path '${work_dir}/airootfs' does not exist" 1
+ if [[ ! -e "${airootfs_dir}" ]]; then
+ _msg_error "The path '${airootfs_dir}' does not exist" 1
fi
- mkdir -p "${work_dir}/iso/${install_dir}/${arch}"
+ mkdir -p -- "${isofs_dir}/${install_dir}/${arch}"
_msg_info "Creating SquashFS image, this may take some time..."
if [[ "${quiet}" = "y" ]]; then
- mksquashfs "${work_dir}/airootfs" "${work_dir}/iso/${install_dir}/${arch}/airootfs.sfs" -noappend \
+ mksquashfs "${airootfs_dir}" "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" -noappend \
-comp "${sfs_comp}" -no-progress &> /dev/null
else
- mksquashfs "${work_dir}/airootfs" "${work_dir}/iso/${install_dir}/${arch}/airootfs.sfs" -noappend \
+ mksquashfs "${airootfs_dir}" "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" -noappend \
-comp "${sfs_comp}"
fi
_msg_info "Done!"
@@ -255,44 +262,340 @@ _mkairootfs_sfs () {
_mkchecksum () {
_msg_info "Creating checksum file for self-test..."
- cd "${work_dir}/iso/${install_dir}/${arch}"
+ cd -- "${isofs_dir}/${install_dir}/${arch}"
sha512sum airootfs.sfs > airootfs.sha512
- cd "${OLDPWD}"
+ cd -- "${OLDPWD}"
_msg_info "Done!"
}
_mksignature () {
_msg_info "Creating signature file..."
- cd "${work_dir}/iso/${install_dir}/${arch}"
+ cd -- "${isofs_dir}/${install_dir}/${arch}"
gpg --detach-sign --default-key "${gpg_key}" airootfs.sfs
- cd "${OLDPWD}"
+ cd -- "${OLDPWD}"
_msg_info "Done!"
}
-command_pkglist () {
- _show_config pkglist
+# Helper function to run functions only one time.
+_run_once() {
+ if [[ ! -e "${work_dir}/build.${1}_${arch}" ]]; then
+ "$1"
+ touch "${work_dir}/build.${1}_${arch}"
+ fi
+}
- _msg_info "Creating a list of installed packages on live-enviroment..."
- pacman -Q --sysroot "${work_dir}/airootfs" > \
- "${work_dir}/iso/${install_dir}/pkglist.${arch}.txt"
- _msg_info "Done!"
+# 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 {parabolaiso_GNUPG_FD}<>"${work_dir}/pubkey.gpg"
+ export parabolaiso_GNUPG_FD
+ fi
+ local pkg_list_arch
+ eval "pkg_list_arch=(\${pkg_list_${arch}[@]})"
+ _pacman "${pkg_list[@]}" "${pkg_list_arch[@]}"
+ if [[ -n "${gpg_key}" ]]; then
+ exec {parabolaiso_GNUPG_FD}<&-
+ unset parabolaiso_GNUPG_FD
+ fi
}
-# Create an ISO9660 filesystem from "iso" directory.
-command_iso () {
- local _iso_efi_boot_args=()
+# 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 parabolaiso 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/parabolaiso.img" "${isofs_dir}/${install_dir}/boot/${arch}/"
+ install -m 0644 -- "${airootfs_dir}/boot/vmlinuz-linux-libre" "${isofs_dir}/${install_dir}/boot/${arch}/"
+}
- if [[ ! -f "${work_dir}/iso/isolinux/isolinux.bin" ]]; then
- _msg_error "The file '${work_dir}/iso/isolinux/isolinux.bin' does not exist." 1
+# 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|%PARABOLAISO_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/"
+
+ if [[ "${arch}" == "dual" ]]; then
+ for arch in i686 x86_64; do
+ local airootfs_dir="${work_dir}/${arch}/airootfs"
+ _make_boot_on_iso
+ done
+ else
+ _make_boot_on_iso
fi
- if [[ ! -f "${work_dir}/iso/isolinux/isohdpfx.bin" ]]; then
- _msg_error "The file '${work_dir}/iso/isolinux/isohdpfx.bin' does not exist." 1
+ _uname_r=$(file -b "${isofs_dir}/${install_dir}/boot/${arch}/vmlinuz-linux-libre" | 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.parabola.nu/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
+}
- # If exists, add an EFI "El Torito" boot image (FAT filesystem) to ISO-9660 image.
- if [[ -f "${work_dir}/iso/EFI/parabolaiso/efiboot.img" ]]; then
- _iso_efi_boot_args+=(
+# Prepare /isolinux
+_make_boot_bios.syslinux.eltorito() {
+ mkdir -p "${isofs_dir}/isolinux"
+ sed "s|%PARABOLAISO_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|%PARABOLAISO_LABEL%|${iso_label}|g;
+ s|%INSTALL_DIR%|${install_dir}|g;
+ s|%ARCH%|${arch}|g" \
+ "${profile}/efiboot/loader/entries/parabolaiso-x86_64-usb.conf" > \
+ "${isofs_dir}/loader/entries/parabolaiso-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
+}
+
+_make_refind_efi() {
+ mkdir -p "${isofs_dir}/EFI/boot/entries"
+ install -m 0644 -- "${airootfs_dir}/usr/share/refind/refind_x64.efi" \
+ "${isofs_dir}/EFI/boot/bootx64.efi"
+
+ install -m 0644 -- "${profile}/efiboot/EFI/boot/refind.conf" "${isofs_dir}/EFI/boot/"
+
+ sed "s|%PARABOLAISO_LABEL%|${iso_label}|g;
+ s|%INSTALL_DIR%|${install_dir}|g;
+ s|%ARCH%|${arch}|g" \
+ "${profile}/efiboot/EFI/boot/entries/parabolaiso-x86_64-usb.conf" > \
+ "${isofs_dir}/EFI/boot/entries/parabolaiso-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/parabolaiso"
+ install -m 0644 -- "${airootfs_dir}/boot/vmlinuz-linux-libre" "${work_dir}/efiboot/EFI/parabolaiso/"
+ install -m 0644 -- "${isofs_dir}/${install_dir}/boot/${arch}/parabolaiso.img" "${work_dir}/efiboot/EFI/parabolaiso/"
+}
+
+# Prepare efiboot.img::/EFI for EFI boot mode
+_make_boot_uefi-x64.systemd-boot.esp() {
+ mkdir -p "${isofs_dir}/EFI/parabolaiso"
+ mkfs.fat -C -n parabolaiso_EFI "${isofs_dir}/EFI/parabolaiso/efiboot.img" 65536
+
+ mkdir -p "${work_dir}/efiboot"
+ mount "${isofs_dir}/EFI/parabolaiso/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|%PARABOLAISO_LABEL%|${iso_label}|g;
+ s|%INSTALL_DIR%|${install_dir}|g;
+ s|%ARCH%|${arch}|g" \
+ "${profile}/efiboot/loader/entries/parabolaiso-x86_64-cd.conf" > \
+ "${work_dir}/efiboot/loader/entries/parabolaiso-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"
+}
+
+_make_boot_uefi-x64.refind.esp() {
+ mkdir -p "${isofs_dir}/EFI/parabolaiso"
+ mkfs.fat -C -n parabolaiso_EFI "${isofs_dir}/EFI/parabolaiso/efiboot.img" 65536
+
+ mkdir -p "${work_dir}/efiboot"
+ mount "${isofs_dir}/EFI/parabolaiso/efiboot.img" "${work_dir}/efiboot"
+
+ mkdir -p "${work_dir}/efiboot/EFI/boot/entries"
+ install -m 0644 -- "${airootfs_dir}/usr/share/refind/refind_x64.efi" \
+ "${work_dir}/efiboot/EFI/boot/bootx64.efi"
+
+ install -m 0644 -- "${profile}/efiboot/EFI/boot/refind.conf" "${work_dir}/efiboot/EFI/boot/"
+
+ sed "s|%PARABOLAISO_LABEL%|${iso_label}|g;
+ s|%INSTALL_DIR%|${install_dir}|g;
+ s|%ARCH%|${arch}|g" \
+ "${profile}/efiboot/EFI/boot/entries/parabolaiso-x86_64-cd.conf" > \
+ "${work_dir}/efiboot/EFI/boot/entries/parabolaiso-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
+}
+
+_make_boot_uefi-x64.refind.eltorito() {
+ _run_once _make_boot_uefi-x64.refind.esp
+ # Set up /EFI on ISO-9660
+ _run_once _make_refind_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.refind.| uefi-x64.systemd-boot.) ]]; then
+ xorrisofs_options+=(
'-eltorito-alt-boot'
'-e' 'EFI/parabolaiso/efiboot.img'
'-no-emul-boot'
@@ -300,45 +603,137 @@ command_iso () {
)
fi
- _show_config iso
-
- mkdir -p "${out_dir}"
_msg_info "Creating ISO image..."
- local _qflag=""
- if [[ "${quiet}" == "y" ]]; then
- xorriso -as mkisofs -quiet \
+ xorriso -as mkisofs \
-iso-level 3 \
-full-iso9660-filenames \
+ -joliet \
+ -joliet-long \
-rational-rock \
-volid "${iso_label}" \
-appid "${iso_application}" \
-publisher "${iso_publisher}" \
- -preparer "prepared by mkparabolaiso" \
- -eltorito-boot isolinux/isolinux.bin \
- -eltorito-catalog isolinux/boot.cat \
- -no-emul-boot -boot-load-size 4 -boot-info-table \
- -isohybrid-mbr "${work_dir}/iso/isolinux/isohdpfx.bin" \
- "${_iso_efi_boot_args[@]}" \
+ -preparer "prepared by ${app_name}" \
+ "${xorrisofs_options[@]}" \
-output "${out_dir}/${img_name}" \
- "${work_dir}/iso/"
+ "${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
- xorriso -as mkisofs \
- -iso-level 3 \
- -full-iso9660-filenames \
- -rational-rock \
- -volid "${iso_label}" \
- -appid "${iso_application}" \
- -publisher "${iso_publisher}" \
- -preparer "prepared by mkparabolaiso" \
- -eltorito-boot isolinux/isolinux.bin \
- -eltorito-catalog isolinux/boot.cat \
- -no-emul-boot -boot-load-size 4 -boot-info-table \
- -isohybrid-mbr "${work_dir}/iso/isolinux/isohdpfx.bin" \
- "${_iso_efi_boot_args[@]}" \
- -output "${out_dir}/${img_name}" \
- "${work_dir}/iso/"
+ # Source profile's variables
+ # shellcheck source=configs/releng/profiledef.sh
+ . "${profile}/profiledef.sh"
+ cd -- "${profile}"
+
+ if [[ "${arch}" == "dual" ]]; then
+ # Resolve paths
+ packages="$(realpath -- "${profile}"/packages.{both,i686,x86_64})"
+ pacman_conf="$(realpath -- "${pacman_conf}")"
+
+ # Enumerate packages
+ local pkgs
+ for pkgs in ${packages}; do
+ if [[ "${pkgs##*/}" = "packages.both" ]]; then
+ [[ -e "${pkgs}" ]] || _msg_error "File '${pkgs}' does not exist!" 1
+ mapfile -t pkg_list < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${pkgs}")
+ if (( ${#pkg_list[@]} == 0 )); then
+ _msg_error "'${pkgs}' does not list any packages!" 1
+ fi
+ elif [[ -e "${pkgs}" ]]; then
+ mapfile -t "pkg_list_${pkgs##*.}" < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${pkgs}")
+ fi
+ done
+ else
+ # 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
+ 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[*]}"
+ if [[ "${arch}" == "dual" ]]; then
+ _msg_info " Packages (x86_64): ${pkg_list_x86_64[*]:-None}"
+ _msg_info " Packages (i686): ${pkg_list_i686[*]:-None}"
+ fi
+ _msg_info " Boot modes: ${bootmodes[*]}"
+}
+
+_export_gpg_publickey() {
+ if [[ -n "${gpg_key}" ]]; then
+ gpg --batch --output "${work_dir}/pubkey.gpg" --export "${gpg_key}"
fi
- _msg_info "Done! | $(ls -sh "${out_dir}/${img_name}")"
+}
+
+
+_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/parabolaiso/efiboot.img" ]]; then
+ if [[ -e "${airootfs_dir}/usr/share/refind/refind_x64.efi" ]]; then
+ bootmodes+=('uefi-x64.refind.esp' 'uefi-x64.refind.eltorito')
+ else
+ bootmodes+=('uefi-x64.systemd-boot.esp' 'uefi-x64.systemd-boot.eltorito')
+ fi
+ fi
+
+ _show_config iso
+ mkdir -p -- "${out_dir}"
+ _make_iso
}
# create airootfs.sfs filesystem, and push it in "iso" directory.
@@ -346,15 +741,7 @@ command_prepare () {
_show_config prepare
_cleanup
- if [[ "${sfs_mode}" == "sfs" ]]; then
- _mkairootfs_sfs
- else
- _mkairootfs_img
- fi
- _mkchecksum
- if [[ "${gpg_key}" ]]; then
- _mksignature
- fi
+ _make_prepare
}
# Install packages on airootfs.
@@ -364,14 +751,14 @@ command_install () {
_msg_error "Pacman config file '${pacman_conf}' does not exist" 1
fi
- if [[ "${#pkg_list[@]}" -eq 0 ]]; then
+ if (( ${#pkg_list[@]} == 0 )); then
_msg_error "Packages must be specified" 0
_usage 1
fi
_show_config install
- _pacman "${pkg_list[@]}"
+ _make_packages
}
command_init() {
@@ -384,26 +771,74 @@ command_run() {
_chroot_run
}
-if [[ "${EUID}" -ne 0 ]]; then
- _msg_error "This script must be run as root." 1
-fi
-
-umask 0022
+command_build_profile() {
+ # 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"
+
+ if [[ "${arch}" == "dual" ]]; then
+ _print_settings
+ for arch in i686 x86_64; do
+ _run_once _set_up_directories
+ done
+ _run_once _make_pacman_conf
+ _run_once _export_gpg_publickey
+ for arch in i686 x86_64; do
+ airootfs_dir="${work_dir}/${arch}/airootfs"
+ _run_once _make_custom_airootfs
+ _run_once _make_packages
+ done
+ for arch in i686 x86_64; do
+ airootfs_dir="${work_dir}/${arch}/airootfs"
+ _run_once _make_customize_airootfs
+ done
+ for arch in i686 x86_64; do
+ _run_once _make_pkglist
+ done
+ arch="dual"
+ _make_bootmodes
+ _run_once _cleanup
+ for arch in i686 x86_64; do
+ airootfs_dir="${work_dir}/${arch}/airootfs"
+ _run_once _make_prepare
+ done
+ _run_once _make_iso
+ else
+ _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
+ fi
+}
-while getopts 'p:r:C:L:P:A:D:w:o:s:c:g:i:vh' arg; do
+while getopts 'B:p:r:C:L:P:A:D:w:o:s:c:g:dvh' 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="${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="${OPTARG}" ;;
- o) out_dir="${OPTARG}" ;;
+ w) work_dir="$(realpath -- "${OPTARG}")" ;;
+ o) out_dir="$(realpath -- "${OPTARG}")" ;;
s) sfs_mode="${OPTARG}" ;;
c) sfs_comp="${OPTARG}" ;;
g) gpg_key="${OPTARG}" ;;
@@ -416,38 +851,55 @@ while getopts 'p:r:C:L:P:A:D:w:o:s:c:g:i:vh' arg; do
esac
done
+if (( EUID != 0 )); then
+ _msg_error "${app_name} must be run as root." 1
+fi
+
shift $((OPTIND - 1))
-if [[ $# -lt 1 ]]; then
+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)
+ _msg_warning "The '${command_name}' command is deprecated!"
command_init
;;
install)
+ _msg_warning "The '${command_name}' command is deprecated!"
command_install
;;
run)
+ _msg_warning "The '${command_name}' command is deprecated!"
command_run
;;
prepare)
+ _msg_warning "The '${command_name}' command is deprecated!"
command_prepare
;;
pkglist)
+ _msg_warning "The '${command_name}' command is deprecated!"
command_pkglist
;;
iso)
- if [[ $# -lt 2 ]]; then
+ _msg_warning "The '${command_name}' command is deprecated!"
+ 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