#!/bin/sh
#
# Copyright 2003 Gray Watson
#
# Permission to use, copy, modify, and distribute this software for
# any purpose and without fee is hereby granted, provided that the
# above copyright notice and this permission notice appear in all
# copies, and that the name of Gray Watson not be used in advertising
# or publicity pertaining to distribution of the document or software
# without specific, written prior permission.
#
# Gray Watson makes no representations about the suitability of the
# software described herein for any purpose.  It is provided "as is"
# without express or implied warranty.
#
# The author may be contacted via http://256.com/gray/
#
# $Id: cf_image.sh,v 1.3 2004/01/03 07:41:25 gray Exp $
#

#
# This script builds a boot image to be written to a compact flash
# device and booted in a Soekris system.  More information online:
#
# http://256.com/gray/docs/soekris_openbsd_diskless/
#

IMAGE_FILE=./image_cf
DISKLABEL_TMP=cf.$$.t
VNODE_DEV=svnd0
VNODE_MOUNT=./__mount_cf__
DEFAULT_ROOT=./root_cf
DEFAULT2_ROOT=./root
FILE_EXTENSION=cf

# boot settings
FIRST_BOOT=usr/mdec/biosboot
SECOND_BOOT=boot
MBR_TEMPLATE=usr/mdec/mbr

###############################################################################

cat << __EOF

Soekris CF Install Script for OpenBSD
Copyright 2003 Gray Watson
http://256.com/gray/docs/soekris_openbsd_diskless/
-------------------------------------

__EOF

#################################################

echo -n "Enter the filename to write the image into: [$IMAGE_FILE] "
read image_file
if [ "$image_file" = "" ]; then
    image_file=$IMAGE_FILE
fi

#################################################

cat << __EOF
----------
Type in size of CF card in MB.  Either: 32, 64, 128, 256.

If your size isn't in the above list then insert the CF card into your
Soekris box and boot it and write down what it says.  When it boots it
should say:

    Pri Mas [ Name of CF Card ]   LBA: CCC-TT-SS  MMM MByte.

The CCC is the number of cylinders, TT is the tracks per cylinder, and SS
is the sectors per track.  The MMM is the number of megabytes on the card
with some space removed for rounding.

__EOF
echo -n "Enter size in MB or just press enter to type in drive specs: "
read cf_size

case $cf_size in 
    32)
	# 32mb CF
	sectors_track=32	# "sectors/track:"
	tracks_cylinder=8	# "tracks/cylinder:"
	cylinders=245		# "cylinders:"
	;;
    64)
	# 64mb CF
	sectors_track=32	# "sectors/track:"
	tracks_cylinder=8	# "tracks/cylinder:"
	cylinders=490		# "cylinders:"
	;;
    128)
	# 128Meg CF
	sectors_track=32	# "sectors/track:"
	tracks_cylinder=64      # "tracks/cylinder:"
	cylinders=122		# "cylinders:"
	;;
    256)
	# 256Meg CF
	sectors_track=32	# "sectors/track:"
	tracks_cylinder=16      # "tracks/cylinder:"
	cylinders=980		# "cylinders:"
	;;
	
    *)
	echo -n "Enter cylinders: "
	read cylinders
	echo -n "Tracks per cylinder: "
	read tracks_cylinder
	echo -n "Sectors per track: "
	read sectors_track
	;;
esac

#################################################

# calculate our total size of the image in 512b blocks
total_blocks=`expr $cylinders \* $tracks_cylinder \* $sectors_track`
total_bytes=`expr $total_blocks \* 512`
# calculate sectors per cylinder
sectors_cylinder=`expr $tracks_cylinder \* $sectors_track`

# calculate the wd0a partition size
a_blocks=`expr $cylinders \* $sectors_cylinder`
#
# From flashdist.sh by Chris Cappuccio and modified by Ron Rosson.
# Here we remove additional cylinders while
# (totalsize <= (cylinders*(sectors/cylinder))) so that
# we have room for the b partition and still lie on a cylinder boundary:
#
while [ "$total_blocks" -le "$a_blocks" ]; do
    a_blocks=`expr $a_blocks - $sectors_cylinder`
done

# remove the first track where the disklabel goes
a_blocks=`expr $a_blocks - $sectors_track`
a_bytes=`expr $a_blocks \* 512`

# start our minimal swap partition
b_offset=`expr $a_blocks + $sectors_track`
b_blocks=1
b_bytes=`expr $b_blocks \* 512`

cat << __EOF
----------

Here are the final settings for the image in terms of partition map
and filesystem settings.  Please review and press control-c to quit if
there are any problems.  If you don't understand any of this then just
press enter.

Partition Map:
             cylinders: $cylinders
   tracks per cylinder: $tracks_cylinder
     sectors per track: $sectors_track
  sectors per cylinder: $sectors_cylinder
    total image blocks: $total_blocks
     total image bytes: $total_bytes

Disklabel:
  partition 'a' offset: $sectors_track
  partition 'a' blocks: $a_blocks
   partition 'a' bytes: $a_bytes
  partition 'b' offset: $b_offset
  partition 'b' blocks: $b_blocks
   partition 'b' bytes: $b_bytes

