# -*- 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 module [args...]
force_load()
{
	manual_add_modules "$1"
	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

	grep '^[^#]' ${1} | while read module args; do
		[ -n "$module" ] || continue
		force_load "${module}" "${args}"
	done
}

add_modules_from_proc()
{
    if [ ! -e /proc/modules ];then
        return
    fi

    awk '{print $1}' /proc/modules | while read module;do
	    manual_add_modules "$module"
    done
}

# Is this module available?
have_module()
{
	modprobe --dirname="${ROOT}" --set-version="${version}" --ignore-install \
		--show-depends "${1}" >/dev/null 2>&1
}

# Add dependent modules + eventual firmware
manual_add_modules()
{
	local prefix kmod options firmware

	if [ $# -eq 0 ]; then
		return
	fi

	# modprobe --ignore-install inhibits processing of 'install'
	# configuration lines, so that instead we will see 'insmod
	# module.ko' as we want.  However it also means that 'softdep'
	# configuration lines and embedded softdep information is not
	# processed.  So we run twice, with and without this option.
	{ modprobe --all --dirname="${ROOT}" --set-version="${version}" --ignore-install --quiet --show-depends "$@";
	  modprobe --all --dirname="${ROOT}" --set-version="${version}" --quiet --show-depends "$@"; } |
	while read prefix kmod options ; do
		if [ "${prefix}" != "insmod" ]; then
			continue
		fi

		copy_file module "${kmod}" "${kmod##${ROOT}}" || continue

		# Add required firmware
		for firmware in $(modinfo -k "${version}" -F firmware "${kmod}"); do
			if [ -e "${DESTDIR}/lib/firmware/${firmware}" ] \
			|| [ -e "${DESTDIR}/lib/firmware/${version}/${firmware}" ]; then
				continue
			fi

			# Print warning for missing fw of module
			if [ ! -e "${ROOT}/lib/firmware/${firmware}" ] \
			&& [ ! -e "${ROOT}/lib/firmware/${version}/${firmware}" ] ; then
				Kmod_modname="${kmod##*/}"
				kmod_modname="${kmod_modname%.ko}"
				echo "W: Possible missing firmware /lib/firmware/${firmware} for module $(basename ${kmod} .ko)" >&2
				continue
			fi

			if [ -e "${ROOT}/lib/firmware/${version}/${firmware}" ]; then
				copy_file firmware \
					"${ROOT}/lib/firmware/${version}/${firmware}" "/lib/firmware/${version}/${firmware}"
			else
				copy_file firmware "${ROOT}/lib/firmware/${firmware}" "/lib/firmware/${firmware}"
			fi
		done
	done
}

extract_base() {
	src=${1}
	local ext=${src##*.}
	case "$ext" in
	  "gz") zcat ${src} | cpio -id; return $? ;;
	  "xz") xzcat ${src} | cpio -id; return $? ;;
	  *) return 1;;
        esac
}

# $1 = file type (for logging)
# $2 = file to copy to ramdisk
# $3 (optional) Name for the file on the ramdisk
# Location of the image dir is assumed to be $DESTDIR
# If the target exists, we leave it and return 1.
# On any other error, we return >1.
copy_file() {
	local type src target link_target

	type="${1}"
	src="${2}"
	target="${3}"

	[ -f "${src}" ] || return 2

    # strip the destination with prefix rootfs
	if [ -d "${DESTDIR}/${target}" ]; then
		target="${target}/${src##*/}"
	fi

	# check if already copied
	[ -e "${DESTDIR}/${target}" ] && return 1

	#FIXME: inst_dir
	mkdir -p "${DESTDIR}/${target%/*}"

	if [ -h "${src}" ]; then
		# We don't need to replicate a chain of links completely;
		# just link directly to the ultimate target
		link_target="$(readlink -f "${src}")" || return $(($? + 1))

		if [ "${link_target}" != "${target}" ]; then
			[ "${verbose}" = "y" ] && echo "Adding ${type}-link ${src} to ${target}"

			# Create a relative link so it always points
			# to the right place
			ln -rs "${DESTDIR}/${link_target}" "${DESTDIR}/${target}"
		fi

		# Copy the link target if it doesn't already exist
		src="${link_target}"
		target="${link_target##${ROOT}}"
		[ -e "${DESTDIR}/${target}" ] && return 0
		mkdir -p "${DESTDIR}/${target%/*}"
	fi

	[ "${verbose}" = "y" ] && echo "Adding ${type} ${src} to ${target}"

	cp -pP "${src}" "${DESTDIR}/${target}" || return $(($? + 1))
}

