Fatdog Arm

Artifact Content
Login

Artifact 80c9e12fc3625ddf7f370c50a7c86053be67ee3b:


#!/bin/bash
# James Budiono 2011-2012, 2013
# Licens: GNU GPL Version 3 or later
# Load / unload SFS on the fly - this is also the system SFS loader for Fatdog64
#
# note: uses bash - we don't need speed here, we need features (uses array etc)
# can probably be converted to ash but I can't be bothered
# version 2 - supports start/stop as a service (to be run from /etc/init.d)
#             When run in this way, supports persistence
# version 3 - proper recovery if we fail to load for whatever reason
# version 4 - canonicalise path if a parameter is passed in.
# version 5 - adapted to be the system SFS loader for Fatdog64, now lives in /sbin
# version 6 - more message, don't show base sfs

# constants and start-up variables
MAXLOOP=250					# highest loop device number we can use
RESERVED=10					# reserve 10 loop devices for filemnt and others
SFS_DIR=/mnt/home			# where to look for sfs
BASE_SFS=fd64-620.sfs		# don't show base sfs
UNLOAD_PERMANENTLY=false
LOAD_PERMANENTLY=false
LISTFILE=/etc/load_sfs
export SFS_DIR BASE_SFS

# boot-time configuration
#BOOTSTATE_PATH=/etc/BOOTSTATE
. $BOOTSTATE_PATH
[ "$BASE_SFS_PATH" ] && BASE_SFS=${BASE_SFS_PATH##*/}


# insertion point - below tmpfs, pup_save and pup_multi
INSERT_POINT=1	# tmpfs / pup_save
[ "$TMPFS_MOUNT" -a "$SAVEFILE_MOUNT" ] && INSERT_POINT=2   # both tmpfs & pup_save
[ "$MULTI_MOUNT" ] && INSERT_POINT=$(( $INSERT_POINT + 1 )) # pup_multi is another layer

########## core load/unload functions ###########

# $@ text to display
function message() {
	if [ "$DISPLAY" ]; then
		Xdialog --infobox "$@" 0 0 10000
	else
		echo "$@"
	fi
}

# output - FREELOOP = free loop device
function find_free_loop() {
	local loops_in_use
	for i in $(losetup-FULL -a | sed 's/:.*$//;s_/dev/loop__'); do
		loops_in_use[$i]=$i
	done
	#echo ${loops_in_use[@]}

	for ((i=$RESERVED; i<$MAXLOOP; i++)); do
		if [ -z "${loops_in_use[$i]}" ]; then
			break
		fi
	done
	#echo loop device $i is free	
	FREELOOP=$i
}

# $1 = loop device number
function check_and_make_loop_device() {
	if [ ! -b /dev/loop$1 ]; then
		mknod /dev/loop$1 b 7 $1
#	else
#		echo loop device $1 already exist
	fi
}

# $1 = pup_ro number
function check_and_make_pupro() {
	if [ ! -d $AUFS_ROOT/pup_ro$1 ]; then
		mkdir -p $AUFS_ROOT/pup_ro$1
#	else
#		echo pup_ro$1 already exist
	fi	
}

# $1 = path to SFS to load
function sfs_load() {
	# make sure we're not loading the same sfs twice
	if [ -z "$(losetup-FULL -a | fgrep "${1##*/}")" ]; then
		# find free loop device, re-using what we can, if we can't, then make new loop/pup_ro mountpoint
		# keep loop-N and pup_ro-N sync at all times
		find_free_loop
		check_and_make_loop_device $FREELOOP
		check_and_make_pupro $FREELOOP
		
		# now ready to load
		if losetup /dev/loop$FREELOOP "$1"; then
			if mount -o ro /dev/loop$FREELOOP $AUFS_ROOT/pup_ro$FREELOOP; then
				if busybox mount -t aufs -o remount,ins:$INSERT_POINT:$AUFS_ROOT/pup_ro$FREELOOP=rr aufs /; then
					message "$1 loaded successfully."
				else
					message "Failed to remount aufs $1" 
					umount -d $AUFS_ROOT/pup_ro$FREELOOP
				fi
			else
				message "Failed to mount branch for $1" 
				losetup -d /dev/loop$FREELOOP
			fi
		else
			message "Failed to assign loop devices loop$FREELOOP for $1" 
		fi
	fi	
}

# $1 = sfs-name to unload - we have to figure out which branch this is
function sfs_unload() {
	# make sure the sfs we want to unload is loaded
	local loopdev=$(losetup-FULL -a | grep "$1" | sed '/loop[0-9]:/ d;s_\(/dev/loop[0-9]*\):.*_\1_')
	if [ -z $loopdev ]; then return; fi
	local branch=$(grep -m 1 $loopdev /proc/mounts | awk '{print $2}')
	
	if [ "$branch" ]; then
		# now ready to unload
		busybox mount -t aufs -o remount,del:"$branch" unionfs / && 
		umount -d "$branch" &&
		message "$1 unloaded successfully." || message "Unable to unload $1"
	else
		losetup -d $loopdev # try to delete the loop anyway	
		message "$1 is not listed in aufs branch - cannot unload." 
	fi
}

############ persistence management  #############
# $1 = path to add
function add_to_list() {
	{ cat $LISTFILE; echo "$1"; } | sort | uniq > $LISTFILE.tmp
	mv $LISTFILE.tmp $LISTFILE
}

# $1 = filename to remove
function remove_from_list() {
	if [ ! -f $LISTFILE ]; then return; fi
	cat $LISTFILE | fgrep -v "$1" > $LISTFILE.tmp
	mv $LISTFILE.tmp $LISTFILE
}

# get all sfs from the list
function get_list() {
	if [ ! -f $LISTFILE ]; then return; fi
	cat $LISTFILE | sort -r
}

############ GUI related stuff and helpers ############

function list_loaded_sfs() {
	losetup-FULL -a | sed '/loop[0-9]:/ d;s_^.*/__;s/)$//'
}
export -f list_loaded_sfs

# don't display already loaded sfs
function list_available_sfs() {
	ls $SFS_DIR 2> /dev/null | grep -Fi ".sfs" | grep -Fiv "$BASE_SFS" |  awk '
BEGIN {
	while ("list_loaded_sfs" | getline) {
		loaded[$0]=1
	}
}
{
	if (loaded[$0] == "") print $0
}
'
}
export -f list_available_sfs

function build_gui() {
export gui='
<window title="System SFS Loader">
	<vbox>
		<hbox>
			<frame Load SFS>
				<vbox>
					<tree>
						<variable>LOADSFS</variable>
						<label>Available SFS</label>
						<input>list_available_sfs</input>
						<height>200</height><width>300</width>
					</tree>
					<checkbox>
						<variable>LOAD_PERMANENTLY</variable>
						<label>Load at every boot</label>
						<default>'$LOAD_PERMANENTLY'</default>
					</checkbox>					
					<button>
						<label>Load</label>
					</button>
					<hbox>
						<entry accept="directory">
							<variable>SFS_DIR</variable>
							<input>echo $SFS_DIR</input>
						</entry>
						<button>
							<input file stock="gtk-open"></input>
							<action type="fileselect">SFS_DIR</action>
						</button>
						<button>
							<label>Refresh</label>
							<action type="refresh">LOADSFS</action>
						</button>					
					</hbox>					
				</vbox>
			</frame>		
			<frame Unload SFS>
				<vbox>
					<tree>
						<variable>UNLOADSFS</variable>
						<label>SFS currenty loaded</label>
						<input>list_loaded_sfs</input>
						<height>200</height><width>300</width>
					</tree>
					<checkbox>
						<variable>UNLOAD_PERMANENTLY</variable>
						<label>Do not load again at next boot</label>
						<default>'$UNLOAD_PERMANENTLY'</default>
					</checkbox>
					<button>
						<label>Unload</label>
					</button>
				</vbox>
			</frame>
		</hbox>
		<button>
			<label>Done</label>
			<action>exit:Done</action>
		</button>
	</vbox>
</window>
'
}

# gui for load/unload
function interactive() {
	while true; do
		build_gui
		OUTPUT=$(gtkdialog -cp gui)
		#echo $OUTPUT
		eval "$OUTPUT"
		case $EXIT in
			Load) 
				if [ "$LOADSFS" ]; then
					sfs_load "$SFS_DIR/$LOADSFS"
					if [ "$LOAD_PERMANENTLY" = "true" ]; then
						add_to_list "$SFS_DIR/$LOADSFS"
						message "$LOADSFS will be loaded at next boot".
					fi
				else
					message "You didn't choose anything to load. Please try again." 
				fi
				;;
				
			Unload) 
				if [ "$UNLOADSFS" ]; then
					sfs_unload "$UNLOADSFS"
					if [ "$UNLOAD_PERMANENTLY" = "true" ]; then
						remove_from_list "$UNLOADSFS"
						message "$UNLOADSFS will be no longer be loaded at next boot".
					fi					
				else
					message "You didn't choose anything to unload. Please try again." 
				fi
				;;
			Done|abort) 
				break 
				;;
		esac
	done
	touch /usr/share/applications/abiword.desktop  #make menu refresh
	return 0
}

