#!/usr/bin/env bash
# =============================================================================
# truenas-tunables-reset.sh  (snapZFS)
# TrueNAS SCALE 25.10.x - post CORE->SCALE migration tunable cleanup
#
#  0. Checks the TrueNAS version, explains what it will do, asks y/n
#  1. Deletes CORE autotune entries (comment "Generated by autotune") and any
#     FreeBSD-only sysctl names (net.inet., net.inet6., kern., vfs., hw., debug.)
#  2. Creates/updates the baseline set below (idempotent - safe to re-run)
#  3. Rebuilds the initramfs once if any ZFS tunable changed (ZFS module
#     parameters only survive a reboot via /etc/modprobe.d/zfs.conf in initrd).
#     Works around CORE-migrated boot pools where middleware's own rebuild
#     fails with "invalid option 'nfs4acl'" on mount -o rw,remount /
#  4. Verifies live kernel values and boot-time ZFS config
#
# Usage:  sudo bash truenas-tunables-reset.sh --diag      # read-only diagnostics
#         sudo bash truenas-tunables-reset.sh --dry-run   # preview changes
#         sudo bash truenas-tunables-reset.sh             # apply
#         sudo bash truenas-tunables-reset.sh --yes       # apply, no prompts
# =============================================================================
set -uo pipefail

# Version this script was written and tested against
TESTED_VERSION="25.10.7"

# --- Baseline tunables: TYPE|VAR|VALUE|COMMENT -------------------------------
# SYSCTL -> /proc/sys (sysctl).  ZFS -> /sys/module/zfs/parameters/<var>
NEW_TUNABLES=(
  "SYSCTL|net.core.rmem_max|33554432|Max TCP receive buffer for high-bandwidth NICs."
  "SYSCTL|net.core.wmem_max|33554432|Max TCP send buffer for high-bandwidth NICs."
  "SYSCTL|net.ipv4.tcp_rmem|4096 1048576 33554432|TCP receive autotune: 4 KiB min, 1 MiB default, 32 MiB max."
  "SYSCTL|net.ipv4.tcp_wmem|4096 1048576 33554432|TCP send autotune: 4 KiB min, 1 MiB default, 32 MiB max."
  "ZFS|l2arc_write_max|8388608|8 MiB/sec steady-state L2ARC write limit."
  "ZFS|l2arc_headroom|2|Smaller L2ARC scan window to reduce overhead."
  "ZFS|l2arc_noprefetch|0|Allow prefetched data into L2ARC."
)

# Stamp appended to each baseline comment: "<desc> | Added by snapZFS on YYYY-MM-DD"
STAMP_PREFIX=" | Added by snapZFS on "
STAMP="${STAMP_PREFIX}$(date +%Y-%m-%d)"

# Entries matching either rule get removed
# (match CORE's exact phrase - our own comments contain the word "autotune")
PURGE_COMMENT_RE='generated by autotune'
PURGE_VAR_RE='^(net\.inet6?\.|kern\.|vfs\.|hw\.|debug\.)'

# Middleware namespace candidates, tried in order
NS_CANDIDATES=(tunable system.tunable)
# Datastore table for fallback deletes
DS_TABLE="system.tunable"

# -----------------------------------------------------------------------------
MODE=apply
ASSUME_YES=0
for arg in "$@"; do
  case "$arg" in
    -n|--dry-run) MODE=dry ;;
    -d|--diag)    MODE=diag ;;
    -y|--yes)     ASSUME_YES=1 ;;
    *) echo "Usage: $0 [--dry-run | --diag] [--yes]"; exit 2 ;;
  esac
done
DRY_RUN=0; [[ $MODE == dry ]] && DRY_RUN=1

log()  { printf '%s\n' "$*"; }
hdr()  { printf '\n=== %s ===\n' "$*"; }
die()  { printf '\nFATAL: %s\n' "$*" >&2; printf 'Run: sudo bash %s --diag 2>&1 | tee /tmp/tunables-diag.txt\n' "$0" >&2; exit 1; }

# confirm "question" -> 0 on yes. --yes answers yes; no terminal and no --yes = no.
confirm() {
  local ans
  if (( ASSUME_YES )); then log "$1 [y/N] y (--yes)"; return 0; fi
  if ! { exec 3</dev/tty; } 2>/dev/null; then
    log "$1 [y/N] - no terminal to ask on; re-run with --yes to proceed unattended."
    return 1
  fi
  read -r -p "$1 [y/N] " ans <&3; exec 3<&-
  [[ "$ans" =~ ^[Yy]([Ee][Ss])?$ ]]
}

