nanobsd.sh revision 171986
1#!/bin/sh
2#
3# Copyright (c) 2005 Poul-Henning Kamp.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD: head/tools/tools/nanobsd/nanobsd.sh 171986 2007-08-26 14:57:08Z phk $
28#
29
30set -e
31
32#######################################################################
33#
34# Setup default values for all controlling variables.
35# These values can be overridden from the config file(s)
36#
37#######################################################################
38
39# Name of this NanoBSD build.  (Used to construct workdir names)
40NANO_NAME=full
41
42# Source tree directory
43NANO_SRC=/usr/src
44
45# Where nanobsd additional files live under the source tree
46NANO_TOOLS=tools/tools/nanobsd
47
48# Where cust_pkg() finds packages to install
49NANO_PACKAGE_DIR=${NANO_SRC}/${NANO_TOOLS}/Pkg
50
51# Object tree directory
52# default is subdir of /usr/obj
53# XXX: MAKEOBJDIRPREFIX handling... ?
54#NANO_OBJ=""
55
56# Parallel Make
57NANO_PMAKE="make -j 3"
58
59# Options to put in make.conf during buildworld only
60CONF_BUILD=' '
61
62# Options to put in make.conf during installworld only
63CONF_INSTALL=' '
64
65# Options to put in make.conf during both build- & installworld.
66CONF_WORLD=' '
67
68# Kernel config file to use
69NANO_KERNEL=GENERIC
70
71# Customize commands.
72NANO_CUSTOMIZE=""
73
74# Newfs paramters to use
75NANO_NEWFS="-b 4096 -f 512 -i 8192 -O1 -U"
76
77# The drive name of the media at runtime
78NANO_DRIVE=ad0
79
80# Target media size in 512 bytes sectors
81NANO_MEDIASIZE=1000000
82
83# Number of code images on media (1 or 2)
84NANO_IMAGES=2
85
86# 0 -> Leave second image all zeroes so it compresses better.
87# 1 -> Initialize second image with a copy of the first
88NANO_INIT_IMG2=1
89
90# Size of code file system in 512 bytes sectors
91# If zero, size will be as large as possible.
92NANO_CODESIZE=0
93
94# Size of configuration file system in 512 bytes sectors
95# Cannot be zero.
96NANO_CONFSIZE=2048
97
98# Size of data file system in 512 bytes sectors
99# If zero: no partition configured.
100# If negative: max size possible
101NANO_DATASIZE=0
102
103# Size of the /etc ramdisk in 512 bytes sectors
104NANO_RAM_ETCSIZE=10240
105
106# Size of the /tmp+/var ramdisk in 512 bytes sectors
107NANO_RAM_TMPVARSIZE=10240
108
109# Media geometry, only relevant if bios doesn't understand LBA.
110NANO_SECTS=63
111NANO_HEADS=16
112
113# boot0 flags/options and configuration
114NANO_BOOT0CFG="-o packet -s 1 -m 3"
115NANO_BOOTLOADER="boot/boot0sio"
116
117#######################################################################
118# Not a variable at this time
119
120NANO_ARCH=i386
121
122#######################################################################
123#
124# The functions which do the real work.
125# Can be overridden from the config file(s)
126#
127#######################################################################
128
129clean_build ( ) (
130	echo "## Clean and create object directory (${MAKEOBJDIRPREFIX})"
131
132	if rm -rf ${MAKEOBJDIRPREFIX} > /dev/null 2>&1 ; then
133		true
134	else
135		chflags -R noschg ${MAKEOBJDIRPREFIX}
136		rm -rf ${MAKEOBJDIRPREFIX}
137	fi
138	mkdir -p ${MAKEOBJDIRPREFIX}
139	printenv > ${MAKEOBJDIRPREFIX}/_.env
140)
141
142make_conf_build ( ) (
143	echo "## Construct build make.conf ($NANO_MAKE_CONF)"
144
145	echo "${CONF_WORLD}" > ${NANO_MAKE_CONF}
146	echo "${CONF_BUILD}" >> ${NANO_MAKE_CONF}
147)
148
149build_world ( ) (
150	echo "## run buildworld"
151	echo "### log: ${MAKEOBJDIRPREFIX}/_.bw"
152
153	cd ${NANO_SRC}
154	${NANO_PMAKE} __MAKE_CONF=${NANO_MAKE_CONF} buildworld \
155		> ${MAKEOBJDIRPREFIX}/_.bw 2>&1
156)
157
158build_kernel ( ) (
159	echo "## build kernel ($NANO_KERNEL)"
160	echo "### log: ${MAKEOBJDIRPREFIX}/_.bk"
161
162	if [ -f ${NANO_KERNEL} ] ; then
163		cp ${NANO_KERNEL} ${NANO_SRC}/sys/${NANO_ARCH}/conf
164	fi
165
166	cd ${NANO_SRC}
167	${NANO_PMAKE} buildkernel \
168		__MAKE_CONF=${NANO_MAKE_CONF} KERNCONF=`basename ${NANO_KERNEL}` \
169		> ${MAKEOBJDIRPREFIX}/_.bk 2>&1
170)
171
172clean_world ( ) (
173	echo "## Clean and create world directory (${NANO_WORLDDIR})"
174	if rm -rf ${NANO_WORLDDIR}/ > /dev/null 2>&1 ; then
175		true
176	else
177		chflags -R noschg ${NANO_WORLDDIR}/
178		rm -rf ${NANO_WORLDDIR}/
179	fi
180	mkdir -p ${NANO_WORLDDIR}/
181)
182
183make_conf_install ( ) (
184	echo "## Construct install make.conf ($NANO_MAKE_CONF)"
185
186	echo "${CONF_WORLD}" > ${NANO_MAKE_CONF}
187	echo "${CONF_INSTALL}" >> ${NANO_MAKE_CONF}
188)
189
190install_world ( ) (
191	echo "## installworld"
192	echo "### log: ${MAKEOBJDIRPREFIX}/_.iw"
193
194	cd ${NANO_SRC}
195	${NANO_PMAKE} __MAKE_CONF=${NANO_MAKE_CONF} installworld \
196		DESTDIR=${NANO_WORLDDIR} \
197		> ${MAKEOBJDIRPREFIX}/_.iw 2>&1
198	chflags -R noschg ${NANO_WORLDDIR}
199)
200
201install_etc ( ) (
202
203	echo "## install /etc"
204	echo "### log: ${MAKEOBJDIRPREFIX}/_.etc"
205
206	cd ${NANO_SRC}
207	${NANO_PMAKE} __MAKE_CONF=${NANO_MAKE_CONF} distribution \
208		DESTDIR=${NANO_WORLDDIR} \
209		> ${MAKEOBJDIRPREFIX}/_.etc 2>&1
210)
211
212install_kernel ( ) (
213	echo "## install kernel"
214	echo "### log: ${MAKEOBJDIRPREFIX}/_.ik"
215
216	cd ${NANO_SRC}
217	${NANO_PMAKE} installkernel \
218		DESTDIR=${NANO_WORLDDIR} \
219		__MAKE_CONF=${NANO_MAKE_CONF} KERNCONF=`basename ${NANO_KERNEL}` \
220		> ${MAKEOBJDIRPREFIX}/_.ik 2>&1
221)
222
223run_customize() (
224
225	echo "## run customize scripts"
226	for c in $NANO_CUSTOMIZE
227	do
228		echo "## customize \"$c\""
229		echo "### log: ${MAKEOBJDIRPREFIX}/_.cust.$c"
230		echo "### `type $c`"
231		( $c ) > ${MAKEOBJDIRPREFIX}/_.cust.$c 2>&1
232	done
233)
234
235setup_nanobsd ( ) (
236	echo "## configure nanobsd setup"
237	echo "### log: ${MAKEOBJDIRPREFIX}/_.dl"
238
239	(
240	cd ${NANO_WORLDDIR}
241
242	# Move /usr/local/etc to /etc/local so that the /cfg stuff
243	# can stomp on it.  Otherwise packages like ipsec-tools which
244	# have hardcoded paths under ${prefix}/etc are not tweakable.
245	if [ -d usr/local/etc ] ; then
246		(
247		mkdir etc/local
248		cd usr/local/etc
249		find . -print | cpio -dumpl ../../../etc/local
250		cd ..
251		rm -rf etc
252		ln -s ../../etc/local etc
253		)
254	fi
255
256	for d in var etc
257	do
258		# link /$d under /conf
259		# we use hard links so we have them both places.
260		# the files in /$d will be hidden by the mount.
261		# XXX: configure /$d ramdisk size
262		mkdir -p conf/base/$d conf/default/$d
263		find $d -print | cpio -dumpl conf/base/
264	done
265
266	echo "$NANO_RAM_ETCSIZE" > conf/base/etc/md_size
267	echo "$NANO_RAM_TMPVARSIZE" > conf/base/var/md_size
268
269	# pick up config files from the special partition
270	echo "mount -o ro /dev/${NANO_DRIVE}s3" > conf/default/etc/remount
271
272	# Put /tmp on the /var ramdisk (could be symlink already)
273	rmdir tmp || true
274	rm tmp || true
275	ln -s var/tmp tmp
276
277	) > ${MAKEOBJDIRPREFIX}/_.dl 2>&1
278)
279
280setup_nanobsd_etc ( ) (
281	echo "## configure nanobsd /etc"
282
283	(
284	cd ${NANO_WORLDDIR}
285
286	# create diskless marker file
287	touch etc/diskless
288
289	# Make root filesystem R/O by default
290	echo "root_rw_mount=NO" >> etc/defaults/rc.conf
291
292	# save config file for scripts
293	echo "NANO_DRIVE=${NANO_DRIVE}" > etc/nanobsd.conf
294
295	echo "/dev/${NANO_DRIVE}s1a / ufs ro 1 1" > etc/fstab
296	echo "/dev/${NANO_DRIVE}s3 /cfg ufs rw,noauto 2 2" >> etc/fstab
297	mkdir -p cfg
298	)
299)
300
301prune_usr() (
302
303	# Remove all empty directories in /usr 
304	find ${NANO_WORLDDIR}/usr -type d -depth -print |
305		while read d
306		do
307			rmdir $d > /dev/null 2>&1 || true 
308		done
309)
310
311create_i386_diskimage ( ) (
312	echo "## build diskimage"
313	echo "### log: ${MAKEOBJDIRPREFIX}/_.di"
314
315	(
316	echo $NANO_MEDIASIZE $NANO_IMAGES \
317		$NANO_SECTS $NANO_HEADS \
318		$NANO_CODESIZE $NANO_CONFSIZE $NANO_DATASIZE |
319	awk '
320	{
321		printf "# %s\n", $0
322
323		# size of cylinder in sectors
324		cs = $3 * $4
325
326		# number of full cylinders on media
327		cyl = int ($1 / cs)
328
329		# output fdisk geometry spec, truncate cyls to 1023
330		if (cyl <= 1023)
331			print "g c" cyl " h" $4 " s" $3
332		else
333			print "g c" 1023 " h" $4 " s" $3
334
335		if ($7 > 0) { 
336			# size of data partition in full cylinders
337			dsl = int (($7 + cs - 1) / cs)
338		} else {
339			dsl = 0;
340		}
341
342		# size of config partition in full cylinders
343		csl = int (($6 + cs - 1) / cs)
344
345		if ($5 == 0) {
346			# size of image partition(s) in full cylinders
347			isl = int ((cyl - dsl - csl) / $2)
348		} else {
349			isl = int (($5 + cs - 1) / cs)
350		}
351
352		# First image partition start at second track
353		print "p 1 165 " $3, isl * cs - $3
354		c = isl * cs;
355
356		# Second image partition (if any) also starts offset one 
357		# track to keep them identical.
358		if ($2 > 1) {
359			print "p 2 165 " $3 + c, isl * cs - $3
360			c += isl * cs;
361		}
362
363		# Config partition starts at cylinder boundary.
364		print "p 3 165 " c, csl * cs
365		c += csl * cs
366
367		# Data partition (if any) starts at cylinder boundary.
368		if ($7 > 0) {
369			print "p 4 165 " c, dsl * cs
370		} else if ($7 < 0 && $1 > $c) {
371			print "p 4 165 " c, $1 - $c
372		} else if ($1 < c) {
373			print "Disk space overcommitted by", \
374			    c - $1, "sectors" > "/dev/stderr"
375			exit 2
376		}
377	}
378	' > ${MAKEOBJDIRPREFIX}/_.fdisk
379
380	IMG=${MAKEOBJDIRPREFIX}/_.disk.full
381	MNT=${MAKEOBJDIRPREFIX}/_.mnt
382	mkdir -p ${MNT}
383
384	dd if=/dev/zero of=${IMG} bs=${NANO_SECTS}b \
385	    count=`expr ${NANO_MEDIASIZE} / ${NANO_SECTS}`
386
387	MD=`mdconfig -a -t vnode -f ${IMG} -x ${NANO_SECTS} -y ${NANO_HEADS}`
388
389	trap "df -i ${MNT} ; umount ${MNT} || true ; mdconfig -d -u $MD" 1 2 15 EXIT
390
391	fdisk -i -f ${MAKEOBJDIRPREFIX}/_.fdisk ${MD}
392	fdisk ${MD}
393	# XXX: params
394	# XXX: pick up cached boot* files, they may not be in image anymore.
395	boot0cfg -B -b ${NANO_WORLDDIR}/${NANO_BOOTLOADER} ${NANO_BOOT0CFG} ${MD}
396	bsdlabel -w -B -b ${NANO_WORLDDIR}/boot/boot ${MD}s1
397	bsdlabel ${MD}s1
398
399	# Create first image
400	newfs ${NANO_NEWFS} /dev/${MD}s1a
401	mount /dev/${MD}s1a ${MNT}
402	df -i ${MNT}
403	( cd ${NANO_WORLDDIR} && find . -print | cpio -dump ${MNT} )
404	df -i ${MNT}
405	( cd ${MNT} && mtree -c ) > ${MAKEOBJDIRPREFIX}/_.mtree
406	( cd ${MNT} && du -k ) > ${MAKEOBJDIRPREFIX}/_.du
407	umount ${MNT}
408
409	if [ $NANO_IMAGES -gt 1 -a $NANO_INIT_IMG2 -gt 0 ] ; then
410		# Duplicate to second image (if present)
411		dd if=/dev/${MD}s1 of=/dev/${MD}s2 bs=64k
412		mount /dev/${MD}s2a ${MNT}
413		for f in ${MNT}/etc/fstab ${MNT}/conf/base/etc/fstab
414		do
415			sed -i "" "s/${NANO_DRIVE}s1/${NANO_DRIVE}s2/g" $f
416		done
417		umount ${MNT}
418
419	fi
420	
421	# Create Config slice
422	newfs ${NANO_NEWFS} /dev/${MD}s3
423	# XXX: fill from where ?
424
425	# Create Data slice, if any.
426	if [ $NANO_DATASIZE -gt 0 ] ; then
427		newfs ${NANO_NEWFS} /dev/${MD}s4
428		# XXX: fill from where ?
429	fi
430
431	dd if=/dev/${MD}s1 of=${MAKEOBJDIRPREFIX}/_.disk.image bs=64k
432	mdconfig -d -u $MD
433	) > ${MAKEOBJDIRPREFIX}/_.di 2>&1
434)
435
436last_orders () (
437	# Redefine this function with any last orders you may have
438	# after the build completed, for instance to copy the finished
439	# image to a more convenient place:
440	# cp ${MAKEOBJDIRPREFIX}/_.disk.image /home/ftp/pub/nanobsd.disk
441)
442
443#######################################################################
444#
445# Optional convenience functions.
446#
447#######################################################################
448
449#######################################################################
450# Common Flash device geometries
451#
452
453FlashDevice () {
454	if [ -d ${NANO_TOOLS} ] ; then
455		. ${NANO_TOOLS}/FlashDevice.sub
456	else
457		. ${NANO_SRC}/${NANO_TOOLS}/FlashDevice.sub
458	fi
459	sub_FlashDevice $1 $2
460}
461
462
463#######################################################################
464# Setup serial console
465
466cust_comconsole () (
467	# Enable getty on console
468	sed -i "" -e /ttyd0/s/off/on/ ${NANO_WORLDDIR}/etc/ttys
469
470	# Disable getty on syscons devices
471	sed -i "" -e '/^ttyv[0-8]/s/	on/	off/' ${NANO_WORLDDIR}/etc/ttys
472
473	# Tell loader to use serial console early.
474	echo " -h" > ${NANO_WORLDDIR}/boot.config
475)
476
477#######################################################################
478# Allow root login via ssh
479
480cust_allow_ssh_root () (
481	sed -i "" -e '/PermitRootLogin/s/.*/PermitRootLogin yes/' \
482	    ${NANO_WORLDDIR}/etc/ssh/sshd_config
483)
484
485#######################################################################
486# Install the stuff under ./Files
487
488cust_install_files () (
489	cd ${NANO_TOOLS}/Files
490	find . -print | grep -v /CVS | cpio -dumpv ${NANO_WORLDDIR}
491)
492
493#######################################################################
494# Install packages from ${NANO_PACKAGE_DIR}
495
496cust_pkg () (
497
498	# Copy packages into chroot
499	mkdir -p ${NANO_WORLDDIR}/Pkg
500	cp ${NANO_PACKAGE_DIR}/* ${NANO_WORLDDIR}/Pkg
501
502	# Count & report how many we have to install
503	todo=`ls ${NANO_WORLDDIR}/Pkg | wc -l`
504	echo "=== TODO: $todo"
505	ls ${NANO_WORLDDIR}/Pkg
506	echo "==="
507	while true
508	do
509		# Record how may we have now
510		have=`ls ${NANO_WORLDDIR}/var/db/pkg | wc -l`
511
512		# Attempt to install more packages
513		# ...but no more than 200 at a time due to pkg_add's internal
514		# limitations.
515		chroot ${NANO_WORLDDIR} sh -c \
516			'ls Pkg/*tbz | xargs -n 200 pkg_add -F' || true
517
518		# See what that got us
519		now=`ls ${NANO_WORLDDIR}/var/db/pkg | wc -l`
520		echo "=== NOW $now"
521		ls ${NANO_WORLDDIR}/var/db/pkg
522		echo "==="
523
524
525		if [ $now -eq $todo ] ; then
526			echo "DONE $now packages"
527			break
528		elif [ $now -eq $have ] ; then
529			echo "FAILED: Nothing happened on this pass"
530			exit 2
531		fi
532	done
533	rm -rf ${NANO_WORLDDIR}/Pkg
534)
535
536#######################################################################
537# Convenience function:
538# 	Register $1 as customize function.
539
540customize_cmd () {
541	NANO_CUSTOMIZE="$NANO_CUSTOMIZE $1"
542}
543
544#######################################################################
545#
546# All set up to go...
547#
548#######################################################################
549
550usage () {
551	(
552	echo "Usage: $0 [-b/-k/-w] [-c config_file]"
553	echo "	-b	suppress builds (both kernel and world)"
554	echo "	-k	suppress buildkernel"
555	echo "	-w	suppress buildworld"
556	echo "	-c	specify config file"
557	) 1>&2
558	exit 2
559}
560
561#######################################################################
562# Parse arguments
563
564do_kernel=true
565do_world=true
566
567set +e
568args=`getopt bc:hkw $*`
569if [ $? -ne 0 ] ; then
570	usage
571	exit 2
572fi
573set -e
574
575set -- $args
576for i
577do
578	case "$i" 
579	in
580	-b)
581		shift;
582		do_world=false
583		do_kernel=false
584		;;
585	-k)
586		shift;
587		do_kernel=false
588		;;
589	-c)
590		. "$2"
591		shift;
592		shift;
593		;;
594	-h)
595		usage
596		;;
597	-w)
598		shift;
599		do_world=false
600		;;
601	--)
602		shift;
603		break;
604	esac
605done
606
607if [ $# -gt 0 ] ; then
608	echo "$0: Extraneous arguments supplied"
609	usage
610fi
611
612#######################################################################
613# Setup and Export Internal variables
614#
615if [ "x${NANO_OBJ}" = "x" ] ; then
616	MAKEOBJDIRPREFIX=/usr/obj/nanobsd.${NANO_NAME}/
617	NANO_OBJ=${MAKEOBJDIRPREFIX}
618else
619	MAKEOBJDIRPREFIX=${NANO_OBJ}
620fi
621
622NANO_WORLDDIR=${MAKEOBJDIRPREFIX}/_.w
623NANO_MAKE_CONF=${MAKEOBJDIRPREFIX}/make.conf
624
625if [ -d ${NANO_TOOLS} ] ; then
626	true
627elif [ -d ${NANO_SRC}/${NANO_TOOLS} ] ; then
628	NANO_TOOLS=${NANO_SRC}/${NANO_TOOLS}
629else
630	echo "NANO_TOOLS directory does not exist" 1>&2
631	exit 1
632fi
633
634export MAKEOBJDIRPREFIX
635
636export NANO_ARCH
637export NANO_CODESIZE
638export NANO_CONFSIZE
639export NANO_CUSTOMIZE
640export NANO_DATASIZE
641export NANO_DRIVE
642export NANO_HEADS
643export NANO_IMAGES
644export NANO_MAKE_CONF
645export NANO_MEDIASIZE
646export NANO_NAME
647export NANO_NEWFS
648export NANO_OBJ
649export NANO_PMAKE
650export NANO_SECTS
651export NANO_SRC
652export NANO_TOOLS
653export NANO_WORLDDIR
654export NANO_BOOT0CFG
655export NANO_BOOTLOADER
656
657#######################################################################
658# And then it is as simple as that...
659
660if $do_world ; then
661	clean_build
662	make_conf_build
663	build_world
664else
665	echo "## Skipping buildworld (as instructed)"
666fi
667
668if $do_kernel ; then
669	build_kernel
670else
671	echo "## Skipping buildkernel (as instructed)"
672fi
673
674clean_world
675make_conf_install
676install_world
677install_etc
678setup_nanobsd_etc
679install_kernel
680
681run_customize
682setup_nanobsd
683prune_usr
684create_${NANO_ARCH}_diskimage
685last_orders
686
687echo "# NanoBSD image completed"
688