#!/bin/sh
#
# Copyright © 2006 Martin F. Krafft <madduck@debian.org>
# based on the scripts in the initramfs-tools package.
# released under the terms of the Artistic Licence.
#
# $Id: hook 290 2006-12-19 08:18:50Z madduck $
#

set -eu

PREREQ=""

prereqs()
{
	echo "$PREREQ"
}

case ${1:-} in
  prereqs)
    prereqs
    exit 0
    ;;
esac

is_true()
{
  case "${1:-}" in
    [Yy]es|[Yy]|1|[Tt]rue|[Tt]) return 0;;
    *) return 1;
  esac
}

write()
{
  local PREFIX; PREFIX=$1; shift
  echo "${PREFIX}: mdadm: $@" >&2
}

info()
{
  is_true ${VERBOSE:-false} && write I "$@" || :
}

warn()
{
  write W "$@"
}

err()
{
  write E "$@"
}

if [ -e /usr/share/initramfs-tools/hooks/md ]; then
  warn "initramfs md hook still present, stepping out of its way." >&2
  warn "Please see /usr/share/doc/mdadm/README.initramfs-transition ." >&2
  exit 0
fi

if [ -e /etc/initramfs-tools/hooks/md ]; then
  warn "I found /etc/initramfs-tools/hooks/md, which may conflict" >&2
  warn "with this version of mdadm. Please see /usr/share/doc/mdadm/README.mdrun" >&2
  warn "and /usr/share/initramfs-tools/hooks/mdadm for reference and update" >&2
  warn "(or remove) the file." >&2
  exit 0
fi

MDADM=/sbin/mdadm
[ -x "$MDADM" ] || exit 0

[ -r /usr/share/initramfs-tools/hook-functions ] || exit 0
. /usr/share/initramfs-tools/hook-functions

# copy the binary as early as possible
copy_exec $MDADM /sbin

# copy all modules into the initramfs, just for safety.
# we copy raid456 / raid5+raid6 because the hook script just won't do
# anything when the module cannot be found.
modules="linear multipath raid0 raid1 raid456 raid5 raid6 raid10"
for mod in $modules; do manual_add_modules $mod; done

# read in the configuration
CONFIG=/etc/mdadm/mdadm.conf
ALTCONFIG=/etc/mdadm.conf
[ ! -f $CONFIG ] && [ -f $ALTCONFIG ] && CONFIG=$ALTCONFIG || :

DEBIANCONFIG=/etc/default/mdadm
INITRDSTART=all
[ -s $DEBIANCONFIG ] && . $DEBIANCONFIG
[ -z "$INITRDSTART" ] && INITRDSTART=none

DESTMDADMCONF=$DESTDIR/etc/mdadm/mdadm.conf
DESTCONFIG=$DESTDIR/conf/md.conf

# save the homehost for now, even if we don't use it yet
if [ -f $CONFIG ]; then
  homehost="$(sed -ne 's,^[[:space:]]*HOMEHOST[[:space:]]*,,p' $CONFIG)"
fi
if [ -z "${homehost:-}" ] || [ "${homehost:-}" = '<system>' ]; then
  homehost="$(hostname)"
fi
echo "MD_HOMEHOST='$homehost'" > $DESTCONFIG