[[ $EUID -eq 0 ]] || { echo "ERROR: run as root (sudo -i or sudo bash $0)"; exit 1; }
for b in midclt jq; do
  command -v "$b" >/dev/null || { echo "ERROR: $b not found - is this TrueNAS SCALE?"; exit 1; }
done

# --- Diagnostics (read-only) -------------------------------------------------
diag_cmd() {  # label, command...
  printf '\n--- %s\n$ %s\n' "$1" "${*:2}"
  "${@:2}" 2>&1; printf '[exit %s]\n' "$?"
}

run_diag() {
  hdr "snapZFS tunable diagnostics - $(hostname -s) - $(date -Is)"
  diag_cmd "TrueNAS version (file)"   cat /etc/version
  diag_cmd "TrueNAS version (API)"    midclt call system.version
  diag_cmd "Kernel"                   uname -r
  diag_cmd "OpenZFS"                  zfs version
  diag_cmd "midclt help (job flag?)"  midclt call --help
  diag_cmd "midclt location/version"  bash -c 'command -v midclt; midclt --version 2>&1 || true'
  diag_cmd "jq version"               jq --version

  printf '\n--- API methods containing "tunable"\n'
  if midclt call core.get_methods >/tmp/.snapzfs_methods.json 2>/tmp/.snapzfs_methods.err; then
    jq -r 'keys[] | select(test("tunable"))' /tmp/.snapzfs_methods.json
    printf '\n--- Method details (job flag + accepts schema)\n'
    jq '[to_entries[] | select(.key|test("tunable")) |
         {method:.key, job:(.value.job // null), accepts:(.value.accepts // .value.params // null)}]' \
       /tmp/.snapzfs_methods.json
  else
    cat /tmp/.snapzfs_methods.err
    log "core.get_methods failed - trying candidates directly"
  fi
  rm -f /tmp/.snapzfs_methods.json /tmp/.snapzfs_methods.err

  for ns in "${NS_CANDIDATES[@]}"; do
    diag_cmd "Probe ${ns}.query"                  midclt call "${ns}.query"
    diag_cmd "Probe ${ns}.tunable_type_choices"   midclt call "${ns}.tunable_type_choices"
  done

  diag_cmd "Datastore rows (${DS_TABLE})"         midclt call datastore.query "$DS_TABLE"

  printf '\n--- Live values for baseline tunables\n'
  for entry in "${NEW_TUNABLES[@]}"; do
    IFS='|' read -r type var value _ <<<"$entry"
    case "$type" in
      SYSCTL) live="$(sysctl -n "$var" 2>&1 | tr -s '[:space:]' ' ')" ;;
      ZFS)    p="/sys/module/zfs/parameters/$var"
              if [[ -e $p ]]; then live="$(cat "$p") (writable: $([[ -w $p ]] && echo yes || echo no))"
              else live="<param file missing: $p>"; fi ;;
    esac
    printf '%-7s %-20s want=%-24s live=%s\n' "$type" "$var" "$value" "$live"
  done

  # ZFS tunable changes need the root dataset remounted rw to rebuild initrd
  root_state
  printf '\n--- Root filesystem (needed rw-remountable for initramfs rebuilds)\n'
  printf 'root dataset=%s  mounted=%s  readonly property=%s (%s)  acltype=%s\n' \
    "${ROOT_DS:-?}" "${ROOT_MNT_RO:-?}" "${ROOT_RO_PROP:-?}" "${ROOT_RO_SRC:-?}" "${ROOT_ACLTYPE:-?}"
  [[ $ROOT_ACLTYPE == nfsv4 ]] && printf '(acltype=nfsv4 = CORE-created boot pool: plain "mount -o rw,remount /" fails here)\n'
  diag_cmd "Boot pool"                  zpool list -o name,health,readonly,size,free,frag "$BOOT_POOL"
  diag_cmd "Boot pool status"           zpool status -x "$BOOT_POOL"
  diag_cmd "Root mount"                 findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS /
  diag_cmd "Kernel log (mount/zfs)"     bash -c 'dmesg 2>/dev/null | grep -iE "zfs|remount|read-only|EXT|I/O error" | tail -n 20'
  diag_cmd "Recent middleware errors (tunable/initramfs)" bash -c \
    'grep -iE "tunable|initramfs|remount" /var/log/middlewared.log 2>/dev/null | tail -n 25 || echo "no matches"'
  hdr "End diagnostics"
}

