# This file contains common functions used in init and in hooks

msg () {
    [ "${quiet}" != "y" ] && echo $@
}
err () {
    echo "ERROR: $@"
}

poll_device() {
    local device=$1 seconds=${2//[!0-9]}

    [ "${seconds:-x}" = x ] && seconds=10
    deciseconds=$(( seconds * 10 ))

    # tenths of a second
    sleepinterval=1

    [ -b "$device" ] && return 0

    if [ "$udevd_running" -eq 1 ]; then
        msg "Waiting $seconds seconds for device $device ..." >&2
        while [ ! -b "$device" -a "$deciseconds" -gt 0 ]; do
            if [ "$sleepinterval" -ge 10 ]; then
                sleep 1
                deciseconds=$(( deciseconds - 10 ))
            else
                sleep .$sleepinterval
                deciseconds=$(( deciseconds - sleepinterval ))
                sleepinterval=$(( sleepinterval * 2 ))
            fi
        done
    fi

    [ -b "$device" ]
}

launch_interactive_shell() {
    export PS1='[rootfs \W]\$ '
    [ "$1" = "--exec" ] && exec sh -i
    sh -i
}

major_minor_to_device() {
    local dev

    [ -e "/sys/dev/block/$1:$2" ] || return 1

    if dev=$(readlink -f "/sys/dev/block/$1:$2" 2>/dev/null); then
        echo "/dev/${dev##*/}"
        return 0
    fi

    return 1
}

run_hookfunctions() {
    local hook fn=$1 desc=$2

    shift 2
    for hook in "$@"; do
        [ -x "/hooks/$hook" ] || continue

        unset "$fn"
        . "/hooks/$hook"
        type "$fn" >/dev/null || continue

        msg ":: running $desc [$hook]"
        "$fn"
    done
}

parse_cmdline() {
    local _w _quoted _lhs _rhs _cmdline
    read -r _cmdline </proc/cmdline
    for _w in $_cmdline; do
        if [ -z "$_quoted" ]; then
            case $_w in
                # ignore everything after a # in the commandline
                \#*) break ;;
                # special cases
                rw|ro) rwopt=$_w ;;
                fsck.mode=*)
                    case ${_w#*=} in
                        force) forcefsck=y ;;
                        skip) fastboot=y ;;
                    esac
                    ;;
                # abide by shell variable naming rules
                [[:alpha:]_]*=*)
                    _rhs=${_w#*=}
                    _lhs=${_w%%=*}
                    _lhs=${_lhs//[-.]/_}
                    if [ '"' = "${_rhs:0:1}" ]; then
                        if [ '"' = "${_rhs:$((${#_rhs}-1))}" ]; then
                            _rhs="${_rhs:1:$((${#_rhs}-2))}"
                        else
                            _rhs=${_rhs:1}
                            _quoted=1
                            continue
                        fi
                    fi
                    eval $_lhs=\$_rhs
                    ;;
                [[:alpha:]_]*)
                    _lhs=${_w//[-.]/_}
                    eval $_lhs=y
                    ;;
            esac
        else
            if [ '"' = "${_w:$((${#_w}-1))}" ]; then
                _rhs="$_rhs ${_w%\"}"
                unset _quoted
                eval $_lhs=\$_rhs
            else
                _rhs="$_rhs $_w"
            fi
        fi
    done
}

fsck_device() {
    [ -x /sbin/fsck ] || return 255

    if [ ! -b "$1" ]; then
        err "device '$1' not found. Skipping fsck."
        return 255
    fi

    if [ -n "$fastboot" ]; then
        msg ":: skipping fsck on '$1'"
        return
    fi

    msg ":: performing fsck on '$1'"
    fsck -Ta -C"$FSCK_FD" "$1" -- ${forcefsck+-f}
}

fsck_root() {
    fsck_device "$root"
    fsckret=$?

    fsck_ret() {
        [ -z "$fsckret" ] && return 1
        [ "$fsckret" -eq "$1" ] && return 0
        [ "$(( fsckret & $1 ))" -eq "$1" ]
    }

    if [ "$fsckret" -ne 255 ]; then
        if [ "$fsckret" = '0' ] || fsck_ret 1; then
            echo "$fsckret" > /run/initramfs/root-fsck
        elif fsck_ret 4; then
            err "Bailing out. Run 'fsck $root' manually"
            printf '%s\n' \
                "********** FILESYSTEM CHECK FAILED **********" \
                "*                                           *" \
                "*  Please run fsck manually. After leaving  *" \
                "*  this maintenance shell, the system will  *" \
                "*  reboot automatically.                    *" \
                "*                                           *" \
                "*********************************************"
            launch_interactive_shell
            echo ":: Automatic reboot in progress"
            sleep 2
            reboot -f
        elif fsck_ret 2; then
            printf '%s\n' \
                "************** REBOOT REQUIRED **************" \
                "*                                           *" \
                "*   automatically restarting in 10 seconds  *" \
                "*                                           *" \
                "*********************************************"
            sleep 10
            reboot -f
        elif fsck_ret 8; then
            err "fsck failed on '$root'"
        elif fsck_ret 16; then
            err "Failed to invoke fsck: usage or syntax error"
        elif fsck_ret 32; then
            echo ":: fsck cancelled on user request"
        elif fsck_ret 128; then
            err "fatal error invoking fsck"
        fi
    fi
}

resolve_device() {
    local major minor dev tag tagval device=$1

    # attempt to resolve devices immediately. if this fails
    # and udev is running, fall back on lazy resolution using
    # /dev/disk/by-* symlinks. this is flexible enough to support
    # usage of tags without udev and "slow" devices like root on
    # USB, which might not immediately show up.
    case $device in
        UUID=*|LABEL=*|PARTUUID=*|PARTLABEL=*)
            dev=$(blkid -lt "$device" -o device)
            if [ -z "$dev" -a "$udevd_running" -eq 1 ]; then
                tag=$(awk -v t="${device%%=*}" 'BEGIN { print tolower(t) }')
                tagval=${device#*=}
                dev=/dev/disk/by-$tag/$tagval
            fi
    esac

    [ -n "$dev" ] && device=$dev

    case $device in
        # path to kernel named block device
        /dev/*)
            if poll_device "$device" "$rootdelay"; then
                echo "$device"
                return 0
            fi
            ;;
        # hex encoded major/minor, such as from LILO
        [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]|[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
            major=$(( 0x0$device >> 8 ))
            minor=$(( 0x0$device & 0xff ))
            ;;
        0x[0-9a-fA-F][0-9a-fA-F]*)
            major=$(( device >> 8 ))
            minor=$(( device & 0xff ))
            ;;
    esac

    if [ -n "$major" -a -n "$minor" ]; then
        device=$(major_minor_to_device "$major" "$minor" || echo '/dev/root')

        if [ ! -b "$device" ]; then
            msg "Creating device node with major $major and minor $minor." >&2
            mknod "$device" b "$major" "$minor"
        fi
        echo "$device"
        return 0
    fi

    return 1
}

default_mount_handler() {
    if [ ! -b "$root" ]; then
        err "Unable to find root device '$root'."
        echo "You are being dropped to a recovery shell"
        echo "    Type 'exit' to try and continue booting"
        launch_interactive_shell
        msg "Trying to continue (this will most likely fail) ..."
    fi

    msg ":: mounting '$root' on real root"
    if ! mount ${fstype:+-t $fstype} -o ${rwopt:-ro}${rootflags:+,$rootflags} "$root" "$1"; then
        echo "You are now being dropped into an emergency shell."
        launch_interactive_shell
        msg "Trying to continue (this will most likely fail) ..."
    fi
}

# vim: set ft=sh ts=4 sw=4 et:
