#!/bin/sh

# xcat_static_ip6_fields <ip= parameter> -- print "<address> <prefix> <gateway>" for the
# static IPv6 form dracut takes, client:peer:gateway:prefix:hostname:interface:autoconf.
#
# An IPv6 literal carries colons, so the fields cannot be cut on a colon: dracut brackets
# the address and the gateway, and the brackets are what delimits them.
xcat_static_ip6_fields()
{
    local spec addr rest gw prefix

    spec="${1#ip=}"
    case "$spec" in
    \[*) ;;
    *) return 1 ;;
    esac
    addr="${spec#\[}"
    addr="${addr%%\]*}"
    rest="${spec#*\]:}"

    # peer, which xCAT never sets
    rest="${rest#*:}"

    case "$rest" in
    \[*)
        gw="${rest#\[}"
        gw="${gw%%\]*}"
        rest="${rest#*\]:}"
        ;;
    *)
        gw=""
        rest="${rest#*:}"
        ;;
    esac
    prefix="${rest%%:*}"

    [ -n "$addr" ] || return 1
    case "$prefix" in
    ''|*[!0-9]*) return 1 ;;
    esac
    printf '%s %s %s\n' "$addr" "$prefix" "$gw"
}

NEWROOT=$3
RWDIR=.statelite

# The kernel gives init the arguments without a dot as variables. A systemd initial ramdisk
# runs this script from dracut-initqueue, which does not get them, so getarg reads them.
for xcat_arg in XCAT imgurl rootimgchecksum STATEMNT BOOTIF ifname netdev; do
	eval "xcat_value=\${$xcat_arg}"
	[ -z "$xcat_value" ] || continue
	xcat_value=$(. /lib/dracut-lib.sh && getarg "$xcat_arg=") || continue
	eval "$xcat_arg=\$xcat_value"
done
unset xcat_arg xcat_value

