# -*- shell-script -*-

catenate_cpiogz() {
	# Sanity check
	if [ ! -e "${1}" ]; then
		echo "W:catenate_cpiogz: arg1='${1}' does not exist." >&2
		return
	fi

	cat "${1}" >>"${__TMPCPIOGZ}"
}

force_load()
{
		manual_add_modules ${@}
		echo "${@}" >>"${DESTDIR}/conf/modules"
}

# Takes a file containing a list of modules to be added as an
# argument, figures out dependancies, and adds them.
#
# Input file syntax:
#
#   # comment
#   modprobe_module_name [args ...]
#   [...]
#
add_modules_from_file()
{
	# Sanity check
	if [ ! -e "${1}" ]; then
		echo "W:add_modules_from_file: arg1='${1}' does not exist." >&2
		return
	fi

	sed -e '/^#/d' ${1} | while read module rest; do
		force_load "${module}" "${rest}"
	done
}

# Add dependent modules + eventual firmware
manual_add_modules()
{
	local mam_x firmwares firmware

	for mam_x in $(modprobe --set-version="${version}" --ignore-install \
	--show-depends "${1}" 2>/dev/null | awk '/^insmod/ { print $2 }'); do
		# Prune duplicates
		if [ -e "${DESTDIR}/${mam_x}" ]; then
			continue
		fi

		mkdir -p "${DESTDIR}/$(dirname "${mam_x}")"
		ln -s "${mam_x}" "${DESTDIR}/$(dirname "${mam_x}")"
		if [ "${verbose}" = "y" ]; then
			echo "Adding module ${mam_x}"
		fi

		# Add firmware files if necessary
		firmwares=$(modinfo -F firmware "${mam_x}")
		if [ -z "${firmwares}" ]; then
			continue
		fi
		for firmware in $firmwares; do
			if [ -e "${DESTDIR}/lib/firmware/${version}/${firmware}" ]; then
				continue
			fi

			# Only print warning for missing fw of loaded module
			# or forced loaded module
			if [ ! -e "/lib/firmware/${version}/${firmware}" ]; then
				if grep -q "^$(basename "${mam_x}" .ko)" \
				/proc/modules \
				|| grep -q "^$(basename "${mam_x}" .ko)" \
				"${CONFDIR}/modules"; then
					echo "W: Possible missing firmware /lib/firmware/${version}/${firmware} for module $(basename ${mam_x} .ko)" >&2
				fi
				continue
			fi

			copy_exec "/lib/firmware/${version}/${firmware}"
			if [ "${verbose}" = "y" ]; then
				echo "Adding firmware ${firmware}"
			fi
		done
	done
}

