1#!/bin/sh
2#
3# Copyright (c) 1994-2009 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$
28#
29
30set -e
31
32exec < /dev/null
33
34if [ `uname -m` = "i386" -o `uname -m` = "amd64" ] ; then
35	TARGET_PART=`df / | sed '
36	1d
37	s/[    ].*//
38	s,/dev/,,
39	s,s1a,s3a,
40	s,s2a,s1a,
41	s,s3a,s2a,
42	'`
43
44	FREEBSD_PART=`sed -n	\
45		-e 's/#.*//'	\
46		-e '/[ 	]\/freebsd[ 	]/!d'	\
47		-e 's/[ 	].*//p'	\
48		/etc/fstab`
49
50	# Calculate a suggested gpart command
51	TARGET_DISK=`expr ${TARGET_PART} : '\(.*\)s[12]a$' || true`
52	TARGET_SLICE=`expr ${TARGET_PART} : '.*s\([12]\)a$' || true`
53	GPART_SUGGESTION="gpart set -a active -i $TARGET_SLICE /dev/$TARGET_DISK"
54	unset TARGET_DISK TARGET_SLICE
55else
56	TARGET_PART=unknown
57	FREEBSD_PART=unknown
58	GPART_SUGGESTION=unknown
59fi
60
61
62# Relative to /freebsd
63PORTS_PATH=ports
64SRC_PATH=src
65# OBJ_PATH=obj
66
67# Name of kernel
68KERNCONF=GENERIC
69
70# srcconf
71#SRCCONF="SRCCONF=/usr/src/src.conf"
72
73# -j arg to make(1)
74
75ncpu=`sysctl -n kern.smp.cpus`
76if [ $ncpu -gt 1 ] ; then
77	JARG="-j $ncpu"
78fi
79
80# serial console ?
81SERCONS=false
82
83# Remotely mounted distfiles
84# REMOTEDISTFILES=fs:/rdonly/distfiles
85
86# Proxy
87#FTP_PROXY=http://127.0.0.1:3128/
88#HTTP_PROXY=http://127.0.0.1:3128/
89#export FTP_PROXY HTTP_PROXY
90
91PORTS_WE_WANT='
92'
93
94PORTS_OPTS="BATCH=YES A4=yes"
95
96CONFIGFILES='
97'
98
99SBMNT="/mnt.sysbuild"
100
101cleanup() (
102)
103
104before_ports() (
105)
106
107before_ports_chroot() (
108)
109
110final_root() (
111)
112
113final_chroot() (
114)
115
116#######################################################################
117# -P is a pretty neat way to clean junk out from your ports dist-files:
118#
119#	mkdir /freebsd/ports/distfiles.old
120#	mv /freebsd/ports/distfiles/* /freebsd/ports/distfiles.old
121#	sh sysbuild.sh -c $yourconfig -P /freebsd/ports/distfiles.old
122#	rm -rf /freebsd/ports/distfiles.old
123#
124# Unfortunately bsd.ports.mk does not attempt to use a hard-link so
125# while this runs you need diskspace for both your old and your "new"
126# distfiles.
127#
128#######################################################################
129
130usage () {
131	(
132        echo "Usage: $0 [-b/-k/-w] [-c config_file]"
133        echo "  -b      suppress builds (both kernel and world)"
134        echo "  -k      suppress buildkernel"
135        echo "  -w      suppress buildworld"
136        echo "  -p      used cached packages"
137        echo "  -P <dir> prefetch ports"
138        echo "  -c      specify config file"
139        ) 1>&2
140        exit 2
141}
142
143#######################################################################
144#######################################################################
145
146if [ ! -f $0 ] ; then
147	echo "Must be able to access self ($0)" 1>&2
148	exit 1
149fi
150
151if grep -q 'Magic String: 0`0nQT40W%l,CX&' $0 ; then
152	true
153else
154	echo "self ($0) does not contain magic string" 1>&2
155	exit 1
156fi
157
158#######################################################################
159
160set -e
161
162log_it() (
163	set +x
164	a="$*"
165	set `cat /tmp/_sb_log`
166	TX=`date +%s`
167	echo "$1 $TX" > /tmp/_sb_log
168	DT=`expr $TX - $1 || true`
169	DL=`expr $TX - $2 || true`
170	echo -n "### `date +%H:%M:%S`"
171	printf " ### %5d ### %5d ### %s\n" $DT $DL "$a"
172)
173
174#######################################################################
175
176
177ports_recurse() (
178	set +x
179	t=$1
180	shift
181	if [ "x$t" = "x." ] ; then
182		true > /tmp/_.plist
183		true > /tmp/_.plist.tdone
184		echo 'digraph {' > /tmp/_.plist.dot
185	fi
186	if grep -q "^$t\$" /tmp/_.plist.tdone ; then
187		return
188	fi
189	echo "$t" >> /tmp/_.plist.tdone
190	for d
191	do
192		if [ ! -d $d ] ; then
193			echo "Missing port $d" 1>&2
194			continue
195		fi
196		if [ ! -f $d/Makefile ] ; then
197			echo "Missing port $d" 1>&2
198			continue
199		fi
200		if [ "x$t" != "x." ] ; then
201			echo "\"$t\" -> \"$d\"" >> /tmp/_.plist.dot
202		fi
203		if grep -q "^$d\$" /tmp/_.plist ; then
204			true
205		elif grep -q "^$d\$" /tmp/_.plist.tdone ; then
206			true
207		else
208			(
209			cd $d
210			ports_recurse $d `make -V _DEPEND_DIRS ${PORTS_OPTS}`
211			)
212			echo "$d" >> /tmp/_.plist
213		fi
214	done
215	if [ "x$t" = "x." ] ; then
216		echo '}' >> /tmp/_.plist.dot
217	fi
218)
219
220ports_build() (
221	set +x
222
223	ports_recurse . $PORTS_WE_WANT 
224
225	# Now build & install them
226	for p in `cat /tmp/_.plist`
227	do
228		b=`echo $p | tr / _`
229		t=`echo $p | sed 's,/usr/ports/,,'`
230		pn=`cd $p && make package-name`
231
232		if pkg info $pn > /dev/null 2>&1 ; then
233			log_it "Already installed: $t ($pn)"
234			continue
235		fi
236
237		if [ "x$p" == "x/usr/ports/ports-mgmt/pkg" ] ; then
238			log_it "Very Special: $t ($pn)"
239			(
240			cd $p
241			make clean all install ${PORTS_OPTS}
242			) > _.$b 2>&1 < /dev/null
243			continue
244		fi
245
246		if [ "x${PKG_DIR}" != "x" -a -f ${PKG_DIR}/$pn.txz ] ; then
247			if [ "x$use_pkg" = "x-p" ] ; then
248				log_it "Install $t ($pn)"
249				(
250				set +e
251				pkg add ${PKG_DIR}/$pn.txz || true
252				) > _.$b 2>&1 < /dev/null
253				continue
254			fi
255		fi
256
257		miss=`(cd $p ; make missing ${PORTS_OPTS}) || true`
258
259		if [ "x${miss}" != "x" ] ; then
260			log_it "MISSING for $p:" $miss
261			continue
262		fi
263
264		log_it "build $pn ($p)"
265		(
266			set +e
267			cd $p
268			make clean ${PORTS_OPTS}
269			if make install ${PORTS_OPTS} ; then
270				if [ "x${PKG_DIR}" != "x" ] ; then
271					make package ${PORTS_OPTS}
272				fi
273			else
274				log_it FAIL build $p
275			fi
276			make clean
277		) > _.$b 2>&1 < /dev/null
278	done
279)
280
281ports_prefetch() (
282	(
283	set +x
284	ldir=$1
285	true > /${ldir}/_.prefetch
286	echo "Building /tmp/_.plist" >> /${ldir}/_.prefetch
287
288	ports_recurse . $PORTS_WE_WANT
289
290	echo "Completed /tmp/_.plist" >> /${ldir}/_.prefetch
291	# Now checksump/fetch them
292	for p in `cat /tmp/_.plist`
293	do
294		b=`echo $p | tr / _`
295		(
296			cd $p
297			if make checksum $PORTS_OPTS ; then
298				rm -f /${ldir}/_.prefetch.$b
299				echo "OK $p" >> /${ldir}/_.prefetch
300				exit 0
301			fi
302			make distclean
303			make checksum $PORTS_OPTS || true
304
305			if make checksum $PORTS_OPTS > /dev/null 2>&1 ; then
306				rm -f /${ldir}/_.prefetch.$b
307				echo "OK $p" >> /${ldir}/_.prefetch
308			else
309				echo "BAD $p" >> /${ldir}/_.prefetch
310			fi
311		) > /${ldir}/_.prefetch.$b 2>&1
312	done
313	echo "Done" >> /${ldir}/_.prefetch
314	) 
315)
316
317#######################################################################
318
319do_world=true
320do_kernel=true
321do_prefetch=false
322use_pkg=""
323c_arg=""
324
325set +e
326args=`getopt bc:hkpP:w $*`
327if [ $? -ne 0 ] ; then
328	usage
329fi
330set -e
331
332set -- $args
333for i
334do
335	case "$i"
336	in
337	-b)
338		shift;
339		do_world=false
340		do_kernel=false
341		;;
342	-c)
343		c_arg=$2
344		if [ ! -f "$c_arg" ] ; then
345			echo "Cannot read $c_arg" 1>&2
346			usage
347		fi
348		. "$2"
349		shift
350		shift
351		;;
352	-h)
353		usage
354		;;
355	-k)
356		shift;
357		do_kernel=false
358		;;
359	-p)
360		shift;
361		use_pkg="-p"
362		;;
363	-P)
364		shift;
365		do_prefetch=true
366		distfile_cache=$1
367		shift;
368		;;
369	-w)
370		shift;
371		do_world=false
372		;;
373	--)
374		shift
375		break;
376		;;
377	esac
378done
379
380#######################################################################
381
382if [ "x$1" = "xchroot_script" ] ; then
383	set +x
384	set -e
385
386	shift
387
388	before_ports_chroot
389
390	ports_build
391
392	exit 0
393fi
394
395if [ "x$1" = "xfinal_chroot" ] ; then
396	final_chroot
397	exit 0
398fi
399
400if [ $# -gt 0 ] ; then
401        echo "$0: Extraneous arguments supplied"
402        usage
403fi
404
405#######################################################################
406
407T0=`date +%s`
408echo $T0 $T0 > /tmp/_sb_log
409
410[ ! -d ${SBMNT} ] && mkdir -p ${SBMNT}
411
412if $do_prefetch ; then
413	rm -rf /tmp/sysbuild/ports
414	mkdir -p /tmp/sysbuild/ports
415	ln -s ${distfile_cache} /tmp/sysbuild/ports/distfiles
416	export PORTS_OPTS=CD_MOUNTPTS=/tmp/sysbuild
417	ports_prefetch /tmp 
418	exit 0
419fi
420
421log_it Unmount everything
422(
423	( cleanup )
424	umount /freebsd/distfiles || true
425	umount ${SBMNT}/freebsd/distfiles || true
426	umount ${FREEBSD_PART} || true
427	umount ${SBMNT}/freebsd || true
428	umount ${SBMNT}/dev || true
429	umount ${SBMNT} || true
430	umount /dev/${TARGET_PART} || true
431) # > /dev/null 2>&1
432
433log_it Prepare running image
434mkdir -p /freebsd
435mount ${FREEBSD_PART} /freebsd
436
437#######################################################################
438
439if [ ! -d /freebsd/${PORTS_PATH} ] ;  then
440	echo PORTS_PATH does not exist 1>&2
441	exit 1
442fi
443
444if [ ! -d /freebsd/${SRC_PATH} ] ;  then
445	echo SRC_PATH does not exist 1>&2
446	exit 1
447fi
448
449log_it TARGET_PART $TARGET_PART
450sleep 5
451
452rm -rf /usr/ports
453ln -s /freebsd/${PORTS_PATH} /usr/ports
454
455rm -rf /usr/src
456ln -s /freebsd/${SRC_PATH} /usr/src
457
458if $do_world ; then
459	if [ "x${OBJ_PATH}" != "x" ] ; then
460		rm -rf /usr/obj
461		mkdir -p /freebsd/${OBJ_PATH}
462		ln -s /freebsd/${OBJ_PATH} /usr/obj
463	else
464		rm -rf /usr/obj
465		mkdir -p /usr/obj
466	fi
467fi
468
469#######################################################################
470
471for i in ${PORTS_WE_WANT}
472do
473	if [ ! -d $i ]  ; then
474		echo "Port $i not found" 1>&2
475		exit 2
476	fi
477done
478
479export PORTS_WE_WANT
480export PORTS_OPTS
481
482#######################################################################
483
484log_it Prepare destination partition
485newfs -t -E -O2 -U /dev/${TARGET_PART} > /dev/null
486mount /dev/${TARGET_PART} ${SBMNT}
487mkdir -p ${SBMNT}/dev
488mount -t devfs devfs ${SBMNT}/dev
489
490if [ "x${REMOTEDISTFILES}" != "x" ] ; then
491	rm -rf /freebsd/${PORTS_PATH}/distfiles
492	ln -s /freebsd/distfiles /freebsd/${PORTS_PATH}/distfiles
493	mkdir -p /freebsd/distfiles
494	mount  ${REMOTEDISTFILES} /freebsd/distfiles
495fi
496
497log_it copy ports config files
498(cd / ; find var/db/ports -print | cpio -dumpv ${SBMNT} > /dev/null 2>&1)
499
500log_it "Start prefetch of ports distfiles"
501ports_prefetch ${SBMNT} &
502
503if $do_world ; then
504	(
505	cd /usr/src
506	log_it "Buildworld"
507	make ${JARG} -s buildworld ${SRCCONF} > ${SBMNT}/_.bw 2>&1
508	)
509fi
510
511if $do_kernel ; then
512	(
513	cd /usr/src
514	log_it "Buildkernel"
515	make ${JARG} -s buildkernel KERNCONF=$KERNCONF > ${SBMNT}/_.bk 2>&1
516	)
517fi
518
519
520log_it Installworld
521(cd /usr/src && make ${JARG} installworld DESTDIR=${SBMNT} ${SRCCONF} ) \
522	> ${SBMNT}/_.iw 2>&1
523
524log_it distribution
525(cd /usr/src/etc && make -m /usr/src/share/mk distribution DESTDIR=${SBMNT} ${SRCCONF} ) \
526	> ${SBMNT}/_.dist 2>&1
527
528log_it Installkernel
529(cd /usr/src && make ${JARG} installkernel DESTDIR=${SBMNT} KERNCONF=$KERNCONF ) \
530	> ${SBMNT}/_.ik 2>&1
531
532if [ "x${OBJ_PATH}" != "x" ] ; then
533	rmdir ${SBMNT}/usr/obj
534	ln -s /freebsd/${OBJ_PATH} ${SBMNT}/usr/obj
535fi
536
537log_it Wait for ports prefetch
538log_it "(Tail ${SBMNT}/_.prefetch for progress)"
539wait
540
541log_it Move filesystems
542
543if [ "x${REMOTEDISTFILES}" != "x" ] ; then
544	umount /freebsd/distfiles
545fi
546umount ${FREEBSD_PART} || true
547mkdir -p ${SBMNT}/freebsd
548mount ${FREEBSD_PART} ${SBMNT}/freebsd
549if [ "x${REMOTEDISTFILES}" != "x" ] ; then
550	mount  ${REMOTEDISTFILES} ${SBMNT}/freebsd/distfiles
551fi
552
553rm -rf ${SBMNT}/usr/ports || true
554ln -s /freebsd/${PORTS_PATH} ${SBMNT}/usr/ports
555
556rm -rf ${SBMNT}/usr/src || true
557ln -s /freebsd/${SRC_PATH} ${SBMNT}/usr/src
558
559log_it Build and install ports
560
561# Make sure fetching will work in the chroot
562if [ -f /etc/resolv.conf ] ; then
563	log_it copy resolv.conf
564	cp /etc/resolv.conf ${SBMNT}/etc
565	chflags schg ${SBMNT}/etc/resolv.conf
566fi
567
568if [ -f /etc/localtime ] ; then
569	log_it copy localtime
570	cp /etc/localtime ${SBMNT}/etc
571fi
572
573log_it ldconfig in chroot
574chroot ${SBMNT} sh /etc/rc.d/ldconfig start
575
576log_it before_ports
577( 
578	before_ports 
579)
580
581log_it fixing fstab
582sed "/[ 	]\/[ 	]/s;^[^ 	]*[ 	];/dev/${TARGET_PART}	;" \
583	/etc/fstab > ${SBMNT}/etc/fstab
584
585log_it build ports
586
587cp $0 ${SBMNT}/root
588cp /tmp/_sb_log ${SBMNT}/tmp
589b=`basename $0`
590if [ "x$c_arg" != "x" ] ; then
591	cp $c_arg ${SBMNT}/root
592	chroot ${SBMNT} sh /root/$0 -c /root/`basename $c_arg` $use_pkg chroot_script 
593else
594	chroot ${SBMNT} sh /root/$0 $use_pkg chroot_script
595fi
596cp ${SBMNT}/tmp/_sb_log /tmp
597
598log_it create all mountpoints
599grep -v '^[ 	]*#' ${SBMNT}/etc/fstab | 
600while read a b c
601do
602	mkdir -p ${SBMNT}/$b
603done
604
605if [ "x$SERCONS" != "xfalse" ] ; then
606	log_it serial console
607	echo " -h" > ${SBMNT}/boot.config
608	sed -i "" -e /ttyd0/s/off/on/ ${SBMNT}/etc/ttys
609	sed -i "" -e /ttyu0/s/off/on/ ${SBMNT}/etc/ttys
610	sed -i "" -e '/^ttyv[0-8]/s/	on/	off/' ${SBMNT}/etc/ttys
611fi
612
613log_it move dist config files "(expect warnings)"
614(
615	cd ${SBMNT}
616	mkdir root/configfiles_dist
617	find ${CONFIGFILES} -print | cpio -dumpv root/configfiles_dist
618)
619
620log_it copy live config files
621(cd / && find ${CONFIGFILES} -print | cpio -dumpv ${SBMNT})
622
623log_it final_root
624( final_root )
625log_it final_chroot
626cp /tmp/_sb_log ${SBMNT}/tmp
627if [ "x$c_arg" != "x" ] ; then
628	chroot ${SBMNT} sh /root/$0 -c /root/`basename $c_arg` final_chroot
629else
630	chroot ${SBMNT} sh /root/$0 final_chroot
631fi
632cp ${SBMNT}/tmp/_sb_log /tmp
633log_it "Check these messages (if any):"
634grep '^Stop' ${SBMNT}/_* || true
635log_it DONE
636echo "Now you probably want to:"
637echo "    $GPART_SUGGESTION"
638echo "    shutdown -r now"
639