#!/bin/sh
# busybox based initramfs script

add_kmod() {
	while [ "$1" ]; do
		kmod=$1; 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
			[ -f /lib/firmware/$firmware.xz ] && add_file /lib/firmware/$firmware.xz
			[ -f /lib/firmware/$firmware.gz ] && add_file /lib/firmware/$firmware.gz
			[ -f /lib/firmware/$firmware ]    && add_file /lib/firmware/$firmware
		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); shift
		[ "$bin" ] || {
			echo "FAILED: $bin: missing binary"
			continue
		}
		add_file "$bin"
		[ -L "$bin" ] && bin=$(readlink -f $bin)
		case $(file -bi $bin) in
			application/x-pie-executable*)
				for file in $(ldd $bin | awk '{print $3}'); do
					add_file $file
				done;;
		esac
	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" ] && echo "-m $m") -D $f $W${t:-$f} 2>/dev/null
	echo "$([ "$?" = 0 ] && echo OK || echo FAILED): $f"
}

K=$(uname -r)
W=/tmp/mkinitrd-xxx
O=initrd
I=/usr/share/mkinitrd/init

while [ "$1" ]; do
	case $1 in
		-k) K=$2; shift;;
		-i) I=$2; 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

# create temporary bin path for busybox's applet
# we trying to use busybox utils as possible
mkdir -p /tmp/busybox-bin.$$
for a in $(busybox --list); do
	ln -s $(command -v busybox) /tmp/busybox-bin.$$/$a
done
export PATH=/tmp/busybox-bin.$$:$PATH

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

rm -rf $W
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

( cd $W ; find . | cpio -o -H newc --quiet | gzip -9 ) > "$O"
du -sh "$O"
rm -rf $W /tmp/busybox-bin.$$

exit 0
