From 47dd6e46e5bb03f6968d5f34e12434d0007c702e Mon Sep 17 00:00:00 2001 From: David P Date: Wed, 30 Jun 2021 16:22:12 -0400 Subject: sync with archiso v54 Imported changes: 28ab118 (tag: v54) Add changelog for v54 9827cb1 ci: Use environment variables to override build settings cde7296 ci: Consolidate build-host script 8e44a8b ci: consolidate build script d0d4fa5 configs/releng/airootfs/etc/systemd/system/pacman-init.service: don't hardcode the keyrings 3678bba configs/releng/packages.x86_64: explicitly add wanted packages instead of relying on pulling them in as dependencies bd2b861 configs/*: add VM guest packages and enable their services f86cb0f ci: Expand parallel matrix with build modes 5630a23 ci: build based on buildmodes 6b11d7b mkarchiso: Also create package list for netboot ace88aa Update project documentation related to netboot 4d1e898 docs: Add netboot to buildmodes documentation d54bf63 mkarchiso: Add buildmode to export netboot artifacts 1fed84c Extend project overview with info on bootstrap images 1630d76 docs: Add documentation for buildmodes and bootstrap build mode 2cac539 mkarchiso: Implement buildmodes that allow building bootstrap images 535bc3c baseline: Add bootstrap packages file b7fd696 baseline: Add buildmodes to profiledef.sh 6afa695 releng: Add buildmodes to profiledef.sh 9c84b7c releng: Add packages file for bootstrap image 2a07aa2 Increase timeout for initial prompt in build VM 4b14350 Use QEMU 6.x options c58b44f Use QEMU 6.x options Parabola changes: * add dual architecture support for bootstrap buildmode by creating two separated tarballs * sync openrc profiles with releng updates * disable haveged initscript since we removed it from the packages list * add speakup_soft kernel module in openrc profiles to make espeakup work TODO: * add braille support for GUI lxde-openrc (w/orca?) NOTE: We don't have/use CI (Arch uses GitLab), so we don't import those changes Signed-off-by: David P --- parabolaiso/mkparabolaiso | 594 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 449 insertions(+), 145 deletions(-) (limited to 'parabolaiso') diff --git a/parabolaiso/mkparabolaiso b/parabolaiso/mkparabolaiso index 6f04947..db49ba7 100755 --- a/parabolaiso/mkparabolaiso +++ b/parabolaiso/mkparabolaiso @@ -14,10 +14,10 @@ app_name="${0##*/}" # Define global variables. All of them will be overwritten later pkg_list=() +bootstrap_pkg_list=() quiet="" work_dir="" out_dir="" -img_name="" gpg_key="" iso_name="" iso_label="" @@ -29,9 +29,15 @@ arch="" pacman_conf="" packages="" packages_dual="" +bootstrap_packages="" +bootstrap_packages_dual="" +pacstrap_dir="" +buildmodes=() bootmodes=() airootfs_image_type="" airootfs_image_tool_options=() +cert_list=() +sign_netboot_artifacts="" declare -A file_permissions=() @@ -64,8 +70,8 @@ _msg_error() { _mount_airootfs() { trap "_umount_airootfs" EXIT HUP INT TERM install -d -m 0755 -- "${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 "Mounting '${pacstrap_dir}.img' on '${work_dir}/mnt/airootfs'..." + mount -- "${pacstrap_dir}.img" "${work_dir}/mnt/airootfs" _msg_info "Done!" } @@ -94,11 +100,17 @@ usage: ${app_name} [options] Default: '${iso_label}' -P Set the ISO publisher Default: '${iso_publisher}' + -c [cert ..] Provide certificates for codesigning of netboot artifacts + Multiple files are provided as quoted, space delimited list. + The first file is considered as the signing certificate, + the second as the key. -g Set the PGP key ID to be used for signing the rootfs image -h This message + -m [mode ..] Build mode(s) to use (valid modes are: 'bootstrap', 'iso' and 'netboot'). + Multiple build modes are provided as quoted, space delimited list. -o Set the output directory Default: '${out_dir}' - -p PACKAGE(S) Package(s) to install. + -p [package ..] Package(s) to install. Multiple packages are provided as quoted, space delimited list. -v Enable verbose output -w Set the working directory @@ -120,42 +132,46 @@ _show_config() { _msg_info " Installation directory: ${install_dir}" _msg_info " Build date: ${build_date}" _msg_info " Output directory: ${out_dir}" + _msg_info " Current build mode: ${buildmode}" + _msg_info " Build modes: ${buildmodes[*]}" _msg_info " GPG key: ${gpg_key:-None}" + _msg_info "Code signing certificates: ${cert_list[*]}" _msg_info " Profile: ${profile}" _msg_info "Pacman configuration file: ${pacman_conf}" - _msg_info " Image file name: ${img_name}" + _msg_info " Image file name: ${image_name:-None}" _msg_info " ISO volume label: ${iso_label}" _msg_info " ISO publisher: ${iso_publisher}" _msg_info " ISO application: ${iso_application}" _msg_info " Boot modes: ${bootmodes[*]}" - _msg_info " Packages: ${pkg_list[*]}" + _msg_info " Packages File: ${buildmode_packages}" + _msg_info " Packages: ${buildmode_pkg_list[*]}" if [[ "${arch}" == "dual" ]]; then - _msg_info " Packages (x86_64): ${pkg_list_x86_64[*]:-None}" - _msg_info " Packages (i686): ${pkg_list_i686[*]:-None}" + _msg_info " Packages (i686): ${buildmode_pkg_list_i686[*]:-None}" + _msg_info " Packages (x86_64): ${buildmode_pkg_list_x86_64[*]:-None}" fi } # Cleanup airootfs -_cleanup_airootfs() { - _msg_info "Cleaning up what we can on ${arch} airootfs..." +_cleanup_pacstrap_dir() { + _msg_info "Cleaning up in ${arch} pacstrap location..." # Delete all files in /boot - [[ -d "${airootfs_dir}/boot" ]] && find "${airootfs_dir}/boot" -mindepth 1 -delete + [[ -d "${pacstrap_dir}/boot" ]] && find "${pacstrap_dir}/boot" -mindepth 1 -delete # Delete pacman database sync cache files (*.tar.gz) - [[ -d "${airootfs_dir}/var/lib/pacman" ]] && find "${airootfs_dir}/var/lib/pacman" -maxdepth 1 -type f -delete + [[ -d "${pacstrap_dir}/var/lib/pacman" ]] && find "${pacstrap_dir}/var/lib/pacman" -maxdepth 1 -type f -delete # Delete pacman database sync cache - [[ -d "${airootfs_dir}/var/lib/pacman/sync" ]] && find "${airootfs_dir}/var/lib/pacman/sync" -delete + [[ -d "${pacstrap_dir}/var/lib/pacman/sync" ]] && find "${pacstrap_dir}/var/lib/pacman/sync" -delete # Delete pacman package cache - [[ -d "${airootfs_dir}/var/cache/pacman/pkg" ]] && find "${airootfs_dir}/var/cache/pacman/pkg" -type f -delete + [[ -d "${pacstrap_dir}/var/cache/pacman/pkg" ]] && find "${pacstrap_dir}/var/cache/pacman/pkg" -type f -delete # Delete all log files, keeps empty dirs. - [[ -d "${airootfs_dir}/var/log" ]] && find "${airootfs_dir}/var/log" -type f -delete + [[ -d "${pacstrap_dir}/var/log" ]] && find "${pacstrap_dir}/var/log" -type f -delete # Delete all temporary files and dirs - [[ -d "${airootfs_dir}/var/tmp" ]] && find "${airootfs_dir}/var/tmp" -mindepth 1 -delete + [[ -d "${pacstrap_dir}/var/tmp" ]] && find "${pacstrap_dir}/var/tmp" -mindepth 1 -delete # Delete package pacman related files. find "${work_dir}" \( -name '*.pacnew' -o -name '*.pacsave' -o -name '*.pacorig' \) -delete # Create an empty /etc/machine-id - rm -f -- "${airootfs_dir}/etc/machine-id" - printf '' > "${airootfs_dir}/etc/machine-id" + rm -f -- "${pacstrap_dir}/etc/machine-id" + printf '' > "${pacstrap_dir}/etc/machine-id" _msg_info "Done!" } @@ -171,36 +187,36 @@ _run_mksquashfs() { # Makes a ext4 filesystem inside a SquashFS from a source directory. _mkairootfs_ext4+squashfs() { - [[ -e "${airootfs_dir}" ]] || _msg_error "The path '${airootfs_dir}' does not exist" 1 + [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1 _msg_info "Creating ext4 image of 32 GiB..." if [[ "${quiet}" == "y" ]]; then - mkfs.ext4 -q -O '^has_journal,^resize_inode' -E 'lazy_itable_init=0' -m 0 -F -- "${airootfs_dir}.img" 32G + mkfs.ext4 -q -O '^has_journal,^resize_inode' -E 'lazy_itable_init=0' -m 0 -F -- "${pacstrap_dir}.img" 32G else - mkfs.ext4 -O '^has_journal,^resize_inode' -E 'lazy_itable_init=0' -m 0 -F -- "${airootfs_dir}.img" 32G + mkfs.ext4 -O '^has_journal,^resize_inode' -E 'lazy_itable_init=0' -m 0 -F -- "${pacstrap_dir}.img" 32G fi - tune2fs -c 0 -i 0 -- "${airootfs_dir}.img" > /dev/null + tune2fs -c 0 -i 0 -- "${pacstrap_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/" + _msg_info "Copying '${pacstrap_dir}/' to '${work_dir}/mnt/airootfs/'..." + cp -aT -- "${pacstrap_dir}/" "${work_dir}/mnt/airootfs/" chown -- 0:0 "${work_dir}/mnt/airootfs/" _msg_info "Done!" _umount_airootfs install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}" _msg_info "Creating SquashFS image, this may take some time..." - _run_mksquashfs "${airootfs_dir}.img" + _run_mksquashfs "${pacstrap_dir}.img" _msg_info "Done!" - rm -- "${airootfs_dir}.img" + rm -- "${pacstrap_dir}.img" } # Makes a SquashFS filesystem from a source directory. _mkairootfs_squashfs() { - [[ -e "${airootfs_dir}" ]] || _msg_error "The path '${airootfs_dir}' does not exist" 1 + [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1 install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}" _msg_info "Creating ${arch} SquashFS image, this may take some time..." - _run_mksquashfs "${airootfs_dir}" + _run_mksquashfs "${pacstrap_dir}" _msg_info "Done!" } @@ -219,14 +235,14 @@ _mkchecksum() { # Makes an EROFS file system from a source directory. _mkairootfs_erofs() { local fsuuid - [[ -e "${airootfs_dir}" ]] || _msg_error "The path '${airootfs_dir}' does not exist" 1 + [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1 install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}" local image_path="${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" # Generate reproducible file system UUID from SOURCE_DATE_EPOCH fsuuid="$(uuidgen --sha1 --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 --name "${SOURCE_DATE_EPOCH}")" _msg_info "Creating EROFS image, this may take some time..." - mkfs.erofs -U "${fsuuid}" "${airootfs_image_tool_options[@]}" -- "${image_path}" "${airootfs_dir}" + mkfs.erofs -U "${fsuuid}" "${airootfs_image_tool_options[@]}" -- "${image_path}" "${pacstrap_dir}" _msg_info "Done!" } @@ -245,9 +261,9 @@ _mksignature() { # Helper function to run functions only one time. _run_once() { - if [[ ! -e "${work_dir}/build.${1}_${arch}" ]]; then + if [[ ! -e "${work_dir}/${run_once_mode}.${1}" ]]; then "$1" - touch "${work_dir}/build.${1}_${arch}" + touch "${work_dir}/${run_once_mode}.${1}_${arch}" fi } @@ -257,7 +273,11 @@ _run_dual() { if [[ "${arch}" == "dual" ]]; then local arch for arch in i686 x86_64; do - airootfs_dir="${work_dir}/${arch}/airootfs" + if [[ "${buildmode}" == "bootstrap" ]]; then + pacstrap_dir="${work_dir}/${arch}/bootstrap/root.${arch}" + else + pacstrap_dir="${work_dir}/${arch}/airootfs" + fi for cmd in "$@"; do ${cmd} done @@ -283,7 +303,7 @@ _make_pacman_conf() { _cache_dirs="${_system_cache_dirs}" fi - _msg_info "Copying custom pacman.conf to work directory..." + _msg_info "Copying custom pacman.conf to ${arch} work directory..." _msg_info "Using pacman CacheDir: ${_cache_dirs}" # take the profile pacman.conf and strip all settings that would break in chroot when using pacman -r # append CacheDir and HookDir to [options] section @@ -291,7 +311,7 @@ _make_pacman_conf() { # see `man 8 pacman` for further info setarch "${arch}" pacman-conf --config "${pacman_conf}" | \ sed "/CacheDir/d;/DBPath/d;/HookDir/d;/LogFile/d;/RootDir/d;/\[options\]/a CacheDir = ${_cache_dirs} - /\[options\]/a HookDir = ${airootfs_dir}/etc/pacman.d/hooks/" > "${work_dir}/pacman_${arch}.conf" + /\[options\]/a HookDir = ${pacstrap_dir}/etc/pacman.d/hooks/" > "${work_dir}/${buildmode}.pacman.conf.${arch}" } # Prepare working directory and copy custom airootfs files (airootfs) @@ -299,27 +319,27 @@ _make_custom_airootfs() { local passwd=() local filename permissions - install -d -m 0755 -o 0 -g 0 -- "${airootfs_dir}" + install -d -m 0755 -o 0 -g 0 -- "${pacstrap_dir}" if [[ -d "${profile}/airootfs" ]]; then _msg_info "Copying custom airootfs files..." - cp -af --no-preserve=ownership,mode -- "${profile}/airootfs/." "${airootfs_dir}" + cp -af --no-preserve=ownership,mode -- "${profile}/airootfs/." "${pacstrap_dir}" # Set ownership and mode for files and directories for filename in "${!file_permissions[@]}"; do IFS=':' read -ra permissions <<< "${file_permissions["${filename}"]}" - # Prevent file path traversal outside of $airootfs_dir - if [[ "$(realpath -q -- "${airootfs_dir}${filename}")" != "${airootfs_dir}"* ]]; then - _msg_error "Failed to set permissions on '${airootfs_dir}${filename}'. Outside of valid path." 1 + # Prevent file path traversal outside of $pacstrap_dir + if [[ "$(realpath -q -- "${pacstrap_dir}${filename}")" != "${pacstrap_dir}"* ]]; then + _msg_error "Failed to set permissions on '${pacstrap_dir}${filename}'. Outside of valid path." 1 # Warn if the file does not exist - elif [[ ! -e "${airootfs_dir}${filename}" ]]; then - _msg_warning "Cannot change permissions of '${airootfs_dir}${filename}'. The file or directory does not exist." + elif [[ ! -e "${pacstrap_dir}${filename}" ]]; then + _msg_warning "Cannot change permissions of '${pacstrap_dir}${filename}'. The file or directory does not exist." else if [[ "${filename: -1}" == "/" ]]; then - chown -fhR -- "${permissions[0]}:${permissions[1]}" "${airootfs_dir}${filename}" - chmod -fR -- "${permissions[2]}" "${airootfs_dir}${filename}" + chown -fhR -- "${permissions[0]}:${permissions[1]}" "${pacstrap_dir}${filename}" + chmod -fR -- "${permissions[2]}" "${pacstrap_dir}${filename}" else - chown -fh -- "${permissions[0]}:${permissions[1]}" "${airootfs_dir}${filename}" - chmod -f -- "${permissions[2]}" "${airootfs_dir}${filename}" + chown -fh -- "${permissions[0]}:${permissions[1]}" "${pacstrap_dir}${filename}" + chmod -f -- "${permissions[2]}" "${pacstrap_dir}${filename}" fi fi done @@ -329,10 +349,10 @@ _make_custom_airootfs() { # Install desired packages to airootfs _make_packages() { - _msg_info "Installing packages to '${airootfs_dir}/'..." + _msg_info "Installing packages to '${pacstrap_dir}/'..." - local pkg_list_arch - eval "pkg_list_arch=(\${pkg_list_${arch}[@]})" + local buildmode_pkg_list_arch + eval "buildmode_pkg_list_arch=(\${buildmode_pkg_list_${arch}[@]})" if [[ -n "${gpg_key}" ]]; then exec {PARABOLAISO_GNUPG_FD}<>"${work_dir}/pubkey.gpg" @@ -340,9 +360,9 @@ _make_packages() { fi if [[ "${quiet}" = "y" ]]; then - pacstrap -C "${work_dir}/pacman_${arch}.conf" -c -G -M -- "${airootfs_dir}" "${pkg_list[@]}" "${pkg_list_arch[@]}" &> /dev/null + pacstrap -C "${work_dir}/${buildmode}.pacman.conf.${arch}" -c -G -M -- "${pacstrap_dir}" "${buildmode_pkg_list[@]}" "${buildmode_pkg_list_arch[@]}" &> /dev/null else - pacstrap -C "${work_dir}/pacman_${arch}.conf" -c -G -M -- "${airootfs_dir}" "${pkg_list[@]}" "${pkg_list_arch[@]}" + pacstrap -C "${work_dir}/${buildmode}.pacman.conf.${arch}" -c -G -M -- "${pacstrap_dir}" "${buildmode_pkg_list[@]}" "${buildmode_pkg_list_arch[@]}" fi if [[ -n "${gpg_key}" ]]; then @@ -365,27 +385,27 @@ _make_customize_airootfs() { # Skip invalid home directories [[ "${passwd[5]}" == '/' ]] && continue [[ -z "${passwd[5]}" ]] && continue - # Prevent path traversal outside of $airootfs_dir - if [[ "$(realpath -q -- "${airootfs_dir}${passwd[5]}")" == "${airootfs_dir}"* ]]; then - if [[ ! -d "${airootfs_dir}${passwd[5]}" ]]; then - install -d -m 0750 -o "${passwd[2]}" -g "${passwd[3]}" -- "${airootfs_dir}${passwd[5]}" + # Prevent path traversal outside of $pacstrap_dir + if [[ "$(realpath -q -- "${pacstrap_dir}${passwd[5]}")" == "${pacstrap_dir}"* ]]; then + if [[ ! -d "${pacstrap_dir}${passwd[5]}" ]]; then + install -d -m 0750 -o "${passwd[2]}" -g "${passwd[3]}" -- "${pacstrap_dir}${passwd[5]}" fi - cp -dnRT --preserve=mode,timestamps,links -- "${airootfs_dir}/etc/skel/." "${airootfs_dir}${passwd[5]}" - chmod -f 0750 -- "${airootfs_dir}${passwd[5]}" - chown -hR -- "${passwd[2]}:${passwd[3]}" "${airootfs_dir}${passwd[5]}" + cp -dnRT --preserve=mode,timestamps,links -- "${pacstrap_dir}/etc/skel/." "${pacstrap_dir}${passwd[5]}" + chmod -f 0750 -- "${pacstrap_dir}${passwd[5]}" + chown -hR -- "${passwd[2]}:${passwd[3]}" "${pacstrap_dir}${passwd[5]}" else - _msg_error "Failed to set permissions on '${airootfs_dir}${passwd[5]}'. Outside of valid path." 1 + _msg_error "Failed to set permissions on '${pacstrap_dir}${passwd[5]}'. Outside of valid path." 1 fi done < "${profile}/airootfs/etc/passwd" _msg_info "Done!" fi - if [[ -e "${airootfs_dir}/root/customize_airootfs.sh" ]]; then - _msg_info "Running customize_airootfs.sh in '${airootfs_dir}' chroot..." + if [[ -e "${pacstrap_dir}/root/customize_airootfs.sh" ]]; then + _msg_info "Running customize_airootfs.sh in '${pacstrap_dir}' chroot..." _msg_warning "customize_airootfs.sh is deprecated! Support for it will be removed in a future parabolaiso version." - chmod -f -- +x "${airootfs_dir}/root/customize_airootfs.sh" - eval -- arch-chroot "${airootfs_dir}" "/root/customize_airootfs.sh" - rm -- "${airootfs_dir}/root/customize_airootfs.sh" + chmod -f -- +x "${pacstrap_dir}/root/customize_airootfs.sh" + eval -- arch-chroot "${pacstrap_dir}" "/root/customize_airootfs.sh" + rm -- "${pacstrap_dir}/root/customize_airootfs.sh" _msg_info "Done! customize_airootfs.sh run successfully." fi } @@ -402,8 +422,8 @@ _make_bootmodes() { _make_boot_on_iso9660() { _msg_info "Preparing ${arch} kernel and initramfs for the ISO 9660 file system..." install -d -m 0755 -- "${isofs_dir}/${install_dir}/boot/${arch}" - install -m 0644 -- "${airootfs_dir}/boot/initramfs-"*".img" "${isofs_dir}/${install_dir}/boot/${arch}/" - install -m 0644 -- "${airootfs_dir}/boot/vmlinuz-"* "${isofs_dir}/${install_dir}/boot/${arch}/" + install -m 0644 -- "${pacstrap_dir}/boot/initramfs-"*".img" "${isofs_dir}/${install_dir}/boot/${arch}/" + install -m 0644 -- "${pacstrap_dir}/boot/vmlinuz-"* "${isofs_dir}/${install_dir}/boot/${arch}/" _msg_info "Done!" } @@ -420,28 +440,28 @@ _make_bootmode_bios.syslinux.mbr() { if [[ -e "${profile}/syslinux/splash.png" ]]; then install -m 0644 -- "${profile}/syslinux/splash.png" "${isofs_dir}/syslinux/" fi - install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/"*.c32 "${isofs_dir}/syslinux/" - install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/lpxelinux.0" "${isofs_dir}/syslinux/" - install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/memdisk" "${isofs_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/"*.c32 "${isofs_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/lpxelinux.0" "${isofs_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/memdisk" "${isofs_dir}/syslinux/" _run_dual '_run_once _make_boot_on_iso9660' if [[ -e "${isofs_dir}/syslinux/hdt.c32" ]]; then install -d -m 0755 -- "${isofs_dir}/syslinux/hdt" - if [[ -e "${airootfs_dir}/usr/share/hwdata/pci.ids" ]]; then - gzip -cn9 "${airootfs_dir}/usr/share/hwdata/pci.ids" > \ + if [[ -e "${pacstrap_dir}/usr/share/hwdata/pci.ids" ]]; then + gzip -cn9 "${pacstrap_dir}/usr/share/hwdata/pci.ids" > \ "${isofs_dir}/syslinux/hdt/pciids.gz" fi - find "${airootfs_dir}/usr/lib/modules" -name 'modules.alias' -print -exec gzip -cn9 '{}' ';' -quit > \ + find "${pacstrap_dir}/usr/lib/modules" -name 'modules.alias' -print -exec gzip -cn9 '{}' ';' -quit > \ "${isofs_dir}/syslinux/hdt/modalias.gz" fi # Add other aditional/extra files to ${install_dir}/boot/ - if [[ -e "${airootfs_dir}/boot/memtest86+/memtest.bin" ]]; then + if [[ -e "${pacstrap_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" + install -m 0644 -- "${pacstrap_dir}/boot/memtest86+/memtest.bin" "${isofs_dir}/${install_dir}/boot/memtest" install -d -m 0755 -- "${isofs_dir}/${install_dir}/boot/licenses/memtest86+/" - install -m 0644 -- "${airootfs_dir}/usr/share/licenses/common/GPL2/license.txt" \ + install -m 0644 -- "${pacstrap_dir}/usr/share/licenses/common/GPL2/license.txt" \ "${isofs_dir}/${install_dir}/boot/licenses/memtest86+/" fi _msg_info "Done! SYSLINUX set up for BIOS booting from a disk successfully." @@ -451,8 +471,8 @@ _make_bootmode_bios.syslinux.mbr() { _make_bootmode_bios.syslinux.eltorito() { _msg_info "Setting up SYSLINUX for BIOS booting from an optical disc..." install -d -m 0755 -- "${isofs_dir}/syslinux" - install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/isolinux.bin" "${isofs_dir}/syslinux/" - install -m 0644 -- "${airootfs_dir}/usr/lib/syslinux/bios/isohdpfx.bin" "${isofs_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/isolinux.bin" "${isofs_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/isohdpfx.bin" "${isofs_dir}/syslinux/" # ISOLINUX and SYSLINUX installation is shared _run_once _make_bootmode_bios.syslinux.mbr @@ -464,7 +484,7 @@ _make_bootmode_bios.syslinux.eltorito() { _make_efi_dir_on_iso9660() { _msg_info "Preparing an /EFI directory for the ISO 9660 file system..." install -d -m 0755 -- "${isofs_dir}/EFI/BOOT" - install -m 0644 -- "${airootfs_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \ + install -m 0644 -- "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \ "${isofs_dir}/EFI/BOOT/BOOTx64.EFI" install -d -m 0755 -- "${isofs_dir}/loader/entries" @@ -479,15 +499,15 @@ _make_efi_dir_on_iso9660() { # 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" + if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then + install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${isofs_dir}/shellx64.efi" fi } _make_refind_efi_dir_on_iso9660() { _msg_info "Preparing an /EFI directory for the ISO 9660 file system..." install -d -m 0755 -- "${isofs_dir}/EFI/BOOT/entries" - install -m 0644 -- "${airootfs_dir}/usr/share/refind/refind_x64.efi" \ + install -m 0644 -- "${pacstrap_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/" @@ -501,8 +521,8 @@ _make_refind_efi_dir_on_iso9660() { # 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" + if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then + install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${isofs_dir}/shellx64.efi" fi } @@ -511,8 +531,8 @@ _make_boot_on_fat() { _msg_info "Preparing kernel and initramfs for the FAT file system..." mmd -i "${work_dir}/efiboot.img" \ "::/${install_dir}" "::/${install_dir}/boot" "::/${install_dir}/boot/x86_64" - mcopy -i "${work_dir}/efiboot.img" "${airootfs_dir}/boot/vmlinuz-"* \ - "${airootfs_dir}/boot/initramfs-"*".img" "::/${install_dir}/boot/x86_64/" + mcopy -i "${work_dir}/efiboot.img" "${pacstrap_dir}/boot/vmlinuz-"* \ + "${pacstrap_dir}/boot/initramfs-"*".img" "::/${install_dir}/boot/x86_64/" _msg_info "Done!" } @@ -523,11 +543,11 @@ _make_bootmode_uefi-x64.systemd-boot.esp() { # the required image size in KiB (rounded up to the next full MiB with an additional MiB for reserved sectors) efiboot_imgsize="$(du -bc \ - "${airootfs_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \ - "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" \ + "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \ + "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" \ "${profile}/efiboot/" \ - "${airootfs_dir}/boot/vmlinuz-"* \ - "${airootfs_dir}/boot/initramfs-"*".img" \ + "${pacstrap_dir}/boot/vmlinuz-"* \ + "${pacstrap_dir}/boot/initramfs-"*".img" \ 2>/dev/null | awk 'function ceil(x){return int(x)+(x>int(x))} function byte_to_kib(x){return x/1024} function mib_to_kib(x){return x*1024} @@ -541,7 +561,7 @@ _make_bootmode_uefi-x64.systemd-boot.esp() { mmd -i "${work_dir}/efiboot.img" ::/EFI ::/EFI/BOOT mcopy -i "${work_dir}/efiboot.img" \ - "${airootfs_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" ::/EFI/BOOT/BOOTx64.EFI + "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" ::/EFI/BOOT/BOOTx64.EFI mmd -i "${work_dir}/efiboot.img" ::/loader ::/loader/entries mcopy -i "${work_dir}/efiboot.img" "${profile}/efiboot/loader/loader.conf" ::/loader/ @@ -553,9 +573,9 @@ _make_bootmode_uefi-x64.systemd-boot.esp() { done # shellx64.efi is picked up automatically when on / - if [[ -e "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then + if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then mcopy -i "${work_dir}/efiboot.img" \ - "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ::/shellx64.efi + "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ::/shellx64.efi fi # Copy kernel and initramfs @@ -570,11 +590,11 @@ _make_bootmode_uefi-x64.refind.esp() { # the required image size in KiB (rounded up to the next full MiB with an additional MiB for reserved sectors) efiboot_imgsize="$(du -bc \ - "${airootfs_dir}/usr/share/refind/refind_x64.efi" \ - "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" \ + "${pacstrap_dir}/usr/share/refind/refind_x64.efi" \ + "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" \ "${profile}/efiboot/" \ - "${airootfs_dir}/boot/vmlinuz-"* \ - "${airootfs_dir}/boot/initramfs-"*".img" \ + "${pacstrap_dir}/boot/vmlinuz-"* \ + "${pacstrap_dir}/boot/initramfs-"*".img" \ 2>/dev/null | awk 'function ceil(x){return int(x)+(x>int(x))} function byte_to_kib(x){return x/1024} function mib_to_kib(x){return x*1024} @@ -588,7 +608,7 @@ _make_bootmode_uefi-x64.refind.esp() { mmd -i "${work_dir}/efiboot.img" ::/EFI ::/EFI/BOOT mcopy -i "${work_dir}/efiboot.img" \ - "${airootfs_dir}/usr/share/refind/refind_x64.efi" ::/EFI/BOOT/BOOTx64.EFI + "${pacstrap_dir}/usr/share/refind/refind_x64.efi" ::/EFI/BOOT/BOOTx64.EFI mmd -i "${work_dir}/efiboot.img" ::/EFI/BOOT/entries mcopy -i "${work_dir}/efiboot.img" "${profile}/efiboot/EFI/BOOT/refind.conf" ::/EFI/BOOT/ @@ -600,9 +620,9 @@ _make_bootmode_uefi-x64.refind.esp() { done # shellx64.efi is picked up automatically when on / - if [[ -e "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then + if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then mcopy -i "${work_dir}/efiboot.img" \ - "${airootfs_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ::/shellx64.efi + "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ::/shellx64.efi fi # Copy kernel and initramfs @@ -699,8 +719,8 @@ _validate_requirements_bootmode_uefi-x64.systemd-boot.esp() { fi # Check for optional packages - # shellcheck disable=SC2076 if [[ "${arch}" == "dual" ]]; then + # shellcheck disable=SC2154 if [[ ! " ${pkg_list_x86_64[*]} " =~ ' edk2-shell ' ]]; then _msg_info "'edk2-shell' is not in the x86_64 package list. The ISO will not contain a bootable UEFI shell." fi @@ -744,7 +764,6 @@ _validate_requirements_bootmode_uefi-x64.refind.esp() { fi # Check for optional packages - # shellcheck disable=SC2076 if [[ "${arch}" == "dual" ]]; then if [[ ! " ${pkg_list_x86_64[*]} " =~ ' edk2-shell ' ]]; then _msg_info "'edk2-shell' is not in the x86_64 package list. The ISO will not contain a bootable UEFI shell." @@ -775,6 +794,34 @@ _prepare_airootfs_image() { fi } +# export build artifacts for netboot +_export_netboot_artifacts() { + _msg_info "Exporting netboot artifacts..." + install -d -m 0755 "${out_dir}" + cp -a -- "${isofs_dir}/${install_dir}/" "${out_dir}/" + _msg_info "Done!" + du -h -- "${out_dir}/${install_dir}" +} + +# sign build artifacts for netboot +_sign_netboot_artifacts() { + local _file _dir + _msg_info "Signing netboot artifacts..." + _dir="${isofs_dir}/${install_dir}/" + for _file in "${_dir}/boot/${arch}/vmlinuz-"* "${_dir}/boot/${arch}/initramfs-"*.img; do + openssl cms \ + -sign \ + -binary \ + -noattr \ + -in "${_file}" \ + -signer "${cert_list[0]}" \ + -inkey "${cert_list[1]}" \ + -outform DER \ + -out "${_file}".ipxe.sig + done + _msg_info "Done!" +} + _validate_requirements_airootfs_image_type_squashfs() { if ! command -v mksquashfs &> /dev/null; then (( validation_error=validation_error+1 )) @@ -797,6 +844,44 @@ _validate_requirements_airootfs_image_type_erofs() { fi } +_validate_requirements_buildmode_all() { + if ! command -v pacman &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating build mode '${_buildmode}': pacman is not available on this host. Install 'pacman'!" 0 + fi + if ! command -v find &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating build mode '${_buildmode}': find is not available on this host. Install 'findutils'!" 0 + fi + if ! command -v gzip &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating build mode '${_buildmode}': gzip is not available on this host. Install 'gzip'!" 0 + fi +} + +_validate_requirements_buildmode_bootstrap() { + _validate_requirements_buildmode_all + if ! command -v bsdtar &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating build mode '${_buildmode}': bsdtar is not available on this host. Install 'libarchive'!" 0 + fi +} + +_validate_requirements_buildmode_iso() { + _validate_requirements_buildmode_all + if ! command -v awk &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating build mode '${_buildmode}': awk is not available on this host. Install 'awk'!" 0 + fi +} +_validate_requirements_buildmode_netboot() { + _validate_requirements_buildmode_all + if ! command -v openssl &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating build mode '${_buildmode}': openssl is not available on this host. Install 'openssl'!" 0 + fi +} + # SYSLINUX El Torito _add_xorrisofs_options_bios.syslinux.eltorito() { xorrisofs_options+=( @@ -944,8 +1029,28 @@ _add_xorrisofs_options_uefi-x64.refind.eltorito() { [[ " ${bootmodes[*]} " =~ ' bios.' ]] || xorrisofs_options+=('-eltorito-catalog' 'EFI/boot.cat') } +# Build bootstrap image +_build_bootstrap_image() { + local _bootstrap_parent + _bootstrap_parent="$(dirname -- "${pacstrap_dir}")" + + local image_name_arch + eval "image_name_arch=\${image_name_${arch}}" + [[ -z "${image_name_arch}" ]] || image_name="${image_name_arch}" + + [[ -d "${out_dir}" ]] || install -d -- "${out_dir}" + + cd -- "${_bootstrap_parent}" + + _msg_info "Creating ${arch} bootstrap image..." + bsdtar -cf - "root.${arch}" | gzip -cn9 > "${out_dir}/${image_name}" + _msg_info "Done!" + du -h -- "${out_dir}/${image_name}" + cd -- "${OLDPWD}" +} + # Build ISO -_build_iso() { +_build_iso_image() { local xorrisofs_options=() local bootmode @@ -970,10 +1075,10 @@ _build_iso() { -publisher "${iso_publisher}" \ -preparer "prepared by ${app_name}" \ "${xorrisofs_options[@]}" \ - -output "${out_dir}/${img_name}" \ + -output "${out_dir}/${image_name}" \ "${isofs_dir}/" _msg_info "Done!" - du -h -- "${out_dir}/${img_name}" + du -h -- "${out_dir}/${image_name}" } # Read profile's values from profiledef.sh @@ -992,16 +1097,31 @@ _read_profile() { # shellcheck source=configs/releng/profiledef.sh . "${profile}/profiledef.sh" - # Resolve paths of files that are expected to reside in the profile's directory if [[ "${arch}" == "dual" ]]; then - [[ -n "$packages_dual" ]] || packages_dual=("${profile}/packages."{both,i686,x86_64}) - # shellcheck disable=SC2178 - packages_dual="$(realpath -- "${packages_dual[@]}")" + # Resolve paths of files that are expected to reside in the profile's directory for each architecture + [[ -n "$packages_dual" ]] || packages_files_dual=("${profile}/packages."{both,i686,x86_64}) + packages_dual="$(realpath -- "${packages_files_dual[@]}")" + pacman_conf="$(realpath -- "${pacman_conf}")" + + # Resolve paths of files that may reside in the profile's directory for each architecture + if [[ -z "$bootstrap_packages_dual" ]] && [[ -e "${profile}/bootstrap_packages.both" ]]; then + bootstrap_packages_files_dual=("${profile}/bootstrap_packages."{both,i686,x86_64}) + bootstrap_packages_dual="$(realpath -- "${bootstrap_packages_files_dual[@]}")" + pacman_conf="$(realpath -- "${pacman_conf}")" + fi else + # Resolve paths of files that are expected to reside in the profile's directory [[ -n "$packages" ]] || packages="${profile}/packages.${arch}" packages="$(realpath -- "${packages}")" + pacman_conf="$(realpath -- "${pacman_conf}")" + + # Resolve paths of files that may reside in the profile's directory + if [[ -z "$bootstrap_packages" ]] && [[ -e "${profile}/bootstrap_packages.${arch}" ]]; then + bootstrap_packages="${profile}/bootstrap_packages.${arch}" + bootstrap_packages="$(realpath -- "${bootstrap_packages}")" + pacman_conf="$(realpath -- "${pacman_conf}")" + fi fi - pacman_conf="$(realpath -- "${pacman_conf}")" cd -- "${OLDPWD}" fi @@ -1009,50 +1129,132 @@ _read_profile() { # Validate set options _validate_options() { - local validation_error=0 bootmode + local validation_error=0 bootmode _cert _buildmode local pkg_list_from_file=() + # shellcheck disable=SC2034 + local pkg_list_from_file_i686=() + # shellcheck disable=SC2034 + local pkg_list_from_file_x86_64=() + local bootstrap_pkg_list_from_file=() + # shellcheck disable=SC2034 + local bootstrap_pkg_list_from_file_i686=() + # shellcheck disable=SC2034 + local bootstrap_pkg_list_from_file_x86_64=() + local _override_cert_list=() + _msg_info "Validating options..." - # Check if the package list file exists and read packages from it if [[ "${arch}" == "dual" ]]; then + # Check if the package list files exist and read packages from them for each architecture # shellcheck disable=SC2128 for packages in ${packages_dual}; do - if [[ "${packages##*/}" = "packages.both" ]]; then + if [[ "${packages##*/}" == "packages.both" ]]; then if [[ -e "${packages}" ]]; then mapfile -t pkg_list_from_file < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") pkg_list+=("${pkg_list_from_file[@]}") if (( ${#pkg_list_from_file} < 1 )); then (( validation_error=validation_error+1 )) - _msg_error "No package specified in '${packages}'." 0 + _msg_error "Packages file '${packages}' does not exist." 0 fi else (( validation_error=validation_error+1 )) - _msg_error "File '${packages}' does not exist." 0 + _msg_error "Packages file '${packages}' does not exist." 0 fi elif [[ -e "${packages}" ]]; then mapfile -t "pkg_list_from_file_${packages##*.}" < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") eval "pkg_list_${packages##*.}+=(\${pkg_list_from_file_${packages##*.}[@]})" fi done + + # Check if packages for the bootstrap image are specified for each architecture + if [[ "${buildmodes[*]}" == *bootstrap* ]]; then + for bootstrap_packages in ${bootstrap_packages_dual}; do + if [[ "${bootstrap_packages##*/}" == "bootstrap_packages.both" ]]; then + if [[ -e "${bootstrap_packages}" ]]; then + mapfile -t bootstrap_pkg_list_from_file < \ + <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${bootstrap_packages}") + bootstrap_pkg_list+=("${bootstrap_pkg_list_from_file[@]}") + if (( ${#bootstrap_pkg_list_from_file} < 1 )); then + (( validation_error=validation_error+1 )) + _msg_error "No package specified in '${bootstrap_packages}'." 0 + fi + else + (( validation_error=validation_error+1 )) + _msg_error "Bootstrap packages file '${bootstrap_packages}' does not exist." 0 + fi + elif [[ -e "${bootstrap_packages}" ]]; then + mapfile -t "bootstrap_pkg_list_from_file_${bootstrap_packages##*.}" < \ + <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${bootstrap_packages}") + eval "bootstrap_pkg_list_${bootstrap_packages##*.}+=(\${bootstrap_pkg_list_from_file_${bootstrap_packages##*.}[@]})" + fi + done + fi else + # Check if the package list file exists and read packages from it if [[ -e "${packages}" ]]; then mapfile -t pkg_list_from_file < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") pkg_list+=("${pkg_list_from_file[@]}") if (( ${#pkg_list_from_file} < 1 )); then (( validation_error=validation_error+1 )) - _msg_error "No package specified in '${packages}'." 0 + _msg_error "Packages file '${packages}' does not exist." 0 fi else (( validation_error=validation_error+1 )) - _msg_error "File '${packages}' does not exist." 0 + _msg_error "Packages file '${packages}' does not exist." 0 + fi + # Check if packages for the bootstrap image are specified + if [[ "${buildmodes[*]}" == *bootstrap* ]]; then + if [[ -e "${bootstrap_packages}" ]]; then + mapfile -t bootstrap_pkg_list_from_file < \ + <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${bootstrap_packages}") + bootstrap_pkg_list+=("${bootstrap_pkg_list_from_file[@]}") + if (( ${#bootstrap_pkg_list_from_file} < 1 )); then + (( validation_error=validation_error+1 )) + _msg_error "No package specified in '${bootstrap_packages}'." 0 + fi + else + (( validation_error=validation_error+1 )) + _msg_error "Bootstrap packages file '${bootstrap_packages}' does not exist." 0 + fi + fi + fi + if [[ "${sign_netboot_artifacts}" == "y" ]]; then + # Check if the certificate files exist + for _cert in "${cert_list[@]}"; do + if [[ -e "${_cert}" ]]; then + _override_cert_list+=("$(realpath -- "${_cert}")") + else + (( validation_error=validation_error+1 )) + _msg_error "File '${_cert}' does not exist." 0 + fi + done + cert_list=("${_override_cert_list[@]}") + # Check if there are at least two certificate files + if (( "${#cert_list[@]}" < 2 )); then + (( validation_error=validation_error+1 )) + _msg_error "Two certificates are required for codesigning, but '${cert_list[*]}' is provided." 0 fi fi - # Check if pacman configuration file exists if [[ ! -e "${pacman_conf}" ]]; then (( validation_error=validation_error+1 )) _msg_error "File '${pacman_conf}' does not exist." 0 fi + + # Check if the specified buildmodes are supported + for _buildmode in "${buildmodes[@]}"; do + if typeset -f "_build_buildmode_${_buildmode}" &> /dev/null; then + if typeset -f "_validate_requirements_buildmode_${_buildmode}" &> /dev/null; then + "_validate_requirements_buildmode_${_buildmode}" + else + _msg_warning "Function '_validate_requirements_buildmode_${_buildmode}' does not exist. Validating the requirements of '${_buildmode}' build mode will not be possible." + fi + else + (( validation_error=validation_error+1 )) + _msg_error "${_buildmode} is not a valid build mode!" 0 + fi + done + # Check if the specified bootmodes are supported for bootmode in "${bootmodes[@]}"; do if typeset -f "_make_bootmode_${bootmode}" &> /dev/null; then @@ -1066,6 +1268,7 @@ _validate_options() { _msg_error "${bootmode} is not a valid boot mode!" 0 fi done + # Check if the specified airootfs_image_type is supported if typeset -f "_mkairootfs_${airootfs_image_type}" &> /dev/null; then if typeset -f "_validate_requirements_airootfs_image_type_${airootfs_image_type}" &> /dev/null; then @@ -1077,6 +1280,7 @@ _validate_options() { (( validation_error=validation_error+1 )) _msg_error "Unsupported image type: '${airootfs_image_type}'" 0 fi + if (( validation_error )); then _msg_error "${validation_error} errors were encountered while validating the profile. Aborting." 1 fi @@ -1086,6 +1290,10 @@ _validate_options() { # Set defaults and, if present, overrides from mkparabolaiso command line option parameters _set_overrides() { # Set variables that have command line overrides + [[ ! -v override_buildmodes ]] || buildmodes=("${override_buildmodes[@]}") + if (( "${#buildmodes[@]}" < 1 )); then + buildmodes+=('iso') + fi if [[ -v override_work_dir ]]; then work_dir="$override_work_dir" elif [[ -z "$work_dir" ]]; then @@ -1105,6 +1313,7 @@ _set_overrides() { fi pacman_conf="$(realpath -- "$pacman_conf")" [[ ! -v override_pkg_list ]] || pkg_list+=("${override_pkg_list[@]}") + # TODO: allow overriding bootstrap_pkg_list if [[ -v override_iso_label ]]; then iso_label="$override_iso_label" elif [[ -z "$iso_label" ]]; then @@ -1126,6 +1335,10 @@ _set_overrides() { install_dir="${app_name}" fi [[ ! -v override_gpg_key ]] || gpg_key="$override_gpg_key" + if [[ -v override_cert_list ]]; then + sign_netboot_artifacts="y" + fi + [[ ! -v override_cert_list ]] || cert_list+=("${override_cert_list[@]}") if [[ -v override_quiet ]]; then quiet="$override_quiet" elif [[ -z "$quiet" ]]; then @@ -1136,7 +1349,6 @@ _set_overrides() { [[ -n "$arch" ]] || arch="$(uname -m)" [[ -n "$airootfs_image_type" ]] || airootfs_image_type="squashfs" [[ -n "$iso_name" ]] || iso_name="${app_name}" - [[ -n "$img_name" ]] || img_name="${iso_name}-${iso_version}-${arch}.iso" } _export_gpg_publickey() { @@ -1144,42 +1356,65 @@ _export_gpg_publickey() { } _make_version() { - local osrelease - install -d -m 0755 -- "${isofs_dir}/${install_dir}" - _msg_info "Creating files with iso version..." - # Write version file to airootfs - rm -f -- "${airootfs_dir}/version" - printf '%s\n' "${iso_version}" > "${airootfs_dir}/version" - # Write version file to ISO 9660 - printf '%s\n' "${iso_version}" > "${isofs_dir}/${install_dir}/version" - # Write grubenv with version information to ISO 9660 - printf '%.1024s' "$(printf '# GRUB Environment Block\nNAME=%s\nVERSION=%s\n%s' \ - "${iso_name}" "${iso_version}" "$(printf '%0.1s' "#"{1..1024})")" \ - > "${isofs_dir}/${install_dir}/grubenv" + local _os_release + + _msg_info "Creating ${arch} version files..." + # Write version file to system installation dir + rm -f -- "${pacstrap_dir}/version" + printf '%s\n' "${iso_version}" > "${pacstrap_dir}/version" + + if [[ "${buildmode}" == "iso" ]]; then + install -d -m 0755 -- "${isofs_dir}/${install_dir}" + # Write version file to ISO 9660 + printf '%s\n' "${iso_version}" > "${isofs_dir}/${install_dir}/version" + # Write grubenv with version information to ISO 9660 + printf '%.1024s' "$(printf '# GRUB Environment Block\nNAME=%s\nVERSION=%s\n%s' \ + "${iso_name}" "${iso_version}" "$(printf '%0.1s' "#"{1..1024})")" \ + > "${isofs_dir}/${install_dir}/grubenv" + fi + # Append IMAGE_ID & IMAGE_VERSION to os-release - osrelease="$(realpath -- "${airootfs_dir}/etc/os-release")" - if [[ ! -e "${airootfs_dir}/etc/os-release" && -e "${airootfs_dir}/usr/lib/os-release" ]]; then - osrelease="$(realpath -- "${airootfs_dir}/usr/lib/os-release")" + _os_release="$(realpath -- "${pacstrap_dir}/etc/os-release")" + if [[ ! -e "${pacstrap_dir}/etc/os-release" && -e "${pacstrap_dir}/usr/lib/os-release" ]]; then + _os_release="$(realpath -- "${pacstrap_dir}/usr/lib/os-release")" fi - if [[ "${osrelease}" != "${airootfs_dir}"* ]]; then - _msg_warning "os-release file '${osrelease}' is outside of valid path." + if [[ "${_os_release}" != "${pacstrap_dir}"* ]]; then + _msg_warning "os-release file '${_os_release}' is outside of valid path." else - [[ ! -e "${osrelease}" ]] || sed -i '/^IMAGE_ID=/d;/^IMAGE_VERSION=/d' "${osrelease}" - printf 'IMAGE_ID=%s\nIMAGE_VERSION=%s\n' "${iso_name}" "${iso_version}" >> "${osrelease}" + [[ ! -e "${_os_release}" ]] || sed -i '/^IMAGE_ID=/d;/^IMAGE_VERSION=/d' "${_os_release}" + printf 'IMAGE_ID=%s\nIMAGE_VERSION=%s\n' "${iso_name}" "${iso_version}" >> "${_os_release}" fi _msg_info "Done!" } _make_pkglist() { - install -d -m 0755 -- "${isofs_dir}/${install_dir}" _msg_info "Creating a list of installed packages on ${arch} live-enviroment..." - pacman -Q --sysroot "${airootfs_dir}" > "${isofs_dir}/${install_dir}/pkglist.${arch}.txt" + case "${buildmode}" in + "bootstrap") + pacman -Q --sysroot "${pacstrap_dir}" > "${pacstrap_dir}/pkglist.${arch}.txt" + ;; + "iso"|"netboot") + install -d -m 0755 -- "${isofs_dir}/${install_dir}" + pacman -Q --sysroot "${pacstrap_dir}" > "${isofs_dir}/${install_dir}/pkglist.${arch}.txt" + ;; + esac _msg_info "Done!" } -_build_profile() { +# build the base for an ISO and/or a netboot target +_build_iso_base() { + local run_once_mode="base" + local buildmode_packages="${packages}" + if [[ "${arch}" == "dual" ]]; then + local buildmode_packages="${packages_dual}" + # Set the package list to use for each architecture + local buildmode_pkg_list_i686=("${pkg_list_i686[@]}") + local buildmode_pkg_list_x86_64=("${pkg_list_x86_64[@]}") + fi + # Set the package list to use + local buildmode_pkg_list=("${pkg_list[@]}") # Set up essential directory paths - airootfs_dir="${work_dir}/${arch}/airootfs" + pacstrap_dir="${work_dir}/${arch}/airootfs" isofs_dir="${work_dir}/iso" # Create working directory [[ -d "${work_dir}" ]] || install -d -- "${work_dir}" @@ -1199,12 +1434,79 @@ _build_profile() { _run_dual '_run_once _make_customize_airootfs' _run_dual '_run_once _make_pkglist' _make_bootmodes - _run_dual '_run_once _cleanup_airootfs' \ + _run_dual '_run_once _cleanup_pacstrap_dir' \ '_run_once _prepare_airootfs_image' - _run_once _build_iso } -while getopts 'p:C:L:P:A:D:w:o:g:vh?' arg; do +# Build the bootstrap buildmode +_build_buildmode_bootstrap() { + local run_once_mode="${buildmode}" + if [[ "${arch}" == "dual" ]]; then + local image_name_i686="${iso_name}-bootstrap-${iso_version}-i686.tar.gz" + local image_name_x86_64="${iso_name}-bootstrap-${iso_version}-x86_64.tar.gz" + local image_name="${image_name_i686} ${image_name_x86_64}" + local buildmode_packages="${bootstrap_packages_dual}" + # Set the package lists to use + local buildmode_pkg_list_i686=("${bootstrap_pkg_list_i686[@]}") + local buildmode_pkg_list_x86_64=("${bootstrap_pkg_list_x86_64[@]}") + local buildmode_pkg_list=("${bootstrap_pkg_list[@]}") + + # Set up essential directory paths + pacstrap_dir_i686="${work_dir}/i686/bootstrap/root.i686" + pacstrap_dir_x86_64="${work_dir}/x86_64/bootstrap/root.x86_64" + [[ -d "${work_dir}" ]] || install -d -- "${work_dir}" + install -d -m 0755 -o 0 -g 0 -- "${pacstrap_dir_i686}" "${pacstrap_dir_x86_64}" + else + local image_name="${iso_name}-bootstrap-${iso_version}-${arch}.tar.gz" + local buildmode_packages="${bootstrap_packages}" + # Set the package list to use + local buildmode_pkg_list=("${bootstrap_pkg_list[@]}") + + # Set up essential directory paths + pacstrap_dir="${work_dir}/${arch}/bootstrap/root.${arch}" + [[ -d "${work_dir}" ]] || install -d -- "${work_dir}" + install -d -m 0755 -o 0 -g 0 -- "${pacstrap_dir}" + fi + + [[ "${quiet}" == "y" ]] || _show_config + _run_dual '_run_once _make_pacman_conf' + _run_dual '_run_once _make_packages' + _run_dual '_run_once _make_version' + _run_dual '_run_once _make_pkglist' + _run_dual '_run_once _cleanup_pacstrap_dir' + _run_dual '_run_once _build_bootstrap_image' +} + +# Build the netboot buildmode +_build_buildmode_netboot() { + local run_once_mode="${buildmode}" + + _run_once _build_iso_base + if [[ -v cert_list ]]; then + _run_once _sign_netboot_artifacts + fi + _run_once _export_netboot_artifacts +} + +# Build the ISO buildmode +_build_buildmode_iso() { + local image_name="${iso_name}-${iso_version}-${arch}.iso" + local run_once_mode="${buildmode}" + _run_once _build_iso_base + _run_once _build_iso_image +} + +# build all buildmodes +_build() { + local buildmode + local run_once_mode="build" + + for buildmode in "${buildmodes[@]}"; do + _run_once "_build_buildmode_${buildmode}" + done +} + +while getopts 'c:p:C:L:P:A:D:w:m:o:g:vh?' arg; do case "${arg}" in p) read -r -a override_pkg_list <<< "${OPTARG}" ;; C) override_pacman_conf="${OPTARG}" ;; @@ -1212,7 +1514,9 @@ while getopts 'p:C:L:P:A:D:w:o:g:vh?' arg; do P) override_iso_publisher="${OPTARG}" ;; A) override_iso_application="${OPTARG}" ;; D) override_install_dir="${OPTARG}" ;; + c) read -r -a override_cert_list <<< "${OPTARG}" ;; w) override_work_dir="${OPTARG}" ;; + m) read -r -a override_buildmodes <<< "${OPTARG}" ;; o) override_out_dir="${OPTARG}" ;; g) override_gpg_key="${OPTARG}" ;; v) override_quiet="n" ;; @@ -1241,6 +1545,6 @@ profile="$(realpath -- "${1}")" _read_profile _set_overrides _validate_options -_build_profile +_build # vim:ts=4:sw=4:et: -- cgit v1.2.2