#!/usr/bin/env bash
# This file based in part on the mkinitrafms script for the LFS LiveCD
# written by Alexander E. Patrakov and Jeremy Huntwork.
# adapted by Thierry Nuttens for NuTyX

copyModules() {

  local MODS TEXT
  unset modfile
  ARG="$1"
  TEXT="$2"

  mkdir -p $WDIR/usr/lib/modules/$KERNEL_VERSION

  case $ARG in
    auto)
      MODS+="$(for mod in $(cut -d " " -f1 /proc/modules); do modinfo -k $KERNEL_VERSION $mod --filename; done) "
      cut -d " " -f1 /proc/modules > $WDIR/$INITRD_MOD_FILE
    ;;
    list)
      MODS+="$(for mod in $(< /etc/mkinitramfs/modules.conf); do modinfo -k $KERNEL_VERSION $mod --filename; done) "
      cp $MKINIT_MOD_FILE $WDIR/$INITRD_MOD_FILE
    ;;
    standard)
      MODS="/usr/lib/modules/$KERNEL_VERSION/kernel/crypto \
          /usr/lib/modules/$KERNEL_VERSION/kernel/fs \
          /usr/lib/modules/$KERNEL_VERSION/kernel/lib \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/hv \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/scsi \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/message \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/net/ethernet \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/virtio \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/md \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/ata \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/block \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/input/keyboard \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/mmc \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/cdrom  \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/usb/host \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/usb/storage \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/usb/core \
          /usr/lib/modules/$KERNEL_VERSION/kernel/drivers/usb/common"
    ;;
  esac

  printf "$TEXT \n"
  printf "Copying $(find $MODS -type f 2> /dev/null | wc -l) modules...\n"
  find $MODS -type f 2>/dev/null | cpio --make-directories -p 2> /dev/null $WDIR

  for i in builtin order builtin.modinfo
  do
    cp /usr/lib/modules/$KERNEL_VERSION/modules.$i \
    $WDIR/usr/lib/modules/$KERNEL_VERSION
  done
  depmod -a -b $WDIR/usr $KERNEL_VERSION
}

copyFirmwares() {
  cp -a /usr/lib/firmware/amd-ucode \
  $WDIR/usr/lib/firmware > /dev/null 2>&1

  cp -a /usr/lib/firmware/rtlwifi \
  $WDIR/usr/lib/firmware > /dev/null 2>&1
  cp -a /usr/lib/firmware/ath6k \
  $WDIR/usr/lib/firmware > /dev/null 2>&1
  cp -a /usr/lib/firmware/RTL8192E \
  $WDIR/usr/lib/firmware > /dev/null 2>&1
  cp -a /usr/lib/firmware/bnx2x-* \
  $WDIR/usr/lib/firmware > /dev/null 2>&1
  cp -a /usr/lib/firmware/brcm \
  $WDIR/usr/lib/firmware > /dev/null 2>&1
  cp -a /usr/lib/firmware/iwlwifi-* \
  $WDIR/usr/lib/firmware > /dev/null 2>&1
  cp -a /usr/lib/firmware/radeon \
  $WDIR/usr/lib/firmware > /dev/null 2>&1
  cp -a /usr/lib/firmware/ar3k \
  $WDIR/usr/lib/firmware > /dev/null 2>&1
}

copy() {
  local file

  if [ "$2" == "usr/lib" ]; then
    file=$(PATH=/usr/lib:/usr/lib/systemd type -p $1)
  else
    file=$(PATH=$PATH:/usr/lib/systemd type -p $1)
  fi

  if [ -n $file ] ; then
    case "$file" in
      /usr/lib/systemd/libsystemd*.so)
        cp $file $WDIR/$2/systemd
        ;;
      *)
        cp $file $WDIR/$2
        ;;
    esac
  else
    echo "Missing required file: $1 for directory $2"
    rm -rf $WDIR
    exit 1
  fi
}


printf "Creating $INITRAMFS_FILE... \n"
BUSYBOX=$(which busybox)
DATADIR=/usr/lib/services
INITIN=init.in
INITRD_MOD_FILE=etc/modules
MKINIT_MOD_FILE=/etc/mkinitramfs/modules.conf