# Root dataset state.
#
# Why this matters - CORE->SCALE migrated boxes: middleware rebuilds the
# initramfs (needed for ZFS tunables to persist) by doing
#     zfs set readonly=off <root ds>      # this alone remounts / rw, live
#     mount -o rw,remount /               # ...and this then FAILS:
#   "cannot be mounted due to invalid option 'nfs4acl'"
# because the boot pool was created by CORE (FreeBSD) and its datasets carry
# acltype=nfsv4, which mount.zfs on Linux rejects. The rebuild aborts and the
# property is left readonly=off. Middleware skips the remount when / is
# already rw, so we flip readonly ourselves around the rebuild (see step 3).
root_state() {
  BOOT_POOL="$(midclt call boot.pool_name 2>/dev/null | tr -d '"')"
  ROOT_DS="$(findmnt -no SOURCE / 2>/dev/null)"
  ROOT_MNT_RO="rw"; [[ ",$(findmnt -no OPTIONS / 2>/dev/null)," == *,ro,* ]] && ROOT_MNT_RO="ro"
  ROOT_RO_PROP="$(zfs get -H -o value readonly "$ROOT_DS" 2>/dev/null)"
  ROOT_RO_SRC="$(zfs get -H -o source readonly "$ROOT_DS" 2>/dev/null)"
  ROOT_ACLTYPE="$(zfs get -H -o value acltype "$ROOT_DS" 2>/dev/null)"
}

# Set by unlock_root when WE made / writable; the EXIT trap puts it back.
ROOT_UNLOCKED_BY_US=0
unlock_root() {
  root_state
  [[ $ROOT_MNT_RO == ro && $ROOT_RO_PROP == on && $ROOT_RO_SRC == local ]] || return 0
  log "  making / writable for the rebuild (zfs set readonly=off $ROOT_DS)"
  zfs set readonly=off "$ROOT_DS" || return 1
  ROOT_UNLOCKED_BY_US=1
}
relock_root() {
  (( ROOT_UNLOCKED_BY_US )) || return 0
  if zfs set readonly=on "$ROOT_DS"; then
    log "  / back to read-only (zfs set readonly=on $ROOT_DS)"
    ROOT_UNLOCKED_BY_US=0
  else
    log "  !! could not restore read-only root - run: zfs set readonly=on $ROOT_DS"
  fi
}
trap relock_root EXIT

if [[ $MODE == diag ]]; then run_diag; exit 0; fi

# --- Version check -----------------------------------------------------------
TN_VERSION="$(midclt call system.version 2>/dev/null | tr -d '"')"
[[ -n $TN_VERSION ]] || TN_VERSION="$(cat /etc/version 2>/dev/null)"
TN_VERSION="${TN_VERSION#TrueNAS-}"; TN_VERSION="${TN_VERSION#TrueNAS-SCALE-}"
if [[ "$TN_VERSION" != "$TESTED_VERSION" ]]; then
  cat <<EOF

WARNING: This system is running TrueNAS ${TN_VERSION:-<unknown>}.
         This script was tested on TrueNAS ${TESTED_VERSION} only. It may or may
         not work on this version (the tunable API and boot handling change
         between releases). Use --dry-run first if unsure.

EOF
  confirm "Continue anyway?" || { log "Aborted. No changes made."; exit 0; }
fi

# --- Detect API namespace -----------------------------------------------------
NS=""
for ns in "${NS_CANDIDATES[@]}"; do
  if out="$(midclt call "${ns}.query" 2>&1)" && jq -e 'type=="array"' >/dev/null 2>&1 <<<"$out"; then
    NS="$ns"; break
  fi
done
[[ -n $NS ]] || die "No working tunable API found (tried: ${NS_CANDIDATES[*]}). Last output: $out"

query() {
  local o
  o="$(midclt call "${NS}.query" 2>&1)" || die "${NS}.query failed: $o"
  jq -e 'type=="array"' >/dev/null 2>&1 <<<"$o" || die "${NS}.query returned non-JSON: $o"
  printf '%s' "$o"
}

