#!/usr/bin/bash
#
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
# SPDX-License-Identifier: LicenseRef-NVIDIA-Proprietary
#
# Real: NVConfig param presence check via mlxconfig q
# Runs mlxconfig q once (cached per BDF), checks each requested param.
#
# Usage: mlxconfig-nvconfig-caps GET <BDF> <PARAM1> <PARAM2> ...
# Output: leaf-name=true/false per param
#
# Cache: stores mlxconfig q output in a root-owned runtime directory
# to avoid repeated 4-5s calls within the same session.

[[ -n "${DMS_BACKEND_TRACE:-}" ]] && echo "$(date -Iseconds)|mlxconfig-nvconfig-caps|$*" >> "$DMS_BACKEND_TRACE"

OP="$1"; shift
TARGET="$1"; shift

BDF="${TARGET#pci/}"

CACHE_DIR="/run/dms/nvconfig-caps"
CACHE_TTL_SECONDS="${DMS_NVCONFIG_CAPS_CACHE_TTL_SECONDS:-3600}"

SAFE_BDF="${BDF//[!A-Za-z0-9_.:-]/_}"
CACHE_FILE="${CACHE_DIR}/${SAFE_BDF}.mlxconfig_q"
TMP_CACHE=""
trap '[[ -n "$TMP_CACHE" ]] && rm -f "$TMP_CACHE"' EXIT

if [[ "$OP" != "GET" ]]; then
    echo "Error: caps are read-only" >&2
    exit 1
fi

if [[ -L "$CACHE_DIR" ]]; then
    echo "Error: cache directory must not be a symlink: $CACHE_DIR" >&2
    exit 1
fi
if [[ -e "$CACHE_DIR" && ! -d "$CACHE_DIR" ]]; then
    echo "Error: cache path is not a directory: $CACHE_DIR" >&2
    exit 1
fi
mkdir -p "$CACHE_DIR" || { echo "Error: failed to create cache directory $CACHE_DIR" >&2; exit 1; }
if [[ -L "$CACHE_DIR" ]] || [[ "$(stat -c %u "$CACHE_DIR" 2>/dev/null)" != "0" ]]; then
    echo "Error: cache directory must be root-owned and not a symlink: $CACHE_DIR" >&2
    exit 1
fi
chmod 0700 "$CACHE_DIR" || { echo "Error: failed to chmod cache directory $CACHE_DIR" >&2; exit 1; }

if [[ -e "$CACHE_FILE" ]] && { [[ ! -f "$CACHE_FILE" ]] || [[ -L "$CACHE_FILE" ]] || [[ "$(stat -c %u "$CACHE_FILE" 2>/dev/null)" != "0" ]]; }; then
    rm -f "$CACHE_FILE"
fi

if [[ ! -f "$CACHE_FILE" ]] || [[ $(( $(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) )) -gt "$CACHE_TTL_SECONDS" ]]; then
    old_umask=$(umask)
    umask 077
    TMP_CACHE=$(mktemp "${CACHE_DIR}/.mlxconfig_q.XXXXXX") || exit 1
    umask "$old_umask"
    if ! mlxconfig -d "$BDF" -e q > "$TMP_CACHE" 2>/dev/null; then
        echo "Error: mlxconfig q failed for $BDF" >&2
        exit 1
    fi
    if mv -f -T "$TMP_CACHE" "$CACHE_FILE"; then
        TMP_CACHE=""
    else
        echo "Error: failed to update cache file $CACHE_FILE" >&2
        exit 1
    fi
fi

declare -A PARAM_TO_LEAF=(
    ["ROCE_ADAPTIVE_ROUTING_EN"]="adaptive-routing"
    ["TX_SCHEDULER_LOCALITY_MODE"]="tx-sched-locality-mode"
    ["MULTIPATH_DSCP"]="multipath-dscp"
    ["ROCE_CC_STEERING_EXT"]="cc-steering-ext"
    ["USER_PROGRAMMABLE_CC"]="user-programmable"
    ["ROCE_CC_SHAPER_COALESCE_P1"]="cc-shaper-coalesce"
    ["SRIOV_EN"]="sriov-enabled"
    ["NUM_OF_VFS"]="max-vfs"
    ["NUM_OF_PF"]="num-pfs"
    ["RDE_DISABLE"]="rde-disable"
    ["VF_LOG_BAR_SIZE"]="vf-log-bar-size"
    ["LAG_RESOURCE_ALLOCATION"]="resource-allocation"
    ["NUM_OF_PLANES_P1"]="num-planes"
    ["LOAD_BALANCE_MODE_P1"]="load-balance-mode"
    ["KEEP_ETH_LINK_UP_P1"]="keep-eth-link-up"
    ["INTERNAL_CPU_OFFLOAD_ENGINE"]="operating-mode"
    ["FLEX_PARSER_PROFILE_ENABLE"]="flex-parser-profile"
)

for PARAM in "$@"; do
    LEAF="${PARAM_TO_LEAF[$PARAM]:-$PARAM}"
    BASE_PARAM="${PARAM%%__*}"
    if grep -q "^[[:space:]]*\\*\\?[[:space:]]*\\(RO \\)\\?${BASE_PARAM}[[:space:]]" "$CACHE_FILE"; then
        echo "${LEAF}=true"
    else
        echo "${LEAF}=false"
    fi
done