# $1 is the source path (e.g. /usr/bin/time)
# $2 is the relative destination (e.g. /usr or /usr/time)
#
# The destination is interpreted in the same way "cp" would, meaning
# (assuming /bin is a directory):
#
#   "copy_exec /usr/bin/time /bin"        -> /bin/time
#   "copy_exec /usr/bin/time /bin/mytime" -> /bin/mytime
# 
# If $2 is left out, the same destination path as for the source arg will
# be used and directories will be created as needed, so:
#
#   "copy_exec /usr/bin/time"             -> /usr/bin/time
#
copy_exec() {
	local source target destination final_destination x nonoptlib
	local libname dirname

	source="${1}"
	if [ -n "${2}" ]; then
		target="${2}"
	else
		if [ ! -e "${DESTDIR}/$(dirname "${1}")" ]; then
			mkdir -p "${DESTDIR}/$(dirname "${1}")"
		fi
		target="${1}"
	fi

	if [ -d "${DESTDIR}/${target}" ]; then
		destination="${target}/$(basename "${source}")"
	else
		destination="${target}"
	fi
	final_destination="${DESTDIR}/${destination}"

	if [ -L "$final_destination" ]; then
		if [ $(readlink "${final_destination}") != "${source}" ]; then
			echo "W:copy_exec: Not copying ${source} to \$DESTDIR${destination}, which is already a copy of $(readlink ${final_destination})" >&2
			return
		fi
	else
		ln -s ${source} ${DESTDIR}/${destination}
		if [ "${verbose}" = "y" ]; then
			echo "Adding binary ${source}"
		fi
	fi

	# Copy the dependant libraries
	for x in $(ldd ${source} 2>/dev/null | sed -e '
	    /\//!d;
	    /linux-gate/d;
	    /=>/ {s/.*=>[[:blank:]]*\([^[:blank:]]*\).*/\1/};
	    s/[[:blank:]]*\([^[:blank:]]*\) (.*)/\1/' 2>/dev/null); do

		# Try to use non-optimised libraries where possible.
		# We assume that all HWCAP libraries will be in tls,
		# sse2, vfp or neon
		nonoptlib=$(echo "${x}" | sed -e 's#/lib/\(tls\|i686\|sse2\|neon\|vfp\).*/\(lib.*\)#/lib/\2#')

		if [ -e "${nonoptlib}" ]; then
			x="${nonoptlib}"
		fi

		libname=$(basename "${x}")
		dirname=$(dirname "${x}")

		mkdir -p "${DESTDIR}/${dirname}"
		if [ ! -e "${DESTDIR}/${dirname}/${libname}" ]; then
			ln -s "${x}" "${DESTDIR}/${dirname}"
			if [ "${verbose}" = "y" ]; then
				echo "Adding library ${x}"
			fi
		fi
	done
}

# Copy entire subtrees to the initramfs
copy_modules_dir()
{
	local x_mod

	if ! [ -d "${MODULESDIR}/${1}" ]; then
		return;
	fi
	if [ "${verbose}" = "y" ]; then
		echo "Copying module directory ${1}"
	fi
	for x_mod in $(find "${MODULESDIR}/${1}" -name '*.ko' -print); do
		manual_add_modules $(basename ${x_mod} .ko)
	done
}

# walk /sys for relevant modules
sys_walk_mod_add()
{
	local driver_path module
	device_path="$1"
	
	while [ "${device_path}" != "/sys" ]; do
		driver_path="$(readlink -f ${device_path}/driver)"
		if [ -e "$driver_path" ]; then
			module="$(basename $(readlink -f $driver_path))"
			if [ -n "${module}" ]; then
				force_load "${module}"
			fi
		fi
		device_path="$(dirname ${device_path})"
	done
}

# walk /sys for relevant modalias
sys_walk_modalias()
{
	local device_path modalias

	device_path="$(dirname "${1}")"
	device_path="$(dirname "${device_path}")"
	if [ -e "${device_path}/modalias" ]; then
		modalias=$(cat "${device_path}/modalias")
	fi

	if [ -n "${modalias}" ]; then
		force_load "${modalias}"
	fi
}

# find and only copy root relevant modules
dep_add_modules()
{
	local block minor root FSTYPE root_dev_path x

	# findout root block device + fstype
	eval "$(mount | awk '/\/dev\// {if ($3 == "/") {print "root=" $1 "\nFSTYPE=" $5; exit}}')"
	if [ "${root}" = "/dev/root" ] ; then
		root="/dev/disk/by-uuid/"$(/lib/udev/vol_id --uuid ${root}) 2>/dev/null
	fi
	root="$(readlink -f ${root})"

	# find out real rootfs on auto type
	if [ "${FSTYPE}" = "auto" ]; then
		eval "$(/usr/lib/klibc/bin/fstype ${root})"
	fi

	# check that fstype rootfs recognition
	if [ "${FSTYPE}" = "unknown" ]; then
		echo "mkinitramfs: unknown fstype on root ${root}"
		echo "mkinitramfs: workaround is MODULES=most" 
		echo "mkinitramfs: Error please report bug on initramfs-tools" 
		exit 1
	fi

	# Add rootfs
	manual_add_modules "${FSTYPE}"

	# lvm luks root
	if [ "${root#/dev/mapper/}" != "${root}" ]; then
		minor=$((0x$(stat --format "%T" ${root}) % 256))
		block=$(ls -1 /sys/block/dm-${minor}/slaves | head -n 1)
		if [ "${block#dm-}" != "${block}" ]; then
			block=$(ls -1 /sys/block/${block}/slaves | head -n 1)
		fi
		block=${block%[0-9]*}
	# md root new naming scheme /dev/md/X
	elif [ "${root#/dev/md/}" != "${root}" ]; then
		root=${root#/dev/md/}
		block=$(awk "/^md${root}/{print substr(\$5, 1, 3); exit}" \
			/proc/mdstat)
	# md root /dev/mdX
	elif [ "${root#/dev/md}" != "${root}" ]; then
		root=${root#/dev/}
		block=$(awk "/^${root}/{print substr(\$5, 1, 3); exit}" \
			/proc/mdstat)
	# classical root device
	else
		block=${root#/dev/}
		block=${block%[0-9]*}
	fi

	# Error out if /sys lack block dev
	if [ -z "${block}" ] || [ ! -e /sys/block/${block} ]; then
		echo "mkinitramfs: missing ${block} root ${root} /sys entry"
		echo "mkinitramfs: workaround is MODULES=most" 
		echo "mkinitramfs: Error please report the bug" 
		exit 1
	fi

	# sys walk ATA
	root_dev_path=$(readlink -f /sys/block/${block}/device)
	sys_walk_mod_add ${root_dev_path}

	# catch old-style IDE
	if [ -e /sys/bus/ide/devices/ ]; then
		sys_walk_modalias ${root_dev_path}
		manual_add_modules ide-disk
		manual_add_modules ide-cd
	fi

	if [ -e /sys/bus/scsi/devices/ ]; then
		manual_add_modules sd_mod
	fi

	if [ -e /sys/bus/i2o/devices/ ]; then
		force_load i2o_block
		force_load i2o_config
	fi

	if [ -e /sys/bus/ps3_system_bus/ ]; then
		for x in ps3disk ps3rom ps3-gelic ps3_sys_manager; do
			manual_add_modules "${x}"
		done
	fi

	if [ -e /sys/bus/vio/ ]; then
		for x in sunvnet sunvdc; do
			manual_add_modules "${x}"
		done
	fi
}


# The modules "most" classes added per default to the initramfs
auto_add_modules()
{
	case "$1" in
	base)
		for x in ehci-hcd ohci-hcd uhci-hcd usbhid hid_a4tech \
		hid_apple hid_belkin hid_bright hid_cherry hid_chicony \
		hid_cypress hid_dell hid_ezkey hid_gyration hid_logitech \
		hid_microsoft hid_monterey hid_petalynx hid_pl 	hid_samsung \
		hid_sony hid_sunplus hid_tmff hid_zpff usb-storage ext2 \
		ext3 ext4 isofs jfs nfs reiserfs udf xfs af_packet atkbd i8042 \
		virtio_pci vfat nls_cp437 nls_iso8859-1; do
			manual_add_modules "${x}"
		done
	;;
	net)
		for x in 3c59x 8139cp 8139too 8390 atl1 atl1e b44 bmac \
		bnx2 cxgb3 defxx dl2k e100 e1000 e1000e ehea epic100 \
		ep93xx_eth eql fealnx famachi forcedeth gelic_net \
		hp100 igb ipg mace mv643xx_eth myri10ge \
		natsemi ne2k-pci netconsole niu ns83820 pcnet32 qla3xxx \
		r8169 s2io sis900 skge sky2 slhc smc911x starfire \
		sundance sungem sungem_phy sunhme sunvnet tg3 tlan de2104x \
		de4x5 dmfe tulip winbond-840 xircom_cb xircom_tulip_cb \
		typhon via-rhine via-velocity yellowfin; do
			manual_add_modules "${x}"
		done
	;;
	ide)
		copy_modules_dir kernel/drivers/ide
	;;
	scsi)
		copy_modules_dir kernel/drivers/scsi
		for x in mptfc mptsas mptscsih mptspi; do
			manual_add_modules "${x}"
		done
	;;
	ata)
		copy_modules_dir kernel/drivers/ata
	;;
	block)
		copy_modules_dir kernel/drivers/block
	;;
	# FIXME: can be removed after Lenny release
	ieee1394)
		for x in ohci1394 sbp2; do
			manual_add_modules "${x}"
		done
	;;
	firewire)
		for x in firewire-ohci  firewire-sbp2; do
			manual_add_modules "${x}"
		done
	;;
	i2o)
		for x in i2o_block; do
			manual_add_modules "${x}"
		done
	;;
	dasd)
		for x in dasd_eckd_mod dasd_fba_mod; do
			manual_add_modules "${x}"
		done
	;;
	*)
		auto_add_modules base
		auto_add_modules net
		auto_add_modules ide
		auto_add_modules scsi
		auto_add_modules block
		auto_add_modules ata
		auto_add_modules i2o
		auto_add_modules dasd
		auto_add_modules ieee1394
		auto_add_modules firewire
	;;
	esac
}

