#!/bin/bash
#
# verify-genesis-payload [--commands-from <module-setup.sh>] <payload-root> [required-path ...]
#
# dracut_install() reports a missing binary and returns, so the module install function keeps
# going and the image ships without it. Check the extracted payload before it is packaged.
#
# Paths given on the command line are relative to <payload-root>. --commands-from reads back
# what the dracut module installs: a bare command name is looked for in the four binary
# directories, an absolute path under <payload-root> itself. The caller adds what only it
# knows (the DHCP client is not the same package on every release); the rules below come from
# the payload itself.

set -u

commands_from=""
while [ $# -gt 0 ]; do
    case "$1" in
        --commands-from)
            commands_from=${2:-}
            shift 2 || true
            ;;
        --commands-from=*)
            commands_from=${1#*=}
            shift
            ;;
        *)
            break
            ;;
    esac
done

payload=${1:-}
if [ -z "$payload" ] || [ ! -d "$payload" ]; then
    echo "verify-genesis-payload: not a payload directory: ${payload:-<empty>}" >&2
    exit 2
fi
shift

missing=""

# have PATH: true when the payload carries PATH as a file, following the usr-merge symlinks
# the image ships (/sbin -> usr/sbin).
have() {
    [ -e "$payload/$1" ]
}

require() {
    local path=$1 why=$2
    have "$path" || missing="$missing
  $path ($why)"
}

for path in "$@"; do
    require "$path" "required by the build"
done

# The dracut module names every command and every data file Genesis needs. A name the build
# root does not supply installs nothing and says nothing, so read the names back and check
# each one. Names under a condition are release-dependent, so only the top level of install()
# counts.
if [ -n "$commands_from" ]; then
    if [ ! -r "$commands_from" ]; then
        echo "verify-genesis-payload: cannot read $commands_from" >&2
        exit 2
    fi
    commands=$(awk '
        /^install\(\)/     { in_install = 1; next }
        in_install && /^}/ { in_install = 0 }
        in_install && /^    dracut_install / {
            sub(/#.*/, "")
            sub(/^    dracut_install /, "")
            print
        }' "$commands_from" | tr ' \t' '\n\n' | grep -v '^$' | grep -v '^-' | sort -u)
    if [ -z "$commands" ]; then
        echo "verify-genesis-payload: no command name read from $commands_from" >&2
        exit 2
    fi
    for want in $commands; do
        case "$want" in
            # dracut_install installs an absolute path at that same path, so read it back
            # under the payload root. Dropping these let an image with no /usr/bin/awk pass.
            /*) have "${want#/}" || missing="$missing
  $want (installed by $commands_from)"
                ;;
            *)  have "bin/$want" || have "sbin/$want" \
                    || have "usr/bin/$want" || have "usr/sbin/$want" \
                    || missing="$missing
  $want (installed by $commands_from)"
                ;;
        esac
    done
fi

require usr/sbin/sshd "Genesis is reached over ssh"
require usr/bin/mktemp "getdestiny makes its request file with it"

# OpenSSH 9.8 split the per-connection work into sshd-session, which sshd execs by absolute
# path. EL9 carries OpenSSH 9.9, so an image with sshd alone refuses every connection.
if have usr/sbin/sshd && grep -qa 'sshd-session' "$payload/usr/sbin/sshd" 2>/dev/null; then
    if ! have usr/libexec/openssh/sshd-session && ! have usr/lib/openssh/sshd-session; then
        missing="$missing
  usr/libexec/openssh/sshd-session (this sshd execs it for every connection)"
    fi
fi

# tmux exits under the C locale. The hook falls back to running doxcat directly, so this is
# not fatal to booting, but a Genesis shell without tmux loses the console attach.
if have usr/bin/tmux && ! have usr/lib/locale/C.utf8/LC_CTYPE; then
    missing="$missing
  usr/lib/locale/C.utf8/LC_CTYPE (tmux refuses to start without a UTF-8 locale)"
fi

if [ -n "$missing" ]; then
    echo "verify-genesis-payload: $payload is incomplete:$missing" >&2
    exit 1
fi

echo "verify-genesis-payload: $payload is complete"
exit 0