install_config()
{
  # install the configuration file
  mkdir -p ${2%/*}
  # only copy ARRAY and DEVICE lines, and merge continuation lines into one
  sed -e :a -re '$!N;s/\n[[:space:]]+/ /;ta' -ne '/^(ARRAY|DEVICE)/P;D' $1 > $2
}

if [ ! -f $CONFIG ]; then
  # there is no configuration file, so let's create one
  
  if /usr/share/mdadm/mkconf generate $CONFIG; then
    # all is well
    install_config $CONFIG $DESTMDADMCONF
    info "auto-generated the mdadm.conf configuration file." >&2
  else
    # we failed to auto-generate, so let the emergency procedure take over
    warn "failed to auto-generate the mdadm.conf file." >&2
    warn "please read /usr/share/doc/mdadm/README.upgrading-2.5.3.gz ." >&2
  fi
  
else 
  
  use_temp=0
  if [ -e /var/lib/mdadm/CONF-UNCHECKED ]; then
    # the file comes from an old installation and hence is not guaranteed to
    # work. We thus better create one and start all arrays to be sure.

    warn "unchecked configuration file: $CONFIG" >&2
    warn "please read /usr/share/doc/mdadm/README.upgrading-2.5.3.gz ." >&2

    use_temp=1
  elif ! grep -q '^ARRAY' $CONFIG; then
    # the file defines no ARRAYs. We better create a temporary file to be
    # sure.

    warn "$CONFIG defines no arrays." >&2
    use_temp=1

  else
    # this is the ideal case
    install_config $CONFIG $DESTMDADMCONF
    info "using configuration file: $CONFIG" >&2
  fi

  if [ $use_temp -eq 1 ]; then
    mkdir --parents ${DESTMDADMCONF%/*}
    tmpfile="${DESTMDADMCONF}.tmp"
    if /usr/share/mdadm/mkconf > $tmpfile; then
      # all is well, we now have a temporary configuration file
      info "auto-generated temporary mdadm.conf configuration file." >&2
      install_config $tmpfile $DESTMDADMCONF
    else
      # stuff's really broke, as we failed to generate a temporary file.
      # let's hope the unchecked file works, provided it contains at least one
      # ARRAY statement...
      warn "failed to auto-generate temporary mdadm.conf file." >&2
      if grep -q '^ARRAY' $CONFIG; then
        warn "using the unchecked file and hoping for the best..." >&2
        install_config $CONFIG $DESTMDADMCONF
      fi
    fi
    rm -f $tmpfile
  fi

fi

# if at this point, $DESTMDADMCONF does not exist or it does not contain any
# ARRAY statements, we must let the initramfs handle stuff.
if [ ! -f $DESTMDADMCONF ]; then
  warn "no configuration file available." >&2
  warn "falling back to emergency procedure in initramfs." >&2
  exit 0
elif ! grep -q '^ARRAY' $DESTMDADMCONF; then
  warn "no arrays defined in configuration file." >&2
  warn "falling back to emergency procedure in initramfs." >&2
  exit 0
else
  # obtain dev:level pairs from config file, honouring multiline entries
  devpairs="$(
    while read line; do
      case "$line" in
        (ARRAY*) :;;
        (*) continue;;
      esac
      for atom in $line; do
        case "$atom" in
          (/dev*) dev=$atom;;
          (level=*) level=${atom#level=};;
          (*) :;;
        esac
      done
      [ -n "${dev:-}" ] || continue
      echo -n "${dev}:"
      if [ -n "${level:-}" ]; then
        echo -n "$level"
      else
        echo -n "$($MDADM --detail $dev | sed -rne 's,[[:space:]]+Raid Level : ,,p')"
      fi
      echo -n ' '
    done < $DESTMDADMCONF)"
fi

uniquify()
{
  for i in $@; do echo "$i"; done | sort -u
}

if [ "$INITRDSTART" != none ] && [ -n "$devpairs" ]; then
  echo "MD_DEVPAIRS='${devpairs% }'" >> $DESTCONFIG

  devs=''; levels=''
  for i in $devpairs; do
    dev=${i%:*}
    level=${i##*:}
    case "$INITRDSTART" in
      all|*${dev}*)
        devs="${devs:+$devs }$dev"
        levels="${levels:+$levels }$level"
        ;;
      *) :;;
    esac
  done

  for i in $INITRDSTART; do
    case "$INITRDSTART" in all|none|'') break;; *) :;; esac
    case "$devs" in
      *${i}*) continue;;
      *) :;;
    esac

    warn "I am supposed to start $i from the initial ramdisk," >&2
    warn "yet I cannot find the array in the configuration file." >&2
    warn "I am thus reverting to starting all arrays." >&2
    INITRDSTART=all
    break
  done

  echo "MD_LEVELS='$levels'" >> $DESTCONFIG
  if [ "$INITRDSTART" = all ]; then
    echo "MD_DEVS=all" >> $DESTCONFIG
  else
    echo "MD_DEVS='$devs'" >> $DESTCONFIG
  fi

  # raid4 is handled by raid5
  modules="$(echo $levels | sed -e 's,raid4,raid5,g')"

  # if raid456 module is present, use it for raid[456]
  modprobe --set-version="$version" --show-depends raid456 >/dev/null 2>&1 \
    && modules="$(echo $modules | sed -e 's,raid[456],raid456,g')"

  modules="$(uniquify $modules | tr '\n' ' ')"
  echo "MD_MODULES='${modules% }'" >> $DESTCONFIG

  if [ "$INITRDSTART" = all ]; then
    info "will start all available MD arrays from the initial ramdisk." >&2
  else
    for i in $devs; do
      info "will start MD array $i from the initial ramdisk." >&2
    done
  fi

else
  echo "MD_DEVS=none" >> $DESTCONFIG
  echo "MD_MODULES=''" >> $DESTCONFIG
  info "no MD arrays will be started from the initial ramdisk." >&2
fi

# only output this on Debian systems
[ -s /etc/default/mdadm ] && \
  info 'use `dpkg-reconfigure --priority=low mdadm` to change this.' >&2

exit 0