# Create temporaries working directories
WDIR=$(mktemp -d /tmp/initrd-work.XXXXXXXXXX)
unsorted=$(mktemp /tmp/unsorted.XXXXXXXXXX)

while [[ $# -gt 0 ]]
do
  case "$1" in
    -a|--auto)
      MODULES=auto
      TEXT='Autodiscover modules list in initrd'
      shift 1
      ;;
    -l|--list)
      MODULES=list
      TEXT='Manual list of modules included in intrd'
      if ! [ -e $MKINIT_MOD_FILE ]; then echo -e "Please create the list at $MKINIT_MOD_FILE, one module per line"; exit 1; fi
      shift 1
      ;;
    -k|--kver)
      KERNEL_VERSION=$(basename /usr/lib/modules/$2)
      shift 2
      ;;
    -nf|--no-firmwares)
      FIRMWARE="$1"
      shift 1
    ;;
  esac
done

if [[ -z $MODULES ]]; then MODULES=standard TEXT='Historic standard list of modules added in initrd'; fi

if [[ -z $KERNEL_VERSION ]]; then
  echo -e "Please pass -k or --kver for the kernel version"
  exit 5
else
  INITRAMFS_FILE=initrd-${KERNEL_VERSION}
fi

# Create base directory structure
# mkdir -p $WDIR/{bin,dev,lib/firmware,run,sbin,sys,proc}
mkdir -p $WDIR/{dev,etc/{sysconfig,modprobe.d,udev/{rules.d,hwdb.d}},proc,run,sys,tmp,usr/{bin,lib/{lsb,firmware,udev,systemd},sbin,share}}

# Create symbolic links
ln -s usr/bin $WDIR/bin
ln -s usr/lib $WDIR/lib
ln -s usr/lib64 $WDIR/lib64
ln -s usr/sbin $WDIR/sbin

touch $WDIR/etc/modprobe.d/modprobe.conf

# cp /etc/modules $WDIR/etc/modules

[ -d /usr/lib64 ] && cp -a /usr/lib64 $WDIR/usr/lib64

if [ ! -d "/usr/lib/modules/${KERNEL_VERSION}" ] ; then
  echo "No modules directory named ${KERNEL_VERSION}"
  exit 1
fi

libsystemdexecfiles='systemd systemd-sulogin-shell systemd-shutdown systemd-udevd'
usrbinfiles="cut groups basename hostname udevadm sh cat clear date grep cp dd killall ls mkdir mknod more mount mountpoint chmod umount stty sed unsquashfs sleep ln rm uname systemd-tmpfiles"
sbinfiles="ip blkid losetup switch_root sulogin"

##  Optional files and locations
#
# Add mdadm if present
if [ -x /usr/sbin/mdadm ]; then
  if [ -z $BUSYBOX ]; then
    sbinfiles="$sbinfiles mdadm"
  else
    cp /usr/sbin/mdadm $WDIR/usr/sbin/mdadm
    ldd /usr/sbin/mdadm | sed "s/\t//" | cut -d " " -f1 >> $unsorted
  fi
fi
#
# Copy the RAID configuration file if present
[ -f /etc/mdadm.conf ] && cp /etc/mdadm.conf $WDIR/etc

# Copy rootfs
cp -r /root $WDIR/

