#!/bin/sh
# gen-mounts.sh — generate schema-init mount services from a machine's /etc/fstab.
#
# schema-init does NOT parse /etc/fstab. This reads the live system's fstab,
# resolves every real (non-pseudo) mount to a stable UUID=, and emits a single
# self-contained mount-fstab.sh + mount-fstab.svc so the box's disks, swap, and
# bind mounts come up under schema-init exactly as they did under systemd.
#
# Usage:
#   scripts/gen-mounts.sh              preview generated files on stdout (default)
#   scripts/gen-mounts.sh -o DIR       write mount-fstab.sh + mount-fstab.svc into DIR
#   scripts/gen-mounts.sh -f FSTAB     read a different fstab (e.g. a mounted target root)
#
# DIR is a distros/<host>/ profile dir (with services/ + scripts/ subdirs) or a
# staging dir. Re-run any time; the generated files carry a provenance header.

set -eu

FSTAB=/etc/fstab
OUTDIR=""

usage() { sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-0}"; }

while [ $# -gt 0 ]; do
    case "$1" in
        -o) OUTDIR="${2:?-o needs a dir}"; shift 2 ;;
        -f) FSTAB="${2:?-f needs a path}"; shift 2 ;;
        -h|--help) usage 0 ;;
        *) echo "unknown arg: $1" >&2; usage 1 ;;
    esac
done

[ -r "$FSTAB" ] || { echo "cannot read $FSTAB" >&2; exit 1; }

PSEUDO='proc|sysfs|tmpfs|devtmpfs|devpts|cgroup|cgroup2|mqueue|hugetlbfs|debugfs|securityfs|configfs|fusectl|bpf|tracefs|pstore|efivarfs|ramfs|rpc_pipefs|binfmt_misc|autofs|overlay|squashfs'