############## main ##############
if [ -z "$1" ]; then
	# no parameter passed - use interactive gui
	[ $(id -u) -ne 0 -a "$DISPLAY" ] && exec gtksu "System SFS Loader" $0
	[ "$DISPLAY" ] && interactive || echo "Try ${0##*/} --help"
else
	case "$1" in
		start)
			#echo start service - loadall
			[ $(id -u) -ne 0 -a "$DISPLAY" ] && exec gtksu "System SFS Loader" $0 start
			[ $(id -u) -ne 0 -a -z "$DISPLAY" ] && exec su -c "$0 start"
			for a in $(get_list); do
				sfs_load "$a"
			done
			;;
		stop)
			#echo stop service - unloadall
			[ $(id -u) -ne 0 -a "$DISPLAY" ] && exec gtksu "System SFS Loader" $0 stop
			[ $(id -u) -ne 0 -a -z "$DISPLAY" ] && exec su -c "$0 stop"
			for a in $(get_list); do
				sfs_unload "${a##*/}"
			done
			;;
		--help|-h|help)
			cat << EOF
Usage: $0 [start|stop|help|--help|-h|/path/to/sfsfile]

Without parameters, $0 will show the GUI.

$0 has a config file, located in $LISTFILE.
The config file stores the list of sfs to be automatically loaded or
unloaded by "start" and "stop" parameters below.
"Permanent" option in the GUI will update this config file.

With "start", it will load all the sfs found in the config file.
With "stop", it will unload all the sfs found in the config file.
"start" and "stop" is meant for when $0 is symlink-ed to 
/etc/init.d as system service.
Any other parameter will be taken as an sfs filename to load.
EOF
			;;
		*)
			# anything else - assume it's path to sfs to load 
			[ $(id -u) -ne 0 -a "$DISPLAY" ] && exec gtksu "System SFS Loader" $0 $(readlink -f "$1")
			[ $(id -u) -ne 0 -a -z "$DISPLAY" ] && exec su -c "$0 \"$(readlink -f "$1")\""	
			sfs_load $(readlink -f "$1")
			;;
	esac
fi