1#!/bin/sh -
2# Copyright (c) 2006 Jean Milanez Melo <jmelo@freebsdbrasil.com.br>
3#				       <jmelo@FreeBSD.org>
4#		     Patrick Tracanelli <eksffa@freebsdbrasil.com.br>
5#
6# $FreeBSD: releng/10.3/tools/tools/tinybsd/tinybsd 199812 2009-11-25 19:00:30Z julian $
7#set -xv
8CURRENTDIR=/usr/src/tools/tools/tinybsd
9if [ ! -d $CURRENTDIR ]
10then
11  CURRENTDIR=`pwd`
12else
13  cd $CURRENTDIR
14fi
15
16WORKDIR=/usr/obj/tinybsdbuild
17KERNCONF=TINYBSD
18BASEFILE="tinybsd.basefiles"
19PORTSFILE="tinybsd.ports"
20PORTSDIR=/usr/ports
21DEFINSTARGS="-o 0 -g 0 -m 555"
22TINYARCH=$(uname -p)
23
24TS="=====>"
25
26splitarg1 () {
27   local IFS
28   IFS='='
29   set $1
30   echo $1
31}
32
33splitarg2 () {
34   local IFS
35   IFS='='
36   set $1
37   echo $2
38}
39
40getargs () {
41 ARGS="$*"
42 for arg in $* 
43 do
44   ARG=`splitarg1 $arg`
45   VAL=`splitarg2 $arg`
46   case $ARG in
47     sectors)
48        SECTUNIT=$VAL
49        ;;
50     heads)
51        TRACKCYL=$VAL 
52        ;;
53     spt)
54        SECTRACK=$VAL
55        ;;
56     conf)
57        CONF=$VAL
58        ;;
59     mfsroot)
60        MFSROOT=$VAL
61        ;;
62     image)
63        IMG=$VAL
64        ;;
65     batch)
66        NO_PROMPTS="YES"
67        ;;
68     new)
69        NO_READ="YES"
70        ;;
71     *)
72         usage
73         ;;
74    esac
75 done
76}
77
78usage () {
79    echo "Woops
80    Usage: $0 sectors=<size of media> [80000]
81              heads=<heads according to firmware>  [4]
82              spt=<sectors per track according to firmware> [32]
83              conf=<configuration name> (see conf/name) [default]
84              mfsroot[=<yes|no>] [no]
85              image=<tinybsd image name> [tinybsd.bin]
86              batch[=<anything>]  (do not ask interactively)
87              new[=<anything>]  (do not read previous values)
88
89     Examples:
90    $0 sectors=65536 heads=8 spt=16 conf=wireless mfsroot=yes image=myimage.img batch
91
92     Default values are set in the program.
93     Environment values override defaults.
94     Previous values override environment values but can be disabled.
95     Command arguments override previous values.
96     Interactive values override command arguments but can be disabled. 
97
98     Run diskinfo(8) -v against your CF device to get correct information
99     about your disk. USB keys do not need any specific geometry"
100    exit 1
101}
102
103########
104# Load variables from stdin (could be a file)
105# Look for lines that match foo=bar
106# do not run the file.. that is asking for trouble
107########
108loadvars () {
109  while :
110  do
111    OIFS=$IFS
112    IFS="="
113    if read PART1 PART2
114    then
115      IFS="$OIFS"
116      case "$PART1" in
117      \#*)
118        ;;
119      "")
120        ;;
121      *)
122        set "${PART1}"
123        if [ $# = "1" ]
124        then
125          eval "${PART1}='${PART2}'"
126        fi
127        ;;
128      esac
129    else
130      IFS="$OIFS"
131      return 0
132    fi
133  done
134}
135
136########
137# get values from the user
138########
139confirm_action(){
140    local ANSWER
141    local MESSAGE
142    ANSWER=$1
143    MESSAGE=$2
144    if [ "$NO_PROMPTS" != "YES" ]
145    then
146        echo -n "$MESSAGE [$ANSWER] " > /dev/tty
147        read result
148        [ "$result" != "" ] && ANSWER=$result
149    fi
150
151    ANSWER=`eval "echo $ANSWER"`
152    echo $ANSWER
153}
154
155########
156# These are only used the VERY first time you run the program (on this machine)
157########
158setdefaults () {
159  NO_PROMPTS=${NO_PROMPTS:-NO}
160  NO_READ=${NO_READ:-NO}
161  SECTUNIT=${SECTUNIT:-80000}; export SECTUNIT
162  TRACKCYL=${TRACKCYL:-4}; export TRACKCYL
163  SECTRACK=${SECTRACK:-32}; export SECTRACK
164  CONF=${CONF:-default}; export CONF
165  MFSROOT=${MFSROOT:-NO}; export MFSROOT
166  IMG=${IMG:-tinybsd.bin}; export IMG
167
168}
169
170#######
171# get ourselves set up.
172# Partly by reading config files and partly from asking questions.
173#######
174loadconfig () {
175  if [ "${NO_READ}" = "YES" ]
176  then
177    return
178  fi
179
180  HOSTNAME=`hostname`
181  HOSTPART=${HOSTNAME%%.*}
182  FILENAME=".tinybsd.${HOSTPART}.${USER}"
183  FULLFILENAME=$HOME/$FILENAME
184
185  if [ -f ${FULLFILENAME} ]
186  then
187    loadvars <${FULLFILENAME}
188  fi
189
190  SECTUNIT=`confirm_action "$SECTUNIT" "512 byte sectors per unit?"`
191  TRACKCYL=`confirm_action "$TRACKCYL" "Tracks per cylinder?"`
192  SECTRACK=`confirm_action "$SECTRACK" "Sectors per track?"`
193  while :
194  do
195    echo "The following configurations exist:"
196    ls -l conf|awk '/^d/{print "    ",$9}'|grep -v CVS
197    CONF=`confirm_action "$CONF" "Configuration name?"`
198    if [ ! -d "${CURRENTDIR}/conf/$CONF" ]
199    then
200      echo "${TS} Error: Could not find config (${CONF})"
201      if [ "$NO_PROMPTS" = "YES" ]
202      then
203        exit 1
204      fi
205    else
206      break
207    fi
208  done
209  MFSROOT=`confirm_action "$MFSROOT" "Use an MFSROOT? (YES/NO)"`
210  IMG=`confirm_action "$IMG" "Image file to generate?"`
211
212# example of formatted value (NNN in this case)
213#  #condition and format the number
214#  if [ -z "${BUILDNUM}" ]
215#  then
216#    echo "Starting with build 001"
217#    BUILDNUM="001"
218#  else
219#    BUILDNUM=`printf "%03d\n" $(($BUILDNUM))`
220#  fi
221
222
223}
224
225saveconfig () {
226  HOSTNAME=`hostname`
227  HOSTPART=${HOSTNAME%%.*}
228  FILENAME=".tinybsd.${HOSTPART}.${USER}"
229  FULLFILENAME=$HOME/$FILENAME
230  (
231    echo "# written by tinybsd" `date` 
232    echo "SECTUNIT=${SECTUNIT}"
233    echo "TRACKCYL=${TRACKCYL}"
234    echo "SECTRACK=${SECTRACK}"
235    echo "CONF=${CONF}"
236    echo "MFSROOT=${MFSROOT:-NO}"
237    echo "IMG=${IMG}"
238  ) >${FULLFILENAME}
239}
240
241check_alt_imgname() {
242	if [ ${IMG} = 'tinybsd.bin' ]
243	then
244		echo "${TS} Alternative image name not set; defaulting to 'tinybsd.bin'"
245	fi
246}
247
248rotate_buidlog() {
249	mv -f ${HOME}/tinybsd.log ${HOME}/tinybsd.log.old
250}
251
252remove_workdir() {
253	# Before removing check if there is not a mount under $WORKDIR anymore
254	MOUNT_CHECK=`mount|egrep "on ${WORKDIR}"`
255
256	if [ ! -z "${MOUNT_CHECK}" ]; then
257		echo "There are mounts under the workdir (${WORKDIR}). Please umount them before running this script"
258		exit 1
259	else
260		chflags -R noschg ${WORKDIR}
261		echo "${TS} Removing "${WORKDIR}
262		rm -rf ${WORKDIR}
263		echo "${TS} Removing Build Kernel Directory"
264		rm -rf /usr/obj/usr/src/sys/${KERNCONF}
265		echo "${TS}  done."
266	fi
267}
268
269
270prework() {
271	remove_workdir
272	mkdir -p ${WORKDIR}
273}
274
275
276create_tree() {
277	echo "${TS} Creating directory hierarchy... "
278	mtree -deU -f /etc/mtree/BSD.root.dist -p ${WORKDIR}
279	mtree -deU -f /etc/mtree/BIND.chroot.dist -p ${WORKDIR}
280	mtree -deU -f /etc/mtree/BSD.usr.dist -p ${WORKDIR}/usr
281	mtree -deU -f /etc/mtree/BSD.local.dist -p ${WORKDIR}/usr/local
282	mtree -deU -f /etc/mtree/BSD.var.dist -p ${WORKDIR}/var
283}
284
285copy_binaries() {
286	cd ${CURRENTDIR}/conf/${CONF}
287
288	for file in `cat ${CURRENTDIR}/conf/${CONF}/${BASEFILE} | grep -v "#" | \
289		cut -f1 -d":" | sort | uniq` ; do
290		echo "${TS} Copying "/${file}" to "${WORKDIR}/${file} 
291		cp -fp /${file} ${WORKDIR}/${file} ;
292	done
293}
294
295install_ports() {
296	for portname in `cat ${CURRENTDIR}/conf/${CONF}/${PORTSFILE} | grep -v '#'` ; do
297		if [ ! -d "${WORKDIR}/usr/ports" ]; then
298			mkdir -p "${WORKDIR}/usr/ports"
299		fi
300
301		PNAME=`/usr/bin/basename "${portname}"`
302		PORT_OPTION_FILE="/var/db/ports/${PNAME}/options"
303
304		if [ -f "${PORT_OPTION_FILE}" ]; then
305			mkdir -p "${WORKDIR}/var/db/ports/${PNAME}"
306			cp "${PORT_OPTION_FILE}" "${WORKDIR}/var/db/ports/${PNAME}/"
307		fi
308
309		mount_nullfs /lib "${WORKDIR}/lib"
310		mount_nullfs /usr/bin "${WORKDIR}/usr/bin"
311		mount_nullfs /usr/sbin "${WORKDIR}/usr/sbin"
312		mount_nullfs /usr/ports "${WORKDIR}/usr/ports"
313		mount_nullfs /usr/share "${WORKDIR}/usr/share"
314		mount_nullfs /usr/libexec "${WORKDIR}/usr/libexec"
315		mount_nullfs /usr/lib "${WORKDIR}/usr/lib"
316		mount_nullfs /usr/include "${WORKDIR}/usr/include"
317
318		cd ${PORTSDIR}/${portname}
319		make fetch-recursive
320		make DESTDIR_ENV_LIST=PATH DESTDIR="${WORKDIR}" install
321		make clean
322
323		umount "${WORKDIR}/lib"
324		umount "${WORKDIR}/usr/ports"
325		umount "${WORKDIR}/usr/bin"
326		umount "${WORKDIR}/usr/sbin"
327		umount "${WORKDIR}/usr/share"
328		umount "${WORKDIR}/usr/libexec"
329		umount "${WORKDIR}/usr/lib"
330		umount "${WORKDIR}/usr/include"
331	done
332}
333
334make_kernel() {
335	echo "${TS} Building customized tiny beastie kernel... "
336	cp -p ${CURRENTDIR}/conf/${CONF}/${KERNCONF} /usr/src/sys/${TINYARCH}/conf
337	cd /usr/src
338	make buildkernel KERNCONF=${KERNCONF} || exit 1
339	gzip -9 /usr/obj/usr/src/sys/${KERNCONF}/kernel
340	install ${DEFINSTARGS} /usr/obj/usr/src/sys/${KERNCONF}/kernel.gz ${WORKDIR}/boot/kernel/
341	install ${DEFINSTARGS} \
342	  /usr/obj/usr/src/sys/${KERNCONF}/modules/usr/src/sys/modules/acpi/acpi/acpi.ko \
343	    ${WORKDIR}/boot/kernel
344	install -o 0 -g 0 -m 444 /sys/${TINYARCH}/conf/GENERIC.hints ${WORKDIR}/boot/device.hints
345}
346
347copy_libraries() {
348	TDEPFILE="`mktemp -t deps`"
349	TDEPFILES="`mktemp -t depsymlnk`"
350
351	find "${WORKDIR}" -type f |while read file; do
352		ldd -f "%p\n" ${file} >> ${TDEPFILE} ; # don't worry on progs been "not dynamic"
353	done
354
355	for libdeplib in `cat ${TDEPFILE} | sort | uniq`; do
356		ldd -f "%p\n" /${libdeplib} >> ${TDEPFILE} ;
357	done
358
359	for pamdep in `ls -1 /usr/lib/pam*`; do
360		echo $pamdep >> ${TDEPFILE} ;
361		ldd -f "%p\n" /${pamdep} >> ${TDEPFILE} ;
362	done	
363
364	for lib in `cat ${TDEPFILE} | sort | uniq`; do
365		echo "${TS} Copying "${lib}" to "${WORKDIR}${lib}
366		cp -fp ${lib} ${WORKDIR}${lib} ;
367	done
368
369	for depsymlink in `cat ${TDEPFILE}`; do
370		echo "${TS} Checking if ${depsymlink} is a symbolic link"
371		/bin/ls -l $depsymlink | grep "\->" | awk '{print $11":"$9}' >> ${TDEPFILES}
372	done
373
374	for i in `cat ${TDEPFILES}`; do
375                SOURCE_FILE=`echo $i | awk -F ":" '{print $1}'`
376                TARGET_FILE=`echo $i | awk -F ":" '{print $2}'`
377
378		echo "${TS} Unlinking ${WORKDIR}${TARGET_FILE}"
379		chroot ${WORKDIR} /bin/chflags 0 ${TARGET_FILE}
380                chroot ${WORKDIR} /bin/rm -f ${TARGET_FILE}
381
382		echo "${TS} Symlinking ${SOURCE_FILE} to ${TARGET_FILE}"
383                chroot ${WORKDIR} /bin/ln -s ${SOURCE_FILE} ${TARGET_FILE}
384        done
385
386	echo -n "${TS} Unlinking "
387	rm -fv ${TDEPFILE} ${TDEPFILES}
388}
389
390create_etc() {
391	cd /usr/src/etc/sendmail/
392	make freebsd.cf freebsd.submit.cf
393
394	cd /usr/src/etc/
395	mkdir -p ${WORKDIR}/var/named/etc/namedb
396	make distribution DESTDIR=${WORKDIR} || exit 1
397}
398
399create_ssh_keys() {
400	echo "Creating ssh keys..."
401	ssh-keygen -t rsa1 -b 1024  -f ${WORKDIR}/etc/ssh/ssh_host_key -N ''
402	ssh-keygen -t dsa -f ${WORKDIR}/etc/ssh/ssh_host_dsa_key -N ''
403	ssh-keygen -t rsa -f ${WORKDIR}/etc/ssh/ssh_host_rsa_key -N ''
404}
405
406personal_conf() {
407	echo "${TS} Copying your custom configuration on conf/ ..."
408	for custom in `find ${CURRENTDIR}/conf/${CONF}/ -type d -depth 1 \! -name CVS`; do
409		cp -Rp ${custom}/* ${WORKDIR}/${custom#${CURRENTDIR}/conf/${CONF}/}/
410	done
411
412	if [ -f ${CURRENTDIR}/conf/${CONF}/boot.config ]; then
413		cp ${CURRENTDIR}/conf/${CONF}/boot.config ${WORKDIR}/boot.config
414	fi
415}
416
417symlinks() {
418#set -xv
419	for i in `cat ${CURRENTDIR}/conf/${CONF}/${BASEFILE}| grep -v "#" | grep ":"`; do
420		SOURCE_FILE=`echo $i | awk -F ":" {'print $1'}`
421		TARGET_FILE=`echo $i | awk -F ":" {'print $2'}`
422		chroot ${WORKDIR} /bin/ln -vs /${SOURCE_FILE} ${TARGET_FILE}
423	done
424#set +xv
425}
426
427
428create_image() {
429	VNODEFILE=`mktemp -t tinybsd`
430	IMGMNT=`mktemp -d -t tinybsd`
431
432	dd if=/dev/zero of=${VNODEFILE} count=${SECTUNIT}
433
434	MD=`mdconfig -a -t vnode -f ${VNODEFILE} -x ${SECTRACK} -y ${TRACKCYL}`
435
436	diskinfo -v /dev/${MD}
437
438	fdisk -I /dev/${MD}
439	fdisk /dev/${MD}
440
441	cp -p /boot/boot0 ${WORKDIR}/boot/boot0
442	bsdlabel -w -B /dev/${MD}
443	newfs -O2 -U /dev/${MD}a
444
445	mount /dev/${MD}a ${IMGMNT}
446
447	if [ ${MFSROOT} = 'yes' ]
448	then
449		echo "${TS} Creating MFS root..."
450		# Update is not done yet
451		#mkdir -p ${WORKDIR}/usr/local/bin/
452		#cp -p ${CURRENTDIR}/update/update ${WORKDIR}/usr/local/bin/
453		rm ${WORKDIR}/etc/fstab
454		cd ${WORKDIR} && find . -print | sed '/kernel/ d' | cpio -dump ${IMGMNT} || true
455		umount ${IMGMNT}
456		dd if=/dev/${MD} of=${CURRENTDIR}/mfsroot.img		
457		gzip -9 < ${CURRENTDIR}/mfsroot.img > ${CURRENTDIR}/mfsroot.gz
458		rm ${CURRENTDIR}/mfsroot.img
459		mount /dev/${MD}a ${IMGMNT}
460		rm -rf ${IMGMNT}/*
461		cp -rp ${WORKDIR}/boot ${IMGMNT}
462		rm ${IMGMNT}/boot/device.hints
463		( \
464		echo 'set vfs.root.mountfrom="ufs:/dev/md0a"' ; \
465		echo 'set bootfile="/boot/kernel/kernel"' ; \
466		sed -e  '/^#/ d' -e 's/^/set /' < ${WORKDIR}/boot/device.hints ; \
467		echo 'load /boot/kernel/kernel' ; \
468		echo 'echo Loading mfsroot' ; \
469		echo 'load -t mfs_root /mfsroot' ;\
470		echo 'boot' ; \
471		) > ${IMGMNT}/boot/loader.rc
472		mv ${CURRENTDIR}/mfsroot.gz ${IMGMNT}
473	else
474		( cd ${WORKDIR} && find . -print | cpio -dump ${IMGMNT} ) || true
475	fi
476
477	df ${IMGMNT}
478	sleep 1
479	umount ${IMGMNT}
480
481	dd if=/dev/${MD} of=${CURRENTDIR}/${IMG} bs=64k
482
483	rm -vf ${VNODEFILE}
484	rm -rvf ${IMGMNT}
485	mdconfig -d -u ${MD}
486	echo ""
487	echo "${TS} Done!"
488	echo "${TS} Your configuration options were saved in ${FULLFILENAME}"
489	echo "${TS} You can see your build log in ${HOME}/tinybsd.log"
490	echo "${TS} Your final image is in ${CURRENTDIR}/${IMG}"
491	echo "${TS} Now use dd(1) to write it."
492}
493
494##########################
495# run things
496
497##########################################
498## The actual program
499##########################################
500getargs $*
501setdefaults
502# Load as many of the configuration options as we can
503loadconfig
504saveconfig
505
506# Rotate build log
507rotate_buidlog
508
509# Now start logging.
510(
511  # Do the build
512  prework
513  check_alt_imgname
514  create_tree
515  copy_binaries
516  create_etc
517  install_ports
518  make_kernel
519  copy_libraries
520  symlinks
521  create_ssh_keys
522  personal_conf
523  create_image
524#set +xv
525) 2>&1 |tee -a  ${HOME}/tinybsd.log
526
527