#!/bin/bash
#
# adsrootbuilder by Josh Malone (jmalone@applieddata.net)
#
# Copyright 2004-2006 Joshua Malone, Applied Data Systems
#
# This file is subject to the terms and conditions of the GNU General Public
# License.

# adsrootbuilder.conf file format:
# Example conf file for jffs2 root with cramfs /usr
#  and ramfs /tmp

# FILELIST=/tmp/adsrootbuilder.files
# TMP_IS_RAMFS=y
# FILESYSTEM_1_PATH=/
# FILESYSTEM_1_TYPE=jffs2
# FILESYSTEM_1_ISROOT=y
# FILESYSTEM_2_PATH=/usr
# FILESYSTEM_2_TYPE=cramfs

MAX_FS=10

# Functions

LIBDIR=/usr/share/adsrootbuilder
VARDIR=/var/lib/adsrootbuilder

. $LIBDIR/adsshell.inc

printusage () {
	echo "Usage: adsrootbuilder [--rescan] [--noscan] [--noreboot] [--clean] [-c <config>]"
	echo ""
}

is_beneath () {
	if [ -z "$1" ] || [ -z "$2" ]; then
		errexit "internal error calling is_beneath()"
	fi

	result=$(echo ${2} | grep -e "^${1}" )
	if [ -z "${result}" ]; then
		# False
		return 1
	else
		return 0
	fi
}

scan_clargs () {
	# Have we been asked for help?
	if [ "$1" == '-h' ] || [ "$1" == '-?' ]; then
		printusage
		exit
	fi

	# Option to skip filesystem scan part and build from and
	#  existing tarball or to force recreate tarball
	if [ "$1" == '--noscan' ]; then
		skip_scan='y'
		shift
	elif [ "$1" == '--rescan' ]; then
		if [ -f $VARDIR/startdone ]; then
			# Remove signal file to force tool to restart from beginning
			rm $VARDIR/startdone
		fi
		force_scan='y'
		shift
	fi

	if [ "$1" == '--noreboot' ]; then
		reboot_between_modes=n
		shift
	fi

	if [ "$1" == '--clean' ]; then
		clean=y
		shift
	fi

	if [ "$1" == '-c' ]; then
		if [ -z "$2" ]; then
			errexit "Config filename not given"
		else
			if [ -f "$2" ]; then
				configfile=$2
			elif [ -f "/etc/adsrootbuilder/$2" ]; then
				configfile=/etc/adsrootbuilder/$2
			else
				errexit "Config file not found"
			fi
			. ${configfile}
			shift
		fi
		shift
	fi

	# Read config file now so it can override any saved params
	if ! [ -z "${configfile}" ]; then
		# Read FS configuration from saved config file
		if [ -f ${configfile} ]; then
			. ${configfile}
		else
			errexit "Configuration file cannot be found"
		fi
	fi

	# Read an image file name prefix from command line or
	# the config file (cmdline overrides config file)
	imgprefix=${IMAGENAME:-adsrootimage}
	if	[ "$1"  == -'i' ]; then
		if ! [ -z "$2" ]; then
			imgprefix=$2
			shift
		fi
		shift
	fi

	# Set tarball name as default or specified in config file
	TARBALL_NAME=${TARNAME:-root.tar}

	if [ -z "${TARPREFIX}" ]; then
		TARPREFIX=/var/lib/adsrootbuilder
	fi

}

run_stageone () {

	# First, lets set the start point for mkroottarball
	echo
	echo
	echo
	cat <<EOF

This tool will generate filesystem images based on your current
running system according to your config file.  Before using this
tool, you should ensure that your running system matches the
configuration you would like your programmed flash to have (i.e.,
running services, using busybox or not, startup configuration).

We'll begin by creating a "start point" for these tools to mark the
beginning of a search.  After creating this start point, all files
accessed after that point will be included in your filesystem
images.

To create the "starting point" and begin analyzing your running
system, press enter.  To abort, press Ctrl-C now.
EOF
	read readvar

	if ! mkroottarball start; then
		errexit "Cannot run 'mkroottarball start' properly"
	fi

	cat <<EOF

Start point created!

EOF
	if ! touch $VARDIR/startdone; then
		errexit "Cannot create startdone signal file"
	fi

	if [ $reboot_between_modes == 'y' ]; then
		sleep 2 # allow start file to age a bit before reboot
		
		cat << EOF
THIS SYSTEM WILL NOW REBOOT to ensure all startup files are touched.
After reboot, you should run your application through a full usage
cycle to ensure all of its components are touched and then run this
tool again.

Press enter to continue or Ctrl-C to abort.
EOF
		read readvar

		# Temporary rcS script to make the root filesystem
		# read-write early in boot allows atimes to be set for the
		# early startup scripts.
		cat > /etc/rcS.d/S00rwroot <<EOF
#!/bin/sh
echo Software clock currently:
date
echo Hardware clock currently:
hwclock
echo Setting software clock using hardware clock..
hwclock --hctosys
echo Mounting root rw for adsrootbuilder..
mount -o remount,rw /
touch /etc/init.d/rcS || true
touch /etc/init.d/rc || true
rm -f \$0
EOF
		chmod +x /etc/rcS.d/S00rwroot
		
		/sbin/shutdown -r now
		# I wouldn't think this is needed, but sometimes the script
		# seems to continue even during shutdown...  hmmm
		exit

	fi

}

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