#
# Add lvm if present
if [ -x /usr/sbin/lvm ]; then
  if [ -z $BUSYBOX ]; then
    sbinfiles="$sbinfiles lvm dmsetup"
  else
    for I in lvm dmsetup
    do
      cp /usr/sbin/$I $WDIR/usr/sbin/$I
      ldd /usr/sbin/$I | sed "s/\t//" | cut -d " " -f1 >> $unsorted
    done
  fi
  ln -s lvm $WDIR/usr/sbin/lvchange
  ln -s lvm $WDIR/usr/sbin/lvrename
  ln -s lvm $WDIR/usr/sbin/lvextend
  ln -s lvm $WDIR/usr/sbin/lvcreate
  ln -s lvm $WDIR/usr/sbin/lvdisplay
  ln -s lvm $WDIR/usr/sbin/lvscan

  ln -s lvm $WDIR/usr/sbin/pvchange
  ln -s lvm $WDIR/usr/sbin/pvck
  ln -s lvm $WDIR/usr/sbin/pvcreate
  ln -s lvm $WDIR/usr/sbin/pvdisplay
  ln -s lvm $WDIR/usr/sbin/pvscan

  ln -s lvm $WDIR/usr/sbin/vgchange
  ln -s lvm $WDIR/usr/sbin/vgcreate
  ln -s lvm $WDIR/usr/sbin/vgscan
  ln -s lvm $WDIR/usr/sbin/vgrename
  ln -s lvm $WDIR/usr/sbin/vgck

  # Conf file(s)
  cp -a /etc/lvm $WDIR/etc
fi
#
# Add cryptsetup if present
if [ -x /usr/sbin/cryptsetup ]; then
  [[ -f /etc/crypttab ]] && cp /etc/crypttab $WDIR/etc/crypttab
  if [ -z $BUSYBOX ]; then
    sbinfiles="$sbinfiles cryptsetup"
  else
    cp /usr/sbin/cryptsetup $WDIR/usr/sbin/cryptsetup
    ldd /usr/sbin/cryptsetup | sed "s/\t//" | cut -d " " -f1 >> $unsorted
  fi
  if [ -f /root/.crypto/keyfile.bin ]; then
    mkdir -p $WDIR/root/.crypto
    cp /root/.crypto/keyfile.bin $WDIR/root/.crypto
  fi
fi


# Create necessary device nodes
mknod -m 640 $WDIR/dev/console c 5 1
mknod -m 664 $WDIR/dev/null    c 1 3

# Install the init-functions
[ -f /usr/lib/lsb/init-functions ] && cp /usr/lib/lsb/init-functions $WDIR/usr/lib/lsb/init-functions
cp /etc/group $WDIR/etc

cat > $WDIR/etc/fstab << "EOF"
# Begin /etc/fstab

proc       /proc    proc     nosuid,noexec,nodev 0     0
sysfs      /sys     sysfs    nosuid,noexec,nodev 0     0
devpts     /dev/pts devpts   gid=5,mode=620      0     0
tmpfs      /run     tmpfs    defaults            0     0
devtmpfs   /dev     devtmpfs mode=0755,nosuid    0     0
EOF

# echo and false are a bit wird
if [ -z $BUSYBOX ]; then
  cp /usr/bin/echo $WDIR/usr/bin/echo
  cp /usr/bin/false $WDIR/usr/bin/false
else
  cp $BUSYBOX $WDIR/$BUSYBOX
  ln -s $BUSYBOX $WDIR/usr/bin/echo
  ln -s $BUSYBOX $WDIR/usr/bin/false
fi

# terminal stuff
mkdir -p $WDIR/usr/share/terminfo/l
cp /usr/share/terminfo/l/linux \
 $WDIR/usr/share/terminfo/l/

# Install the udev configuration files if needed
[ -f /etc/udev/udev.conf ] && cp /etc/udev/udev.conf $WDIR/etc/udev/udev.conf
for file in $(find /etc/udev/rules.d/ -type f) ; do
  cp $file $WDIR/etc/udev/rules.d
done
for file in $(find /etc/udev/hwdb.d/ -type f) ; do
  cp $file $WDIR/etc/udev/hwdb.d
done
cat > $WDIR/etc/udev/rules.d/55-lfs.rules << "EOF"
# /etc/udev/rules.d/55-lfs.rules: Rule definitions for LFS.

# Core kernel devices

# This causes the system clock to be set as soon as /dev/rtc becomes available.
SUBSYSTEM=="rtc", ACTION=="add", MODE="0644", RUN="do_start_clock"
KERNEL=="rtc", ACTION=="add", MODE="0644", RUN="do_start_clock"

# Comms devices

