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 [ -z "${VM_EXTRA_PACKAGES}" ]; then 167 return 0 168 fi 169 if [ -n "${NO_ROOT}" ]; then 170 for pkg in ${VM_EXTRA_PACKAGES}; do 171 INSTALL_AS_USER=yes \ 172 ${PKG_CMD} \ 173 -o METALOG=${DESTDIR}/METALOG.pkg \ 174 -o REPOS_DIR=${PKG_REPOS_DIR} \ 175 -o PKG_DBDIR=${DESTDIR}/var/db/pkg \ 176 -r ${DESTDIR} \ 177 install -y -r ${PKG_REPO_NAME} $pkg 178 done 179 metalog_add_data ./var/db/pkg/local.sqlite 180 else 181 if [ -n "${WITHOUT_QEMU}" ]; then 182 return 0 183 fi 184 185 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 186 /usr/sbin/pkg bootstrap -y 187 for p in ${VM_EXTRA_PACKAGES}; do 188 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 189 /usr/sbin/pkg install -y ${p} 190 done 191 fi 192 193 return 0 194} 195 196vm_extra_install_ports() { 197 # Prototype. When overridden, installs additional ports within the 198 # virtual machine environment. 199 200 return 0 201} 202 203vm_extra_pre_umount() { 204 # Prototype. When overridden, performs additional tasks within the 205 # virtual machine environment prior to unmounting the filesystem. 206 207 return 0 208} 209 210vm_emulation_cleanup() { 211 if [ -n "${WITHOUT_QEMU}" ]; then 212 return 0 213 fi 214 215 if ! [ -z "${QEMUSTATIC}" ]; then 216 rm -f ${DESTDIR}/${EMULATOR} 217 fi 218 rm -f ${DESTDIR}/etc/resolv.conf 219 umount_loop ${DESTDIR}/dev 220 return 0 221} 222 223vm_extra_pkg_rmcache() { 224 if [ -e ${DESTDIR}/usr/local/sbin/pkg ]; then 225 chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \ 226 /usr/local/sbin/pkg clean -y -a 227 fi 228 229 return 0 230} 231 232buildfs() { 233 local md tmppool 234 235 if [ -f ${DESTDIR}/METALOG.pkg ]; then 236 cat ${DESTDIR}/METALOG.pkg >> ${DESTDIR}/METALOG 237 fi 238 239 case "${VMFS}" in 240 ufs) 241 cd ${DESTDIR} && ${MAKEFS} ${MAKEFSARGS} -o label=rootfs -o version=2 -o softupdates=1 \ 242 ${VMBASE} .${NO_ROOT:+/METALOG} 243 ;; 244 zfs) 245 cd ${DESTDIR} && ${MAKEFS} -t zfs ${MAKEFSARGS} \ 246 -o poolname=zroot -o bootfs=zroot/ROOT/default -o rootpath=/ \ 247 -o fs=zroot\;mountpoint=none \ 248 -o fs=zroot/ROOT\;mountpoint=none \ 249 -o fs=zroot/ROOT/default\;mountpoint=/\;canmount=noauto \ 250 -o fs=zroot/home\;mountpoint=/home \ 251 -o fs=zroot/tmp\;mountpoint=/tmp\;exec=on\;setuid=off \ 252 -o fs=zroot/usr\;mountpoint=/usr\;canmount=off \ 253 -o fs=zroot/usr/ports\;setuid=off \ 254 -o fs=zroot/usr/src \ 255 -o fs=zroot/usr/obj \ 256 -o fs=zroot/var\;mountpoint=/var\;canmount=off \ 257 -o fs=zroot/var/audit\;setuid=off\;exec=off \ 258 -o fs=zroot/var/crash\;setuid=off\;exec=off \ 259 -o fs=zroot/var/log\;setuid=off\;exec=off \ 260 -o fs=zroot/var/mail\;atime=on \ 261 -o fs=zroot/var/tmp\;setuid=off \ 262 ${VMBASE} .${NO_ROOT:+/METALOG} 263 ;; 264 *) 265 echo "Unexpected VMFS value '${VMFS}'" 266 exit 1 267 ;; 268 esac 269} 270 271umount_loop() { 272 DIR=$1 273 i=0 274 sync 275 while ! umount ${DIR}; do 276 i=$(( $i + 1 )) 277 if [ $i -ge 10 ]; then 278 # This should never happen. But, it has happened. 279 echo "Cannot umount(8) ${DIR}" 280 echo "Something has gone horribly wrong." 281 return 1 282 fi 283 sleep 1 284 done 285 286 return 0 287} 288 289vm_create_disk() { 290 local BOOTFILES BOOTPARTSOFFSET FSPARTTYPE X86GPTBOOTFILE 291 292 if [ -z "${NOSWAP}" ]; then 293 SWAPOPT="-p freebsd-swap/swapfs::${SWAPSIZE}" 294 fi 295 296 if [ -n "${VM_BOOTPARTSOFFSET}" ]; then 297 BOOTPARTSOFFSET=":${VM_BOOTPARTSOFFSET}" 298 fi 299 300 if [ -n "${CONFIG_DRIVE}" ]; then 301 CONFIG_DRIVE="-p freebsd/config-drive::${CONFIG_DRIVE_SIZE}" 302 fi 303 304 case "${VMFS}" in 305 ufs) 306 FSPARTTYPE=freebsd-ufs 307 X86GPTBOOTFILE=i386/gptboot/gptboot 308 ;; 309 zfs) 310 FSPARTTYPE=freebsd-zfs 311 X86GPTBOOTFILE=i386/gptzfsboot/gptzfsboot 312 ;; 313 *) 314 echo "Unexpected VMFS value '${VMFS}'" 315 return 1 316 ;; 317 esac 318 319 echo "Creating image... Please wait." 320 echo 321 322 BOOTFILES="$(env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} \ 323 WITH_UNIFIED_OBJDIR=yes \ 324 make -C ${WORLDDIR}/stand -V .OBJDIR)" 325 BOOTFILES="$(realpath ${BOOTFILES})" 326 MAKEFSARGS="-s ${VMSIZE} -D" 327 328 case "${TARGET}:${TARGET_ARCH}" in 329 amd64:amd64 | i386:i386) 330 ESP=yes 331 BOOTPARTS="-b ${BOOTFILES}/i386/pmbr/pmbr \ 332 -p freebsd-boot/bootfs:=${BOOTFILES}/${X86GPTBOOTFILE}${BOOTPARTSOFFSET}" 333 ROOTFSPART="-p ${FSPARTTYPE}/rootfs:=${VMBASE}" 334 MAKEFSARGS="$MAKEFSARGS -B little" 335 ;; 336 arm:armv7 | arm64:aarch64 | riscv:riscv64*) 337 ESP=yes 338 BOOTPARTS= 339 ROOTFSPART="-p ${FSPARTTYPE}/rootfs:=${VMBASE}" 340 MAKEFSARGS="$MAKEFSARGS -B little" 341 ;; 342 powerpc:powerpc*) 343 ESP=no 344 BOOTPARTS="-p prepboot:=${BOOTFILES}/powerpc/boot1.chrp/boot1.elf -a 1" 345 ROOTFSPART="-p freebsd:=${VMBASE}" 346 if [ ${TARGET_ARCH} = powerpc64le ]; then 347 MAKEFSARGS="$MAKEFSARGS -B little" 348 else 349 MAKEFSARGS="$MAKEFSARGS -B big" 350 fi 351 ;; 352 *) 353 echo "vmimage.subr: unsupported target '${TARGET}:${TARGET_ARCH}'" >&2 354 exit 1 355 ;; 356 esac 357 358 if [ ${ESP} = "yes" ]; then 359 # Create an ESP 360 espfilename=$(mktemp /tmp/efiboot.XXXXXX) 361 make_esp_file ${espfilename} ${fat32min} ${BOOTFILES}/efi/loader_lua/loader_lua.efi 362 BOOTPARTS="${BOOTPARTS} -p efi/efiboot0:=${espfilename}" 363 364 # Add this to fstab 365 mkdir -p ${DESTDIR}/boot/efi 366 echo "/dev/${ROOTLABEL}/efiboot0 /boot/efi msdosfs rw 2 2" \ 367 >> ${DESTDIR}/etc/fstab 368 fi 369 370 # Add a marker file which indicates that this image has never 371 # been booted. Some services run only upon the first boot. 372 touch ${DESTDIR}/firstboot 373 metalog_add_data ./firstboot 374 375 echo "Building filesystem... Please wait." 376 buildfs 377 378 echo "Building final disk image... Please wait." 379 ${MKIMG} -s ${PARTSCHEME} -f ${VMFORMAT} \ 380 ${BOOTPARTS} \ 381 ${SWAPOPT} \ 382 ${CONFIG_DRIVE} \ 383 ${ROOTFSPART} \ 384 -o ${VMIMAGE} 385 386 echo "Disk image ${VMIMAGE} created." 387 388 if [ ${ESP} = "yes" ]; then 389 rm ${espfilename} 390 fi 391 392 return 0 393} 394 395vm_extra_create_disk() { 396 397 return 0 398} 399