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