# Copy entire subtrees to the initramfs
copy_modules_dir()
{
	local kmod exclude
	local modules=
	local dir="$1"
	shift

	if ! [ -d "${MODULESDIR}/${dir}" ]; then
		return;
	fi
	if [ "${verbose}" = "y" ]; then
		echo "Copying module directory ${dir}"
		if [ $# -ge 1 ]; then
			echo "(excluding $*)"
		fi
	fi
	while [ $# -ge 1 ]; do
		exclude="${exclude:-} -name $1 -prune -o "
		shift
	done
	for kmod in $(find "${MODULESDIR}/${dir}" ${exclude:-} -name '*.ko' -printf '%f\n'); do
		modules="$modules ${kmod%.ko}"
	done
	manual_add_modules $modules
}

auto_add_modules()
{
	local arg
	local modules=

	for arg in "$@" ; do
		case "$arg" in
		base)
			modules="$modules btrfs ext2 ext3 ext4 ext4dev "
			modules="$modules isofs jfs reiserfs squashfs udf xfs"
			modules="$modules af_packet atkbd i8042 psmouse"
			# nls not automatically pulled in as ubuntu has built-in vfat
			modules="$modules vfat nls_cp437 nls_iso8859-1"

			# Include most USB host and dual-role drivers
			copy_modules_dir kernel/drivers/usb/host \
				hwa-hc.ko sl811_cs.ko sl811-hcd.ko \
				u132-hcd.ko whci-hcd.ko
			copy_modules_dir kernel/drivers/usb/c67x00
			copy_modules_dir kernel/drivers/usb/chipidea
			copy_modules_dir kernel/drivers/usb/dwc2
			copy_modules_dir kernel/drivers/usb/dwc3
			copy_modules_dir kernel/drivers/usb/isp1760
			copy_modules_dir kernel/drivers/usb/musb
			copy_modules_dir kernel/drivers/usb/renesas_usbhs
			# and any extcon drivers for USB
			modules="$modules extcon-usb-gpio"

			# Include all HID drivers unless we're sure they
			# don't support keyboards.  hid-*ff covers various
			# game controllers with force feedback.
			copy_modules_dir kernel/drivers/hid \
				'hid-*ff.ko' hid-a4tech.ko hid-cypress.ko \
				hid-dr.ko hid-elecom.ko hid-gyration.ko \
				hid-icade.ko hid-kensington.ko hid-kye.ko \
				hid-lcpower.ko hid-magicmouse.ko \
				hid-multitouch.ko hid-ntrig.ko \
				hid-petalynx.ko hid-picolcd.ko hid-pl.ko \
				hid-ps3remote.ko hid-quanta.ko \
				'hid-roccat-ko*.ko' hid-roccat-pyra.ko \
				hid-saitek.ko hid-sensor-hub.ko hid-sony.ko \
				hid-speedlink.ko hid-tivo.ko hid-twinhan.ko \
				hid-uclogic.ko hid-wacom.ko hid-waltop.ko \
				hid-wiimote.ko hid-zydacron.ko

			# Any of these might be needed by other drivers
			copy_modules_dir kernel/drivers/bus
			copy_modules_dir kernel/drivers/clk
			copy_modules_dir kernel/drivers/gpio
			copy_modules_dir kernel/drivers/i2c/busses
			copy_modules_dir kernel/drivers/i2c/muxes
			copy_modules_dir kernel/drivers/phy
			copy_modules_dir kernel/drivers/pinctrl
			copy_modules_dir kernel/drivers/regulator
			copy_modules_dir kernel/drivers/usb/phy

			# Needed for periodic fsck
			copy_modules_dir kernel/drivers/rtc
		;;
		net)
			copy_modules_dir kernel/drivers/net \
				appletalk arcnet bonding can dummy.ko \
				hamradio hippi ifb.ko irda macvlan.ko \
				macvtap.ko pcmcia sb1000.ko team tokenring \
				tun.ko usb veth.ko wan wimax wireless \
				xen-netback.ko
			# Include modules that can be request_module'd from
			# their network drivers (e.g. from mlx4_core)
			copy_modules_dir kernel/drivers/infiniband/hw/mlx4
			copy_modules_dir kernel/drivers/infiniband/hw/mlx5
			# Ubuntu backported bnxt driver
			copy_modules_dir kernel/ubuntu/bnxt
			modules="$modules nfs nfsv2 nfsv3 nfsv4"
		;;
		ide)
			copy_modules_dir kernel/drivers/ide
		;;
		mmc)
			copy_modules_dir kernel/drivers/mmc
		;;
		scsi)
			copy_modules_dir kernel/drivers/scsi
			modules="$modules mptfc mptsas mptscsih mptspi zfcp"
		;;
		ata)
			copy_modules_dir kernel/drivers/ata
		;;
		block)
			copy_modules_dir kernel/drivers/block
			copy_modules_dir kernel/drivers/nvme
			modules="$modules vmd"
		;;
		ubi)
			modules="$modules deflate zlib lzo ubi ubifs"
		;;
		ieee1394)
			modules="$modules ohci1394 sbp2"
		;;
		firewire)
			modules="$modules firewire-ohci firewire-sbp2"
		;;
		i2o)
			modules="$modules i2o_block"
		;;
		dasd)
			modules="$modules dasd_diag_mod dasd_eckd_mod dasd_fba_mod"
		;;
		usb_storage)
			copy_modules_dir kernel/drivers/usb/storage
		;;
		virtual)
			# Hyper-V
			modules="$modules hv_vmbus hv_utils hv_netvsc hv_mouse hv_storvsc hyperv-keyboard"
			modules="$modules virtio_pci virtio_mmio"
		;;
		nx)
			# PowerPC NX Crypto Coprocessor
			modules="$modules nx-compress nx-compress-crypto nx-compress-platform"
			modules="$modules nx-compress-pseries nx-compress-powernv 842-decompress"
		;;
		esac
	done

	manual_add_modules $modules
}

