Path: blob/next/scripts/debootstrap.sh
14996 views
#!/bin/bash1#2# Copyright (c) 2013-2021 Igor Pecovnik, igor.pecovnik@gma**.com3#4# This file is licensed under the terms of the GNU General Public5# License version 2. This program is licensed "as is" without any6# warranty of any kind, whether express or implied.789# Functions:1011# debootstrap_ng12# create_rootfs_cache13# prepare_partitions14# update_initramfs15# create_image1617181920# debootstrap_ng21#22debootstrap_ng()23{24display_alert "Starting rootfs and image building process for" "${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED:-null} ${DESKTOP_ENVIRONMENT:-null} ${BUILD_MINIMAL}" "info"2526[[ $ROOTFS_TYPE != ext4 ]] && display_alert "Assuming $BOARD $BRANCH kernel supports $ROOTFS_TYPE" "" "wrn"2728# trap to unmount stuff in case of error/manual interruption29trap unmount_on_exit INT TERM EXIT3031# stage: clean and create directories32rm -rf $SDCARD $MOUNT33mkdir -p $SDCARD $MOUNT $DEST/images $EXTER/cache/rootfs3435# stage: verify tmpfs configuration and mount36# CLI needs ~1.5GiB, desktop - ~3.5GiB37# calculate and set tmpfs mount to use 9/10 of available RAM+SWAP38local phymem=$(( (($(awk '/MemTotal/ {print $2}' /proc/meminfo) + $(awk '/SwapTotal/ {print $2}' /proc/meminfo))) / 1024 * 9 / 10 )) # MiB39if [[ $BUILD_DESKTOP == yes ]]; then local tmpfs_max_size=3500; else local tmpfs_max_size=1500; fi # MiB40if [[ $FORCE_USE_RAMDISK == no ]]; then local use_tmpfs=no41elif [[ $FORCE_USE_RAMDISK == yes || $phymem -gt $tmpfs_max_size ]]; then42local use_tmpfs=yes43fi44[[ -n $FORCE_TMPFS_SIZE ]] && phymem=$FORCE_TMPFS_SIZE4546[[ $use_tmpfs == yes ]] && mount -t tmpfs -o size=${phymem}M tmpfs $SDCARD4748# stage: prepare basic rootfs: unpack cache or create from scratch49create_rootfs_cache5051call_extension_method "pre_install_distribution_specific" "config_pre_install_distribution_specific" << 'PRE_INSTALL_DISTRIBUTION_SPECIFIC'52*give config a chance to act before install_distribution_specific*53Called after `create_rootfs_cache` (_prepare basic rootfs: unpack cache or create from scratch_) but before `install_distribution_specific` (_install distribution and board specific applications_).54PRE_INSTALL_DISTRIBUTION_SPECIFIC5556# stage: install kernel and u-boot packages57# install distribution and board specific applications5859if [[ ${RELEASE} == "raspi" ]]; then60install_opi_specific61else62install_distribution_specific63install_common6465# install locally built packages or install pre-built packages from orangepi66[[ $EXTERNAL_NEW == compile || $EXTERNAL_NEW == prebuilt ]] && chroot_installpackages_local6768#[[ $EXTERNAL_NEW == prebuilt ]] && chroot_installpackages "yes"6970# stage: user customization script71# NOTE: installing too many packages may fill tmpfs mount72customize_image7374# remove packages that are no longer needed. Since we have intrudoced uninstall feature, we might want to clean things that are no longer needed75display_alert "No longer needed packages" "purge" "info"76chroot $SDCARD /bin/bash -c "apt-get autoremove -y" >/dev/null 2>&17778# create list of installed packages for debug purposes79chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > $DEST/${LOG_SUBPATH}/installed-packages-${RELEASE}$([[ ${BUILD_MINIMAL} == yes ]] && echo "-minimal")$([[ ${BUILD_DESKTOP} == yes ]] && echo "-desktop").list 2>&18081fi8283# clean up / prepare for making the image84umount_chroot "$SDCARD"85post_debootstrap_tweaks8687if [[ $ROOTFS_TYPE == fel ]]; then88FEL_ROOTFS=$SDCARD/89display_alert "Starting FEL boot" "$BOARD" "info"90source $SRC/scripts/fel-load.sh91else92if [[ $BOARDFAMILY == "cix" ]]; then93create_cix_rootfs94create_cix_image95else96prepare_partitions97create_image98fi99fi100101# stage: unmount tmpfs102umount $SDCARD 2>&1103if [[ $use_tmpfs = yes ]]; then104while grep -qs "$SDCARD" /proc/mounts105do106umount $SDCARD107sleep 5108done109fi110rm -rf $SDCARD111112# remove exit trap113trap - INT TERM EXIT114} #############################################################################115116bootstrap(){117local BOOTSTRAP_CMD=debootstrap118local BOOTSTRAP_ARGS=()119120export CAPSH_ARG="--drop=cap_setfcap"121export http_proxy=${APT_PROXY}122123BOOTSTRAP_ARGS+=(--arch arm64)124BOOTSTRAP_ARGS+=(--include gnupg)125#BOOTSTRAP_ARGS+=(--components "main,contrib,non-free")126BOOTSTRAP_ARGS+=(--components "main")127BOOTSTRAP_ARGS+=(--exclude=info)128BOOTSTRAP_ARGS+=(--include=ca-certificates)129BOOTSTRAP_ARGS+=("$@")130printf -v BOOTSTRAP_STR '%q ' "${BOOTSTRAP_ARGS[@]}"131132${BOOTSTRAP_CMD} $BOOTSTRAP_STR || true133}134export -f bootstrap135136# create_rootfs_cache137#138# unpacks cached rootfs for $RELEASE or creates one139#140create_rootfs_cache()141{142143local packages_hash=$(get_package_list_hash "$ROOTFSCACHE_VERSION")144local cache_type="cli"145[[ ${BUILD_DESKTOP} == yes ]] && local cache_type="xfce-desktop"146[[ -n ${DESKTOP_ENVIRONMENT} ]] && local cache_type="${DESKTOP_ENVIRONMENT}"147[[ ${BUILD_MINIMAL} == yes ]] && local cache_type="minimal"148local cache_name=${RELEASE}-${cache_type}-${ARCH}.$packages_hash.tar.lz4149local cache_fname=${EXTER}/cache/rootfs/${cache_name}150local display_name=${RELEASE}-${cache_type}-${ARCH}.${packages_hash:0:3}...${packages_hash:29}.tar.lz4151152if [[ -f $cache_fname && "$ROOT_FS_CREATE_ONLY" != "force" ]]; then153local date_diff=$(( ($(date +%s) - $(stat -c %Y $cache_fname)) / 86400 ))154display_alert "Extracting $display_name" "$date_diff days old" "info"155pv -p -b -r -c -N "[ .... ] $display_name" "$cache_fname" | lz4 -dc | tar xp --xattrs -C $SDCARD/156[[ $? -ne 0 ]] && rm $cache_fname && exit_with_error "Cache $cache_fname is corrupted and was deleted. Restart."157rm $SDCARD/etc/resolv.conf158echo "nameserver $NAMESERVER" >> $SDCARD/etc/resolv.conf159create_sources_list "$RELEASE" "$SDCARD/"160elif [[ $RELEASE == "raspi" ]]; then161display_alert "local not found" "Creating new rootfs cache for $RELEASE" "info"162163cd $SDCARD # this will prevent error sh: 0: getcwd() failed164165bootstrap bullseye "$SDCARD" "https://mirrors.ustc.edu.cn/debian/"166167mount_chroot "$SDCARD"168169display_alert "Diverting" "initctl/start-stop-daemon" "info"170# policy-rc.d script prevents starting or reloading services during image creation171printf '#!/bin/sh\nexit 101' > $SDCARD/usr/sbin/policy-rc.d172LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/initctl" &> /dev/null173LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/start-stop-daemon" &> /dev/null174printf '#!/bin/sh\necho "Warning: Fake start-stop-daemon called, doing nothing"' > $SDCARD/sbin/start-stop-daemon175printf '#!/bin/sh\necho "Warning: Fake initctl called, doing nothing"' > $SDCARD/sbin/initctl176chmod 755 $SDCARD/usr/sbin/policy-rc.d177chmod 755 $SDCARD/sbin/initctl178chmod 755 $SDCARD/sbin/start-stop-daemon179180install_raspi_specific181182umount_chroot "$SDCARD"183184tar cp --xattrs --directory=$SDCARD/ --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \185--exclude='./sys/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "$display_name" | lz4 -5 -c > $cache_fname186else187display_alert "local not found" "Creating new rootfs cache for $RELEASE" "info"188189# stage: debootstrap base system190if [[ $NO_APT_CACHER != yes ]]; then191# apt-cacher-ng apt-get proxy parameter192local apt_extra="-o Acquire::http::Proxy=\"http://${APT_PROXY_ADDR:-localhost:3142}\""193local apt_mirror="http://${APT_PROXY_ADDR:-localhost:3142}/$APT_MIRROR"194else195local apt_mirror="http://$APT_MIRROR"196fi197198# fancy progress bars199[[ -z $OUTPUT_DIALOG ]] && local apt_extra_progress="--show-progress -o DPKG::Progress-Fancy=1"200201# Ok so for eval+PIPESTATUS.202# Try this on your bash shell:203# ONEVAR="testing" eval 'bash -c "echo value once $ONEVAR && false && echo value twice $ONEVAR"' '| grep value' '| grep value' ; echo ${PIPESTATUS[*]}204# Notice how PIPESTATUS has only one element. and it is always true, although we failed explicitly with false in the middle of the bash.205# That is because eval itself is considered a single command, no matter how many pipes you put in there, you'll get a single value, the return code of the LAST pipe.206# Lets export the value of the pipe inside eval so we know outside what happened:207# ONEVAR="testing" eval 'bash -e -c "echo value once $ONEVAR && false && echo value twice $ONEVAR"' '| grep value' '| grep value' ';EVALPIPE=(${PIPESTATUS[@]})' ; echo ${EVALPIPE[*]}208209local release_version=${RELEASE}210211if [[ ${RELEASE} == "sid" ]]; then212release_version=unstable213apt_mirror="https://snapshot.debian.org/archive/debian-ports/20221225T084846Z"214DEBOOTSTRAP_OPTION="--no-check-gpg --no-merged-usr"215PACKAGE_LIST_EXCLUDE="usr-is-merged"216fi217218display_alert "Installing base system" "Stage 1/2" "info"219cd $SDCARD # this will prevent error sh: 0: getcwd() failed220221eval 'debootstrap --variant=minbase --include=${DEBOOTSTRAP_LIST// /,} ${PACKAGE_LIST_EXCLUDE:+ --exclude=${PACKAGE_LIST_EXCLUDE// /,}} \222--no-check-gpg --arch=$ARCH --components=${DEBOOTSTRAP_COMPONENTS} $DEBOOTSTRAP_OPTION --foreign ${release_version} $SDCARD/ ${apt_mirror}' \223${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \224${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Debootstrap (stage 1/2)..." $TTY_Y $TTY_X'} \225${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'226227[[ ${EVALPIPE[0]} -ne 0 || ! -f $SDCARD/debootstrap/debootstrap ]] && exit_with_error "Debootstrap base system for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} first stage failed"228229cp /usr/bin/$QEMU_BINARY $SDCARD/usr/bin/230231mkdir -p $SDCARD/usr/share/keyrings/232cp /usr/share/keyrings/*-archive-keyring.gpg $SDCARD/usr/share/keyrings/233234display_alert "Installing base system" "Stage 2/2" "info"235eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "/debootstrap/debootstrap --second-stage"' \236${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \237${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Debootstrap (stage 2/2)..." $TTY_Y $TTY_X'} \238${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'239240[[ ${EVALPIPE[0]} -ne 0 || ! -f $SDCARD/bin/bash ]] && exit_with_error "Debootstrap base system for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} second stage failed"241242mount_chroot "$SDCARD"243244if [[ ${RELEASE} == "sid" ]]; then245mkdir -p $SDCARD/etc/apt/apt.conf.d/246echo "Acquire::Check-Valid-Until no;" > $SDCARD/etc/apt/apt.conf.d/99-no-check-valid-until247wget -qnc -P ${EXTER}/cache/debs/ https://snapshot.debian.org/archive/debian-ports/20220616T194833Z/pool-riscv64/main/i/icu/libicu71_71.1-3_riscv64.deb248cp -v ${EXTER}/cache/debs/libicu71_71.1-3_riscv64.deb $SDCARD/249LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg -i /libicu71_71.1-3_riscv64.deb" &> /dev/null250fi251252display_alert "Diverting" "initctl/start-stop-daemon" "info"253# policy-rc.d script prevents starting or reloading services during image creation254printf '#!/bin/sh\nexit 101' > $SDCARD/usr/sbin/policy-rc.d255LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/initctl" &> /dev/null256LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/start-stop-daemon" &> /dev/null257printf '#!/bin/sh\necho "Warning: Fake start-stop-daemon called, doing nothing"' > $SDCARD/sbin/start-stop-daemon258printf '#!/bin/sh\necho "Warning: Fake initctl called, doing nothing"' > $SDCARD/sbin/initctl259chmod 755 $SDCARD/usr/sbin/policy-rc.d260chmod 755 $SDCARD/sbin/initctl261chmod 755 $SDCARD/sbin/start-stop-daemon262263# stage: configure language and locales264display_alert "Configuring locales" "$DEST_LANG" "info"265266[[ -f $SDCARD/etc/locale.gen ]] && sed -i "s/^# $DEST_LANG/$DEST_LANG/" $SDCARD/etc/locale.gen267eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "locale-gen $DEST_LANG"' ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}268eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "update-locale LANG=$DEST_LANG LANGUAGE=$DEST_LANG LC_MESSAGES=$DEST_LANG"' \269${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}270271if [[ -f $SDCARD/etc/default/console-setup ]]; then272sed -e 's/CHARMAP=.*/CHARMAP="UTF-8"/' -e 's/FONTSIZE=.*/FONTSIZE="8x16"/' \273-e 's/CODESET=.*/CODESET="guess"/' -i $SDCARD/etc/default/console-setup274eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "setupcon --save --force"'275fi276277# stage: create apt-get sources list278create_sources_list "$RELEASE" "$SDCARD/"279280# add armhf arhitecture to arm64, unless configured not to do so.281if [[ "a${ARMHF_ARCH}" != "askip" ]]; then282[[ $ARCH == arm64 ]] && eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg --add-architecture armhf"'283fi284285# this should fix resolvconf installation failure in some cases286chroot $SDCARD /bin/bash -c 'echo "resolvconf resolvconf/linkify-resolvconf boolean false" | debconf-set-selections'287288# stage: update packages list289display_alert "Updating package list" "$RELEASE" "info"290eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "apt-get -q -y $apt_extra update"' \291${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \292${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Updating package lists..." $TTY_Y $TTY_X'} \293${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'294295[[ ${EVALPIPE[0]} -ne 0 ]] && display_alert "Updating package lists" "failed" "wrn"296297# stage: upgrade base packages from xxx-updates and xxx-backports repository branches298display_alert "Upgrading base packages" "Orange Pi" "info"299eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \300$apt_extra $apt_extra_progress upgrade"' \301${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \302${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Upgrading base packages..." $TTY_Y $TTY_X'} \303${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'304305# Myy: Dividing the desktop packages installation steps into multiple306# ones. We first install the "ADDITIONAL_PACKAGES" in order to get307# access to software-common-properties installation.308# THEN we add the APT sources and install the Desktop packages.309# TODO : Find a way to add APT sources WITHOUT software-common-properties310311[[ ${EVALPIPE[0]} -ne 0 ]] && display_alert "Upgrading base packages" "failed" "wrn"312313# stage: install additional packages314display_alert "Installing the main packages for" "Orange Pi" "info"315eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \316$apt_extra $apt_extra_progress --no-install-recommends install $PACKAGE_MAIN_LIST"' \317${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \318${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Installing Orange Pi main packages..." $TTY_Y $TTY_X'} \319${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'320321[[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi main packages for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} failed"322323if [[ $BUILD_DESKTOP == "yes" ]]; then324# FIXME Myy : Are we keeping this only for Desktop users,325# or should we extend this to CLI users too ?326# There might be some clunky boards that require Debian packages from327# specific repos...328display_alert "Adding apt sources for Desktop packages"329add_desktop_package_sources330331local apt_desktop_install_flags=""332if [[ ! -z ${DESKTOP_APT_FLAGS_SELECTED+x} ]]; then333for flag in ${DESKTOP_APT_FLAGS_SELECTED}; do334apt_desktop_install_flags+=" --install-${flag}"335done336else337# Myy : Using the previous default option, if the variable isn't defined338# And ONLY if it's not defined !339apt_desktop_install_flags+=" --no-install-recommends"340fi341342display_alert "Installing the desktop packages for" "Orange Pi" "info"343eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \344$apt_extra $apt_extra_progress install ${apt_desktop_install_flags} $PACKAGE_LIST_DESKTOP"' \345${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \346${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Installing Orange Pi desktop packages..." $TTY_Y $TTY_X'} \347${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'348349[[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi desktop packages for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} failed"350fi351352install_docker353[[ ${BOARDFAMILY} == "starfive2" ]] && jh7110_install_libs354[[ ${BOARDFAMILY} == "cix" ]] && install_cix_debs355356# Remove packages from packages.uninstall357358display_alert "Uninstall packages" "$PACKAGE_LIST_UNINSTALL" "info"359eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq \360$apt_extra $apt_extra_progress purge $PACKAGE_LIST_UNINSTALL"' \361${PROGRESS_LOG_TO_FILE:+' >> $DEST/${LOG_SUBPATH}/debootstrap.log'} \362${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Removing packages.uninstall packages..." $TTY_Y $TTY_X'} \363${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'364365[[ ${EVALPIPE[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi packages failed"366367# stage: purge residual packages368display_alert "Purging residual packages for" "Orange Pi" "info"369PURGINGPACKAGES=$(chroot $SDCARD /bin/bash -c "dpkg -l | grep \"^rc\" | awk '{print \$2}' | tr \"\n\" \" \"")370eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \371$apt_extra $apt_extra_progress remove --purge $PURGINGPACKAGES"' \372${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \373${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Purging residual Orange Pi packages..." $TTY_Y $TTY_X'} \374${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'375376[[ ${EVALPIPE[0]} -ne 0 ]] && exit_with_error "Purging of residual Orange Pi packages failed"377378# stage: remove downloaded packages379chroot $SDCARD /bin/bash -c "apt-get -y autoremove; apt-get clean"380381# DEBUG: print free space382local freespace=$(LC_ALL=C df -h)383echo $freespace >> $DEST/${LOG_SUBPATH}/debootstrap.log384display_alert "Free SD cache" "$(echo -e "$freespace" | grep $SDCARD | awk '{print $5}')" "info"385display_alert "Mount point" "$(echo -e "$freespace" | grep $MOUNT | head -1 | awk '{print $5}')" "info"386387# create list of installed packages for debug purposes388chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > ${cache_fname}.list 2>&1389390# creating xapian index that synaptic runs faster391if [[ $BUILD_DESKTOP == yes ]]; then392display_alert "Recreating Synaptic search index" "Please wait" "info"393chroot $SDCARD /bin/bash -c "[[ -f /usr/sbin/update-apt-xapian-index ]] && /usr/sbin/update-apt-xapian-index -u"394fi395396# this is needed for the build process later since resolvconf generated file in /run is not saved397rm $SDCARD/etc/resolv.conf398echo "nameserver $NAMESERVER" >> $SDCARD/etc/resolv.conf399400# stage: make rootfs cache archive401display_alert "Ending debootstrap process and preparing cache" "$RELEASE" "info"402sync403# the only reason to unmount here is compression progress display404# based on rootfs size calculation405umount_chroot "$SDCARD"406407tar cp --xattrs --directory=$SDCARD/ --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \408--exclude='./sys/*' --exclude='./home/*' --exclude='./root/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "$display_name" | lz4 -5 -c > $cache_fname409410# sign rootfs cache archive that it can be used for web cache once. Internal purposes411if [[ -n "${GPG_PASS}" && "${SUDO_USER}" ]]; then412[[ -n ${SUDO_USER} ]] && sudo chown -R ${SUDO_USER}:${SUDO_USER} "${DEST}"/images/413echo "${GPG_PASS}" | sudo -H -u ${SUDO_USER} bash -c "gpg --passphrase-fd 0 --armor --detach-sign --pinentry-mode loopback --batch --yes ${cache_fname}" || exit 1414fi415416# needed for backend to keep current only417touch $cache_fname.current418419fi420421# used for internal purposes. Faster rootfs cache rebuilding422if [[ -n "$ROOT_FS_CREATE_ONLY" ]]; then423umount --lazy "$SDCARD"424rm -rf $SDCARD425426display_alert "Rootfs build done" "@host" "info"427display_alert "Target directory" "${EXTER}/cache/rootfs" "info"428display_alert "File name" "${cache_name}" "info"429430# remove exit trap431trap - INT TERM EXIT432exit433fi434435mount_chroot "$SDCARD"436} #############################################################################437438# prepare_partitions439#440# creates image file, partitions and fs441# and mounts it to local dir442# FS-dependent stuff (boot and root fs partition types) happens here443#444prepare_partitions() {445display_alert "Preparing image file for rootfs" "$BOARD $RELEASE" "info"446447# possible partition combinations448# /boot: none, ext4, ext2, fat (BOOTFS_TYPE)449# root: ext4, btrfs, f2fs, nfs (ROOTFS_TYPE)450451# declare makes local variables by default if used inside a function452# NOTE: mountopts string should always start with comma if not empty453454# array copying in old bash versions is tricky, so having filesystems as arrays455# with attributes as keys is not a good idea456declare -A parttype mkopts mkopts_label mkfs mountopts457458parttype[ext4]=ext4459parttype[ext2]=ext2460parttype[fat]=fat16461parttype[f2fs]=ext4 # not a copy-paste error462parttype[btrfs]=btrfs463parttype[xfs]=xfs464# parttype[nfs] is empty465466# metadata_csum and 64bit may need to be disabled explicitly when migrating to newer supported host OS releases467if [[ $HOSTRELEASE =~ buster|bullseye|bookworm|bionic|focal|jammy|noble|kinetic|sid ]]; then468mkopts[ext4]="-q -m 2 -O ^64bit,^metadata_csum"469fi470# mkopts[fat] is empty471mkopts[ext2]='-q'472# mkopts[f2fs] is empty473mkopts[btrfs]='-m dup'474# mkopts[xfs] is empty475# mkopts[nfs] is empty476477mkopts_label[ext4]='-L '478mkopts_label[ext2]='-L '479mkopts_label[fat]='-n '480mkopts_label[f2fs]='-l '481mkopts_label[btrfs]='-L '482mkopts_label[xfs]='-L '483# mkopts_label[nfs] is empty484485mkfs[ext4]=ext4486mkfs[ext2]=ext2487mkfs[fat]=vfat488mkfs[f2fs]=f2fs489mkfs[btrfs]=btrfs490mkfs[xfs]=xfs491# mkfs[nfs] is empty492493mountopts[ext4]=',commit=600,errors=remount-ro'494# mountopts[ext2] is empty495# mountopts[fat] is empty496# mountopts[f2fs] is empty497mountopts[btrfs]=',commit=600'498# mountopts[xfs] is empty499# mountopts[nfs] is empty500501# default BOOTSIZE to use if not specified502DEFAULT_BOOTSIZE=1024 # MiB503# size of UEFI partition. 0 for no UEFI. Don't mix UEFISIZE>0 and BOOTSIZE>0504UEFISIZE=${UEFISIZE:-0}505BIOSSIZE=${BIOSSIZE:-0}506UEFI_MOUNT_POINT=${UEFI_MOUNT_POINT:-/boot/efi}507UEFI_FS_LABEL="${UEFI_FS_LABEL:-opi_efi}"508ROOT_FS_LABEL="${ROOT_FS_LABEL:-opi_root}"509BOOT_FS_LABEL="${BOOT_FS_LABEL:-opi_boot}"510511call_extension_method "pre_prepare_partitions" "prepare_partitions_custom" << 'PRE_PREPARE_PARTITIONS'512*allow custom options for mkfs*513Good time to change stuff like mkfs opts, types etc.514PRE_PREPARE_PARTITIONS515516# stage: determine partition configuration517local next=1518# Check if we need UEFI partition519if [[ $UEFISIZE -gt 0 ]]; then520# Check if we need BIOS partition521[[ $BIOSSIZE -gt 0 ]] && local biospart=$((next++))522local uefipart=$((next++))523fi524# Check if we need boot partition525if [[ -n $BOOTFS_TYPE || $ROOTFS_TYPE != ext4 || $CRYPTROOT_ENABLE == yes ]]; then526local bootpart=$((next++))527local bootfs=${BOOTFS_TYPE:-ext4}528[[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=${DEFAULT_BOOTSIZE}529else530BOOTSIZE=0531fi532# Check if we need root partition533[[ $ROOTFS_TYPE != nfs ]] &&534local rootpart=$((next++))535536# stage: calculate rootfs size537export rootfs_size=$(du -sm $SDCARD/ | cut -f1) # MiB538display_alert "Current rootfs size" "$rootfs_size MiB" "info"539540call_extension_method "prepare_image_size" "config_prepare_image_size" << 'PREPARE_IMAGE_SIZE'541*allow dynamically determining the size based on the $rootfs_size*542Called after `${rootfs_size}` is known, but before `${FIXED_IMAGE_SIZE}` is taken into account.543A good spot to determine `FIXED_IMAGE_SIZE` based on `rootfs_size`.544UEFISIZE can be set to 0 for no UEFI partition, or to a size in MiB to include one.545Last chance to set `USE_HOOK_FOR_PARTITION`=yes and then implement create_partition_table hook_point.546PREPARE_IMAGE_SIZE547548if [[ -n $FIXED_IMAGE_SIZE && $FIXED_IMAGE_SIZE =~ ^[0-9]+$ ]]; then549display_alert "Using user-defined image size" "$FIXED_IMAGE_SIZE MiB" "info"550local sdsize=$FIXED_IMAGE_SIZE551# basic sanity check552if [[ $ROOTFS_TYPE != nfs && $sdsize -lt $rootfs_size ]]; then553exit_with_error "User defined image size is too small" "$sdsize <= $rootfs_size"554fi555else556local imagesize=$(($rootfs_size + $OFFSET + $BOOTSIZE + $UEFISIZE + $EXTRA_ROOTFS_MIB_SIZE)) # MiB557# Hardcoded overhead +25% is needed for desktop images,558# for CLI it could be lower. Align the size up to 4MiB559if [[ $BUILD_DESKTOP == yes ]]; then560local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.35) / 1 + 0) / 4 + 1) * 4")561else562local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.30) / 1 + 0) / 4 + 1) * 4")563fi564fi565566# stage: create blank image567display_alert "Creating blank image for rootfs" "$sdsize MiB" "info"568if [[ $FAST_CREATE_IMAGE == yes ]]; then569truncate --size=${sdsize}M ${SDCARD}.raw # sometimes results in fs corruption, revert to previous know to work solution570sync571else572dd if=/dev/zero bs=1M status=none count=$sdsize | pv -p -b -r -s $(($sdsize * 1024 * 1024)) -N "[ .... ] dd" | dd status=none of=${SDCARD}.raw573fi574575# stage: create partition table576display_alert "Creating partitions" "${bootfs:+/boot: $bootfs }root: $ROOTFS_TYPE" "info"577if [[ "${USE_HOOK_FOR_PARTITION}" == "yes" ]]; then578{579[[ "$IMAGE_PARTITION_TABLE" == "msdos" ]] &&580echo "label: dos" ||581echo "label: $IMAGE_PARTITION_TABLE"582} | sfdisk ${SDCARD}.raw >> "${DEST}/${LOG_SUBPATH}/install.log" 2>&1 ||583exit_with_error "Create partition table fail. Please check" "${DEST}/${LOG_SUBPATH}/install.log"584585call_extension_method "create_partition_table" <<- 'CREATE_PARTITION_TABLE'586*only called when USE_HOOK_FOR_PARTITION=yes to create the complete partition table*587Finally, we can get our own partition table. You have to partition ${SDCARD}.raw588yourself. Good luck.589CREATE_PARTITION_TABLE590else591{592[[ "$IMAGE_PARTITION_TABLE" == "msdos" ]] &&593echo "label: dos" ||594echo "label: $IMAGE_PARTITION_TABLE"595596local next=$OFFSET597if [[ -n "$biospart" ]]; then598# gpt: BIOS boot599local type="21686148-6449-6E6F-744E-656564454649"600echo "$biospart : name=\"bios\", start=${next}MiB, size=${BIOSSIZE}MiB, type=${type}"601local next=$(($next + $BIOSSIZE))602fi603if [[ -n "$uefipart" ]]; then604# dos: EFI (FAT-12/16/32)605# gpt: EFI System606[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&607local type="ef" ||608local type="C12A7328-F81F-11D2-BA4B-00A0C93EC93B"609echo "$uefipart : name=\"efi\", start=${next}MiB, size=${UEFISIZE}MiB, type=${type}"610local next=$(($next + $UEFISIZE))611fi612if [[ -n "$bootpart" ]]; then613# Linux extended boot614[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&615local type="ea" ||616local type="BC13C2FF-59E6-4262-A352-B275FD6F7172"617if [[ -n "$rootpart" ]]; then618echo "$bootpart : name=\"bootfs\", start=${next}MiB, size=${BOOTSIZE}MiB, type=${type}"619local next=$(($next + $BOOTSIZE))620else621# no `size` argument mean "as much as possible"622echo "$bootpart : name=\"bootfs\", start=${next}MiB, type=${type}"623fi624fi625if [[ -n "$rootpart" ]]; then626# dos: Linux627# gpt: Linux filesystem628[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&629local type="83" ||630local type="0FC63DAF-8483-4772-8E79-3D69D8477DE4"631# no `size` argument mean "as much as possible"632echo "$rootpart : name=\"rootfs\", start=${next}MiB, type=${type}"633fi634} | sfdisk ${SDCARD}.raw >> "${DEST}/${LOG_SUBPATH}/install.log" 2>&1 ||635exit_with_error "Partition fail. Please check" "${DEST}/${LOG_SUBPATH}/install.log"636fi637638call_extension_method "post_create_partitions" <<- 'POST_CREATE_PARTITIONS'639*called after all partitions are created, but not yet formatted*640POST_CREATE_PARTITIONS641642# stage: mount image643# lock access to loop devices644exec {FD}> /var/lock/orangepi-debootstrap-losetup645flock -x $FD646647LOOP=$(losetup -f)648[[ -z $LOOP ]] && exit_with_error "Unable to find free loop device"649650check_loop_device "$LOOP"651652losetup -P $LOOP ${SDCARD}.raw653654# loop device was grabbed here, unlock655flock -u $FD656657# stage: create fs, mount partitions, create fstab658rm -f $SDCARD/etc/fstab659if [[ -n $rootpart ]]; then660local rootdevice="${LOOP}p${rootpart}"661662if [[ $CRYPTROOT_ENABLE == yes ]]; then663display_alert "Encrypting root partition with LUKS..." "cryptsetup luksFormat $rootdevice" ""664echo -n $CRYPTROOT_PASSPHRASE | cryptsetup luksFormat $CRYPTROOT_PARAMETERS $rootdevice -665echo -n $CRYPTROOT_PASSPHRASE | cryptsetup luksOpen $rootdevice $ROOT_MAPPER -666display_alert "Root partition encryption complete." "" "ext"667# TODO: pass /dev/mapper to Docker668rootdevice=/dev/mapper/$ROOT_MAPPER # used by `mkfs` and `mount` commands669fi670671check_loop_device "$rootdevice"672display_alert "Creating rootfs" "$ROOTFS_TYPE on $rootdevice"673mkfs.${mkfs[$ROOTFS_TYPE]} ${mkopts[$ROOTFS_TYPE]} ${mkopts_label[$ROOTFS_TYPE]:+${mkopts_label[$ROOTFS_TYPE]}"$ROOT_FS_LABEL"} $rootdevice >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1674[[ $ROOTFS_TYPE == ext4 ]] && tune2fs -o journal_data_writeback $rootdevice > /dev/null675if [[ $ROOTFS_TYPE == btrfs && $BTRFS_COMPRESSION != none ]]; then676local fscreateopt="-o compress-force=${BTRFS_COMPRESSION}"677fi678mount ${fscreateopt} $rootdevice $MOUNT/679# create fstab (and crypttab) entry680if [[ $CRYPTROOT_ENABLE == yes ]]; then681# map the LUKS container partition via its UUID to be the 'cryptroot' device682echo "$ROOT_MAPPER UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart}) none luks" >> $SDCARD/etc/crypttab683local rootfs=$rootdevice # used in fstab684else685local rootfs="UUID=$(blkid -s UUID -o value $rootdevice)"686fi687echo "$rootfs / ${mkfs[$ROOTFS_TYPE]} defaults,noatime${mountopts[$ROOTFS_TYPE]} 0 1" >> $SDCARD/etc/fstab688else689# update_initramfs will fail if /lib/modules/ doesn't exist690mount --bind --make-private $SDCARD $MOUNT/691echo "/dev/nfs / nfs defaults 0 0" >> $SDCARD/etc/fstab692fi693if [[ -n $bootpart ]]; then694display_alert "Creating /boot" "$bootfs on ${LOOP}p${bootpart}"695check_loop_device "${LOOP}p${bootpart}"696mkfs.${mkfs[$bootfs]} ${mkopts[$bootfs]} ${mkopts_label[$bootfs]:+${mkopts_label[$bootfs]}"$BOOT_FS_LABEL"} ${LOOP}p${bootpart} >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1697mkdir -p $MOUNT/boot/698mount ${LOOP}p${bootpart} $MOUNT/boot/699echo "UUID=$(blkid -s UUID -o value ${LOOP}p${bootpart}) /boot ${mkfs[$bootfs]} defaults${mountopts[$bootfs]} 0 2" >> $SDCARD/etc/fstab700fi701if [[ -n $uefipart ]]; then702display_alert "Creating EFI partition" "FAT32 ${UEFI_MOUNT_POINT} on ${LOOP}p${uefipart} label ${UEFI_FS_LABEL}"703check_loop_device "${LOOP}p${uefipart}"704mkfs.fat -F32 -n "${UEFI_FS_LABEL}" ${LOOP}p${uefipart} >> "${DEST}"/debug/install.log 2>&1705mkdir -p "${MOUNT}${UEFI_MOUNT_POINT}"706mount ${LOOP}p${uefipart} "${MOUNT}${UEFI_MOUNT_POINT}"707echo "UUID=$(blkid -s UUID -o value ${LOOP}p${uefipart}) ${UEFI_MOUNT_POINT} vfat defaults 0 2" >> $SDCARD/etc/fstab708fi709echo "tmpfs /tmp tmpfs defaults,nosuid 0 0" >> $SDCARD/etc/fstab710711call_extension_method "format_partitions" <<- 'FORMAT_PARTITIONS'712*if you created your own partitions, this would be a good time to format them*713The loop device is mounted, so ${LOOP}p1 is it's first partition etc.714FORMAT_PARTITIONS715716# stage: adjust boot script or boot environment717if [[ -f $SDCARD/boot/orangepiEnv.txt ]]; then718if [[ $CRYPTROOT_ENABLE == yes ]]; then719echo "rootdev=$rootdevice cryptdevice=UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart}):$ROOT_MAPPER" >> $SDCARD/boot/orangepiEnv.txt720else721echo "rootdev=$rootfs" >> $SDCARD/boot/orangepiEnv.txt722fi723echo "rootfstype=$ROOTFS_TYPE" >> $SDCARD/boot/orangepiEnv.txt724elif [[ $rootpart != 1 ]] && [[ $SRC_EXTLINUX != yes ]]; then725local bootscript_dst=${BOOTSCRIPT##*:}726sed -i 's/mmcblk0p1/mmcblk0p2/' $SDCARD/boot/$bootscript_dst727sed -i -e "s/rootfstype=ext4/rootfstype=$ROOTFS_TYPE/" \728-e "s/rootfstype \"ext4\"/rootfstype \"$ROOTFS_TYPE\"/" $SDCARD/boot/$bootscript_dst729fi730731# if we have boot.ini = remove orangepiEnv.txt and add UUID there if enabled732if [[ -f $SDCARD/boot/boot.ini ]]; then733sed -i -e "s/rootfstype \"ext4\"/rootfstype \"$ROOTFS_TYPE\"/" $SDCARD/boot/boot.ini734if [[ $CRYPTROOT_ENABLE == yes ]]; then735local rootpart="UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart})"736sed -i 's/^setenv rootdev .*/setenv rootdev "\/dev\/mapper\/'$ROOT_MAPPER' cryptdevice='$rootpart':'$ROOT_MAPPER'"/' $SDCARD/boot/boot.ini737else738sed -i 's/^setenv rootdev .*/setenv rootdev "'$rootfs'"/' $SDCARD/boot/boot.ini739fi740if [[ $LINUXFAMILY != meson64 ]]; then741[[ -f $SDCARD/boot/orangepiEnv.txt ]] && rm $SDCARD/boot/orangepiEnv.txt742fi743fi744745# if we have a headless device, set console to DEFAULT_CONSOLE746if [[ -n $DEFAULT_CONSOLE && -f $SDCARD/boot/orangepiEnv.txt ]]; then747if grep -lq "^console=" $SDCARD/boot/orangepiEnv.txt; then748sed -i "s/^console=.*/console=$DEFAULT_CONSOLE/" $SDCARD/boot/orangepiEnv.txt749else750echo "console=$DEFAULT_CONSOLE" >> $SDCARD/boot/orangepiEnv.txt751fi752fi753754# recompile .cmd to .scr if boot.cmd exists755756if [[ -f $SDCARD/boot/boot.cmd ]]; then757if [ -z $BOOTSCRIPT_OUTPUT ]; then BOOTSCRIPT_OUTPUT=boot.scr; fi758mkimage -C none -A arm -T script -d $SDCARD/boot/boot.cmd $SDCARD/boot/$BOOTSCRIPT_OUTPUT > /dev/null 2>&1759fi760761# create extlinux config762if [[ -f $SDCARD/boot/extlinux/extlinux.conf ]]; then763echo " append root=$rootfs $SRC_CMDLINE $MAIN_CMDLINE" >> $SDCARD/boot/extlinux/extlinux.conf764[[ -f $SDCARD/boot/orangepiEnv.txt ]] && rm $SDCARD/boot/orangepiEnv.txt765fi766767}768769# update_initramfs770#771# this should be invoked as late as possible for any modifications by772# customize_image (userpatches) and prepare_partitions to be reflected in the773# final initramfs774#775# especially, this needs to be invoked after /etc/crypttab has been created776# for cryptroot-unlock to work:777# https://serverfault.com/questions/907254/cryproot-unlock-with-dropbear-timeout-while-waiting-for-askpass778#779# since Debian buster, it has to be called within create_image() on the $MOUNT780# path instead of $SDCARD (which can be a tmpfs and breaks cryptsetup-initramfs).781#782update_initramfs()783{784local chroot_target=$1785local target_dir=$(786find ${chroot_target}/lib/modules/ -maxdepth 1 -type d -name "*${VER}*"787)788if [ "$target_dir" != "" ]; then789update_initramfs_cmd="update-initramfs -uv -k $(basename $target_dir)"790else791exit_with_error "No kernel installed for the version" "${VER}"792fi793display_alert "Updating initramfs..." "$update_initramfs_cmd" ""794cp /usr/bin/$QEMU_BINARY $chroot_target/usr/bin/795mount_chroot "$chroot_target/"796797chroot $chroot_target /bin/bash -c "$update_initramfs_cmd" >> $DEST/${LOG_SUBPATH}/install.log 2>&1 || {798display_alert "Updating initramfs FAILED, see:" "$DEST/${LOG_SUBPATH}/install.log" "err"799exit 23800}801display_alert "Updated initramfs." "for details see: $DEST/${LOG_SUBPATH}/install.log" "info"802803display_alert "Re-enabling" "initramfs-tools hook for kernel"804chroot $chroot_target /bin/bash -c "chmod -v +x /etc/kernel/postinst.d/initramfs-tools" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1805806umount_chroot "$chroot_target/"807rm $chroot_target/usr/bin/$QEMU_BINARY808809} #############################################################################810811# create_image812#813# finishes creation of image from cached rootfs814#815create_image()816{817# stage: create file name818if [[ $SELECTED_CONFIGURATION == "cli_standard" ]]; then819IMAGE_TYPE=server820elif [[ $SELECTED_CONFIGURATION == "cli_minimal" ]]; then821IMAGE_TYPE=minimal822else823IMAGE_TYPE=desktop824fi825826local version="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")"827828if [[ ${RELEASE} == "raspi" ]]; then829local version="${BOARD^}_${REVISION}_raspios_bullseye_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")"830fi831832[[ $ROOTFS_TYPE == nfs ]] && version=${version}_nfsboot833834destimg=$DEST/images/${version}835rm -rf $destimg836mkdir -p $destimg837838if [[ $ROOTFS_TYPE != nfs ]]; then839display_alert "Copying files to" "/"840echo -e "\nCopying files to [/]" >>"${DEST}"/${LOG_SUBPATH}/install.log841rsync -aHWXh \842--exclude="/boot/*" \843--exclude="/dev/*" \844--exclude="/proc/*" \845--exclude="/run/*" \846--exclude="/tmp/*" \847--exclude="/sys/*" \848--info=progress0,stats1 $SDCARD/ $MOUNT/ >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1849else850display_alert "Creating rootfs archive" "rootfs.tgz" "info"851tar cp --xattrs --directory=$SDCARD/ --exclude='./boot/*' --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \852--exclude='./sys/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "rootfs.tgz" | gzip -c > $destimg/${version}-rootfs.tgz853fi854855# stage: rsync /boot856display_alert "Copying files to" "/boot"857echo -e "\nCopying files to [/boot]" >>"${DEST}"/${LOG_SUBPATH}/install.log858if [[ $(findmnt --target $MOUNT/boot -o FSTYPE -n) == vfat ]]; then859# fat32860rsync -rLtWh \861--info=progress0,stats1 \862--log-file="${DEST}"/${LOG_SUBPATH}/install.log $SDCARD/boot $MOUNT >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1863else864# ext4865rsync -aHWXh \866--info=progress0,stats1 \867--log-file="${DEST}"/${LOG_SUBPATH}/install.log $SDCARD/boot $MOUNT >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1868fi869870call_extension_method "pre_update_initramfs" "config_pre_update_initramfs" << 'PRE_UPDATE_INITRAMFS'871*allow config to hack into the initramfs create process*872Called after rsync has synced both `/root` and `/root` on the target, but before calling `update_initramfs`.873PRE_UPDATE_INITRAMFS874875# stage: create final initramfs876[[ -n $KERNELSOURCE ]] && {877update_initramfs $MOUNT878}879880# DEBUG: print free space881local freespace=$(LC_ALL=C df -h)882echo $freespace >> $DEST/${LOG_SUBPATH}/debootstrap.log883display_alert "Free SD cache" "$(echo -e "$freespace" | grep $SDCARD | awk '{print $5}')" "info"884display_alert "Mount point" "$(echo -e "$freespace" | grep $MOUNT | head -1 | awk '{print $5}')" "info"885886# stage: write u-boot887write_uboot $LOOP888889# fix wrong / permissions890chmod 755 $MOUNT891892call_extension_method "pre_umount_final_image" "config_pre_umount_final_image" << 'PRE_UMOUNT_FINAL_IMAGE'893*allow config to hack into the image before the unmount*894Called before unmounting both `/root` and `/boot`.895PRE_UMOUNT_FINAL_IMAGE896897# unmount /boot/efi first, then /boot, rootfs third, image file last898sync899[[ $UEFISIZE != 0 ]] && umount -l "${MOUNT}${UEFI_MOUNT_POINT}"900[[ $BOOTSIZE != 0 ]] && umount -l $MOUNT/boot901[[ $ROOTFS_TYPE != nfs ]] && umount -l $MOUNT902[[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose $ROOT_MAPPER903904call_extension_method "post_umount_final_image" "config_post_umount_final_image" << 'POST_UMOUNT_FINAL_IMAGE'905*allow config to hack into the image after the unmount*906Called after unmounting both `/root` and `/boot`.907POST_UMOUNT_FINAL_IMAGE908909# to make sure its unmounted910while grep -Eq '(${MOUNT}|${DESTIMG})' /proc/mounts911do912display_alert "Wait for unmount" "${MOUNT}" "info"913sleep 5914done915916losetup -d $LOOP917rm -rf --one-file-system $DESTIMG $MOUNT918919mkdir -p $DESTIMG920mv ${SDCARD}.raw $DESTIMG/${version}.img921922FINALDEST=${destimg}923924# custom post_build_image_modify hook to run before fingerprinting and compression925[[ $(type -t post_build_image_modify) == function ]] && display_alert "Custom Hook Detected" "post_build_image_modify" "info" && post_build_image_modify "${DESTIMG}/${version}.img"926927if [[ $BUILD_ALL != yes ]]; then928929if [[ $COMPRESS_OUTPUTIMAGE == "" || $COMPRESS_OUTPUTIMAGE == no ]]; then930COMPRESS_OUTPUTIMAGE="sha,gpg,img"931elif [[ $COMPRESS_OUTPUTIMAGE == yes ]]; then932COMPRESS_OUTPUTIMAGE="sha,gpg,7z"933fi934935if [[ $COMPRESS_OUTPUTIMAGE == *gz* ]]; then936display_alert "Compressing" "${DESTIMG}/${version}.img.gz" "info"937pigz -3 < $DESTIMG/${version}.img > $DESTIMG/${version}.img.gz938compression_type=".gz"939fi940941if [[ $COMPRESS_OUTPUTIMAGE == *xz* ]]; then942display_alert "Compressing" "${DESTIMG}/${version}.img.xz" "info"943# compressing consumes a lot of memory we don't have. Waiting for previous packing job to finish helps to run a lot more builds in parallel944available_cpu=$(grep -c 'processor' /proc/cpuinfo)945[[ ${BUILD_ALL} == yes ]] && available_cpu=$(( $available_cpu * 30 / 100 )) # lets use 20% of resources in case of build-all946[[ ${available_cpu} -gt 8 ]] && available_cpu=8 # using more cpu cores for compressing is pointless947available_mem=$(LC_ALL=c free | grep Mem | awk '{print $4/$2 * 100.0}' | awk '{print int($1)}') # in percentage948# build optimisations when memory drops below 5%949if [[ ${BUILD_ALL} == yes && ( ${available_mem} -lt 15 || $(ps -uax | grep "pixz" | wc -l) -gt 4 )]]; then950while [[ $(ps -uax | grep "pixz" | wc -l) -gt 2 ]]951do echo -en "#"952sleep 20953done954fi955pixz -7 -p ${available_cpu} -f $(expr ${available_cpu} + 2) < $DESTIMG/${version}.img > ${DESTIMG}/${version}.img.xz956compression_type=".xz"957fi958959if [[ $COMPRESS_OUTPUTIMAGE == *img* || $COMPRESS_OUTPUTIMAGE == *7z* ]]; then960# mv $DESTIMG/${version}.img ${FINALDEST}/${version}.img || exit 1961compression_type=""962fi963964if [[ $COMPRESS_OUTPUTIMAGE == *sha* ]]; then965cd ${DESTIMG}966display_alert "SHA256 calculating" "${version}.img${compression_type}" "info"967sha256sum -b ${version}.img${compression_type} > ${version}.img${compression_type}.sha968fi969970if [[ $COMPRESS_OUTPUTIMAGE == *gpg* ]]; then971cd ${DESTIMG}972if [[ -n $GPG_PASS ]]; then973display_alert "GPG signing" "${version}.img${compression_type}" "info"974[[ -n ${SUDO_USER} ]] && sudo chown -R ${SUDO_USER}:${SUDO_USER} "${DESTIMG}"/975echo "${GPG_PASS}" | sudo -H -u ${SUDO_USER} bash -c "gpg --passphrase-fd 0 --armor --detach-sign --pinentry-mode loopback --batch --yes ${DESTIMG}/${version}.img${compression_type}" || exit 1976#else977# display_alert "GPG signing skipped - no GPG_PASS" "${version}.img" "wrn"978fi979fi980981#fingerprint_image "${DESTIMG}/${version}.img${compression_type}.txt" "${version}"982983if [[ $COMPRESS_OUTPUTIMAGE == *7z* ]]; then984display_alert "Compressing" "${DESTIMG}/${version}.7z" "info"9857za a -t7z -bd -m0=lzma2 -mx=3 -mfb=64 -md=32m -ms=on \986${DESTIMG}/${version}.7z ${version}.key ${version}.img* >/dev/null 2>&1987find ${DESTIMG}/ -type \988f \( -name "${version}.img" -o -name "${version}.img.asc" -o -name "${version}.img.txt" -o -name "${version}.img.sha" \) -print0 \989>/dev/null 2>&1990fi991992fi993#display_alert "Done building" "${DESTIMG}/${version}.img" "info"994display_alert "Done building" "${FINALDEST}/${version}.img" "info"995996# call custom post build hook997[[ $(type -t post_build_image) == function ]] && post_build_image "${DESTIMG}/${version}.img"998999# move artefacts from temporally directory to its final destination1000[[ -n $compression_type ]] && rm $DESTIMG/${version}.img1001mv $DESTIMG/${version}* ${FINALDEST}1002rm -rf $DESTIMG10031004# write image to SD card1005if [[ $(lsblk "$CARD_DEVICE" 2>/dev/null) && -f ${FINALDEST}/${version}.img ]]; then10061007# make sha256sum if it does not exists. we need it for comparisson1008if [[ -f "${FINALDEST}/${version}".img.sha ]]; then1009local ifsha=$(cat ${FINALDEST}/${version}.img.sha | awk '{print $1}')1010else1011local ifsha=$(sha256sum -b "${FINALDEST}/${version}".img | awk '{print $1}')1012fi10131014display_alert "Writing image" "$CARD_DEVICE ${readsha}" "info"10151016# write to SD card1017pv -p -b -r -c -N "[ .... ] dd" ${FINALDEST}/${version}.img | dd of=$CARD_DEVICE bs=1M iflag=fullblock oflag=direct status=none10181019call_extension_method "post_write_sdcard" <<- 'POST_BUILD_IMAGE'1020*run after writing img to sdcard*1021After the image is written to `$CARD_DEVICE`, but before verifying it.1022You can still set SKIP_VERIFY=yes to skip verification.1023POST_BUILD_IMAGE10241025if [[ "${SKIP_VERIFY}" != "yes" ]]; then1026# read and compare1027display_alert "Verifying. Please wait!"1028local ofsha=$(dd if=$CARD_DEVICE count=$(du -b ${FINALDEST}/${version}.img | cut -f1) status=none iflag=count_bytes oflag=direct | sha256sum | awk '{print $1}')1029if [[ $ifsha == $ofsha ]]; then1030display_alert "Writing verified" "${version}.img" "info"1031else1032display_alert "Writing failed" "${version}.img" "err"1033fi1034fi1035elif [[ `systemd-detect-virt` == 'docker' && -n $CARD_DEVICE ]]; then1036# display warning when we want to write sd card under Docker1037display_alert "Can't write to $CARD_DEVICE" "Enable docker privileged mode in config-docker.conf" "wrn"1038fi10391040} #############################################################################104110421043