#
# Copyright 2021  Stuart Winter, Donostia, Spain.
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

###########################################################################
# Script : /await_device
# Purpose: Await for the root device, a single or a list of devices to
#          become available within /dev.
#
#          This solves some issues on the RockPro64 where it takes longer
#          than usual on some boots for a USB-connected drive to appear.
# Status : ARM LOCAL
# Author : Stuart Winter <mozes@slackware.com>
# Date...: 27-Apr-2021
# Version: 1.00
###########################################################################
#
# An exposition of the Kernel cmdline options follows:
#
# awaitrootdev Await the appearance of the root device (as configured
#              by the 'rootdev=' cmdline operator) within /dev, after
#              waiting for the duration specified by the 'waitforroot='
#              Kernel cmdline operator.
#
#              On occasion, the RockPro64's USB storage bus would take time
#              to come online and would cause a boot failure.
#              This option has been tested using native storage devices
#              such as /dev/sda.
#              However, this has not been tested with LUKS, LVM or RAID
#              and this author suspects it will be incompatible.
#
#              If you have trouble with this, remove it from the boot
#              options below, and if you have trouble with your storage
#              sub systems coming online, try the next option:
#
# awaitdev=    Await the appearance of one or a number of devices within
#              /dev.
#
#              If you have trouble with LVM, LUKS or RAID subsystems during
#              boot, it may be because the underlying storage layer upon
#              which the abstraction layer relies, has yet to come online.
#
#              This code is executed directly after '/init' pauses for any
#              delay specified in the Kernel cmdline operators:
#              waitforroot=*|rootdelay=
#              Subsequently, the LVM, RAID and LUKS handling code will
#              execute.
#
#              Do not use both 'awaitdev=' and 'awaitrootdev' together.
#              If you are not using any abstraction layer, and have the
#              root filesystem on 'native' storage (e.g. /dev/sda) then
#              only use 'awaitrootdev'.
#
#              The syntax a comma separated list of device names, relative
#              to /dev:
#                awaitdev=sda2,sda3
#
###########################################################################

# Scan for file system labels:
function scan_label() {
   lsblk -o label -rin | egrep -q "^${1}$"
}

# Await a block device:
function await_block_device() {
   [ -r ${1} ] && return 0 # exit silently if the device is present
   echo -n "Waiting for ${1} to come online.."
   while [ ! -r ${1} ]; do
      echo -n .
      sleep 1
   done
   echo
}

# Await a labeled file system:
function await_labeled_device() {
   local label=${1#LABEL=} # remove the 'LABEL=' prefix
   # Exit silently if the labeled file system is already present:
   scan_label "${label}" && return 0
   echo -n "Waiting for labeled file system '${label}' to come online.."
   while ! scan_label "${label}"; do
      echo -n .
      sleep 1
   done
   echo
}

# Await the root device if configured:
if grep -iq "awaitrootdev" /proc/cmdline 2> /dev/null ; then
   # Ensure the root device is a block device rather than
   # set by 'LABEL='
   # This is the Slackware default.
   # If you need to await a particular device, use the awaitdev= functionality
   # below, as this only takes block device names.
   [[ "$ROOTDEV" =~ ^/dev/.* ]] && await_block_device $ROOTDEV
   [[ "$ROOTDEV" =~ LABEL= ]]   && await_labeled_device $ROOTDEV
fi

# Await any other configured devices to become available within /dev
if grep -iq "awaitdev=[a-zA-Z0-9]" /proc/cmdline 2> /dev/null ; then
   awaitdevlist="$( sed 's/.*awaitdev=\([^ ]*\).*/\1/' < /proc/cmdline )"
    if [ ! -z "${awaitdevlist}" ]; then
       IFS=,
       for awaiteddev in ${awaitdevlist} ; do
        await_device /dev/${awaiteddev}
       done
    fi
fi
