1278985Sgjb#!/bin/sh
2278985Sgjb#
3278985Sgjb# $FreeBSD: stable/10/release/tools/vmimage.subr 325491 2017-11-06 18:07:23Z gjb $
4278985Sgjb#
5278985Sgjb#
6278985Sgjb# Common functions for virtual machine image build scripts.
7278985Sgjb#
8278985Sgjb
9278985Sgjbexport PATH="/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
10278985Sgjbtrap "cleanup" INT QUIT TRAP ABRT TERM
11278985Sgjb
12278985Sgjbwrite_partition_layout() {
13278985Sgjb	if [ -z "${NOSWAP}" ]; then
14318811Snp		SWAPOPT="-p freebsd-swap/swapfs::${SWAPSIZE}"
15278985Sgjb	fi
16278985Sgjb
17282111Sgjb	_OBJDIR="$(make -C ${WORLDDIR} -V .OBJDIR)"
18317442Sgjb	_OBJDIR="$(realpath ${_OBJDIR})"
19282111Sgjb	if [ -d "${_OBJDIR%%/usr/src}/${TARGET}.${TARGET_ARCH}" ]; then
20282111Sgjb		BOOTFILES="/${_OBJDIR%%/usr/src}/${TARGET}.${TARGET_ARCH}/usr/src/sys/boot"
21282111Sgjb	else
22282111Sgjb		BOOTFILES="/${_OBJDIR}/sys/boot"
23282111Sgjb	fi
24282111Sgjb
25278985Sgjb	case "${TARGET}:${TARGET_ARCH}" in
26278985Sgjb		amd64:amd64 | i386:i386)
27282877Sgjb			mkimg -s gpt -f ${VMFORMAT} \
28282877Sgjb				-b ${BOOTFILES}/i386/pmbr/pmbr \
29282111Sgjb				-p freebsd-boot/bootfs:=${BOOTFILES}/i386/gptboot/gptboot \
30278985Sgjb				${SWAPOPT} \
31278985Sgjb				-p freebsd-ufs/rootfs:=${VMBASE} \
32278985Sgjb				-o ${VMIMAGE}
33278985Sgjb			;;
34278985Sgjb		powerpc:powerpc*)
35282877Sgjb			mkimg -s apm -f ${VMFORMAT} \
36282111Sgjb				-p apple-boot/bootfs:=${BOOTFILES}/powerpc/boot1.chrp/boot1.hfs \
37278985Sgjb				${SWAPOPT} \
38278985Sgjb				-p freebsd-ufs/rootfs:=${VMBASE} \
39278985Sgjb				-o ${VMIMAGE}
40278985Sgjb			;;
41278985Sgjb		*)
42278985Sgjb			# ENOTSUPP
43278985Sgjb			return 1
44278985Sgjb			;;
45278985Sgjb	esac
46278985Sgjb
47278985Sgjb	return 0
48278985Sgjb}
49278985Sgjb
50278985Sgjberr() {
51278985Sgjb	printf "${@}\n"
52278985Sgjb	cleanup
53278985Sgjb	return 1
54278985Sgjb}
55278985Sgjb
56278985Sgjbcleanup() {
57282111Sgjb	if [ -c "${DESTDIR}/dev/null" ]; then
58282111Sgjb		umount_loop ${DESTDIR}/dev 2>/dev/null
59282111Sgjb	fi
60282111Sgjb	umount_loop ${DESTDIR}
61278985Sgjb	if [ ! -z "${mddev}" ]; then
62278985Sgjb		mdconfig -d -u ${mddev}
63278985Sgjb	fi
64278985Sgjb
65278985Sgjb	return 0
66278985Sgjb}
67278985Sgjb
68278985Sgjbvm_create_base() {
69278985Sgjb	# Creates the UFS root filesystem for the virtual machine disk,
70278985Sgjb	# written to the formatted disk image with mkimg(1).
71278985Sgjb
72278985Sgjb	mkdir -p ${DESTDIR}
73278985Sgjb	truncate -s ${VMSIZE} ${VMBASE}
74278985Sgjb	mddev=$(mdconfig -f ${VMBASE})
75282111Sgjb	newfs /dev/${mddev}
76278985Sgjb	mount /dev/${mddev} ${DESTDIR}
77278985Sgjb
78278985Sgjb	return 0
79278985Sgjb}
80278985Sgjb
81282111Sgjbvm_copy_base() {
82282111Sgjb	# Creates a new UFS root filesystem and copies the contents of the
83282111Sgjb	# current root filesystem into it.  This produces a "clean" disk
84282111Sgjb	# image without any remnants of files which were created temporarily
85282111Sgjb	# during image-creation and have since been deleted (e.g., downloaded
86282111Sgjb	# package archives).
87282111Sgjb
88282111Sgjb	mkdir -p ${DESTDIR}/old
89282111Sgjb	mdold=$(mdconfig -f ${VMBASE})
90282111Sgjb	mount /dev/${mdold} ${DESTDIR}/old
91282111Sgjb
92282111Sgjb	truncate -s ${VMSIZE} ${VMBASE}.tmp
93282111Sgjb	mkdir -p ${DESTDIR}/new
94282111Sgjb	mdnew=$(mdconfig -f ${VMBASE}.tmp)
95282111Sgjb	newfs /dev/${mdnew}
96282111Sgjb	mount /dev/${mdnew} ${DESTDIR}/new
97282111Sgjb
98282111Sgjb	tar -cf- -C ${DESTDIR}/old . | tar -xUf- -C ${DESTDIR}/new
99282111Sgjb
100282111Sgjb	umount_loop /dev/${mdold}
101282111Sgjb	rmdir ${DESTDIR}/old
102282111Sgjb	mdconfig -d -u ${mdold}
103282111Sgjb
104282111Sgjb	umount_loop /dev/${mdnew}
105282111Sgjb	rmdir ${DESTDIR}/new
106288468Sgjb	tunefs -n enable /dev/${mdnew}
107282111Sgjb	mdconfig -d -u ${mdnew}
108282111Sgjb	mv ${VMBASE}.tmp ${VMBASE}
109282111Sgjb}
110282111Sgjb
111278985Sgjbvm_install_base() {
112278985Sgjb	# Installs the FreeBSD userland/kernel to the virtual machine disk.
113278985Sgjb
114278985Sgjb	cd ${WORLDDIR} && \
115278985Sgjb		make DESTDIR=${DESTDIR} \
116278985Sgjb		installworld installkernel distribution || \
117278985Sgjb		err "\n\nCannot install the base system to ${DESTDIR}."
118278985Sgjb
119323815Sgjb	# Bootstrap etcupdate(8) and mergemaster(8) databases.
120323815Sgjb	mkdir -p ${DESTDIR}/var/db/etcupdate
121323815Sgjb	etcupdate extract -B \
122323815Sgjb		-M "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \
123323815Sgjb		-s ${WORLDDIR} -d ${DESTDIR}/var/db/etcupdate
124323815Sgjb	sh ${WORLDDIR}/release/scripts/mm-mtree.sh -m ${WORLDDIR} \
125323815Sgjb		-F "TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}" \
126323815Sgjb		-D ${DESTDIR}
127323815Sgjb
128278985Sgjb	echo '# Custom /etc/fstab for FreeBSD VM images' \
129278985Sgjb		> ${DESTDIR}/etc/fstab
130278985Sgjb	echo '/dev/gpt/rootfs   /       ufs     rw      1       1' \
131278985Sgjb		>> ${DESTDIR}/etc/fstab
132278985Sgjb	if [ -z "${NOSWAP}" ]; then
133278985Sgjb		echo '/dev/gpt/swapfs  none    swap    sw      0       0' \
134278985Sgjb			>> ${DESTDIR}/etc/fstab
135278985Sgjb	fi
136278985Sgjb
137325491Sgjb	local hostname
138325491Sgjb	hostname="$(echo $(uname -o) | tr '[:upper:]' '[:lower:]')"
139325491Sgjb	echo "hostname=\"${hostname}\"" >> ${DESTDIR}/etc/rc.conf
140325491Sgjb
141278985Sgjb	mkdir -p ${DESTDIR}/dev
142278985Sgjb	mount -t devfs devfs ${DESTDIR}/dev
143278985Sgjb	chroot ${DESTDIR} /usr/bin/newaliases
144278985Sgjb	chroot ${DESTDIR} /etc/rc.d/ldconfig forcestart
145282111Sgjb	umount_loop ${DESTDIR}/dev
146278985Sgjb
147278985Sgjb	cp /etc/resolv.conf ${DESTDIR}/etc/resolv.conf
148278985Sgjb
149278985Sgjb	return 0
150278985Sgjb}
151278985Sgjb
152278985Sgjbvm_extra_install_base() {
153278985Sgjb	# Prototype.  When overridden, runs extra post-installworld commands
154278985Sgjb	# as needed, based on the target virtual machine image or cloud
155278985Sgjb	# provider image target.
156278985Sgjb
157278985Sgjb	return 0
158278985Sgjb}
159278985Sgjb
160278985Sgjbvm_extra_enable_services() {
161278985Sgjb	if [ ! -z "${VM_RC_LIST}" ]; then
162278985Sgjb		for _rcvar in ${VM_RC_LIST}; do
163278985Sgjb			echo ${_rcvar}_enable="YES" >> ${DESTDIR}/etc/rc.conf
164278985Sgjb		done
165278985Sgjb	fi
166278985Sgjb
167319032Sgjb	if [ -z "${VMCONFIG}" -o -c "${VMCONFIG}" ]; then
168319032Sgjb		echo 'ifconfig_DEFAULT="DHCP inet6 accept_rtadv"' >> \
169319032Sgjb			${DESTDIR}/etc/rc.conf
170319032Sgjb	fi
171319032Sgjb
172278985Sgjb	return 0
173278985Sgjb}
174278985Sgjb
175278985Sgjbvm_extra_install_packages() {
176278985Sgjb	if [ -z "${VM_EXTRA_PACKAGES}" ]; then
177278985Sgjb		return 0
178278985Sgjb	fi
179278985Sgjb	mkdir -p ${DESTDIR}/dev
180278985Sgjb	mount -t devfs devfs ${DESTDIR}/dev
181278985Sgjb	chroot ${DESTDIR} env ASSUME_ALWAYS_YES=yes \
182278985Sgjb		/usr/sbin/pkg bootstrap -y
183278985Sgjb	chroot ${DESTDIR} env ASSUME_ALWAYS_YES=yes \
184278985Sgjb		/usr/sbin/pkg install -y ${VM_EXTRA_PACKAGES}
185282111Sgjb	umount_loop ${DESTDIR}/dev
186278985Sgjb
187278985Sgjb	return 0
188278985Sgjb}
189278985Sgjb
190278985Sgjbvm_extra_install_ports() {
191278985Sgjb	# Prototype.  When overridden, installs additional ports within the
192278985Sgjb	# virtual machine environment.
193278985Sgjb
194278985Sgjb	return 0
195278985Sgjb}
196278985Sgjb
197278985Sgjbvm_extra_pre_umount() {
198282262Sgjb	# Prototype.  When overridden, performs additional tasks within the
199282262Sgjb	# virtual machine environment prior to unmounting the filesystem.
200282262Sgjb	# Note: When overriding this function, removing resolv.conf in the
201282262Sgjb	# disk image must be included.
202278985Sgjb
203278985Sgjb	rm -f ${DESTDIR}/etc/resolv.conf
204278985Sgjb	return 0
205278985Sgjb}
206278985Sgjb
207278985Sgjbvm_extra_pkg_rmcache() {
208278985Sgjb	if [ -e ${DESTDIR}/usr/local/sbin/pkg ]; then
209278985Sgjb		chroot ${DESTDIR} env ASSUME_ALWAYS_YES=yes \
210278985Sgjb			/usr/local/sbin/pkg clean -y -a
211278985Sgjb	fi
212278985Sgjb
213278985Sgjb	return 0
214278985Sgjb}
215278985Sgjb
216282111Sgjbumount_loop() {
217282111Sgjb	DIR=$1
218278985Sgjb	i=0
219278985Sgjb	sync
220282111Sgjb	while ! umount ${DIR}; do
221278985Sgjb		i=$(( $i + 1 ))
222278985Sgjb		if [ $i -ge 10 ]; then
223278985Sgjb			# This should never happen.  But, it has happened.
224282111Sgjb			echo "Cannot umount(8) ${DIR}"
225282111Sgjb			echo "Something has gone horribly wrong."
226282111Sgjb			return 1
227278985Sgjb		fi
228278985Sgjb		sleep 1
229278985Sgjb	done
230278985Sgjb
231278985Sgjb	return 0
232278985Sgjb}
233278985Sgjb
234278985Sgjbvm_create_disk() {
235278985Sgjb	echo "Creating image...  Please wait."
236278985Sgjb	echo
237278985Sgjb
238278985Sgjb	write_partition_layout || return 1
239278985Sgjb
240278985Sgjb	return 0
241278985Sgjb}
242278985Sgjb
243278985Sgjbvm_extra_create_disk() {
244278985Sgjb
245278985Sgjb	return 0
246278985Sgjb}
247278985Sgjb
248