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