# Writes resolv.conf from the systemd-networkd leases when it names no server. dhclient wrote
# it, but systemd-networkd keeps the servers in its lease files, and the initial ramdisk has no
# resolver service to read them.
xcat_resolv_from_leases() {
	local leases="${XCAT_NETWORKD_LEASES:-/run/systemd/netif/leases}"
	local resolv="${XCAT_RESOLV_CONF:-/etc/resolv.conf}"
	local dns="" domain="" lease value server
	grep -qs '^nameserver' "$resolv" && return 0
	for lease in "$leases"/*; do
		[ -r "$lease" ] || continue
		value=$(sed -n 's/^DNS=//p' "$lease")
		[ -n "$value" ] && dns="$dns $value"
		value=$(sed -n 's/^DOMAINNAME=//p' "$lease")
		[ -n "$value" ] && [ -z "$domain" ] && domain=$value
	done
	[ -n "$dns" ] || return 0
	{
		[ -z "$domain" ] || echo "search $domain"
		for server in $dns; do
			echo "nameserver $server"
		done
	} >"$resolv"
}
xcat_resolv_from_leases

XCATMASTER=$XCAT

# Downloads the checksum file published for the image at $1 to $2.
rootimg_fetch_checksum() {
	rm -f "$2"
	wget -q -O "$2" "$1.sha256"
}

# Prints why the file $2 cannot be used, unless it matches the SHA-256 checksum published for the image at $1.
rootimg_matches_checksum() {
	command -v sha256sum >/dev/null 2>&1 || { echo "this initial ramdisk has no sha256sum to check $1"; return 1; }
	rootimg_fetch_checksum "$1" /tmp/rootimg.sha256 || { echo "cannot download $1.sha256"; return 1; }
	rootimg_sum=$(sha256sum <"$2") || { echo "sha256sum cannot read $2"; return 1; }
	# While packimage replaces the archive, the checksum file lists the new and the previous archive.
	# A download that began before the replacement matches the checksum file fetched before it.
	cat /tmp/rootimg.sha256 /tmp/rootimg.sha256.before >/tmp/rootimg.sums 2>/dev/null
	while read -r rootimg_expected rootimg_name; do
		[ "${rootimg_sum%% *}" = "$rootimg_expected" ] && return 0
	done </tmp/rootimg.sums
	echo "$1 does not match $1.sha256"
	return 1
}

# Downloads the image at $1 to the file $2, over UFTP the first time when nodeset asks for it.
rootimg_download() {
	if [ -n "$rootimg_uftp" ]; then
		rootimg_uftp_port=$rootimg_uftp
		rootimg_uftp=
		# nodeset asks for UFTP only with the checksum, which the caller checks on the received file.
		if [ "$rootimgchecksum" = yes ] && [ -r /lib/xcat-rootimg-fetch.sh ] && . /lib/xcat-rootimg-fetch.sh &&
			xcat_uftp_rootimg "$1" "$2" /tmp/rootimg.sha256.before "$rootimg_uftp_port" "$rootimg_uftp_fp"; then
			return 0
		fi
		echo "Cannot receive $1 over UFTP, downloading it over HTTP..."
	fi
	wget "$1"
}

# The kernel does not pass arguments with a dot to init as variables, so getarg reads them.
rootimg_uftp=$(. /lib/dracut-lib.sh && getarg xcat.uftp=)
rootimg_uftp_fp=$(. /lib/dracut-lib.sh && getarg xcat.uftpfp=)

if [ ! -z "$imgurl" ]; then
	if [ xhttp = x${imgurl%%:*} ] || [ xhttps = x${imgurl%%:*} ]; then
		NFS=0
		FILENAME=${imgurl##*/}
		[ "$rootimgchecksum" = yes ] || echo "nodeset did not ask for a check of $FILENAME, so it is not checked."
		# A file that an earlier attempt left is checked like a new download.
		if [ -r "$FILENAME" ] && [ "$rootimgchecksum" = yes ] && ! rootimg_matches_checksum "$imgurl" "$FILENAME" >/dev/null; then
			rm -f $FILENAME
		fi
		while [ ! -r "$FILENAME" ]; do
			echo Getting $imgurl...
			[ "$rootimgchecksum" != yes ] || rootimg_fetch_checksum "$imgurl" /tmp/rootimg.sha256.before || rm -f /tmp/rootimg.sha256.before
			if ! rootimg_download "$imgurl" "$FILENAME"; then
				rm -f $FILENAME
				sleep 27
			elif [ "$rootimgchecksum" = yes ] && ! rootimg_check=$(rootimg_matches_checksum "$imgurl" "$FILENAME"); then
				echo "$rootimg_check, retrying..."
				rm -f $FILENAME
				sleep 27
			fi
		done
	elif [ xnfs = x${imgurl%%:*} ]; then
		NFS=1
		SERVER=${imgurl#nfs:}
		SERVER=${SERVER#/}
		SERVER=${SERVER#/}
		ROOTDIR=$SERVER
		SERVER=${SERVER%%/*}
		SERVER=${SERVER%:}
		ROOTDIR=/${ROOTDIR#*/}
	fi
fi
#echo 0 > /proc/sys/vm/zone_reclaim_mode #Avoid kernel bug

if [ -r /rootimg.sfs ]; then
  echo Setting up squashfs with ram overlay.
  mknod /dev/loop0 b 7 0
  mkdir -p /ro
  mkdir -p /rw
  mount -t squashfs /rootimg.sfs /ro
  mount -t tmpfs rw /rw
  modprobe overlay
  if [ $? -eq 0 ]; then
      echo "Mounting $NEWROOT with type overlay"
      mkdir -p /rw/upper
      mkdir -p /rw/work
      mount -t overlay -o lowerdir=/ro,upperdir=/rw/upper,workdir=/rw/work mergedroot $NEWROOT
  else
      echo "Mounting $NEWROOT with type aufs"
      mount -t aufs -o dirs=/rw:/ro mergedroot $NEWROOT
      mkdir -p $NEWROOT/ro
      mkdir -p $NEWROOT/rw
      mount --move /ro $NEWROOT/ro
      mount --move /rw $NEWROOT/rw
  fi
elif [ -r /rootimg.cpio.gz ] || [ -r /rootimg.cpio.xz ]; then
  echo Setting up RAM-root tmpfs.
  mount -t tmpfs -o mode=755 rootfs $NEWROOT
  cd $NEWROOT
  echo -n "Extracting root filesystem:"
  if [ -r /rootimg.cpio.gz ]; then
    if [ -x /bin/cpio ]; then
      gzip -cd /rootimg.cpio.gz |/bin/cpio -idum
    else
      gzip -cd /rootimg.cpio.gz |cpio -idum
    fi
  elif [ -r /rootimg.cpio.xz ]; then
    if [ -x /bin/cpio ]; then
      xz -cd /rootimg.cpio.xz |/bin/cpio -idum
    else
      xz -cd /rootimg.cpio.xz |cpio -idum
    fi
  fi
  echo Done
elif [ -r /rootimg.tar.gz ] || [ -r /rootimg.tar.xz ]; then
  echo Setting up RAM-root tmpfs.
  mount -t tmpfs -o mode=755 rootfs $NEWROOT
  cd $NEWROOT
  echo -n "Extracting root filesystem:"
  if [ -r /rootimg.tar.gz ]; then
    tar --selinux --xattrs-include='*' -zxf /rootimg.tar.gz
  elif [ -r /rootimg.tar.xz ]; then
    tar --selinux --xattrs-include='*' -Jxf /rootimg.tar.xz
  fi
  echo Done
elif [ -r /rootimg-statelite.gz ]; then
  echo Setting up RAM-root tmpfs for statelite mode.
  mount -t tmpfs -o mode=755 rootfs $NEWROOT
  cd $NEWROOT
  echo -n "Extracting root filesystem:"
  if [ -x /bin/cpio ]; then
    gzip -cd /rootimg-statelite.gz |/bin/cpio -idum
  else
    gzip -cd /rootimg-statelite.gz |cpio -idum
  fi
  echo Done
  # then, the statelite staffs will be processed
  echo Setting up Statelite
  modprobe nfs
  MAXTRIES=7
  ITER=0
  if [ ! -e "$NEWROOT/$RWDIR" ]; then
    echo ""
    echo "The /$RWDIR directory doesn't exist in the rootimg... "
    echo ""
    /bin/sh
  fi

  if [ ! -e "$NEWROOT/etc/init.d/statelite" ]; then
    echo ""
    echo "$NEWROOT/etc/init.d/statelite doesn't exist... "
    echo ""
    /bin/sh
  fi

  mount -t tmpfs rw $NEWROOT/$RWDIR
  mkdir -p $NEWROOT/$RWDIR/tmpfs
  ME=`hostname`

  # mount the SNAPSHOT directory here for persistent use.
  if [ ! -z $STATEMNT ]; then
    SNAPSHOTSERVER=${STATEMNT%:*}
    SNAPSHOTROOT=${STATEMNT#*/}
    if [ -z $SNAPSHOTROOT ]; then
        SNAPSHOTROOT=$SNAPSHOTSERVER
        SNAPSHOTSERVER=
    fi
  fi

  if [ ! -z $SNAPSHOTSERVER ]; then
    mkdir -p $NEWROOT/$RWDIR/persistent
    MAXTRIES=5
    ITER=0
	if [ -z $MNTOPTS ]; then
		MNT_OPTIONS="nolock,rsize=32768,tcp,timeo=14"
	else
		MNT_OPTIONS=$MNTOPTS
	fi
    while ! mount $SNAPSHOTSERVER:/$SNAPSHOTROOT $NEWROOT/$RWDIR/persistent -o $MNT_OPTIONS; do
        ITER=$(( ITER + 1 ))
        if [ "$ITER" == "$MAXTRIES" ]; then
            echo "You are dead, rpower $ME boot to play again."
            echo "Possible problems:
1.  $SNAPSHOTSERVER is not exporting $SNAPSHOTROOT ?
2.  Is DNS set up? Maybe that's why I can't mount $SNAPSHOTSERVER."
            /bin/sh
            exit
        fi
        RS=$(( $RANDOM % 20 ))
        echo "Trying again in $RS seconds ..."
        sleep $RS
    done
  fi

  $NEWROOT/etc/init.d/statelite
  fastboot=yes
  export fastboot
  keep_old_ip=yes
  export keep_old_ip

  mount -n --bind /dev $NEWROOT/dev
  mount -n --bind /proc $NEWROOT/proc
  mount -n --bind /sys $NEWROOT/sys

else
  echo -n Failed to download image, panicing in 5...
  for i in 4 3 2 1 0; do
    /bin/sleep 1
    echo -n $i...
  done
  echo
  echo "You're dead.  rpower nodename reset to play again.

* Did you packimage with -m cpio, -m squashfs, or -m nfs?
* If using -m squashfs did you include aufs.ko with geninitrd?
  e.g.:  -n tg3,squashfs,aufs,loop
* If using -m nfs did you export NFS and sync rootimg?  And
  did you include the aufs and nfs modules in the proper order:
  e.g.:  -n tg3,aufs,loop,sunrpc,lockd,nfs_acl,nfs

"
  /bin/dash
  exit
fi
cd /

if [ -z $STATEMNT ]; then
  for lf in /tmp/dhclient.*.lease; do
    [ -e "$lf" ] || continue
    netif=${lf#*.}
    netif=${netif%.*}
    if [ -d "$NEWROOT/var/lib/dhclient" ]; then
      cp $lf "$NEWROOT/var/lib/dhclient/dhclient-$netif.leases"
    elif [ -d "$NEWROOT/var/lib/dhcp" ]; then
      cp $lf "$NEWROOT/var/lib/dhcp/dhclient.$netif.leases"
    fi
  done

  if [ ! -z "$ifname" ]; then
    MACX=${ifname#*:}
    ETHX=${ifname%:$MACX*}
  elif [ ! -z "$netdev" ]; then
    ETHX=$netdev
    if [ -e "/sys/class/net/$netdev/address" ]; then
      MACX=$(cat /sys/class/net/$netdev/address)
    else
      MACX=$(ip link show $netdev 2>/dev/null | grep ether | awk '{print $2}')
    fi
  elif [ ! -z "$BOOTIF" ]; then
    MACX=$(echo $BOOTIF | sed 's/^01-//;s/-/:/g' | tr 'A-Z' 'a-z')
    for devpath in /sys/class/net/*; do
      [ -e "$devpath/address" ] || continue
      if [ "$(cat $devpath/address)" = "$MACX" ]; then
        ETHX=${devpath##*/}
        break
      fi
    done
    [ -z "$ETHX" ] && ETHX=$(ip -oneline link show | grep -i "$MACX" | awk -F ':' '{print $2}' | grep -o "[^ ]\+\( \+[^ ]\+\)*")
  fi

  if [ ! -z "$MACX" ] && [ ! -z "$ETHX" ]; then
    # The booted system takes the address the node was given on the kernel command line
    # when that address is IPv6. dhcp4 leaves an IPv6-only node with no address at all, and
    # systemd-networkd-wait-online then fails.
    XCAT_IP6_FIELDS=""
    for xcat_cmdline_word in $(cat /proc/cmdline); do
      case "$xcat_cmdline_word" in
      ip=\[*)
        XCAT_IP6_FIELDS=$(xcat_static_ip6_fields "$xcat_cmdline_word") || XCAT_IP6_FIELDS=""
        break
        ;;
      esac
    done

    if [ -x $NEWROOT/usr/sbin/netplan ]; then
      mkdir -p $NEWROOT/etc/netplan
      if [ -n "$XCAT_IP6_FIELDS" ]; then
        set -- $XCAT_IP6_FIELDS
        XCAT_IP6_ADDR=$1
        XCAT_IP6_PREFIX=$2
        XCAT_IP6_GW=$3
        cat > $NEWROOT/etc/netplan/90-xcat.yaml <<XCATNETPLAN6
network:
  version: 2
  ethernets:
    $ETHX:
      dhcp4: false
      addresses: [$XCAT_IP6_ADDR/$XCAT_IP6_PREFIX]
      match:
        macaddress: "$MACX"
XCATNETPLAN6
        if [ -n "$XCAT_IP6_GW" ]; then
          cat >> $NEWROOT/etc/netplan/90-xcat.yaml <<XCATNETPLAN6GW
      routes:
        - to: default
          via: $XCAT_IP6_GW
XCATNETPLAN6GW
        fi
        chmod 600 $NEWROOT/etc/netplan/90-xcat.yaml
      else
      cat > $NEWROOT/etc/netplan/90-xcat.yaml <<XCATNETPLAN
network:
  version: 2
  ethernets:
    $ETHX:
      dhcp4: true
      match:
        macaddress: "$MACX"
XCATNETPLAN
      fi
    elif [ -d $NEWROOT/etc/sysconfig/network-scripts ]; then
      echo "DEVICE=$ETHX" > $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
      echo "BOOTPROTO=dhcp" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
      echo "HWADDR=$MACX" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
      echo "ONBOOT=yes" >> $NEWROOT/etc/sysconfig/network-scripts/ifcfg-$ETHX
    elif [ -d $NEWROOT/etc/network ]; then
      echo "auto $ETHX" >> $NEWROOT/etc/network/interfaces
      echo "iface $ETHX inet dhcp" >> $NEWROOT/etc/network/interfaces
    fi
  fi
fi

# The image links resolv.conf to the systemd-resolved stub, and that stub does not send a short
# name such as the name of the server to the DNS server. Replace the link, not the file it names.
xcat_resolv="${XCAT_RESOLV_CONF:-/etc/resolv.conf}"
if grep -qs '^nameserver' "$xcat_resolv" && [ -L "$NEWROOT/etc/resolv.conf" ]; then
  rm -f "$NEWROOT/etc/resolv.conf"
fi
cp "$xcat_resolv" "$NEWROOT/etc/resolv.conf"

# genimage leaves the hostname file empty, and systemd then names the node localhost. The
# postscripts identify the node by its hostname.
if [ ! -s "$NEWROOT/etc/hostname" ]; then
  for xcat_lease in "${XCAT_NETWORKD_LEASES:-/run/systemd/netif/leases}"/*; do
    xcat_name=$(sed -n 's/^HOSTNAME=//p' "$xcat_lease" 2>/dev/null)
    [ -n "$xcat_name" ] || continue
    echo "${xcat_name%%.*}" >"$NEWROOT/etc/hostname"
    break
  done
fi


if [ -d "$NEWROOT/etc/sysconfig" -a ! -e "$NEWROOT/etc/sysconfig/selinux" ]; then
	echo "SELINUX=disabled" >> "$NEWROOT/etc/sysconfig/selinux"
fi

# signal dracut to proceed with switch_root
[ -e /dev/root ] || ln -s null /dev/root
for HDIR in "" "/lib/dracut/hooks"; do
  [ -d "${HDIR}/initqueue" ] || continue
  echo 'settle_exit_if_exists="--exit-if-exists=/dev/root"; rm -f -- "$job"' > ${HDIR}/initqueue/xcat.sh
  > ${HDIR}/initqueue/work
done