reboot_between_modes=y

# Main code begin

if ! [ -d $VARDIR ]; then
	errexit "please create $VARDIR for this tool to run"
fi

if [ -L $(which bash) ]; then
	echo "It looks like $(which bash) is a symbolic link"
	echo "If $(which bash) points to the real thing, use a"
	echo " hard link to bypass this check."
	echo
	errexit "ADSRootBuilder needs real bash to work properly"
fi
#if [ -L $(which [) ]; then
#	echo "It looks like $(which [) is a symbolic link"
#	echo "If $(which [) points to the real thing, use a"
#	echo " hard link to bypass this check."
#	echo
#	errexit "ADSRootBuilder needs real [ (test) to work properly"
#fi

# loadconfig
# Check for saved command line parameters
if [ -f $VARDIR/adsrootparams ]; then
	. $VARDIR/adsrootparams
fi

scan_clargs $@

# saveconfig
# Write command line parameters to persistent config file
{
	echo "configfile=\"${configfile}\""
} > $VARDIR/adsrootparams



if [ "${clean}" == 'y' ]; then
	echo -n "Cleaning signal files...   "
	for i in startdone scandone; do
		if [ -f $VARDIR/$i ]; then
			rm $VARDIR/$i
		fi
	done
	echo "done"
	exit
fi

# Check that we have both a configfile and image prefix
#  before continuing
if [ -z "${imgprefix}" ] || [ -z "${configfile}" ]; then
	printusage
	exit
fi


# Stage 1
# Run first part by default, unless signal file is present
#  or user tells us not to
if [ ! -f $VARDIR/startdone ] && [ "${skip_scan}" != 'y' ]; then

	run_stageone
	exit
fi


# Stage 2
if [ ! -f $VARDIR/scandone ] && [ "${skip_scan}" != 'y' ]
then
	# Second-stage run; create the image files

	echo
	echo
	echo
	cat <<EOF

adsrootbuilder has detected that you are running this tool the
second time and are ready to create the images.

This tool will now scan your system for files that have been
used since the start point was created and use them to generate
the image files defined in the configuration file.

Press enter to continue or Ctrl-C to exit
EOF
	read readvar
	
	if ! [ -z "${FILELIST}" ]; then
		# Call mkroottarball twice: once to gen file list,
		#  second to gen tarball

		# Export a possible additions list from the
		# rootbuilder conf file
		if [ -n "$ADDED_FILES" ]; then
		    export ADSLIST="${ADDED_FILES}"
		fi
		if [ -n "$EXERCISE_COMMAND" ]; then
		    export EXERCISE_COMMAND
		fi
		mkroottarball -f ${FILELIST} scan ${TARPREFIX}/${TARBALL_NAME}

		echo
		echo
		echo
		cat << EOF

adsrootbuilder has finished scanning for files and is now ready
to build the filesystem images.

If you would like, you can edit this list before the images
are generated.  To do so, type 'e' below and the list will be
opened in \$EDITOR (or 'vi' if \$EDITOR is unset).  Save the
file when you are done and exit the editor to continue.

To continue without editing the file list, just hit 'Enter'

EOF
		read -p "Continue? ('e' or <Return>) " yorn
		if [ "${yorn}" == 'e' ]; then
			editor=${EDITOR:-/usr/bin/vi}
			$editor ${FILELIST}
			if ! [ -f ${FILELIST} ]; then
				errexit "File list was destroyed - cannot continue"
			fi
		echo ""
		echo "Done editing file list...continuing with image build."
		echo ""
		fi

		# Create the tarball...
		mkroottarball -f ${FILELIST} build ${TARPREFIX}/${TARBALL_NAME}
		if ! [ -f ${TARPREFIX}/${TARBALL_NAME} ]; then
			errexit "Root tarball wasn't generated"
		fi
		if ! touch $VARDIR/scandone; then
			errexit "Cannot create scandone signal file"
		fi

	else

		# Just one pass
		mkroottarball build ${TARPREFIX}/${TARBALL_NAME}
		if ! [ -f ${TARPREFIX}/${TARBALL_NAME} ]; then
			errexit "Root tarball wasn't generated"
		fi
		if ! touch $VARDIR/scandone; then
			errexit "Cannot create scandone signal file"
		fi

	fi