# 'depmod' only looks at symbol dependencies; there is no way for
# modules to declare explicit dependencies through module information,
# so dependencies on e.g. crypto providers are hidden.  Until this is
# fixed, we need to handle those hidden dependencies.
hidden_dep_add_modules()
{
	local modules=
	for dep in "lib/libcrc32c crc32c" \
			"fs/ubifs/ubifs deflate zlib lzo" \
			"fs/btrfs/btrfs crc32c"; do
		set -- $dep
		if [ -f "${DESTDIR}/lib/modules/${version}/kernel/$1.ko" ]; then
			shift
			modules="$modules $@"
		fi
	done
	manual_add_modules $modules
}

# mkinitramfs help message
usage()
{
	cat >&2 << EOF

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

Options:
  -c compress	Override COMPRESS setting in initramfs.conf.
  -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 initramfs.conf.

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

}

set_initlist()
{
	unset initlist
	for si_x in ${initdir}/*; do
		# skip empty dirs without warning
		[ "${si_x}" = "${initdir}/*" ] && return

		# skip directories
		if [ -d ${si_x} ]; then
			[ "${verbose}" = "y" ] \
			&& echo "$si_x ignored: a directory" >&2
			continue
		fi
		si_x="$(get_source "${si_x#${initdir}/}")"
		initlist="${initlist:-} ${si_x##*/}"
	done
}

get_source()
{
	if [ -z "$scriptdir" ]; then
		echo "${initdir}/$1"
	elif [ -f "${CONFDIR}${scriptdir}/$1" ]; then
		echo "${CONFDIR}${scriptdir}/$1"
	else
		echo "/usr/share/initramfs-tools${scriptdir}/$1"
	fi
}

call_scripts()
{
    set_initlist
	set -e
	for cs_x in ${initlist:-}; do
		[ -f ${initdir}/${cs_x} ] || continue
		# mkinitramfs verbose output
		echo "Calling hook ${cs_x}"
		. ${initdir}/${cs_x} && ec=$? || ec=$?
		# allow hooks to abort build:
		if [ "$ec" -ne 0 ]; then
			echo "E: ${initdir}/${cs_x} failed with return $ec."
			# only errexit on mkinitramfs
			[ -n "${version}" ] && exit $ec
		fi
	done
	set +e
}

run_scripts_optional()
{
	scriptdir=${2:-}
	initdir=${1}

	[ ! -d ${initdir} ] && return
	call_scripts $scriptdir
}
