Home | History | Annotate | Line # | Download | only in embedded
mkimage revision 1.13
      1   1.1       agc #! /bin/sh
      2   1.1       agc 
      3  1.13  christos # $NetBSD: mkimage,v 1.13 2013/01/16 23:27:34 christos Exp $
      4  1.13  christos #
      5   1.1       agc # Copyright (c) 2012 Alistair Crooks <agc (at] NetBSD.org>
      6   1.1       agc # All rights reserved.
      7   1.1       agc #
      8   1.1       agc # Redistribution and use in source and binary forms, with or without
      9   1.1       agc # modification, are permitted provided that the following conditions
     10   1.1       agc # are met:
     11   1.1       agc # 1. Redistributions of source code must retain the above copyright
     12   1.1       agc #    notice, this list of conditions and the following disclaimer.
     13   1.1       agc # 2. Redistributions in binary form must reproduce the above copyright
     14   1.1       agc #    notice, this list of conditions and the following disclaimer in the
     15   1.1       agc #    documentation and/or other materials provided with the distribution.
     16   1.1       agc #
     17   1.1       agc # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18   1.1       agc # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19   1.1       agc # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20   1.1       agc # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21   1.1       agc # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22   1.1       agc # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23   1.1       agc # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24   1.1       agc # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25   1.1       agc # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26   1.1       agc # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27   1.1       agc #
     28   1.1       agc 
     29   1.1       agc # find next available vnd, from kre
     30   1.1       agc next_avail ()
     31   1.1       agc {
     32   1.1       agc 	local dev="$1"
     33   1.1       agc 	local N=$(( ${#dev} + 1 ))
     34   1.1       agc 	local unit units
     35   1.1       agc 
     36   1.1       agc 	units=$(
     37   1.1       agc 		sysctl -n hw.disknames		|
     38   1.1       agc 			tr ' ' '\012'		|
     39   1.1       agc 			grep '^'"${dev}"'[0-9]'	|
     40   1.1       agc 			sort -n -k 1.$N			)
     41   1.1       agc 
     42   1.1       agc 	test -z "${units}" && {
     43   1.1       agc 		test -e "/dev/${dev}0a" || {
     44   1.1       agc 			echo >&2 "No ${dev}s available!"
     45   1.1       agc 			return 1
     46   1.1       agc 		}
     47   1.1       agc 		echo "${dev}0"
     48   1.1       agc 		return
     49   1.1       agc 	}
     50   1.1       agc 
     51   1.1       agc 	N=0
     52   1.1       agc 	for unit in ${units}
     53   1.1       agc 	do
     54   1.1       agc 		if [ "${unit}" = "${dev}${N}" ]
     55   1.1       agc 		then
     56   1.1       agc 			N=$(( N + 1 ))
     57   1.1       agc 		else
     58   1.1       agc 			echo "${dev}${N}"
     59   1.1       agc 			return
     60   1.1       agc 		fi
     61   1.1       agc 	done
     62   1.1       agc 
     63   1.1       agc 	test -e /dev/"${dev}${N}a" || {
     64   1.1       agc 		echo >&2 "All ${dev}s in use"
     65   1.1       agc 		return 1
     66   1.1       agc 	}
     67   1.1       agc 
     68   1.1       agc 	echo "${dev}${N}"
     69   1.1       agc }
     70   1.1       agc 
     71  1.12  christos usage() {
     72  1.12  christos 	cat << EOF 1>&2
     73  1.12  christos Usage: $PROG [-S <setsdir>] [-c <custom-files-dir>] [-h <host-arch>] [-s <size>]
     74  1.12  christos EOF
     75  1.13  christos 	exit 1
     76  1.12  christos }
     77  1.12  christos 
     78  1.12  christos # Return the filesystem size for an ls -l or tar -xvf list
     79  1.12  christos # Directories and symlinks in tar are 0 size, we assume one block
     80  1.12  christos # (which is too much), we round up by the fragment size the rest.
     81  1.12  christos getfssize() {
     82  1.12  christos 	local bsize="$1"
     83  1.12  christos 	local fsize="$2"
     84  1.12  christos 
     85  1.12  christos 	awk -v fsize=${fsize} -v bsize=${bsize} '
     86  1.12  christos 	NF >= 9 && $1 != "tar:" {
     87  1.12  christos 		if ($5 == 0)
     88  1.12  christos 			tot += bsize;
     89  1.12  christos 		else
     90  1.12  christos 			tot += ((int)(($5 + fsize - 1) / fsize)) * fsize;
     91  1.12  christos 	}
     92  1.12  christos 	END {
     93  1.12  christos 		printf("%d\n", tot);
     94  1.12  christos 	}'
     95  1.12  christos }
     96  1.12  christos 
     97   1.1       agc # find the size of the gzipped files in a .tgz archive
     98  1.11  christos # Directories appear as 0, so count them as one block
     99  1.11  christos # and round up files to a fragment.
    100   1.1       agc sizeone() {
    101   1.4  christos 	if [ ! -f "$1" ]
    102   1.4  christos 	then
    103   1.4  christos 		echo "$PROG: Missing set $1" 1>&2
    104   1.4  christos 		echo 0
    105   1.4  christos 		return;
    106   1.4  christos 	fi
    107  1.12  christos 
    108   1.1       agc         case "$1" in 
    109  1.11  christos         *.tgz|*.tar.gz|*.tbz|*.tar.bz2|*.txz|*.tar.xz)
    110  1.12  christos                 tar tvzf "$1" | getfssize ${bsize} ${fsize}
    111   1.1       agc                 ;;
    112   1.1       agc         *)
    113   1.1       agc                 echo 0
    114   1.1       agc                 ;; 
    115   1.1       agc         esac
    116   1.1       agc }
    117   1.1       agc 
    118   1.4  christos 
    119  1.11  christos # Return the usable filesystem size in bytes, given the total size in bytes,
    120  1.11  christos # and optionally block and fragment sizes
    121  1.11  christos getffssize() {
    122  1.11  christos 	local bytes="$1"
    123  1.11  christos 	local barg
    124  1.11  christos 	local farg
    125  1.11  christos 	local overhead
    126  1.11  christos 
    127  1.11  christos 	if [ -n "$2" ]
    128  1.11  christos 	then
    129  1.11  christos 		barg="-b $2"
    130  1.11  christos 		if [ -n "$3" ]
    131  1.11  christos 		then
    132  1.11  christos 			farg="-f $3"
    133  1.11  christos 		fi
    134  1.11  christos 	fi
    135  1.11  christos 
    136  1.11  christos 	overhead=$(newfs -N ${farg} ${barg} -s "${bytes}b" -F /dev/null |
    137  1.11  christos 	    awk '/using/ {
    138  1.11  christos 		printf("%d\n", substr($6, 1, length($6) - 3) * 1024 * 1024);
    139  1.11  christos 	    }'
    140  1.11  christos 	)
    141  1.11  christos 	echo $(( ${bytes} - ${overhead} ))
    142  1.11  christos }
    143  1.11  christos 
    144  1.11  christos # Compute the size of an ffs filesystem that can fit x bytes.
    145  1.11  christos # Instead of duplicating the newfs calculations here we let
    146  1.11  christos # it do the job, using binary search.
    147  1.11  christos makeffssize() {
    148  1.11  christos 	local bytes=$1
    149  1.11  christos 	local bsize=$2
    150  1.11  christos 	local fsize=$3
    151  1.11  christos 	local max=$(( 2 * ${bytes} ))
    152  1.11  christos 	local min="${bytes}"
    153  1.11  christos 	local cur
    154  1.11  christos 	local res
    155  1.11  christos 	while true; do
    156  1.11  christos 		cur="$(( ( ${max} + ${min} ) / 2 ))"
    157  1.11  christos 		res="$(getffssize "${cur}" ${bsize} ${fsize})"
    158  1.11  christos #		echo ${min} ${cur} ${max} ${res} ${bytes} 1>&2
    159  1.11  christos 		if [ "${res}" -eq "${bytes}" ]
    160  1.11  christos 		then
    161  1.11  christos 		    break
    162  1.11  christos 		elif [ "$(( ${min} + 1 ))" -ge "${max}" ]
    163  1.11  christos 		then
    164  1.11  christos 		    break
    165  1.11  christos 		elif [ "${res}" -lt "${bytes}" ]
    166  1.11  christos 		then
    167  1.11  christos 		    min="${cur}"
    168  1.11  christos 		elif [ "${res}" -gt "${bytes}" ]
    169  1.11  christos 		then
    170  1.11  christos 		    max="${cur}"
    171  1.11  christos 		fi
    172  1.11  christos 	done
    173  1.11  christos 	echo "${cur}"
    174  1.11  christos }
    175  1.11  christos 
    176   1.4  christos finish() {
    177  1.13  christos 	cleanup
    178  1.13  christos 	${sudo} umount ${mnt}
    179  1.13  christos 	${sudo} vnconfig -u ${vnddev}
    180  1.13  christos 	rm -fr ${mnt}
    181   1.4  christos }
    182   1.4  christos 
    183  1.11  christos 
    184   1.4  christos DIR="$(dirname "$0")"
    185   1.4  christos PROG="$(basename "$0")"
    186   1.2       agc bar="==="
    187   1.4  christos sudo=
    188   1.7  christos mnt="${TMPDIR:-/tmp}/image.$$"
    189   1.4  christos src="/usr/src"
    190   1.4  christos obj="/usr/obj"
    191   1.4  christos 
    192  1.13  christos sets="base comp etc games man misc modules text"
    193  1.13  christos xsets="xbase xcomp xetc xfont xserver"
    194  1.11  christos 
    195  1.11  christos # Presumable block and fragment size.
    196  1.11  christos bsize=16384
    197  1.11  christos fsize=2048
    198  1.12  christos mtob=$(( 1024 * 1024 ))
    199  1.11  christos 
    200   1.4  christos # First pass for options to get the host
    201   1.4  christos OPTS="S:c:h:s:x"
    202   1.4  christos while getopts "$OPTS" f
    203   1.4  christos do
    204   1.4  christos 	case $f in
    205   1.4  christos 	h)	h="$OPTARG";;
    206   1.4  christos 	*)	;;
    207   1.4  christos 	esac
    208   1.4  christos done
    209   1.4  christos 
    210   1.4  christos if [ -z "$h" ]
    211   1.4  christos then
    212   1.4  christos 	usage
    213   1.4  christos fi
    214   1.4  christos 
    215   1.5  christos if [ ! -f "${DIR}/conf/${h}.conf" ]
    216   1.4  christos then
    217   1.5  christos 	echo $PROG: ${DIR}/conf/${h}.conf is not present 1>&2
    218   1.4  christos 	exit 1
    219   1.4  christos fi
    220   1.4  christos 
    221   1.5  christos . "${DIR}/conf/${h}.conf"
    222   1.4  christos 
    223   1.4  christos OPTIND=1
    224   1.4  christos while getopts "$OPTS" f
    225   1.4  christos do
    226   1.4  christos 	case $f in
    227   1.4  christos 	S)	setsdir="$OPTARG";;
    228   1.4  christos 	c)	custom="$OPTARG";;
    229   1.4  christos 	h)	;;
    230   1.4  christos 	s)	size="$OPTARG";;
    231  1.13  christos 	x)	sets="$sets $xsets";;
    232   1.6  christos 	*)	usage;;
    233   1.1       agc 	esac
    234   1.1       agc done
    235   1.1       agc 
    236   1.8  jmcneill trap finish 0 1 2 3 15
    237   1.8  jmcneill 
    238   1.4  christos shift $(( "$OPTIND" - 1 ))
    239   1.4  christos if [ -n "$1" ]; then
    240   1.1       agc 	# take the next argument as being the image name
    241   1.1       agc 	image="$1"
    242   1.1       agc 	shift
    243   1.1       agc fi
    244   1.1       agc 
    245  1.11  christos # calculate the set bytes
    246  1.11  christos setbytes=0
    247  1.11  christos echo -n "${bar} computing set sizes ("
    248  1.13  christos b=
    249   1.1       agc for s in ${sets}; do
    250   1.4  christos 	one="$(sizeone ${setsdir}/${s}.tgz)"
    251  1.13  christos 	echo -n "$b$s=$(( ${one} / ${mtob} ))MB"
    252  1.11  christos 	setbytes=$(( ${setbytes} +  ${one} ))
    253  1.13  christos 	b=" "
    254   1.1       agc done
    255  1.12  christos echo "): $(( ${setbytes} / ${mtob} ))MB ${bar}"
    256  1.11  christos 
    257   1.2       agc # calculate size of custom files
    258  1.11  christos custbytes=0
    259   1.2       agc if [ -d "${custom}" ]; then
    260  1.12  christos 	custbytes=$(ls -lR "${custom}" | getfssize ${bsize} ${fsize})
    261   1.2       agc fi
    262  1.12  christos echo "${bar} computing custom sizes: $(( ${custbytes} / ${mtob} ))MB ${bar}"
    263  1.11  christos 
    264  1.11  christos # how many bytes
    265  1.11  christos rawbytes="$(( ${setbytes} + ${custbytes} ))"
    266  1.12  christos echo -n "${bar} computing ffs filesystem size for $(( ${rawbytes} / ${mtob} ))MB: "
    267  1.11  christos ffsbytes="$(makeffssize "${rawbytes}")"
    268  1.12  christos ffsmb=$(( ${ffsbytes} / ${mtob} ))
    269  1.11  christos echo " ${ffsmb}MB ${bar}"
    270  1.11  christos 
    271  1.11  christos # total in MB
    272  1.11  christos total=$(( ${ffsmb} + ${overhead} ))
    273  1.11  christos echo "${bar} overhead: ${overhead}MB ${bar}"
    274  1.11  christos 
    275   1.1       agc if [ $size -eq 0 ]; then
    276   1.1       agc         # auto-size the pkgs fs
    277  1.10  christos         newsize=${total}
    278   1.1       agc else
    279   1.1       agc         # check that we've been given enough space
    280   1.1       agc         if [ ${total} -gt ${size} ]; then
    281   1.4  christos                 echo "$PROG: Given size is ${size} MB, but we need ${total} MB" >&2
    282   1.1       agc                 exit 1
    283   1.1       agc         fi
    284  1.10  christos 	newsize=${size}
    285   1.1       agc fi
    286   1.1       agc 
    287  1.10  christos echo "${bar} making a new ${newsize} MB image in ${image} ${bar}"
    288  1.10  christos dd if=/dev/zero of=${image} bs=1m count=${newsize} conv=sparse
    289   1.1       agc 
    290   1.1       agc vnddev=$(next_avail vnd)
    291   1.1       agc echo "${bar} mounting image via vnd ${vnddev} ${bar}"
    292   1.2       agc ${sudo} vnconfig ${vnddev} ${image}
    293   1.4  christos ${sudo} mkdir -p ${mnt}
    294   1.4  christos make_filesystems
    295   1.4  christos 
    296   1.4  christos ${sudo} mkdir -p ${mnt}/etc ${mnt}/dev
    297   1.1       agc 
    298  1.10  christos echo -n "${bar} installing sets:"
    299   1.4  christos (cd ${mnt} &&
    300   1.1       agc 	for s in ${sets}; do
    301  1.10  christos 		ss="${setsdir}/${s}.tgz"
    302  1.10  christos 		if [ -f "${ss}" ]; then
    303  1.10  christos 			echo -n " ${s}"
    304  1.10  christos 			${sudo} tar xpzf "${ss}"
    305   1.4  christos 		fi
    306   1.1       agc 	done
    307   1.1       agc )
    308  1.10  christos echo " ${bar}"
    309   1.1       agc 
    310   1.1       agc echo "${bar} performing customisations ${bar}"
    311   1.1       agc 
    312   1.4  christos make_fstab
    313   1.1       agc 
    314   1.4  christos ${sudo} cat > ${mnt}/etc/rc.conf << EOF
    315   1.1       agc #
    316   1.1       agc # see rc.conf(5) for more information.
    317   1.1       agc #
    318   1.1       agc # Use program=YES to enable program, NO to disable it. program_flags are
    319   1.1       agc # passed to the program on the command line.
    320   1.1       agc #
    321   1.1       agc 
    322   1.1       agc # Load the defaults in from /etc/defaults/rc.conf (if it's readable).
    323   1.1       agc # These can be overridden below.
    324   1.1       agc #
    325   1.1       agc if [ -r /etc/defaults/rc.conf ]; then
    326   1.1       agc         . /etc/defaults/rc.conf
    327   1.1       agc fi
    328   1.1       agc 
    329   1.1       agc # If this is not set to YES, the system will drop into single-user mode.
    330   1.1       agc #
    331   1.1       agc rc_configured=YES
    332   1.1       agc 
    333   1.4  christos hostname=${h}
    334   1.1       agc 
    335   1.4  christos EOF
    336   1.1       agc 
    337   1.4  christos customize
    338   1.1       agc 
    339   1.1       agc for d in ${specialdirs}; do
    340   1.4  christos 	${sudo} mkdir -p ${mnt}/${d}
    341   1.1       agc done
    342   1.1       agc 
    343   1.4  christos if [ \( -n "${custom}" \) -a \( -d "${custom}" \) ]; then
    344   1.2       agc 	echo "${bar} user customisations from files in ${custom} ${bar}"
    345   1.4  christos 	(cd ${custom} && ${sudo} pax -rwpe . ${mnt})
    346   1.2       agc fi
    347   1.1       agc 
    348   1.1       agc exit 0
    349