fi

# Stage 3: build the filesystem images from the tarball
if [ -f $VARDIR/scandone ] || [ "${skip_scan}" == 'y' ]; then

	sleep 3
	echo
	echo
	echo
	cat <<EOF

adsrootbuilder will now run in build mode to build the filesystem 
images configured in the config file.

Using configuration file ${configfile}.

This tool will create image files in ${TARPREFIX}
named ${imgprefix}_<path>

EOF

	# Process the config file entries
	if [ "${TMP_IS_RAMFS}" = 'y' ]; then
		ramfs_tmp='-r'
	fi
	if [ "${VAR_IS_RAMFS}" = 'y' ]; then
		ramfs_tmp="$ramfs_tmp -R"
	fi

	# Iterate up to MAX_FS and read config values to form filesystem

	# Pull config file values into a convinient array (requires bash)
	for i in $(jot 1 ${MAX_FS}) ; do
		eval path[${i}]="\$FILESYSTEM_${i}_PATH"
		eval type[${i}]="\$FILESYSTEM_${i}_TYPE"
	done

	echo "Generating images for the following filesystems:"
	echo ""
	for i in $(jot 1 10); do
		if [ ! -z "${path[$i]}" ]; then
			echo "${path[$i]}  ->  ${type[$i]}"
		fi
	done

	echo ""
	echo "Press enter to continue or Ctrl-C to exit"
	read readvar

	# Build exclusion paths list
	for i in $(jot 1 ${MAX_FS}) ; do
		if ! [ -z "${path[$i]}" ]; then

			# Initialize this filesystem's exclude-path list
			exclude[$i]=""

			for x in $(jot 1 ${MAX_FS}) ; do
				if [ $x -eq $i ]; then
					continue
				fi
				if [ -z "${path[$x]}" ] ; then
					continue
				fi
				if is_beneath ${path[$i]} ${path[$x]} ; then
					# Add path[x] to path[i]'s exclude list
					exclude[$i]="${exclude[$i]}${path[$x]},"
				fi
			done
			exclude[$i]=$(echo ${exclude[$i]} | sed -e "s/,$//")
		fi
	done
		
	for i in $(jot 1 ${MAX_FS} ); do
		if ! [ -z "${path[$i]}" ]; then

			# Are we excluding any filiesystems?
			if ! [ -z "${exclude[$i]}" ]; then
				excludeopts="-x ${exclude[$i]}"
			else
				excludeopts=""
			fi

			# Convert slashes to underscores for image file name
			if [ "${path[$i]}" == '/' ]; then
				suffix='_root'
			else
				suffix=$(echo "${path[$i]}" | sed -e 's/\//_/g')
			fi

			# This is broken out by filesystem type in case we want
			#  to use different tools for different filesystems
			#  some day
			case ${type[$i]} in

			jffs2)
				echo " * Building jffs2 image for ${path[$i]}..."
				echo ""
				mkrootimg -t jffs2 -p ${path[$i]} \
				${excludeopts} ${ramfs_tmp} ${TARPREFIX}/${TARBALL_NAME} \
				${TARPREFIX}/${imgprefix}${suffix}.img

				;;

			ext2|e2fs)
				echo " * Building ext2 image for ${path[$i]}..."
				echo ""
				mkrootimg -t ext2 -p ${path[$i]} \
				${excludeopts} ${ramfs_tmp} ${TARPREFIX}/${TARBALL_NAME} \
				${TARPREFIX}/${imgprefix}${suffix}.img

				;;

			cramfs)
				echo " * Building cramfs image for ${path[$i]}..."
				echo ""
				mkrootimg -t cramfs -p ${path[$i]} \
				${excludeopts} ${ramfs_tmp} ${TARPREFIX}/${TARBALL_NAME} \
				${TARPREFIX}/${imgprefix}${suffix}.img

				;;

			*)
				echo "ERROR: Filesystem type ${type[$i]} is not supported"
				echo " in ${path[$i]}!"
				;;
			esac
			if [ -f ${TARPREFIX}/${imgprefix}${suffix}.img ]; then
			echo "Created ${type[$i]} image of ${path[$i]} as ${imgprefix}${suffix}.img"
			else
				echo ""
				echo "ERROR: failed to create image for ${path[$i]}!"
				echo "Continuing anyway..."
			fi

		fi
	done

	# Clean up signal files, etc
	if [ -f $VARDIR/startdone ]; then
		rm $VARDIR/startdone
	fi
	if [ -f $VARDIR/scandone ]; then
		rm $VARDIR/scandone
	fi

	if [ -e "$POST_SCRIPT" ]; then
		$POST_SCRIPT
	else
		cat <<EOF

                Tool finished!

You should now have your filesystem image files in
${TARPREFIX}
EOF
	fi

# End of stage 3 code
fi
