#!/bin/sh
# . this file to load the following utility functions
#
# hardware
#  the 'Hardware' string from cpuinfo, or, if not found
#  try a little harder with 'machine'
hardware(){
	local hdw
        hdw=`sed -n 's!^Hardware	*: !!p' /proc/cpuinfo`
	test -n "$hdw" || {
		hdw=`sed -n 's!^machine		*: !!p' /proc/cpuinfo`
	}
	echo $hdw
}
#
# machine
#  outputs an identifier of the current machine - i.e. the board
#  slugos is running on.
machine(){
	case "$(hardware)" in
	*Coyote*)	echo coyote;;
	*IXDPG425*)	echo ixdpg425;;
	*WRV54G*)	echo wrv54g;;
	*IXDP425*)	echo ixdp425;;
	*IXDP465*)	echo ixdp465;;
	*IXCDP1100*)	echo ixcdp1100*;;
	*Avila*)	echo avila;;
	*Loft*)		echo loft;;
	*NAS?100d*)	echo nas100d;;
	*NSLU2*)	echo nslu2;;
	*StorCenter*)	echo storcenter;;
	*)		echo unknown;;
	esac
}
#
# single_user_ok
#  if the machine is capable of single user interaction return
#  true, else return false.  The result of this function is
#  preempted by setting SULOGIN to 'yes' or 'ok' in /etc/default/rcS
single_user_ok() {
	# list known good machines in the 'case'
	test "$SULOGIN" = yes -o "$SULOGIN" = ok ||
		case "$(machine)" in
		ixdp*|avila|loft)
			test "$SULOGIN" != never;;
		*)	return 1;;
		esac
}
#
# load_functions "source"
#  load the functions in '/sbin/source' - relies on /sbin/source being
#  a shell script and having support for this function.
load_functions(){
	test -n "$1" -a -x "/sbin/$1" && . "/sbin/$1" || {
		echo "$0: /sbin/$1: script not found" >&2
		return 1
	}
}
#
# mtdev "name"
#  return (output) the character device name for flash parition "name"
#  /proc/mtd has the general form:
#    dev:    size   erasesize  name
#    mtd5: 00020000 00020000 "FIS directory"
#  use this rather than hard-wiring the device because the partition
#  table can change - looking in /proc/mtd is more reliable.
mtdev(){
	if test $(machine) = storcenter ; then                               
	sed -n 's!^mtd\([0-9][0-9]*\):[^"]*"'"$1"'"$!/dev/mtd/\1!p' /proc/mtd
	else
	sed -n 's!^\(mtd[0-9][0-9]*\):[^"]*"'"$1"'"$!/dev/\1!p' /proc/mtd
	fi
}
#
# mtblockdev "name"
#  as mtdev but output the name of the block (not character) device
mtblockdev(){
	if test "$(machine)" = storcenter ; then
	sed -n 's!^mtd\([0-9][0-9]*\):[^"]*"'"$1"'"$!/dev/mtdblock/\1!p' /proc/mtd
	else
	sed -n 's!^mtd\([0-9][0-9]*\):[^"]*"'"$1"'"$!/dev/mtdblock\1!p' /proc/mtd
	fi
}
#
# mtsize "name"
#  the size of the partition as a hexadecimal value (with 0x at the front)
mtsize(){
	sed -n 's!^mtd[0-9][0-9]*: \([^ ]*\)[^"]*"'"$1"'"$!0x\1!p' /proc/mtd
}
#
# sysvalmatch "section" "name" 'pattern' "configuration file"
# sysvalof "section" "name" "configuration file"
# sysval "section" "name"
#  outputs the value of the SysConf variable 'name' from section 'section',
#  if there are multiple definitions only the last is output
# NOTE: these functions should only be used internally, add entries to 'config'
#  below if necessary.  This is because 'config' does the defaulting.
sysvalmatch(){
	sed -n '/^\['"$1"'\]$/,/^\[.*\]$/s/^'"$2"'=\('"$3"'\)$/\1/p' "$4" | sed -n '$p'
}
sysvalof(){
	sysvalmatch "$1" "$2" '.*' "$3"
}
sysval(){
	test -r "$config_root/etc/default/sysconf" &&
		sysvalof "$1" "$2" "$config_root/etc/default/sysconf"
}
#
# syssection "section"
#  outputs all the values from the given section changed to the format "name value"
#  (i.e. the '=' is dropped).
syssection(){
	test -r "$config_root/etc/default/sysconf" &&
		sed -n '/^\['"$1"'\]$/,/^\[.*\]$/s/^\([^=]*\)=\(.*\)$/\1 \2/p' "$config_root/etc/default/sysconf"
}
#
# config "value"
#  convenience callers for specific values to avoid mis-typing in scripts
#  NOTE: this function does the defaulting, 'sysval' does not!
# config_root: if set this will override the root where config/sysval
#              looks for /etc/default/sysconf
config(){
	local mac
	mac="$(test -r /proc/net/maclist &&
		sed -n '/^[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]$/p' /proc/net/maclist |
		sed -n 1p)"
	#
	case "$1" in
	mac)	test -n "$mac" && echo "$mac";;
	host)	if test -n "$(sysval network disk_server_name)"
		then
			sysval network disk_server_name
		elif test -n "$(sysval network default_server_name)"
		then
			sysval network default_server_name
		elif test -n "$mac"
		then
			echo "$mac" | sed -n 's/^\(..\):\(..\):\(..\):\(..\):\(..\):\(..\)$/slug\1\2\3\4\5\6/p'
		else
			# because we want the name to remain constant:
			echo "turbostation"
		fi;;
	domain)	sysval network w_d_name;;
	iface)  if test -n "$(sysval network lan_interface)"
		then
			sysval network lan_interface
		else
			echo eth0
		fi;;
	ip)	if test -n "$(sysval network ip_addr)"
		then
			sysval network ip_addr
		else
			echo 192.168.1.16
		fi;;
	netmask)sysval network netmask;;
	gateway)sysval network gateway;;
	dns)	sysval network dns_server1;;
	dns2)	sysval network dns_server2;;
	dns3)	sysval network dns_server3;;
	boot)	if test -n "$(sysval network bootproto)"
		then
			sysval network bootproto
		else
			echo dhcp
		fi;;
	valid)	test -r "$config_root/etc/default/sysconf" -a -n "$mac";;
	*)	return 1;;
	esac
}
#
# checkif "iface"
#  Validate an interface name by making sure that it exists
#  in /proc/net/dev (and is not lo).  The listing outputs the
#  interface followed by a :, the check function looks for
#  something of the form '$1[a-zA-Z0-9]*:' and outputs the
#  part preceding the ':'
checkif(){
	sed -n '/^[ 	]*lo:/d;s/^[ 	]*\('"$1"'[a-zA-Z0-9]*\):.*$/\1/p;tE;d;:E;q' /proc/net/dev
}
#
# checkmount "mountpoint"
#  tests an already mounted mountpoint to see whether to attempt to
#  boot with this as root.  Returns success if it appears ok.
checkmount(){
	# basic test for init (the kernel will try to load this)
	# but require a shell in bin/sh too
	test	\( -d "$1/mnt" \) -a \
		\( -x "$1/bin/sh" -o -h "$1/bin/sh" \) -a \
		\( -x "$1/usr/sbin/chroot" -o -h "$1/usr/sbin/chroot" -o \
		   -x "$1/sbin/chroot" -o -h "$1/sbin/chroot" \) -a \
		\( -x "$1/sbin/init" -o -h "$1/sbin/init" -o \
		   -x "$1/etc/init" -o -h "$1/etc/init" -o \
		   -x "$1/bin/init" -o -h "$1/bin/init" \)
}
#
# swivel "new root" "old root"
#  NOTE: the arguments must be paths relative to /, bad things
#  will happen if the arguments themselves start with /
#  Pivot to a new root.  This does all the fancy pivot_root stuff
#  including closing streams and does a umount /proc - it doesn't
#  matter if this fails (failure codes are ignored), but if /proc
#  was mounted it must be restored by the caller on return.
#  Normally this function never returns!
#  On return 0,1,2 are connected to /dev/console - this may not
#  have been true before!
swivel(){
	cd "$1"
	exec <&- >&- 2>&-
	# This is just-in-case the called mounted /proc and was
	# unable to close it because of the streams
	umount /proc 2>/dev/null
	if pivot_root . "$2"
	then
		# everything must move out of the old root, this process
		# is $2/bin/sh so it must die, IO is redirected
		# just in case - typically it will be to a device so it
		# won't hold the old root open.
		# the exec here is the first point at which the old root
		# is unused - before the exec regardless of the close of
		# 0,1,2 above ash still has *this* shell script open!
		# (it's on fd 10).
		# init closes all file descriptors, there's no point
		# supplying it with fds.
		# NOTE: this used to use $2/usr/sbin/chroot, however on
		# linux / is already . when the command is executed
		# therefore it is essential to use the local (new root)
		# chroot to ensure it gets the correct shared libraries.
		if test -x usr/sbin/chroot -o -h usr/sbin/chroot
		then
			chroot=usr/sbin/chroot
		elif test -x sbin/chroot -o -h sbin/chroot
		then
			chroot=sbin/chroot
		else
			chroot=chroot
		fi
		#
		exec "$chroot" . bin/sh -c "\
			test -x sbin/init && exec sbin/init
			test -x etc/init && exec etc/init
			test -x bin/init && exec bin/init
			mount -t sysfs sysfs /mnt
			umount /mnt
			sync;sync;sync
			exit 1"
	fi
	#
	# recovery - must restore the old root
	cd "$2"
	sbin/pivot_root . "$1"
	# cd is back to $1 - either pivot_root doesn't change it and the
	# chroot above was not executed, or pivot_root does change it and
	# has just changed it back!
	exec <>/dev/console >&0 2>&0
}
#
# ifup "interface"
#  bring that interface up with the configured ip and other
#  information
ifup(){
	local ip hostname router subnet iface HOSTNAME NETMASK BROADCAST

	iface="$1"
	ip="$(config ip)"
	hostname="$(config host)"
	router="$(config gateway)"
	broadcast=

	if test -n "$ip"
	then
		# only if an ip was specified
		subnet="$(config netmask)"
	else
		ip=192.168.1.77
	fi

	# First try udhcpc - note that the /boot/udhcpc.script
	# simply records the values returned and the udhcpc
	# is not left running so this will only work for
	# the lease length time!
	ifconfig "$iface" up
	if test "$(config boot)" != static
	then
		test -n "$hostname" && HOSTNAME="-H $hostname"
		# The script writes the required shell variable assignments
		# to file descriptor 9
		eval $(udhcpc -i "$iface" -n -q -r "$ip" $HOSTNAME -s /boot/udhcpc.script 9>&1 >/dev/null)
	fi

	test -n "$broadcast" && BROADCAST="broadcast $broadcast"
	test -n "$subnet" && NETMASK="netmask $subnet"

	if ifconfig "$iface" "$ip" $NETMASK $BROADCAST
	then
		for route in $router
		do
			route add default gw "$route" dev "$iface"
		done
		return 0
	else
		ifconfig "$iface" down
		return 1
	fi
}
#
# ifdown "interface"
#  take the interface down
ifdown(){
	ifconfig "$1" down
}
#
# mountflash "flash device" "flash root directory" {mount options}
#  Finds and mounts the flash file system on the given directory
mountflash(){
	local ffsdev ffsdir

	ffsdev="$1"
	test -n "$ffsdev" -a -b "$ffsdev" || {
		echo "$0: unable to find flash file system to copy ($ffsdev)" >&2
		return 1
	}
	shift

	ffsdir="$1"
	test -n "$ffsdir" -a -d "$ffsdir" || {
		echo "$0: mountflash $ffsdir: not a directory (internal error)" >&2
		return 1
	}
	shift

	mount -t jffs2 "$@" "$ffsdev" "$ffsdir" || {
		echo "$0: $ffsdev: unable to mount flash file system on $ffsdir" >&2
		return 1
	}
	return 0
}
#
# umountflash [-r] "flash device"
#  unmount any instance of the given flash device, if -r is specified a mount on
#  root is an error, otherwise a mount on root is ignored (and remains).
umountflash(){
	local rootok ffsno ffsdev
	rootok=1
	case "$1" in
	-r)	rootok=
		shift;;
	esac
	#
	# The argument is ffsdev
	ffsdev="$1"
	ffsno="$(devio "<<$ffsdev" prd)"
	test -n "$ffsno" -a "$ffsno" -ge 0 || {
		echo "$0: $ffsdev: device number $ffsno is not valid, cannot continue." >&2
		return 1
	}
	#
	# Make sure that Flashdisk isn't mounted on /
	if test -z "$rootok" -a "$(devio "<</etc/init.d/sysconfsetup" prd)" -eq "$ffsno"
	then
		echo "$0: $ffsdev is mounted on /, use turnup ram" >&2
		return 1
	fi
	#
	# The function is currently always used interactively, so output 
	echo "$0: umounting any existing mount of $ffsdev" >&2
	#
	# check each mount point, do this last first because otherwise nested
	# mounts of ffsdev cannot be umounted.
	ffs_umount() {
		local device mp type options stuff

		read device mp type options stuff
		test -z "$device" && return 0

		# handle following entries first
		ffs_umount || return 1

		# handle this entry, since this is currently only used for unmounting
		# the flash root partition we know a file which must exist...
		case "$mp/$type" in
		//jffs2);; # skip /
		*/jffs2)test "$(devio "<<$mp/etc/init.d/sysconfsetup" prd 2>/dev/null)" -ne "$ffsno" ||
			umount "$mp" || {
				echo "$0: $mp: unable to umount $ffsdev" >&2
				return 1
			};;
		esac

		return 0
	}
	#
	ffs_umount </proc/mounts || {
		echo "$0: umount $ffsdev from all mount points then re-run $0" >&2
		return 1
	}

	return 0
}

#
# uuid_by_partition
#  output a list of partitions and their UUIDs
uuid_by_partition() {
	blkid -c /dev/null -s UUID | sed -n 's/^\([^:]*\): .*UUID="\([^"]*\)".*$/\1 \2/p'
}

#
# partition_of uuid
#  return the partition corresponding to the UUID
partition_of() {
	sed -n 's/^\([^ ]*\) '"$1"'$/\1/p'
}