#!/bin/bash set -e # exit on error set -o pipefail # exit on pipeline error set -u # treat unset variable as error #set -x SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" CMD=(setup_host debootstrap run_chroot build_iso) DATE=`TZ="UTC" date +"%y%m%d-%H%M%S"` function help() { # if $1 is set, use $1 as headline message in help() if [ -z ${1+x} ]; then echo -e "This script builds a bootable ubuntu ISO image" echo -e else echo -e $1 echo fi echo -e "Supported commands : ${CMD[*]}" echo -e echo -e "Syntax: $0 [start_cmd] [-] [end_cmd]" echo -e "\trun from start_cmd to end_end" echo -e "\tif start_cmd is omitted, start from first command" echo -e "\tif end_cmd is omitted, end with last command" echo -e "\tenter single cmd to run the specific command" echo -e "\tenter '-' as only argument to run all commands" echo -e exit 0 } function find_index() { local ret; local i; for ((i=0; i<${#CMD[*]}; i++)); do if [ "${CMD[i]}" == "$1" ]; then index=$i; return; fi done help "Command not found : $1" } function chroot_enter_setup() { sudo mount --bind /dev chroot/dev sudo mount --bind /run chroot/run sudo chroot chroot mount none -t proc /proc sudo chroot chroot mount none -t sysfs /sys sudo chroot chroot mount none -t devpts /dev/pts } function chroot_exit_teardown() { sudo chroot chroot umount /proc sudo chroot chroot umount /sys sudo chroot chroot umount /dev/pts sudo umount chroot/dev sudo umount chroot/run } function check_host() { local os_ver os_ver=`lsb_release -i | grep -E "(Ubuntu|Debian)"` if [[ -z "$os_ver" ]]; then echo "WARNING : OS is not Debian or Ubuntu and is untested" fi if [ $(id -u) -eq 0 ]; then echo "This script should not be run as 'root'" exit 1 fi } # Load configuration values from file function load_config() { if [[ -f "$SCRIPT_DIR/config.sh" ]]; then . "$SCRIPT_DIR/config.sh" elif [[ -f "$SCRIPT_DIR/default_config.sh" ]]; then . "$SCRIPT_DIR/default_config.sh" else >&2 echo "Unable to find default config file $SCRIPT_DIR/default_config.sh, aborting." exit 1 fi } # Verify that necessary configuration values are set and they are valid function check_config() { local expected_config_version expected_config_version="0.4" if [[ "$CONFIG_FILE_VERSION" != "$expected_config_version" ]]; then >&2 echo "Invalid or old config version $CONFIG_FILE_VERSION, expected $expected_config_version. Please update your configuration file from the default." exit 1 fi } function setup_host() { echo "=====> running setup_host ..." sudo apt update sudo apt install -y binutils debootstrap squashfs-tools xorriso grub-pc-bin grub-efi-amd64-bin mtools dosfstools unzip grub2-common sudo mkdir -p chroot } function debootstrap() { echo "=====> running debootstrap ... will take a couple of minutes ..." sudo debootstrap --arch=amd64 --variant=minbase $TARGET_UBUNTU_VERSION chroot $TARGET_UBUNTU_MIRROR } function run_chroot() { echo "=====> running run_chroot ..." chroot_enter_setup # Setup build scripts in chroot environment sudo ln -f $SCRIPT_DIR/chroot_build.sh chroot/root/chroot_build.sh sudo ln -f $SCRIPT_DIR/default_config.sh chroot/root/default_config.sh if [[ -f "$SCRIPT_DIR/config.sh" ]]; then sudo ln -f $SCRIPT_DIR/config.sh chroot/root/config.sh fi # Launch into chroot environment to build install image. sudo chroot chroot /usr/bin/env DEBIAN_FRONTEND=${DEBIAN_FRONTEND:-readline} /root/chroot_build.sh - # Cleanup after image changes sudo rm -f chroot/root/chroot_build.sh sudo rm -f chroot/root/default_config.sh if [[ -f "chroot/root/config.sh" ]]; then sudo rm -f chroot/root/config.sh fi chroot_exit_teardown } function build_iso() { echo "=====> running build_iso ..." rm -rf image mkdir -p image/{casper,isolinux,install} # copy kernel files sudo cp chroot/boot/vmlinuz-**-**-generic image/casper/vmlinuz sudo cp chroot/boot/initrd.img-**-**-generic image/casper/initrd # memtest86 sudo cp chroot/boot/memtest86+.bin image/install/memtest86+ wget --progress=dot https://www.memtest86.com/downloads/memtest86-usb.zip -O image/install/memtest86-usb.zip unzip -p image/install/memtest86-usb.zip memtest86-usb.img > image/install/memtest86 rm -f image/install/memtest86-usb.zip # grub touch image/ubuntu cat < image/isolinux/grub.cfg search --set=root --file /ubuntu insmod all_video set default="0" set timeout=30 menuentry "${GRUB_LIVEBOOT_LABEL}" { linux /casper/vmlinuz boot=casper nopersistent toram quiet splash --- initrd /casper/initrd } menuentry "${GRUB_INSTALL_LABEL}" { linux /casper/vmlinuz boot=casper only-ubiquity quiet splash --- initrd /casper/initrd } menuentry "Check disc for defects" { linux /casper/vmlinuz boot=casper integrity-check quiet splash --- initrd /casper/initrd } menuentry "Test memory Memtest86+ (BIOS)" { linux16 /install/memtest86+ } menuentry "Test memory Memtest86 (UEFI, long load time)" { insmod part_gpt insmod search_fs_uuid insmod chain loopback loop /install/memtest86 chainloader (loop,gpt1)/efi/boot/BOOTX64.efi } EOF # generate manifest sudo chroot chroot dpkg-query -W --showformat='${Package} ${Version}\n' | sudo tee image/casper/filesystem.manifest sudo cp -v image/casper/filesystem.manifest image/casper/filesystem.manifest-desktop for pkg in $TARGET_PACKAGE_REMOVE; do sudo sed -i "/$pkg/d" image/casper/filesystem.manifest-desktop done # compress rootfs sudo mksquashfs chroot image/casper/filesystem.squashfs \ -noappend -no-duplicates -no-recovery \ -wildcards \ -e "var/cache/apt/archives/*" \ -e "root/*" \ -e "root/.*" \ -e "tmp/*" \ -e "tmp/.*" \ -e "swapfile" printf $(sudo du -sx --block-size=1 chroot | cut -f1) > image/casper/filesystem.size # create diskdefines cat < image/README.diskdefines #define DISKNAME ${GRUB_LIVEBOOT_LABEL} #define TYPE binary #define TYPEbinary 1 #define ARCH amd64 #define ARCHamd64 1 #define DISKNUM 1 #define DISKNUM1 1 #define TOTALNUM 0 #define TOTALNUM0 1 EOF # create iso image pushd $SCRIPT_DIR/image ( cd isolinux && \ dd if=/dev/zero of=efiboot.img bs=1M count=10 && \ sudo mkfs.vfat efiboot.img && \ LOOP_DEVICE=`sudo losetup --find --show $PWD/efiboot.img` && \ mkdir efi && \ sudo mount $LOOP_DEVICE efi && \ sudo grub-install --efi-directory=efi --uefi-secure-boot --removable --no-nvram $LOOP_DEVICE && \ sudo umount $LOOP_DEVICE && \ sudo losetup --detach $LOOP_DEVICE && \ rm -rf efi ) grub-mkstandalone \ --format=i386-pc \ --output=isolinux/core.img \ --install-modules="linux16 linux normal iso9660 biosdisk memdisk search tar ls" \ --modules="linux16 linux normal iso9660 biosdisk search" \ --locales="" \ --fonts="" \ "boot/grub/grub.cfg=isolinux/grub.cfg" cat /usr/lib/grub/i386-pc/cdboot.img isolinux/core.img > isolinux/bios.img sudo /bin/bash -c "(find . -type f -print0 | xargs -0 md5sum | grep -v -e 'md5sum.txt' -e 'bios.img' -e 'efiboot.img' > md5sum.txt)" sudo xorriso \ -as mkisofs \ -iso-level 3 \ -full-iso9660-filenames \ -volid "$TARGET_NAME" \ -eltorito-boot boot/grub/bios.img \ -no-emul-boot \ -boot-load-size 4 \ -boot-info-table \ --eltorito-catalog boot/grub/boot.cat \ --grub2-boot-info \ --grub2-mbr /usr/lib/grub/i386-pc/boot_hybrid.img \ -eltorito-alt-boot \ -e EFI/efiboot.img \ -no-emul-boot \ -append_partition 2 0xef isolinux/efiboot.img \ -output "$SCRIPT_DIR/$TARGET_NAME.iso" \ -m "isolinux/efiboot.img" \ -m "isolinux/bios.img" \ -graft-points \ "/EFI/efiboot.img=isolinux/efiboot.img" \ "/boot/grub/grub.cfg=isolinux/grub.cfg" \ "/boot/grub/bios.img=isolinux/bios.img" \ "." popd } # ============= main ================ # we always stay in $SCRIPT_DIR cd $SCRIPT_DIR load_config check_config check_host # check number of args if [[ $# == 0 || $# > 3 ]]; then help; fi # loop through args dash_flag=false start_index=0 end_index=${#CMD[*]} for ii in "$@"; do if [[ $ii == "-" ]]; then dash_flag=true continue fi find_index $ii if [[ $dash_flag == false ]]; then start_index=$index else end_index=$(($index+1)) fi done if [[ $dash_flag == false ]]; then end_index=$(($start_index + 1)) fi #loop through the commands for ((ii=$start_index; ii<$end_index; ii++)); do ${CMD[ii]} done echo "$0 - Initial build is done!"