KERNEL=="ippp[0-9]*",       GROUP="dialout"
KERNEL=="isdn[0-9]*",       GROUP="dialout"
KERNEL=="isdnctrl[0-9]*",   GROUP="dialout"
KERNEL=="dcbri[0-9]*",      GROUP="dialout"
EOF

# Hostname
if [ -f /etc/hostname ] ; then
  cp /etc/hostname $WDIR/etc/hostname
else
  echo "great-os" > $WDIR/etc/hostname
fi
# Clock setting
[ -f /etc/sysconfig/clock ] && cp /etc/sysconfig/clock $WDIR/etc/sysconfig

# Install any firmware present
# cp -a /lib/firmware $WDIR/lib

# Install the init file
install -m0755 $DATADIR/$INITIN $WDIR/init

if [ -x /usr/bin/kmod ] ; then
  usrbinfiles="$usrbinfiles kmod"
else
  usrbinfiles="$usrbinfiles lsmod"
  sbinfiles="$sbinfiles insmod"
fi

# Install basic binaries
for f in $usrbinfiles; do
  if [ -z $BUSYBOX ]; then
    ldd /usr/bin/$f | sed "s/\t//" | cut -d " " -f1 >> $unsorted
    copy $f usr/bin
  else
    ln -s $BUSYBOX $WDIR/usr/bin/$f
  fi
done

for f in $sbinfiles ; do
  if [ -z $BUSYBOX ]; then
    ldd /usr/sbin/$f | sed "s/\t//" | cut -d " " -f1 >> $unsorted
    copy $f usr/sbin
  else
    ln -s $BUSYBOX $WDIR/usr/sbin/$f
  fi
done

for f in $libsystemdexecfiles ; do
  if [ -z $BUSYBOX ]; then
    ldd /usr/lib/systemd/$f | sed "s/\t//" | cut -d " " -f1 >> $unsorted
    copy $f usr/lib/systemd
  else
    ln -s $BUSYBOX $WDIR/usr/lib/systemd/$f
  fi
done


if [ -z $BUSYBOX ]; then
  if [ -x /usr/bin/kmod ] ; then
    ln -s kmod $WDIR/usr/bin/lsmod
    ln -s ../bin/kmod $WDIR/usr/sbin/insmod
    ln -s ../bin/kmod $WDIR/usr/sbin/modprobe
    ln -s ../bin/kmod $WDIR/usr/sbin/depmod
  fi
else
  ln -s $BUSYBOX $WDIR/usr/sbin/insmod
  ln -s $BUSYBOX $WDIR/usr/sbin/modprobe
  ln -s $BUSYBOX $WDIR/usr/sbin/depmod
  ln -s $BUSYBOX $WDIR/usr/bin/lsmod
fi


# Install libraries
sort $unsorted | uniq | while read library ; do
  if [ "$library" == "linux-vdso.so.1" ] ||
     [ "$library" == "linux-gate.so.1" ]; then
    continue
  fi

  copy $library usr/lib
done

cp -a /usr/lib/udev $WDIR/usr/lib

# The Hardware clock setting maybe could be faster done
cat > $WDIR/usr/lib/udev/do_start_clock << "EOF"
#!/usr/bin/sh -e
. /usr/lib/lsb/init-functions
do_start_clock
EOF
chmod 755 $WDIR/usr/lib/udev/do_start_clock

# Install requested  kernel modules
 
copyModules "$MODULES" "$TEXT"


if [ "$FIRMWARE" == "-nf" ] ||  [ "$FIRMWARE" == "--no-firmwares" ];then
  printf "No firmware copied, it's for an ISO.\n"
else
  copyFirmwares
fi

rm -f /boot/$INITRAMFS_FILE

( cd $WDIR ; find . | cpio -o -H newc --quiet | gzip -9 ) > $INITRAMFS_FILE

mv $INITRAMFS_FILE /boot/$INITRAMFS_FILE

if [ -d /ISO ];then
	mkdir -p /ISO/boot
	cp -v /boot/kernel-$KERNEL_VERSION /ISO/boot/
	cp -v /boot/initrd-$KERNEL_VERSION /ISO/boot/
fi

# Remove the temporary direc
rm -rf $WDIR $unsorted
printf "done.\n"