usage()
{
	cat >&2 << EOF

Usage: ${0} [OPTION]... <-o outfile> [version]

Options:
  -d confdir  Specify an alternative configuration directory.
  -k          Keep temporary directory used to make the image.
  -o outfile  Write to outfile.
  -r root     Override ROOT setting in mkinitrd.conf.

See mkinitramfs(8) for further details.
EOF
	exit 1

}

# minimal supported kernel version
check_minkver()
{
	local curversion initdir DPKG_ARCH minversion cm_x tmp

	curversion="${1}"
	initdir="${2}"
	if [ -z "${initdir}" ]; then
		DPKG_ARCH=$(dpkg --print-installation-architecture)
		case ${DPKG_ARCH} in
			ia64|hppa)
				minversion="2.6.15"
			;;
			*)
				minversion="2.6.12"
			;;
		esac
		if dpkg --compare-versions "${curversion}" lt "${minversion}"; then
			echo "W: kernel ${curversion} too old for initramfs on ${DPKG_ARCH}" >&2
			echo "W: not generating requested initramfs for kernel ${curversion}" >&2
			exit 2
		fi
		return 0
	fi
	set_initlist
	for cm_x in ${initlist}; do
		# sed: keep last line starting with MINKVER=,
		#      remove MINKVER= and trailing space
		minver=$(sed '/^MINKVER=/!d;$!d;s/^MINKVER=//;s/[[:space:]]*$//' "${initdir}/${cm_x}")
		if [ -z "${tmp}" ]; then
			continue
		elif dpkg --compare-versions "${curversion}" lt "${minver}"; then
			echo "W: ${cm_x} hook script requires at least kernel version ${minver}" >&2
			echo "W: not generating requested initramfs for kernel ${curversion}" >&2
			exit 2
		fi
	done
}
