1#!/bin/sh 2# 3# $FreeBSD$ 4# 5# 6# Common functions for virtual machine image build scripts. 7# 8 9scriptdir=$(dirname $(realpath $0)) 10. ${scriptdir}/../../tools/boot/install-boot.sh 11 12export PATH="/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin" 13trap "cleanup" INT QUIT TRAP ABRT TERM 14 15write_partition_layout() { 16 if [ -z "${NOSWAP}" ]; then 17 SWAPOPT="-p freebsd-swap/swapfs::${SWAPSIZE}" 18 fi 19 20 BOOTFILES="$(env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ 21 WITH_UNIFIED_OBJDIR=yes \ 22 make -C ${WORLDDIR}/stand -V .OBJDIR)" 23 BOOTFILES="$(realpath ${BOOTFILES})" 24 25 case "${TARGET}:${TARGET_ARCH}" in 26 amd64:amd64 | i386:i386) 27 ESP=yes 28 SCHEME=gpt 29 BOOTPARTS="-b ${BOOTFILES}/i386/pmbr/pmbr \ 30 -p freebsd-boot/bootfs:=${BOOTFILES}/i386/gptboot/gptboot" 31 ROOTFSPART="-p freebsd-ufs/rootfs:=${VMBASE}" 32 ;; 33 arm64:aarch64 | riscv:riscv64*) 34 ESP=yes 35 SCHEME=gpt 36 BOOTPARTS= 37 ROOTFSPART="-p freebsd-ufs/rootfs:=${VMBASE}" 38 ;; 39 powerpc:powerpc*) 40 ESP=no 41 SCHEME=apm 42 BOOTPARTS="-p apple-boot/bootfs:=${BOOTFILES}/powerpc/boot1.chrp/boot1.hfs" 43 ROOTFSPART="-p freebsd-ufs/rootfs:=${VMBASE}" 44 ;; 45 *) 46 echo "vmimage.subr: unsupported target '${TARGET}:${TARGET_ARCH}'" >&2 47 exit 1 48 ;; 49 esac 50 51 if [ ${ESP} = "yes" ]; then 52 # Create an ESP 53 espfilename=$(mktemp /tmp/efiboot.XXXXXX) 54 make_esp_file ${espfilename} ${fat32min} ${BOOTFILES}/efi/loader_lua/loader_lua.efi 55 BOOTPARTS="${BOOTPARTS} -p efi:=${espfilename}" 56 fi 57 58 mkimg -s ${SCHEME} -f ${VMFORMAT} \ 59 ${BOOTPARTS} \ 60 ${SWAPOPT} \ 61 ${ROOTFSPART} \ 62 -o ${VMIMAGE} 63 64 if [ ${ESP} = "yes" ]; then 65 rm ${espfilename} 66 fi 67 68 return 0 69} 70 71err() { 72 printf "${@}\n" 73 cleanup 74 return 1 75} 76 77cleanup() { 78 if [ -c "${DESTDIR}/dev/null" ]; then 79 umount_loop ${DESTDIR}/dev 2>/dev/null 80 fi 81 umount_loop ${DESTDIR} 82 if [ ! -z "${mddev}" ]; then 83 mdconfig -d -u ${mddev} 84 fi 85 86 return 0 87} 88 89vm_create_base() { 90 # Creates the UFS root filesystem for the virtual machine disk, 91 # written to the formatted disk image with mkimg(1). 92 93 mkdir -p ${DESTDIR} 94 truncate -s ${VMSIZE} ${VMBASE} 95 mddev=$(mdconfig -f ${VMBASE}) 96 newfs -L rootfs /dev/${mddev} 97 mount /dev/${mddev} ${DESTDIR} 98 99 return 0 100} 101 102vm_copy_base() { 103 # Creates a new UFS root filesystem and copies the contents of the 104 # current root filesystem into it. This produces a "clean" disk 105 # image without any remnants of files which were created temporarily 106 # during image-creation and have since been deleted (e.g., downloaded 107 # package archives). 108 109 mkdir -p ${DESTDIR}/old 110 mdold=$(mdconfig -f ${VMBASE}) 111 mount /dev/${mdold} ${DESTDIR}/old 112 113 truncate -s ${VMSIZE} ${VMBASE}.tmp 114 mkdir -p ${DESTDIR}/new 115 mdnew=$(mdconfig -f ${VMBASE}.tmp) 116 newfs -L rootfs /dev/${mdnew} 117 mount /dev/${mdnew} ${DESTDIR}/new 118 119 tar -cf- -C ${DESTDIR}/old . | tar -xUf- -C ${DESTDIR}/new 120 121 umount_loop /dev/${mdold} 122 rmdir ${DESTDIR}/old 123 mdconfig -d -u ${mdold} 124 125 umount_loop /dev/${mdnew} 126 rmdir ${DESTDIR}/new 127 tunefs -n enable /dev/${mdnew} 128 mdconfig -d -u ${mdnew} 129 mv ${VMBASE}.tmp ${VMBASE} 130} 131 132vm_install_base() { 133 # Installs the FreeBSD userland/kernel to the virtual machine disk. 134 135 cd ${WORLDDIR} && \ 136 make DESTDIR=${DESTDIR} \ 137 installworld installkernel distribution || \ 138 err "\n\nCannot install the base system to ${DESTDIR}." 139 140 # Bootstrap etcupdate(8) and mergemaster(8) databases. 141 mkdir -p ${DESTDIR}/var/db/etcupdate 142 etcupdate extract -B \ 143 -M "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \ 144 -s ${WORLDDIR} -d ${DESTDIR}/var/db/etcupdate 145 sh ${WORLDDIR}/release/scripts/mm-mtree.sh -m ${WORLDDIR} \ 146 -F "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \ 147 -D ${DESTDIR} 148 149 echo '# Custom /etc/fstab for FreeBSD VM images' \ 150 > ${DESTDIR}/etc/fstab 151 echo "/dev/${ROOTLABEL}/rootfs / ufs rw 1 1" \ 152 >> ${DESTDIR}/etc/fstab 153 if [ -z "${NOSWAP}" ]; then 154 echo '/dev/gpt/swapfs none swap sw 0 0' \ 155 >> ${DESTDIR}/etc/fstab 156 fi 157 158 local hostname 159 hostname="$(echo $(uname -o) | tr '[:upper:]' '[:lower:]')" 160 echo "hostname=\"${hostname}\"" >> ${DESTDIR}/etc/rc.conf 161 162 if ! [ -z "${QEMUSTATIC}" ]; then 163 export EMULATOR=/qemu 164 cp ${QEMUSTATIC} ${DESTDIR}/${EMULATOR} 165 fi 166 167 mkdir -p ${DESTDIR}/dev 168 mount -t devfs devfs ${DESTDIR}/dev 169 chroot ${DESTDIR} ${EMULATOR} /usr/bin/newaliases 170 chroot ${DESTDIR} ${EMULATOR} /bin/sh /etc/rc.d/ldconfig forcestart 171 umount_loop ${DESTDIR}/dev 172 173 cp /etc/resolv.conf ${DESTDIR}/etc/resolv.conf 174 175 return 0 176} 177 178vm_extra_install_base() { 179 # Prototype. When overridden, runs extra post-installworld commands 180 # as needed, based on the target virtual machine image or cloud 181 # provider image target. 182 183 return 0 184} 185 186vm_extra_enable_services() { 187 if [ ! -z "${VM_RC_LIST}" ]; then 188 for _rcvar in ${VM_RC_LIST}; do 189 echo ${_rcvar}_enable="YES" >> ${DESTDIR}/etc/rc.conf 190 done 191 fi 192 193 if [ -z "${VMCONFIG}" -o -c "${VMCONFIG}" ]; then 194 echo 'ifconfig_DEFAULT="DHCP inet6 accept_rtadv"' >> \ 195 ${DESTDIR}/etc/rc.conf 196 # Expand the filesystem to fill the disk. 197 echo 'growfs_enable="YES"' >> ${DESTDIR}/etc/rc.conf 198 touch ${DESTDIR}/firstboot 199 fi 200 201 return 0 202} 203 204vm_extra_install_packages() { 205 if [ -z "${VM_EXTRA_PACKAGES}" ]; then 206 return 0 207 fi 208 mkdir -p ${DESTDIR}/dev 209 mount -t devfs devfs ${DESTDIR}/dev 210 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 211 /usr/sbin/pkg bootstrap -y 212 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 213 /usr/sbin/pkg install -y ${VM_EXTRA_PACKAGES} 214 umount_loop ${DESTDIR}/dev 215 216 return 0 217} 218 219vm_extra_install_ports() { 220 # Prototype. When overridden, installs additional ports within the 221 # virtual machine environment. 222 223 return 0 224} 225 226vm_extra_pre_umount() { 227 # Prototype. When overridden, performs additional tasks within the 228 # virtual machine environment prior to unmounting the filesystem. 229 # Note: When overriding this function, removing resolv.conf in the 230 # disk image must be included. 231 232 if ! [ -z "${QEMUSTATIC}" ]; then 233 rm -f ${DESTDIR}/${EMULATOR} 234 fi 235 rm -f ${DESTDIR}/etc/resolv.conf 236 return 0 237} 238 239vm_extra_pkg_rmcache() { 240 if [ -e ${DESTDIR}/usr/local/sbin/pkg ]; then 241 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 242 /usr/local/sbin/pkg clean -y -a 243 fi 244 245 return 0 246} 247 248umount_loop() { 249 DIR=$1 250 i=0 251 sync 252 while ! umount ${DIR}; do 253 i=$(( $i + 1 )) 254 if [ $i -ge 10 ]; then 255 # This should never happen. But, it has happened. 256 echo "Cannot umount(8) ${DIR}" 257 echo "Something has gone horribly wrong." 258 return 1 259 fi 260 sleep 1 261 done 262 263 return 0 264} 265 266vm_create_disk() { 267 echo "Creating image... Please wait." 268 echo 269 270 write_partition_layout || return 1 271 272 return 0 273} 274 275vm_extra_create_disk() { 276 277 return 0 278} 279 280