ERRORS=0
run() {
  if (( DRY_RUN )); then printf '  [dry-run] %s\n' "$*"; return 0; fi
  local o
  if o="$("$@" 2>&1)"; then return 0; fi
  printf '  !! command failed: %s\n' "$*"
  # strip midclt -j progress-bar escape codes before showing the error
  printf '%s\n' "$o" | sed 's/\x1b\[[0-9;]*[A-Za-z]//g' | grep -v '^Status:\|^Total Progress:' \
    | sed 's/^/     | /' | tail -n 15
  return 1
}

# Set when a ZFS tunable is created/updated/removed in a way that does not
# rebuild the initramfs itself; triggers one boot.update_initramfs at the end.
ZFS_CHANGED=0

# --- Pre-flight: can this box rebuild its initramfs? -------------------------
# ZFS tunables only persist via the initramfs, and the rebuild needs the boot
# pool healthy and writable. Check up front rather than fail halfway through.
root_state
PREFLIGHT=()
if [[ -z $BOOT_POOL ]]; then
  PREFLIGHT+=("could not determine the boot pool name (boot.pool_name)")
else
  read -r bp_health bp_ro < <(zpool list -H -o health,readonly "$BOOT_POOL" 2>/dev/null)
  [[ ${bp_health:-} == ONLINE ]] || PREFLIGHT+=("boot pool $BOOT_POOL health is '${bp_health:-unknown}' (expected ONLINE) - see: zpool status $BOOT_POOL")
  [[ ${bp_ro:-} == off ]]        || PREFLIGHT+=("boot pool $BOOT_POOL is imported READ-ONLY - the root filesystem cannot be made writable")
