1#!/bin/sh 2# 3# 4# 5# Common functions for virtual machine image build scripts. 6# 7 8scriptdir=$(dirname $(realpath $0)) 9. ${scriptdir}/../scripts/tools.subr 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 15# Platform-specific large-scale setup 16# Most platforms use GPT, so put that as default, then special cases 17PARTSCHEME=gpt 18ROOTLABEL="gpt" 19case "${TARGET}:${TARGET_ARCH}" in 20 powerpc:powerpc*) 21 PARTSCHEME=mbr 22 ROOTLABEL="ufs" 23 NOSWAP=yes # Can't label swap partition with MBR, so no swap 24 ;; 25esac 26 27err() { 28 printf "${@}\n" 29 cleanup 30 return 1 31} 32 33cleanup() { 34 if [ -c "${DESTDIR}/dev/null" ]; then 35 umount_loop ${DESTDIR}/dev 2>/dev/null 36 fi 37 38 return 0 39} 40 41metalog_add_data() { 42 local file mode type 43 44 if [ -n "${NO_ROOT}" ]; then 45 file=$1 46 if [ -f ${DESTDIR}/${file} ]; then 47 type=file 48 mode=${2:-0644} 49 elif [ -d ${DESTDIR}/${file} ]; then 50 type=dir 51 mode=${2:-0755} 52 else 53 echo "metalog_add_data: ${file} not found" >&2 54 return 1 55 fi 56 echo "${file} type=${type} uname=root gname=wheel mode=${mode}" >> \ 57 ${DESTDIR}/METALOG 58 fi 59} 60 61vm_create_base() { 62 63 mkdir -p ${DESTDIR} 64 65 return 0 66} 67 68vm_copy_base() { 69 # Defunct 70 return 0 71} 72 73vm_install_base() { 74 # Installs the FreeBSD userland/kernel to the virtual machine disk. 75 76 cd ${WORLDDIR} && \ 77 make DESTDIR=${DESTDIR} ${INSTALLOPTS} \ 78 installworld installkernel distribution || \ 79 err "\n\nCannot install the base system to ${DESTDIR}." 80 81 # Bootstrap etcupdate(8) database. 82 mkdir -p ${DESTDIR}/var/db/etcupdate 83 etcupdate extract -B \ 84 -M "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \ 85 -s ${WORLDDIR} -d ${DESTDIR}/var/db/etcupdate \ 86 -L /dev/stdout ${NO_ROOT:+-N} 87 if [ -n "${NO_ROOT}" ]; then 88 # Reroot etcupdate's internal METALOG to the whole tree 89 sed -n 's,^\.,./var/db/etcupdate/current,p' \ 90 ${DESTDIR}/var/db/etcupdate/current/METALOG | \ 91 env -i LC_COLLATE=C sort >> ${DESTDIR}/METALOG 92 rm ${DESTDIR}/var/db/etcupdate/current/METALOG 93 fi 94 95 echo '# Custom /etc/fstab for FreeBSD VM images' \ 96 > ${DESTDIR}/etc/fstab 97 if [ "${VMFS}" != zfs ]; then 98 echo "/dev/${ROOTLABEL}/rootfs / ${VMFS} rw,noatime 1 1" \ 99 >> ${DESTDIR}/etc/fstab 100 fi 101 if [ -z "${NOSWAP}" ]; then 102 echo '/dev/gpt/swapfs none swap sw 0 0' \ 103 >> ${DESTDIR}/etc/fstab 104 fi 105 metalog_add_data ./etc/fstab 106 107 local hostname 108 hostname="$(echo $(uname -o) | tr '[:upper:]' '[:lower:]')" 109 echo "hostname=\"${hostname}\"" >> ${DESTDIR}/etc/rc.conf 110 metalog_add_data ./etc/rc.conf 111 if [ "${VMFS}" = zfs ]; then 112 echo "zfs_enable=\"YES\"" >> ${DESTDIR}/etc/rc.conf 113 echo "zpool_reguid=\"zroot\"" >> ${DESTDIR}/etc/rc.conf 114 echo "zpool_upgrade=\"zroot\"" >> ${DESTDIR}/etc/rc.conf 115 echo "kern.geom.label.disk_ident.enable=0" >> ${DESTDIR}/boot/loader.conf 116 echo "zfs_load=YES" >> ${DESTDIR}/boot/loader.conf 117 metalog_add_data ./boot/loader.conf 118 fi 119 120 return 0 121} 122 123vm_emulation_setup() { 124 if [ -n "${WITHOUT_QEMU}" ]; then 125 return 0 126 fi 127 if [ -n "${QEMUSTATIC}" ]; then 128 export EMULATOR=/qemu 129 cp ${QEMUSTATIC} ${DESTDIR}/${EMULATOR} 130 fi 131 132 mkdir -p ${DESTDIR}/dev 133 mount -t devfs devfs ${DESTDIR}/dev 134 chroot ${DESTDIR} ${EMULATOR} /bin/sh /etc/rc.d/ldconfig forcestart 135 cp /etc/resolv.conf ${DESTDIR}/etc/resolv.conf 136 137 return 0 138} 139 140vm_extra_install_base() { 141 # Prototype. When overridden, runs extra post-installworld commands 142 # as needed, based on the target virtual machine image or cloud 143 # provider image target. 144 145 return 0 146} 147 148vm_extra_enable_services() { 149 if [ -n "${VM_RC_LIST}" ]; then 150 for _rcvar in ${VM_RC_LIST}; do 151 echo ${_rcvar}_enable="YES" >> ${DESTDIR}/etc/rc.conf 152 done 153 fi 154 155 if [ -z "${VMCONFIG}" -o -c "${VMCONFIG}" ]; then 156 echo 'ifconfig_DEFAULT="DHCP inet6 accept_rtadv"' >> \ 157 ${DESTDIR}/etc/rc.conf 158 # Expand the filesystem to fill the disk. 159 echo 'growfs_enable="YES"' >> ${DESTDIR}/etc/rc.conf 160 fi 161 162 return 0 163} 164 165vm_extra_install_packages() { 166 if [ -n "${WITHOUT_QEMU}" ]; then 167 return 0 168 fi 169 170 if [ -z "${VM_EXTRA_PACKAGES}" ]; then 171 return 0 172 fi 173 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 174 /usr/sbin/pkg bootstrap -y 175 for p in ${VM_EXTRA_PACKAGES}; do 176 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 177 /usr/sbin/pkg install -y ${p} 178 done 179 180 return 0 181} 182 183vm_extra_install_ports() { 184 # Prototype. When overridden, installs additional ports within the 185 # virtual machine environment. 186 187 return 0 188} 189 190vm_extra_pre_umount() { 191 # Prototype. When overridden, performs additional tasks within the 192 # virtual machine environment prior to unmounting the filesystem. 193 194 return 0 195} 196 197vm_emulation_cleanup() { 198 if [ -n "${WITHOUT_QEMU}" ]; then 199 return 0 200 fi 201 202 if ! [ -z "${QEMUSTATIC}" ]; then 203 rm -f ${DESTDIR}/${EMULATOR} 204 fi 205 rm -f ${DESTDIR}/etc/resolv.conf 206 umount_loop ${DESTDIR}/dev 207 return 0 208} 209 210vm_extra_pkg_rmcache() { 211 if [ -e ${DESTDIR}/usr/local/sbin/pkg ]; then 212 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 213 /usr/local/sbin/pkg clean -y -a 214 fi 215 216 return 0 217} 218 219buildfs() { 220 local md tmppool 221 222 case "${VMFS}" in 223 ufs) 224 cd ${DESTDIR} && ${MAKEFS} ${MAKEFSARGS} -o label=rootfs -o version=2 -o softupdates=1 \ 225 ${VMBASE} .${NO_ROOT:+/METALOG} 226 ;; 227 zfs) 228 cd ${DESTDIR} && ${MAKEFS} -t zfs ${MAKEFSARGS} \ 229 -o poolname=zroot -o bootfs=zroot/ROOT/default -o rootpath=/ \ 230 -o fs=zroot\;mountpoint=none \ 231 -o fs=zroot/ROOT\;mountpoint=none \ 232 -o fs=zroot/ROOT/default\;mountpoint=/\;canmount=noauto \ 233 -o fs=zroot/home\;mountpoint=/home \ 234 -o fs=zroot/tmp\;mountpoint=/tmp\;exec=on\;setuid=off \ 235 -o fs=zroot/usr\;mountpoint=/usr\;canmount=off \ 236 -o fs=zroot/usr/ports\;setuid=off \ 237 -o fs=zroot/usr/src \ 238 -o fs=zroot/usr/obj \ 239 -o fs=zroot/var\;mountpoint=/var\;canmount=off \ 240 -o fs=zroot/var/audit\;setuid=off\;exec=off \ 241 -o fs=zroot/var/crash\;setuid=off\;exec=off \ 242 -o fs=zroot/var/log\;setuid=off\;exec=off \ 243 -o fs=zroot/var/mail\;atime=on \ 244 -o fs=zroot/var/tmp\;setuid=off \ 245 ${VMBASE} .${NO_ROOT:+/METALOG} 246 ;; 247 *) 248 echo "Unexpected VMFS value '${VMFS}'" 249 exit 1 250 ;; 251 esac 252} 253 254umount_loop() { 255 DIR=$1 256 i=0 257 sync 258 while ! umount ${DIR}; do 259 i=$(( $i + 1 )) 260 if [ $i -ge 10 ]; then 261 # This should never happen. But, it has happened. 262 echo "Cannot umount(8) ${DIR}" 263 echo "Something has gone horribly wrong." 264 return 1 265 fi 266 sleep 1 267 done 268 269 return 0 270} 271 272vm_create_disk() { 273 local BOOTFILES BOOTPARTSOFFSET FSPARTTYPE X86GPTBOOTFILE 274 275 if [ -z "${NOSWAP}" ]; then 276 SWAPOPT="-p freebsd-swap/swapfs::${SWAPSIZE}" 277 fi 278 279 if [ -n "${VM_BOOTPARTSOFFSET}" ]; then 280 BOOTPARTSOFFSET=":${VM_BOOTPARTSOFFSET}" 281 fi 282 283 if [ -n "${CONFIG_DRIVE}" ]; then 284 CONFIG_DRIVE="-p freebsd/config-drive::${CONFIG_DRIVE_SIZE}" 285 fi 286 287 case "${VMFS}" in 288 ufs) 289 FSPARTTYPE=freebsd-ufs 290 X86GPTBOOTFILE=i386/gptboot/gptboot 291 ;; 292 zfs) 293 FSPARTTYPE=freebsd-zfs 294 X86GPTBOOTFILE=i386/gptzfsboot/gptzfsboot 295 ;; 296 *) 297 echo "Unexpected VMFS value '${VMFS}'" 298 return 1 299 ;; 300 esac 301 302 echo "Creating image... Please wait." 303 echo 304 305 BOOTFILES="$(env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ 306 WITH_UNIFIED_OBJDIR=yes \ 307 make -C ${WORLDDIR}/stand -V .OBJDIR)" 308 BOOTFILES="$(realpath ${BOOTFILES})" 309 MAKEFSARGS="-s ${VMSIZE} -D" 310 311 case "${TARGET}:${TARGET_ARCH}" in 312 amd64:amd64 | i386:i386) 313 ESP=yes 314 BOOTPARTS="-b ${BOOTFILES}/i386/pmbr/pmbr \ 315 -p freebsd-boot/bootfs:=${BOOTFILES}/${X86GPTBOOTFILE}${BOOTPARTSOFFSET}" 316 ROOTFSPART="-p ${FSPARTTYPE}/rootfs:=${VMBASE}" 317 MAKEFSARGS="$MAKEFSARGS -B little" 318 ;; 319 arm:armv7 | arm64:aarch64 | riscv:riscv64*) 320 ESP=yes 321 BOOTPARTS= 322 ROOTFSPART="-p ${FSPARTTYPE}/rootfs:=${VMBASE}" 323 MAKEFSARGS="$MAKEFSARGS -B little" 324 ;; 325 powerpc:powerpc*) 326 ESP=no 327 BOOTPARTS="-p prepboot:=${BOOTFILES}/powerpc/boot1.chrp/boot1.elf -a 1" 328 ROOTFSPART="-p freebsd:=${VMBASE}" 329 if [ ${TARGET_ARCH} = powerpc64le ]; then 330 MAKEFSARGS="$MAKEFSARGS -B little" 331 else 332 MAKEFSARGS="$MAKEFSARGS -B big" 333 fi 334 ;; 335 *) 336 echo "vmimage.subr: unsupported target '${TARGET}:${TARGET_ARCH}'" >&2 337 exit 1 338 ;; 339 esac 340 341 if [ ${ESP} = "yes" ]; then 342 # Create an ESP 343 espfilename=$(mktemp /tmp/efiboot.XXXXXX) 344 make_esp_file ${espfilename} ${fat32min} ${BOOTFILES}/efi/loader_lua/loader_lua.efi 345 BOOTPARTS="${BOOTPARTS} -p efi/efiboot0:=${espfilename}" 346 347 # Add this to fstab 348 mkdir -p ${DESTDIR}/boot/efi 349 echo "/dev/${ROOTLABEL}/efiboot0 /boot/efi msdosfs rw 2 2" \ 350 >> ${DESTDIR}/etc/fstab 351 fi 352 353 # Add a marker file which indicates that this image has never 354 # been booted. Some services run only upon the first boot. 355 touch ${DESTDIR}/firstboot 356 metalog_add_data ./firstboot 357 358 echo "Building filesystem... Please wait." 359 buildfs 360 361 echo "Building final disk image... Please wait." 362 ${MKIMG} -s ${PARTSCHEME} -f ${VMFORMAT} \ 363 ${BOOTPARTS} \ 364 ${SWAPOPT} \ 365 ${CONFIG_DRIVE} \ 366 ${ROOTFSPART} \ 367 -o ${VMIMAGE} 368 369 echo "Disk image ${VMIMAGE} created." 370 371 if [ ${ESP} = "yes" ]; then 372 rm ${espfilename} 373 fi 374 375 return 0 376} 377 378vm_extra_create_disk() { 379 380 return 0 381} 382