__EOF

echo -n "Press enter to continue or press control-c to quit and correct. "
read doit

#################################################

cat << __EOF
----------
Enter the vnode device to use.  If you do not know what this is then
just press enter to take the default of "$VNODE_DEV".

__EOF
echo -n "Vnode device [$VNODE_DEV]: "
read vnode_device
if [ "$vnode_device" = "" ]; then
    vnode_device=$VNODE_DEV
fi

#################################################

cat << __EOF
----------
Enter the directory path to the files that you want to load into the
CF image.  The default is the directory $DEFAULT_ROOT in the current directory.
It is assumed that this directory has the kernel files, boot
configuration files, and all other information necessary for the
system to work.  The directory should hold the following boot files in
these specific relevant places.  If you are using the root directory
then the following files should exist:

    # second-stage boot program to be installed in /
    $DEFAULT_ROOT/$SECOND_BOOT
    # first-stage boot program installed in the boot block
    $DEFAULT_ROOT/$FIRST_BOOT
    # alternate master-boot-record template file for fdisk
    $DEFAULT_ROOT/$MBR_TEMPLATE

__EOF

default_root=$DEFAULT_ROOT
if [ ! -d $default_root ]; then
    default_root=$DEFAULT2_ROOT
fi

echo -n "Root filesystem directory [$default_root]: "
read root_dir

if [ "$root_dir" = "" ]; then
    root_dir=$default_root
fi

if [ ! -d $root_dir ]; then
    echo "I cannot see the directory $root_dir"
    exit 1
fi

############################################################################

# create the image file
echo "----------"
doit=yes
if [ -f $image_file ]; then
    echo -n "Image file $image_file exists.  Re-zero or re-create it? [yes] "
    read doit
    if [ "$doit" = "" ]; then
	doit=yes
    fi
fi
if [ "$doit" = "yes" ]; then
    echo "Creating image file $image_file"
    rm -f $image_file
    dd if=/dev/zero of=$image_file bs=512 count=$total_blocks
    if [ $? -ne 0 ]; then
	echo "dd to $image_file failed"
	exit 1
    fi
fi

#################################################

# now map it into the vnode device
echo "----------"
echo "Mapping $image_file to vnode device $vnode_device."
vnconfig $vnode_device $image_file
if [ $? -ne 0 ]; then
    echo "Mapping $image_file to vnode device $vnode_device failed."
    exit 1
fi

#################################################

# configure the partitions
echo "----------"
echo "Updating the partition map for $vnode_device"
fdisk -c $cylinders -h $tracks_cylinder -s $sectors_track \
    -f $root_dir/$MBR_TEMPLATE -e $vnode_device << __EOF
reinit
update
write
quit
__EOF
if [ $? -ne 0 ]; then
    echo "Updating partition map for vnode device $vnode_device failed."
    vnconfig -u $vnode_device
    exit 1
fi

#################################################

# create the disk label
echo ""
echo "----------"
echo "Creating disk label for $vnode_device."
cat >> $DISKLABEL_TMP << __EOF
type: SCSI
disk: vnd device
label: fictitious
flags:
bytes/sector: 512
sectors/track: $sectors_track
tracks/cylinder: $tracks_cylinder
sectors/cylinder: $sectors_cylinder
cylinders: $cylinders
total sectors: $total_blocks
rpm: 3600
interleave: 1
trackskew: 0
cylinderskew: 0
headswitch: 0           # microseconds
track-to-track seek: 0  # microseconds
drivedata: 0 

16 partitions:
#        size        offset         fstype   [fsize bsize   cpg]
  a:   $a_blocks     $sectors_track 4.2BSD    1024  8192  16
  b:   $b_blocks     $b_offset      swap
  c:   $total_blocks 0              unused    0     0
__EOF
if [ $? -ne 0 ]; then
    echo "Creating new disklabel for vnode device $vnode_device failed."
    vnconfig -u $vnode_device
    exit 1
fi

# if you want to display the label before writing, uncomment this
#cat $DISKLABEL_TMP

#################################################

# now write the disklable
echo "----------"
echo "Writing the disklabel to $vnode_device."
disklabel -R $vnode_device $DISKLABEL_TMP
if [ $? -ne 0 ]; then
    echo "Writing new disklabel to vnode device $vnode_device failed."
    vnconfig -u $vnode_device
    exit 1
fi
rm -f $DISKLABEL_TMP

#################################################

# creating new filesystem
echo "----------"
echo "Creating filesystem on /dev/r${vnode_device}a"
newfs -S 512 -u $sectors_track -z $tracks_cylinder /dev/r${vnode_device}a
if [ $? -ne 0 ]; then
    echo "Creating new filesystem on vnode device $vnode_device failed."
    vnconfig -u $vnode_device
    exit 1
fi

#################################################

# making the new filesystem directory
echo "----------"
echo "Making mount directory $VNODE_MOUNT."
mkdir $VNODE_MOUNT
if [ $? -ne 0 ]; then
    echo "Mkdir of $VNODE_MOUNT failed."
    vnconfig -u $vnode_device
    exit 1