fi
if (( ${#PREFLIGHT[@]} )); then
  log ""
  log "PRE-FLIGHT WARNING - ZFS tunable changes may not survive a reboot on this box:"
  printf '  - %s\n' "${PREFLIGHT[@]}"
  log "  SYSCTL tunables are unaffected. Run --diag for details."
fi
# Not a blocker, but worth knowing: a writable root is either developer mode
# (install-dev-tools) or the leftover of an earlier failed initramfs rebuild.
# We don't flip it back on our own - it may be intentional.
if [[ $ROOT_RO_PROP == off && $ROOT_RO_SRC == local ]]; then
  cat <<EOF

NOTE: the root filesystem is writable ($ROOT_DS readonly=off).
      That's either developer mode, or left behind by an earlier failed
      initramfs rebuild (e.g. a previous run of this script, or a ZFS tunable
      change in the web UI). If you did not enable developer mode, restore it
      after this script finishes with:   zfs set readonly=on $ROOT_DS
EOF
fi

# --- 0. Explain and confirm --------------------------------------------------
cat <<EOF

TrueNAS SCALE tunable reset - snapZFS
Host: $(hostname -s)    TrueNAS: ${TN_VERSION:-unknown}    API: ${NS}.*

This script will:

  1. DELETE leftover TrueNAS CORE tunables:
       - any tunable with the description "Generated by autotune"
       - any tunable using a FreeBSD-only name (net.inet.*, kern.*, vfs.*,
         hw.*, debug.*) - these have no effect on Linux-based SCALE

  2. ADD or UPDATE these tunables (description stamped
     "Added by snapZFS on $(date +%Y-%m-%d)"):
EOF
for entry in "${NEW_TUNABLES[@]}"; do
  IFS='|' read -r type var value _ <<<"$entry"
  printf '       %-7s %-20s = %s\n' "$type" "$var" "$value"
done
cat <<EOF

  3. REBUILD the initramfs (once, only if a ZFS tunable changed or the boot
     config is out of date) so ZFS module parameters persist across reboots.
     / is made writable just for the rebuild and put back read-only after.

  4. VERIFY each tunable's configured value against the live kernel value
     and the boot-time ZFS config.

Other tunables (UDEV rules, manually added Linux sysctls) are not touched.
Changes take effect immediately and persist across reboots. No reboot needed.

EOF
(( DRY_RUN )) && log ">>> DRY RUN - changes will be listed but NOT made"

confirm "Proceed?" || { log "Aborted. No changes made."; exit 0; }

# --- 1. Purge ----------------------------------------------------------------
hdr "Purge autotune / FreeBSD tunables"
CURRENT="$(query)" || exit 1
# Baseline "TYPE|VAR" keys - a matching row is updated in place in step 2
# instead of being deleted and recreated.
BASE_KEYS="$(for e in "${NEW_TUNABLES[@]}"; do IFS='|' read -r t v _ <<<"$e"; printf '%s|%s\n' "$t" "$v"; done)"
mapfile -t PURGE < <(jq -r --arg c "$PURGE_COMMENT_RE" --arg v "$PURGE_VAR_RE" --arg keys "$BASE_KEYS" '
  ($keys | split("\n")) as $base
  | .[] | select( ((.comment // "") | ascii_downcase | test($c)) or (.var | test($v)) )
        | select( ("\(.type)|\(.var)" | IN($base[])) | not )
        | "\(.id)\t\(.type)\t\(.var)\t\(.value)\t\(.orig_value // "")"' <<<"$CURRENT")
log "Found $(jq length <<<"$CURRENT") tunables, ${#PURGE[@]} to purge."

for row in "${PURGE[@]}"; do
  IFS=$'\t' read -r id type var val orig <<<"$row"
  log "- id=$id  [$type] $var = $val"
  if [[ $type == ZFS ]]; then
    # tunable.delete on a ZFS row rebuilds the initramfs for EVERY row (and
    # dies if / can't be remounted rw). Drop the row, put the live value back,
    # and leave the single rebuild to step 3.
    p="/sys/module/zfs/parameters/$var"
    if run midclt call datastore.delete "$DS_TABLE" "$id"; then
      ZFS_CHANGED=1
      if [[ -n $orig && -w $p ]] && (( ! DRY_RUN )); then
        printf '%s' "$orig" > "$p" 2>/dev/null && log "  live $var reset to $orig"
      fi
    else
      log "  !! could not remove id=$id"; ((ERRORS++))
    fi
  elif ! run midclt call -j "${NS}.delete" "$id"; then
    log "  falling back to datastore.delete $DS_TABLE $id"
    run midclt call datastore.delete "$DS_TABLE" "$id" || { log "  !! could not remove id=$id"; ((ERRORS++)); }
  fi
done

# --- 2. Apply baseline -------------------------------------------------------
hdr "Apply baseline"
CURRENT="$(query)" || exit 1
for entry in "${NEW_TUNABLES[@]}"; do
  IFS='|' read -r type var value comment <<<"$entry"
  full_comment="${comment}${STAMP}"

  existing="$(jq -c --arg v "$var" '[.[] | select(.var == $v)][0] // empty' <<<"$CURRENT")"

  payload="$(jq -nc --arg t "$type" --arg v "$var" --arg val "$value" --arg c "$full_comment" \
    '{type:$t, var:$v, value:$val, comment:$c, enabled:true, update_initramfs:false}')"

  if [[ -z "$existing" ]]; then
    log "+ create [$type] $var = $value"
    if run midclt call -j "${NS}.create" "$payload"; then
      [[ $type == ZFS ]] && ZFS_CHANGED=1
    else
      ((ERRORS++))
    fi
    continue
  fi

  e_id="$(jq -r '.id' <<<"$existing")"
  e_type="$(jq -r '.type' <<<"$existing")"
  e_val="$(jq -r '.value' <<<"$existing")"
  e_cmt="$(jq -r '.comment // ""' <<<"$existing")"
  e_en="$(jq -r '.enabled' <<<"$existing")"

  if [[ "$e_type" != "$type" ]]; then
    log "~ recreate $var (type $e_type -> $type)"
    run midclt call -j "${NS}.delete" "$e_id" \
      || run midclt call datastore.delete "$DS_TABLE" "$e_id" || ((ERRORS++))
    run midclt call -j "${NS}.create" "$payload" || ((ERRORS++))
    [[ $type == ZFS || $e_type == ZFS ]] && ZFS_CHANGED=1
  elif [[ "$e_val" != "$value" || "${e_cmt%%"$STAMP_PREFIX"*}" != "$comment" \
          || "$e_cmt" != *"$STAMP_PREFIX"* || "$e_en" != "true" ]]; then
    # Existing stamp date is kept unless something actually changes
    if [[ "$e_val" != "$value" ]]; then
      log "~ update [$type] $var: '$e_val' -> '$value'"
    else
      log "~ update [$type] $var = $value (description/enabled only)"
    fi
    upd="$(jq -nc --arg val "$value" --arg c "$full_comment" \
      '{value:$val, comment:$c, enabled:true, update_initramfs:false}')"
    if run midclt call -j "${NS}.update" "$e_id" "$upd"; then
      [[ $type == ZFS ]] && ZFS_CHANGED=1
    else
      ((ERRORS++))
    fi
  else
    log "= ok     [$type] $var = $value"
  fi
done

# --- 3. Persist ZFS tunables across reboot -----------------------------------
# ZFS module parameters are applied at boot from /etc/modprobe.d/zfs.conf inside
# the initramfs. Creates/updates above passed update_initramfs:false so the
# (slow) rebuild runs once here instead of once per tunable.
hdr "Persist ZFS tunables (initramfs)"
# Also rebuild when boot config disagrees with the DB - e.g. an earlier run
# (or a web UI change) whose rebuild failed. Sorted "var=value" lists compared.
zfs_conf_stale() {
  local want have
  want="$(query | jq -r '[.[] | select(.type=="ZFS" and .enabled) | "\(.var)=\(.value)"] | sort | .[]')"
  have="$(sed -n 's/^options zfs //p' /etc/modprobe.d/zfs.conf 2>/dev/null | tr ' ' '\n' | grep . | sort)"
  [[ "$want" != "$have" ]]
}
REBUILD_WHY=""
if (( ZFS_CHANGED )); then REBUILD_WHY="ZFS tunables changed"
elif zfs_conf_stale;   then REBUILD_WHY="boot-time zfs.conf is out of date (earlier rebuild failed?)"
fi
if [[ -n $REBUILD_WHY ]]; then
  log "$REBUILD_WHY - rebuilding initramfs (can take a minute)..."
  if (( DRY_RUN )); then
    run zfs set readonly=off "${ROOT_DS:-<root ds>}"
    run midclt call boot.update_initramfs
    run zfs set readonly=on "${ROOT_DS:-<root ds>}"
  elif unlock_root && run midclt call boot.update_initramfs; then
    relock_root
  else
    ((ERRORS++))
    relock_root
    cat <<EOF
  !! initramfs rebuild failed. The database and live values are correct, but
     ZFS tunables will revert to defaults on the next reboot until the
     rebuild succeeds. See --diag (boot pool / root mount), then re-run.
EOF
  fi
else
  log "No ZFS tunable changes and boot config matches - rebuild not needed."
fi

# --- 4. Verify ---------------------------------------------------------------
hdr "Verify (DB vs live kernel vs boot config)"
(( DRY_RUN )) && { log "Skipped in dry-run."; exit 0; }

# Boot-time ZFS options as written by truenas-initrd.py: "options zfs a=1 b=2"
ZFS_BOOT_OPTS=" $(sed -n 's/^options zfs //p' /etc/modprobe.d/zfs.conf 2>/dev/null) "

printf '%-8s %-22s %-24s %-24s %s\n' TYPE VAR CONFIGURED LIVE STATUS
VERIFY_BAD=0
while IFS='|' read -r type var value enabled; do
  case "$type" in
    SYSCTL) live="$(sysctl -n "$var" 2>/dev/null | tr -s '[:space:]' ' ' | sed 's/ $//')" ;;
    ZFS)    live="$(cat "/sys/module/zfs/parameters/$var" 2>/dev/null || true)" ;;
    *)      live="n/a" ;;
  esac
  if   [[ "$enabled" != "true" ]]; then st="disabled"
  elif [[ "$type" == UDEV ]];     then st="n/a (udev rule)"
  elif [[ -z "$live" ]];          then st="MISSING"; VERIFY_BAD=1
  elif [[ "$live" != "$value" ]]; then st="MISMATCH"; VERIFY_BAD=1
  elif [[ "$type" == ZFS && "$ZFS_BOOT_OPTS" != *" $var=$value "* ]]; then
    st="OK live, NOT PERSISTED (missing from boot zfs.conf)"; VERIFY_BAD=1
  else st="OK"; fi
  printf '%-8s %-22s %-24s %-24s %s\n' "$type" "$var" "$value" "${live:-<none>}" "$st"
done < <(query | jq -r '.[] | "\(.type)|\(.var)|\(.value)|\(.enabled)"')
(( VERIFY_BAD )) && ((ERRORS++))

log ""
if (( ERRORS )); then
  log "Done with $ERRORS error(s). Run with --diag and send the output."
  exit 1
fi
log "Done."