resolve_spec() {
    case "$1" in
        UUID=*|LABEL=*|PARTUUID=*|PARTLABEL=*|ID=*) printf '%s' "$1" ;;
        /dev/*)
            u=$(lsblk -nro UUID "$1" 2>/dev/null | head -1)
            [ -n "$u" ] || u=$(blkid -s UUID -o value "$1" 2>/dev/null || true)
            if [ -n "$u" ]; then printf 'UUID=%s' "$u"; else printf '%s' "$1"; fi ;;
        *) printf '%s' "$1" ;;
    esac
}

clean_opts() {
    printf '%s' "$1" | tr ',' '\n' \
        | grep -viE '^(x-.*|nofail|noauto|auto|_netdev|defaults)$' \
        | paste -sd, - 2>/dev/null || true
}

# Collect: emit tab-separated rows so we can depth-sort mountpoints (parents first).
# kind<TAB>depth<TAB>spec<TAB>target<TAB>fstype<TAB>opts
rows=$(awk -v pseudo="$PSEUDO" '
    /^[[:space:]]*#/ { next }
    NF < 3          { next }
    {
        spec=$1; tgt=$2; fs=$3; opts=(NF>=4?$4:"defaults")
        if (fs ~ ("^(" pseudo ")$")) next
        if (fs == "swap") { print "swap\t0\t" spec "\t-\t-\t" opts; next }
        if (tgt == "/") next
        if (opts ~ /(^|,)noauto(,|$)/) next
        n=gsub(/\//,"/",tgt)
        kind=(opts ~ /(^|,)bind(,|$)/) ? "bind" : "mount"
        print kind "\t" n "\t" spec "\t" tgt "\t" fs "\t" opts
    }' "$FSTAB" | sort -t"$(printf '\t')" -k2,2n)

warn_crypt=""
if [ -r /etc/crypttab ] && grep -qvE '^[[:space:]]*(#|$)' /etc/crypttab 2>/dev/null; then
    warn_crypt=$(grep -vE '^[[:space:]]*(#|$)' /etc/crypttab | awk '{print $1}' | paste -sd, -)
fi

SCRIPT=$(mktemp)
{
    printf '#!/bin/sh\n'
    printf '# GENERATED by scripts/gen-mounts.sh on %s — regenerate, do not hand-edit.\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
    printf '# Source: %s\n' "$FSTAB"
    printf 'exec >> /var/log/schema-mount.log 2>&1\n'
    printf 'printf "schema-mount start: %%s\\n" "$(date)"\n\n'

    IFS='
'
    for row in $rows; do
        kind=$(printf '%s' "$row" | cut -f1)
        spec=$(printf '%s' "$row" | cut -f3)
        tgt=$(printf '%s' "$row"  | cut -f4)
        fs=$(printf '%s' "$row"   | cut -f5)
        opts=$(printf '%s' "$row" | cut -f6)
        co=$(clean_opts "$opts"); [ -n "$co" ] || co=defaults
        case "$kind" in
            swap)
                rs=$(resolve_spec "$spec")
                printf 'swapon %s 2>/dev/null || printf "schema-mount: swap FAILED %%s\\n" %s\n' "$rs" "$rs"
                ;;
            bind)
                printf 'if ! findmnt -rno TARGET %s >/dev/null 2>&1; then\n' "$tgt"
                printf '    mkdir -p %s\n' "$tgt"
                printf '    mount --bind %s %s || printf "schema-mount: bind FAILED %%s\\n" %s\n' "$spec" "$tgt" "$tgt"
                printf 'fi\n'
                ;;
            mount)
                rs=$(resolve_spec "$spec")
                if [ "$fs" = auto ] || [ "$fs" = none ]; then tflag=""; else tflag="-t $fs "; fi
                printf 'if ! findmnt -rno TARGET %s >/dev/null 2>&1; then\n' "$tgt"
                printf '    mkdir -p %s\n' "$tgt"
                printf '    mount %s-o %s %s %s || printf "schema-mount: FAILED %%s\\n" %s\n' "$tflag" "$co" "$rs" "$tgt" "$tgt"
                printf 'fi\n'
                ;;
        esac
    done
    unset IFS
    printf '\nprintf "schema-mount done\\n"\nexit 0\n'
} > "$SCRIPT"

SVC=$(mktemp)
cat > "$SVC" <<'EOF'
name=mount-fstab
exec=/usr/local/bin/mount-fstab.sh
oneshot=1
needs_root=1
critical=0
start_timeout_sec=0
EOF

n_mount=$(printf '%s\n' "$rows" | grep -c '^mount' || true)
n_bind=$(printf '%s\n'  "$rows" | grep -c '^bind'  || true)
n_swap=$(printf '%s\n'  "$rows" | grep -c '^swap'  || true)

emit_summary() {
    echo "gen-mounts: $n_mount fs mount(s), $n_bind bind mount(s), $n_swap swap entry(ies) from $FSTAB" >&2
    [ -z "$warn_crypt" ] || {
        echo "" >&2
        echo "WARNING: /etc/crypttab is non-empty (targets: $warn_crypt)." >&2
        echo "  schema-init has NO crypttab handling. Root-on-LUKS is fine (the initramfs" >&2
        echo "  unlocks it before PID 1 runs), but any SECONDARY LUKS volume above will NOT" >&2
        echo "  be unlocked and its mount will fail until you add an unlock step." >&2
    }
}

if [ -z "$OUTDIR" ]; then
    echo "=================== mount-fstab.sh ==================="
    cat "$SCRIPT"
    echo "=================== mount-fstab.svc =================="
    cat "$SVC"
    echo "====================================================="
    emit_summary
    echo "" >&2
    echo "Preview only. Re-run with -o DIR to write, then install:" >&2
    echo "  install -m755 DIR/scripts/mount-fstab.sh   /usr/local/bin/" >&2
    echo "  install -m644 DIR/services/mount-fstab.svc /etc/schema-init/services/" >&2
    rm -f "$SCRIPT" "$SVC"
else
    mkdir -p "$OUTDIR/scripts" "$OUTDIR/services"
    install -m 0755 "$SCRIPT" "$OUTDIR/scripts/mount-fstab.sh"
    install -m 0644 "$SVC"    "$OUTDIR/services/mount-fstab.svc"
    rm -f "$SCRIPT" "$SVC"
    echo "wrote $OUTDIR/scripts/mount-fstab.sh"
    echo "wrote $OUTDIR/services/mount-fstab.svc"
    emit_summary
fi
