#!/bin/sh
# busybox based initramfs script

add_kmod() {
	while [ "$1" ]; do
		kmod=$(echo "$1" | tr - _); shift
		kmodpath=$(modprobe -S "$K" -l "$kmod" 2>/dev/null) || continue
		path=/lib/modules/$K/${kmodpath}
		[ -f "$W/$path" ] && continue
		add_file "$path"
		for firmware in $(modinfo -k "$K" -F firmware "$kmod" 2>/dev/null); do
			# firmware may be a glob pattern (e.g. brcm/brcmbt4377*.bin),
			# so leave it unquoted here and quote each matched file
			for fw in /lib/firmware/$firmware.xz /lib/firmware/$firmware.gz /lib/firmware/$firmware; do
				[ -f "$fw" ] && add_file "$fw"
			done
		done
		for depends in $(modprobe -S "$K" -D "$kmod" 2>/dev/null | awk '{print $2}'); do
			[ "$depends" ] || continue
			depends=${depends##*/}; depends=${depends%.ko*}
			add_kmod "$depends"
		done
	done
}

add_bin() {
	while [ "$1" ]; do
		bin=$(which "$1") || {
			echo "FAILED: $1: missing binary"
			shift
			continue
		}
		shift
		add_file "$bin"
		[ -L "$bin" ] && bin=$(readlink -f "$bin")
		# shared libraries and the dynamic loader, if any
		for file in $(ldd "$bin" 2>/dev/null | grep -o '/[^ ]*'); do
			add_file "$file"
		done
	done
}

add_file() {
	f=$1
	t=$2
	m=$3
	# skip already copied file
	[ -e "$W${t:-$f}" ] && return
	[ -f "$f" ] || {
		echo "FAILED: $f: missing file"
		return
	}
	if [ -L "$f" ]; then
		mkdir -p "${W}${f%/*}"
		ln -sf "$(readlink "$f")" "${W}${f}"
		f=$(readlink -f "$f")
	fi
	install ${m:+-m "$m"} -D "$f" "$W${t:-$f}" 2>/dev/null
	echo "$([ "$?" = 0 ] && echo OK || echo FAILED): $f"
}

K=$(uname -r)
O=initrd
I=/usr/share/mkinitrd/init
HD=/usr/share/mkinitrd/hooks.d

while [ "$1" ]; do
	case $1 in
		-k|-i)
			[ "$2" ] || { echo "error: option '$1' needs an argument"; exit 1; }
			case $1 in -k) K=$2;; -i) I=$2;; esac
			shift;;
		-a) A=1;;
		-h) H=1;;
		 *) O=$1;;
	esac
	shift
done

if [ "$H" ]; then
	cat <<EOF
mkinitrd: busybox based initramfs generator.

Usage: $0 [-ah] [-k kernel] [-i init] [ OUTPUT ]

	-a          Auto detect kernel modules
	-i init     Use custom init file (default: /usr/share/mkinitrd/init)
	-k kernel   kernel version
	-h          Show this help message
EOF
exit 0
fi

if [ "$(id -u)" != 0 ]; then
	echo "error: this program need to run as root"
	exit 1
fi

M=/lib/modules/$K

if [ ! -d "$M" ]; then
	echo "error: kernel modules directory '$M' not found"
	exit 1
fi

if [ ! -f "$M"/modules.dep ]; then
	echo "error: modules.dep file not found, please run 'depmod' first"
	exit 1
fi

if [ ! "$(command -v busybox)" ]; then
	echo "error: busybox not found in PATH, please install busybox"
	exit 1
fi

if [ ! -f "$I" ]; then
	echo "error: init file '$I' not found"
	exit 1
fi

# work dir and temporary bin path for busybox's applet
# we trying to use busybox utils as possible
W=$(mktemp -d) && B=$(mktemp -d) || exit 1
trap 'rm -rf "$W" "$B"' EXIT
trap 'exit 1' INT TERM HUP
chmod 755 "$W"
for a in $(busybox --list); do
	ln -s "$(command -v busybox)" "$B/$a"
done
export PATH="$B:$PATH"

mkdir -p "$W/$M"

for d in bin dev etc lib proc run sys; do
	mkdir -p "$W/$d"
done
ln -s . "$W"/usr
ln -s bin "$W"/sbin
ln -s lib "$W"/lib64

if [ "$A" ]; then
	for i in /sys/module/*; do
		add_kmod "${i##*/}"
		echo "${i##*/}" >> "$W"/modules
	done
else
	for d in \
		crypto fs lib drivers/block drivers/md drivers/ata \
		drivers/firewire drivers/input/keyboard drivers/input/mouse \
		drivers/scsi drivers/message drivers/pcmcia drivers/virtio \
		drivers/usb/host drivers/usb/storage drivers/hid drivers/cdrom;
	do
		for f in $(find "$M"/kernel/"$d" -name '*.ko*'); do
			f=${f##*/}; f=${f%.ko*}
			add_kmod "$f"
		done
	done
fi
add_file "$M"/modules.builtin
add_file "$M"/modules.order
depmod -b "$W" "$K"

add_bin busybox
install -m755 "$I" "$W"/init

echo '$MODALIAS=.* 0:0 660 @modprobe "$MODALIAS"' > "$W"/etc/mdev.conf

for h in "$HD"/*.sh; do
	[ -f "$h" ] && echo "RUNNING HOOK: $h..." && . "$h"
done

( cd "$W" ; find . | cpio -o -H newc --quiet | gzip -9 ) > "$O"
du -sh "$O"

exit 0
