Home | History | Annotate | Line # | Download | only in embedded
mkimage revision 1.13
      1 #! /bin/sh
      2 
      3 # $NetBSD: mkimage,v 1.13 2013/01/16 23:27:34 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 usage() {
     72 	cat << EOF 1>&2
     73 Usage: $PROG [-S <setsdir>] [-c <custom-files-dir>] [-h <host-arch>] [-s <size>]
     74 EOF
     75 	exit 1
     76 }
     77 
     78 # Return the filesystem size for an ls -l or tar -xvf list
     79 # Directories and symlinks in tar are 0 size, we assume one block
     80 # (which is too much), we round up by the fragment size the rest.
     81 getfssize() {
     82 	local bsize="$1"
     83 	local fsize="$2"
     84 
     85 	awk -v fsize=${fsize} -v bsize=${bsize} '
     86 	NF >= 9 && $1 != "tar:" {
     87 		if ($5 == 0)
     88 			tot += bsize;
     89 		else
     90 			tot += ((int)(($5 + fsize - 1) / fsize)) * fsize;
     91 	}
     92 	END {
     93 		printf("%d\n", tot);
     94 	}'
     95 }
     96 
     97 # find the size of the gzipped files in a .tgz archive
     98 # Directories appear as 0, so count them as one block
     99 # and round up files to a fragment.
    100 sizeone() {
    101 	if [ ! -f "$1" ]
    102 	then
    103 		echo "$PROG: Missing set $1" 1>&2
    104 		echo 0
    105 		return;
    106 	fi
    107 
    108         case "$1" in 
    109         *.tgz|*.tar.gz|*.tbz|*.tar.bz2|*.txz|*.tar.xz)
    110                 tar tvzf "$1" | getfssize ${bsize} ${fsize}
    111                 ;;
    112         *)
    113                 echo 0
    114                 ;; 
    115         esac
    116 }
    117 
    118 
    119 # Return the usable filesystem size in bytes, given the total size in bytes,
    120 # and optionally block and fragment sizes
    121 getffssize() {
    122 	local bytes="$1"
    123 	local barg
    124 	local farg
    125 	local overhead
    126 
    127 	if [ -n "$2" ]
    128 	then
    129 		barg="-b $2"
    130 		if [ -n "$3" ]
    131 		then
    132 			farg="-f $3"
    133 		fi
    134 	fi
    135 
    136 	overhead=$(newfs -N ${farg} ${barg} -s "${bytes}b" -F /dev/null |
    137 	    awk '/using/ {
    138 		printf("%d\n", substr($6, 1, length($6) - 3) * 1024 * 1024);
    139 	    }'
    140 	)
    141 	echo $(( ${bytes} - ${overhead} ))
    142 }
    143 
    144 # Compute the size of an ffs filesystem that can fit x bytes.
    145 # Instead of duplicating the newfs calculations here we let
    146 # it do the job, using binary search.
    147 makeffssize() {
    148 	local bytes=$1
    149 	local bsize=$2
    150 	local fsize=$3
    151 	local max=$(( 2 * ${bytes} ))
    152 	local min="${bytes}"
    153 	local cur
    154 	local res
    155 	while true; do
    156 		cur="$(( ( ${max} + ${min} ) / 2 ))"
    157 		res="$(getffssize "${cur}" ${bsize} ${fsize})"
    158 #		echo ${min} ${cur} ${max} ${res} ${bytes} 1>&2
    159 		if [ "${res}" -eq "${bytes}" ]
    160 		then
    161 		    break
    162 		elif [ "$(( ${min} + 1 ))" -ge "${max}" ]
    163 		then
    164 		    break
    165 		elif [ "${res}" -lt "${bytes}" ]
    166 		then
    167 		    min="${cur}"
    168 		elif [ "${res}" -gt "${bytes}" ]
    169 		then
    170 		    max="${cur}"
    171 		fi
    172 	done
    173 	echo "${cur}"
    174 }
    175 
    176 finish() {
    177 	cleanup
    178 	${sudo} umount ${mnt}
    179 	${sudo} vnconfig -u ${vnddev}
    180 	rm -fr ${mnt}
    181 }
    182 
    183 
    184 DIR="$(dirname "$0")"
    185 PROG="$(basename "$0")"
    186 bar="==="
    187 sudo=
    188 mnt="${TMPDIR:-/tmp}/image.$$"
    189 src="/usr/src"
    190 obj="/usr/obj"
    191 
    192 sets="base comp etc games man misc modules text"
    193 xsets="xbase xcomp xetc xfont xserver"
    194 
    195 # Presumable block and fragment size.
    196 bsize=16384
    197 fsize=2048
    198 mtob=$(( 1024 * 1024 ))
    199 
    200 # First pass for options to get the host
    201 OPTS="S:c:h:s:x"
    202 while getopts "$OPTS" f
    203 do
    204 	case $f in
    205 	h)	h="$OPTARG";;
    206 	*)	;;
    207 	esac
    208 done
    209 
    210 if [ -z "$h" ]
    211 then
    212 	usage
    213 fi
    214 
    215 if [ ! -f "${DIR}/conf/${h}.conf" ]
    216 then
    217 	echo $PROG: ${DIR}/conf/${h}.conf is not present 1>&2
    218 	exit 1
    219 fi
    220 
    221 . "${DIR}/conf/${h}.conf"
    222 
    223 OPTIND=1
    224 while getopts "$OPTS" f
    225 do
    226 	case $f in
    227 	S)	setsdir="$OPTARG";;
    228 	c)	custom="$OPTARG";;
    229 	h)	;;
    230 	s)	size="$OPTARG";;
    231 	x)	sets="$sets $xsets";;
    232 	*)	usage;;
    233 	esac
    234 done
    235 
    236 trap finish 0 1 2 3 15
    237 
    238 shift $(( "$OPTIND" - 1 ))
    239 if [ -n "$1" ]; then
    240 	# take the next argument as being the image name
    241 	image="$1"
    242 	shift
    243 fi
    244 
    245 # calculate the set bytes
    246 setbytes=0
    247 echo -n "${bar} computing set sizes ("
    248 b=
    249 for s in ${sets}; do
    250 	one="$(sizeone ${setsdir}/${s}.tgz)"
    251 	echo -n "$b$s=$(( ${one} / ${mtob} ))MB"
    252 	setbytes=$(( ${setbytes} +  ${one} ))
    253 	b=" "
    254 done
    255 echo "): $(( ${setbytes} / ${mtob} ))MB ${bar}"
    256 
    257 # calculate size of custom files
    258 custbytes=0
    259 if [ -d "${custom}" ]; then
    260 	custbytes=$(ls -lR "${custom}" | getfssize ${bsize} ${fsize})
    261 fi
    262 echo "${bar} computing custom sizes: $(( ${custbytes} / ${mtob} ))MB ${bar}"
    263 
    264 # how many bytes
    265 rawbytes="$(( ${setbytes} + ${custbytes} ))"
    266 echo -n "${bar} computing ffs filesystem size for $(( ${rawbytes} / ${mtob} ))MB: "
    267 ffsbytes="$(makeffssize "${rawbytes}")"
    268 ffsmb=$(( ${ffsbytes} / ${mtob} ))
    269 echo " ${ffsmb}MB ${bar}"
    270 
    271 # total in MB
    272 total=$(( ${ffsmb} + ${overhead} ))
    273 echo "${bar} overhead: ${overhead}MB ${bar}"
    274 
    275 if [ $size -eq 0 ]; then
    276         # auto-size the pkgs fs
    277         newsize=${total}
    278 else
    279         # check that we've been given enough space
    280         if [ ${total} -gt ${size} ]; then
    281                 echo "$PROG: Given size is ${size} MB, but we need ${total} MB" >&2
    282                 exit 1
    283         fi
    284 	newsize=${size}
    285 fi
    286 
    287 echo "${bar} making a new ${newsize} MB image in ${image} ${bar}"
    288 dd if=/dev/zero of=${image} bs=1m count=${newsize} conv=sparse
    289 
    290 vnddev=$(next_avail vnd)
    291 echo "${bar} mounting image via vnd ${vnddev} ${bar}"
    292 ${sudo} vnconfig ${vnddev} ${image}
    293 ${sudo} mkdir -p ${mnt}
    294 make_filesystems
    295 
    296 ${sudo} mkdir -p ${mnt}/etc ${mnt}/dev
    297 
    298 echo -n "${bar} installing sets:"
    299 (cd ${mnt} &&
    300 	for s in ${sets}; do
    301 		ss="${setsdir}/${s}.tgz"
    302 		if [ -f "${ss}" ]; then
    303 			echo -n " ${s}"
    304 			${sudo} tar xpzf "${ss}"
    305 		fi
    306 	done
    307 )
    308 echo " ${bar}"
    309 
    310 echo "${bar} performing customisations ${bar}"
    311 
    312 make_fstab
    313 
    314 ${sudo} cat > ${mnt}/etc/rc.conf << EOF
    315 #
    316 # see rc.conf(5) for more information.
    317 #
    318 # Use program=YES to enable program, NO to disable it. program_flags are
    319 # passed to the program on the command line.
    320 #
    321 
    322 # Load the defaults in from /etc/defaults/rc.conf (if it's readable).
    323 # These can be overridden below.
    324 #
    325 if [ -r /etc/defaults/rc.conf ]; then
    326         . /etc/defaults/rc.conf
    327 fi
    328 
    329 # If this is not set to YES, the system will drop into single-user mode.
    330 #
    331 rc_configured=YES
    332 
    333 hostname=${h}
    334 
    335 EOF
    336 
    337 customize
    338 
    339 for d in ${specialdirs}; do
    340 	${sudo} mkdir -p ${mnt}/${d}
    341 done
    342 
    343 if [ \( -n "${custom}" \) -a \( -d "${custom}" \) ]; then
    344 	echo "${bar} user customisations from files in ${custom} ${bar}"
    345 	(cd ${custom} && ${sudo} pax -rwpe . ${mnt})
    346 fi
    347 
    348 exit 0
    349