fi

#################################################

# mounting the new filesystem
echo "----------"
echo "Mounting vnode device /dev/r${vnode_device}a on directory $VNODE_MOUNT."
mount /dev/${vnode_device}a $VNODE_MOUNT
if [ $? -ne 0 ]; then
    echo "Mounting vnode device on $VNODE_MOUNT failed."
    rmdir $VNODE_MOUNT
    vnconfig -u $vnode_device
    exit 1
fi

#################################################

# we need to copy the boot into place first probably so it can be
# contiguous and at a specific place on the disk early for the
# bootstrap code.
echo "----------"
echo "Copying boot file from $root_dir/$SECOND_BOOT into $VNODE_MOUNT."
cp $root_dir/$SECOND_BOOT $VNODE_MOUNT
if [ $? -ne 0 ]; then
    echo "Copying boot file from $root_dir/$SECOND_BOOT into $VNODE_MOUNT failed."
    umount $VNODE_MOUNT
    rmdir $VNODE_MOUNT
    vnconfig -u $vnode_device
    exit 1
fi

#################################################

# install the boot blocks
echo "----------"
echo "Installing boot blocks on $vnode_device."
/usr/mdec/installboot -h $tracks_cylinder -s $sectors_track \
    $VNODE_MOUNT/$SECOND_BOOT $root_dir/$FIRST_BOOT $vnode_device
if [ $? -ne 0 ]; then
    echo "Installing boot blocks on $vnode_device failed."
    umount $VNODE_MOUNT
    rmdir $VNODE_MOUNT
    vnconfig -u $vnode_device
    exit 1
fi

#################################################

# copy the files over to the new system
# first the kernels
FILES=`( cd $root_dir ; ls -d bsd* )`
echo "----------"
echo "Copying the files from $root_dir into $VNODE_MOUNT"
echo "Kernel files first:"
echo "$FILES"
echo "This may take a bit..."
( cd $root_dir ; tar -cf - $FILES )  | ( cd $VNODE_MOUNT ; tar -xpf - )
if [ $? -ne 0 ]; then
    echo "Copying kernel files from $root_dir to $VNODE_MOUNT failed."
    umount $VNODE_MOUNT
    rmdir $VNODE_MOUNT
    vnconfig -u $vnode_device
    exit 1
fi

#################################################

# next everything else but not boot or bsd
FILES=`( cd $root_dir ; ls -d .??* * | egrep -v '^boot|^bsd' )`
echo "----------"
echo "Now everything else (not boot nor bsd*):"
echo "$FILES"
echo "This may take a while..."
( cd $root_dir ; tar -cf - $FILES ) | ( cd $VNODE_MOUNT ; tar -xpf - )
if [ $? -ne 0 ]; then
    echo "Copying other files from $root_dir to $VNODE_MOUNT failed."
    umount $VNODE_MOUNT
    rmdir $VNODE_MOUNT
    vnconfig -u $vnode_device
    exit 1
fi

#################################################

cat << __EOF
----------
If you have the compact flash and the root disk files in the same
directory, then you can put files in $root_dir with a .$FILE_EXTENSION extension
and this script will rename them to the filename without the .$FILE_EXTENSION.
For example, a different etc/fstab file is needed for each image.

__EOF

FILES=`( cd $VNODE_MOUNT ; find . -name '*.'$FILE_EXTENSION )`
if [ "$FILES" = "" ]; then
    echo "No *.$FILE_EXTENSION files found in $root_dir."
else
    for file in $FILES
    do
        new_file=`echo $file | sed -e "s/[.]$FILE_EXTENSION$//"`
        echo -n "Rename $file to $new_file? [yes] "
        read doit
        if [ "$doit" = "" ]; then
            doit=yes
        fi
        if [ "$doit" = "yes" ]; then
            mv $VNODE_MOUNT/$file $VNODE_MOUNT/$new_file
            if [ $? -eq 0 ]; then
                echo "   Done."
            else
                echo "Moving $file to $new_file failed."
                umount $VNODE_MOUNT
                rmdir $VNODE_MOUNT
                vnconfig -u $vnode_device
                exit 1
            fi
        fi
    done
fi

###############################################################################

# syncing filesystems
sync

###############################################################################

# unmounting the image
sync
echo "----------"
echo "Unmounting $VNODE_MOUNT."
umount $VNODE_MOUNT
if [ $? -ne 0 ]; then
    echo "Unmounting $VNODE_MOUNT failed."
    exit 1
fi
echo "Removing $VNODE_MOUNT directory."
rmdir $VNODE_MOUNT
if [ $? -ne 0 ]; then
    echo "Remolving directory $VNODE_MOUNT failed."
    # continue
fi

# removing the vnode
echo "Removing vnode device $vnode_device."
vnconfig -u $vnode_device
if [ $? -ne 0 ]; then
    echo "Removing vnode device $vnode_device failed."
    exit 1
fi

# sync to disk
sync

#################################################

echo "----------"
echo "Done.  Created image: $image_file"
