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