diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..aa8822d --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,278 @@ +#!/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 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 -d | grep "Ubuntu 20.04"` + if [[ $os_ver == "" ]]; then + echo "WARNING : OS is not Ubuntu 20.04 and is untested" + fi + + if [ $(id -u) -eq 0 ]; then + echo "This script should not be run as 'root'" + 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 + sudo mkdir -p chroot +} + +function debootstrap() { + echo "=====> running debootstrap ... will take a couple of minutes ..." + sudo debootstrap --arch=amd64 --variant=minbase focal chroot http://us.archive.ubuntu.com/ubuntu/ +} + +function run_chroot() { + echo "=====> running run_chroot ..." + + chroot_enter_setup + + sudo ln -f $SCRIPT_DIR/chroot_build.sh chroot/root/chroot_build.sh + sudo cp -f /etc/apt/sources.list chroot/etc/apt/ + sudo chroot chroot /root/chroot_build.sh - + sudo rm -f chroot/root/chroot_build.sh + + 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 "Try Ubuntu FS without installing" { + linux /casper/vmlinuz boot=casper nopersistent toram quiet splash --- + initrd /casper/initrd +} + +menuentry "Install Ubuntu FS" { + 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 + sudo sed -i '/ubiquity/d' image/casper/filesystem.manifest-desktop + sudo sed -i '/casper/d' image/casper/filesystem.manifest-desktop + sudo sed -i '/discover/d' image/casper/filesystem.manifest-desktop + sudo sed -i '/laptop-detect/d' image/casper/filesystem.manifest-desktop + sudo sed -i '/os-prober/d' image/casper/filesystem.manifest-desktop + + # compress rootfs + sudo mksquashfs chroot image/casper/filesystem.squashfs + printf $(sudo du -sx --block-size=1 chroot | cut -f1) > image/casper/filesystem.size + + # create diskdefines + cat < image/README.diskdefines +#define DISKNAME Ubuntu from scratch +#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 + grub-mkstandalone \ + --format=x86_64-efi \ + --output=isolinux/bootx64.efi \ + --locales="" \ + --fonts="" \ + "boot/grub/grub.cfg=isolinux/grub.cfg" + + ( + cd isolinux && \ + dd if=/dev/zero of=efiboot.img bs=1M count=10 && \ + sudo mkfs.vfat efiboot.img && \ + LC_CTYPE=C mmd -i efiboot.img efi efi/boot && \ + LC_CTYPE=C mcopy -i efiboot.img ./bootx64.efi ::efi/boot/ + ) + + 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 "Ubuntu from scratch" \ + -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 "../ubuntu-from-scratch.iso" \ + -m "isolinux/efiboot.img" \ + -m "isolinux/bios.img" \ + -graft-points \ + "/EFI/efiboot.img=isolinux/efiboot.img" \ + "/boot/grub/bios.img=isolinux/bios.img" \ + "." + + popd +} + +# ============= main ================ + +# we always stay in $SCRIPT_DIR +cd $SCRIPT_DIR + +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!" + diff --git a/scripts/chroot_build.sh b/scripts/chroot_build.sh new file mode 100755 index 0000000..1f8d684 --- /dev/null +++ b/scripts/chroot_build.sh @@ -0,0 +1,196 @@ +#!/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 install_pkg finish_up) + +function help() { + # if $1 is set, use $1 as headline message in help() + if [ -z ${1+x} ]; then + echo -e "This script builds Ubuntu from scratch" + 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 check_host() { + if [ $(id -u) -ne 0 ]; then + echo "This script should be run as 'root'" + exit 1 + fi + + export HOME=/root + export LC_ALL=C +} + +function setup_host() { + echo "=====> running setup_host ..." + + echo "ubuntu-fs-live" > /etc/hostname + + # we need to install systemd first, to configure machine id + apt-get update + apt-get install -y libterm-readline-gnu-perl systemd-sysv + + #configure machine id + dbus-uuidgen > /etc/machine-id + ln -fs /etc/machine-id /var/lib/dbus/machine-id + + # don't understand why, but multiple sources indicate this + dpkg-divert --local --rename --add /sbin/initctl + ln -s /bin/true /sbin/initctl +} + +function install_pkg() { + echo "=====> running install_pkg ... will take a long time ..." + apt-get -y upgrade + + # install live linux packages + apt-get install -y \ + ubuntu-standard \ + casper \ + lupin-casper \ + discover \ + laptop-detect \ + os-prober \ + network-manager \ + resolvconf \ + net-tools \ + wireless-tools \ + wpagui \ + locales \ + linux-generic + + # graphic installer - ubiquity + apt-get install -y \ + ubiquity \ + ubiquity-casper \ + ubiquity-frontend-gtk \ + ubiquity-slideshow-ubuntu \ + ubiquity-ubuntu-artwork + + # install graphics and desktop + apt-get install -y \ + plymouth-theme-ubuntu-logo \ + ubuntu-gnome-desktop \ + ubuntu-gnome-wallpapers + + # useful tools + apt-get install -y \ + clamav-daemon \ + terminator \ + apt-transport-https \ + curl \ + vim \ + nano \ + less + + # purge + apt-get purge -y \ + transmission-gtk \ + transmission-common \ + gnome-mahjongg \ + gnome-mines \ + gnome-sudoku \ + aisleriot \ + hitori + + # remove unused and clean up apt cache + apt-get autoremove -y + + # final touch + dpkg-reconfigure locales + dpkg-reconfigure resolvconf + + # network manager + cat < /etc/NetworkManager/NetworkManager.conf +[main] +rc-manager=resolvconf +plugins=ifupdown,keyfile +dns=dnsmasq + +[ifupdown] +managed=false +EOF + dpkg-reconfigure network-manager + + apt-get clean -y +} + +function finish_up() { + echo "=====> finish_up" + + # truncate machine id (why??) + truncate -s 0 /etc/machine-id + + # remove diversion (why??) + rm /sbin/initctl + dpkg-divert --rename --remove /sbin/initctl + + rm -rf /tmp/* ~/.bash_history +} + +# ============= main ================ + +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!" +