#!/bin/sh

CFGFILE="/etc/tiny-initramfs/tiny-initramfs.conf"
PKGLIBEXECDIR="/usr/lib/aarch64-linux-gnu/tiny-initramfs"

set -e
umask 0022

. /usr/share/tiny-initramfs/functions

usage() {
  echo "Usage: $0 -o output_filename [options] [version]"
  echo ""
  echo "Creates a tiny-initramfs image"
  echo ""
  echo "Options (override settings in the configuration file):"
  echo "    -m VAL, --auto-modules=VAL Whether to include modules in the initramfs"
  echo "                               image (yes/no)."
  echo "    -M VAL, --microcode=VAL    Whether to include microcode in the initramfs"
  echo "                               image (x86 only, no/yes/generic)."
  echo "    -d VAL, --debug=VAL        Whether to use the debug variant of"
  echo "                               tiny-initramfs (yes/no)."
  echo "            --include-modules=MOD1,MOD2,..."
  echo "                               Include the specified modules in the"
  echo "                               initramfs image (useful for generating images"
  echo "                               on different hardware, when combined with"
  echo "                               -m no). NOTE: this overrides (not adds to)"
  echo "                               the setting in the configuration file."
}

if [ -f "$CFGFILE" ] ; then
  . "$CFGFILE"
fi

if ! OPTIONS=$(getopt -o o:m:M:d:h -l output=,auto-modules:,include-modules:,microcode:,debug:,help -n "$0" -- "$@") ; then
  usage >&2
  exit 1
fi
eval set -- "$OPTIONS"

OUTPUT=""
while true; do
  case "$1" in
    -h|--help)
      usage
      exit 0
      ;;
    -o|--output)
      OUTPUT="$2"
      shift 2
      ;;
    -m|--auto-modules)
      AUTO_MODULES="$2"
      shift 2
      ;;
    --include-modules)
      INCLUDE_MODULES="$2"
      shift 2
      ;;
    -d|--debug)
      DEBUG="$2"
      shift 2
      ;;
    -M|--microcode)
      MICROCODE="$2"
      shift 2
      ;;
    --)
      shift
      break
      ;;
    *)
      echo "$0: internal error" >&2
      exit 1
      ;;
  esac
done

if [ $# -gt 1 ] ; then
  echo "$0: too many options" >&2
  usage >&2
  exit 1
fi

if [ -z "$OUTPUT" ] ; then
  echo "$0: no output filename specified" >&2
  usage >&2
  exit 1
fi

VERSION="$1"
if [ -z "$VERSION" ] ; then
  VERSION="$(uname -r)"
fi

# Default settings
: ${DEBUG="no"} ${MICROCODE="yes"} ${AUTO_MODULES="yes"} ${INCLUDE_MODULES=""}

case "$DEBUG" in
  y|Y|yes|true|on|1)  DEBUG=yes ;;
  n|N|no|false|off|0) DEBUG=no ;;
  *)
    echo "$0: invalid setting DEBUG=$DEBUG specified in $CFGFILE or on the command line" >&2
    exit 1
    ;;
esac

case "$AUTO_MODULES" in
  y|Y|yes|true|on|1)  AUTO_MODULES=yes ;;
  n|N|no|false|off|0) AUTO_MODULES=no ;;
  *)
    echo "$0: invalid setting AUTO_MODULES=$AUTO_MODULES specified in $CFGFILE or on the command line" >&2
    exit 1
    ;;
esac

case "$MICROCODE" in
  no|yes|generic) ;;
  *)
    echo "$0: invalid setting MICROCODE=$MICROCODE specified in $CFGFILE or on the command line" >&2
    exit 1
    ;;
esac

curdir="$PWD"

cleanup() {
  cd /
  rm -f "$image_name"
  [ ! -d "$initramfs_dir" ]    || rm -r "$initramfs_dir"
  [ ! -d "$early_dir" ]        || rm -r "$early_dir"
  [ ! -d "$extra_early_dir" ]  || rm -r "$extra_early_dir"
}

trap cleanup 0

# Start by assuming we don't need early CPIO
image_name="$(mktemp --tmpdir=${TMPDIR:-/var/tmp} -t tiny_initramfs.IMG_XXXXXXXXXXXX)"
if [ -z "$image_name" ] || ! [ -f "$image_name" ] ; then
  echo "$0: couldn't create temporary file" >&2
  exit 2
fi
initramfs_dir="$(mktemp --tmpdir=${TMPDIR:-/var/tmp} -d -t tiny_initramfs.FS_XXXXXXXXXXXX)"
if [ -z "$initramfs_dir" ] || ! [ -d "$initramfs_dir" ] ; then
  echo "$0: couldn't create temporary directory" >&2
  exit 2
fi
early_dir="$(mktemp --tmpdir=${TMPDIR:-/var/tmp} -d -t tiny_initramfs.EARLY_XXXXXXXXXXXX)"
if [ -z "$early_dir" ] || ! [ -d "$early_dir" ] ; then
  echo "$0: couldn't create temporary directory" >&2
  exit 2
fi
extra_early_dir="$(mktemp --tmpdir=${TMPDIR:-/var/tmp} -d -t tiny_initramfs.EXTRA_EARLY_XXXXXXXXXXXX)"
if [ -z "$extra_early_dir" ] || ! [ -d "$extra_early_dir" ] ; then
  echo "$0: couldn't create temporary directory" >&2
  exit 2
fi
modules_list="${initramfs_dir}/modules.tmp"
> "$modules_list"
extra_early_list="${extra_early_dir}/files"
> "$extra_early_list"

# automatic modules
if [ "$AUTO_MODULES" = "yes" ] ; then
  add_modules
fi

# manual modules
OLDIFS="$IFS"
IFS=","
for mod_name in ${INCLUDE_MODULES} ; do
  printf "%s\n" "$mod_name" >> "$modules_list"
done
IFS="$OLDIFS"
unset OLDIFS

# Run start-hooks
run_hooks start

# Copy executable
case "$DEBUG" in
  yes)
    EXECUTABLE="${PKGLIBEXECDIR}/init-debug-static"
    ;;
  *)
    EXECUTABLE="${PKGLIBEXECDIR}/init-static"
    ;;
esac
cp "$EXECUTABLE" "$initramfs_dir/init"
mkdir "$initramfs_dir/dev" "$initramfs_dir/proc" "$initramfs_dir/target"

# Add microcode
if [ "$MICROCODE" != "no" ] ; then
  add_microcode "$MICROCODE"
fi

# Run middle-hooks
run_hooks middle

# Process all modules
process_modules

# Add extra early data, in order
> "$image_name"
for fn in $(cat "$extra_early_list") ; do
	cat "$extra_early_dir/$fn" >> "$image_name"
done

# Add early data, such as microcode updates, if any are present
cd "$early_dir"
if [ -n "$(find . ! -name .)" ] ; then
  find . ! -name . -print0 | sort -z | cpio --null -o --quiet -R 0:0 -H newc >> "$image_name"
fi

# Compress the initramfs and append it to the image
# (it goes _after_ the microcode updates)
cd "$initramfs_dir"
rm -f "$modules_list"
find . ! -name . -print0 | sort -z | cpio --null -o --quiet -R 0:0 -H newc | gzip -9 >> "$image_name"

cd "$curdir"

# Run end-hooks
run_hooks end

# Copy the image to its destination
mv "$image_name" "$OUTPUT"
