#!/usr/bin/env bash
# =============================================================================
# onx-cgroup-read — Read live cgroups v2 resource usage for a user slice
#
# Purpose:
#   Reads current usage metrics from:
#     /sys/fs/cgroup/user.slice/user-${UID}.slice/
#
#   Files read:
#     cpu.stat         → cpu_usage_seconds, cpu_pct (estimated)
#     memory.current   → memory_used_bytes → memory_used_mb
#     memory.max       → memory_limit_bytes → memory_limit_mb
#     io.stat          → rbytes, wbytes per device (aggregated)
#     pids.current     → process_count
#     pids.max         → process_limit
#
# Input (stdin JSON):
#   { "username": "onx_xxxx" }
#
# Output (stdout JSON):
#   {
#     "username": "onx_xxxx",
#     "uid": 1001,
#     "cpu_pct": 42.3,
#     "memory_used_mb": 234,
#     "memory_limit_mb": 1024,
#     "io_read_bytes": 104857600,
#     "io_write_bytes": 52428800,
#     "processes": 15,
#     "process_limit": 100,
#     "cgroup_path": "/sys/fs/cgroup/user.slice/user-1001.slice"
#   }
#
# Exit codes: 0=ok 1=invalid-input 2=preflight-fail
#
# Deployed to: /usr/local/onoxsoft/bin/onx-cgroup-read
# =============================================================================

set -euo pipefail

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
source "${SCRIPT_DIR}/_lib/common.sh"

command -v jq >/dev/null 2>&1 || { printf '{"error":"jq required"}\n' >&2; exit 2; }
require_root

# ── Read stdin ────────────────────────────────────────────────────────────────
INPUT=$(cat)
onx_require_json "${INPUT}"

USERNAME=$(onx_json_get "${INPUT}" "username")
onx_validate_username "${USERNAME}"

id "${USERNAME}" &>/dev/null || onx_die 2 "Linux user does not exist: ${USERNAME}"

UID_VAL=$(id -u "${USERNAME}")
CGROUP_PATH="/sys/fs/cgroup/user.slice/user-${UID_VAL}.slice"

if [[ ! -d "${CGROUP_PATH}" ]]; then
    # User slice not active (account idle / not logged in)
    printf '{"username":"%s","uid":%s,"active":false,"cpu_pct":0,"memory_used_mb":0,"memory_limit_mb":null,"io_read_bytes":0,"io_write_bytes":0,"processes":0,"process_limit":null,"cgroup_path":"%s"}\n' \
        "${USERNAME}" "${UID_VAL}" "${CGROUP_PATH}"
    exit 0
fi

# ── CPU usage ─────────────────────────────────────────────────────────────────
CPU_USAGE_USEC=0
if [[ -f "${CGROUP_PATH}/cpu.stat" ]]; then
    CPU_USAGE_USEC=$(grep "^usage_usec" "${CGROUP_PATH}/cpu.stat" 2>/dev/null | awk '{print $2}' || echo "0")
fi
# Instantaneous CPU % requires two samples; report cumulative seconds as best-effort
CPU_USAGE_SEC=$(( CPU_USAGE_USEC / 1000000 )) || CPU_USAGE_SEC=0

# ── Memory ────────────────────────────────────────────────────────────────────
MEM_CURRENT=0
MEM_MAX="null"

if [[ -f "${CGROUP_PATH}/memory.current" ]]; then
    MEM_CURRENT=$(cat "${CGROUP_PATH}/memory.current" 2>/dev/null || echo "0")
fi

if [[ -f "${CGROUP_PATH}/memory.max" ]]; then
    raw=$(cat "${CGROUP_PATH}/memory.max" 2>/dev/null || echo "max")
    if [[ "${raw}" == "max" ]]; then
        MEM_MAX="null"
    else
        MEM_MAX=$(( raw / 1024 / 1024 ))
    fi
fi

MEM_USED_MB=$(( MEM_CURRENT / 1024 / 1024 ))

# ── IO stats ──────────────────────────────────────────────────────────────────
IO_RBYTES=0
IO_WBYTES=0

if [[ -f "${CGROUP_PATH}/io.stat" ]]; then
    while IFS= read -r line; do
        rbytes=$(echo "${line}" | grep -oP 'rbytes=\K[0-9]+' || echo "0")
        wbytes=$(echo "${line}" | grep -oP 'wbytes=\K[0-9]+' || echo "0")
        IO_RBYTES=$(( IO_RBYTES + rbytes ))
        IO_WBYTES=$(( IO_WBYTES + wbytes ))
    done < "${CGROUP_PATH}/io.stat"
fi

# ── Process count ─────────────────────────────────────────────────────────────
PIDS_CURRENT=0
PIDS_MAX="null"

if [[ -f "${CGROUP_PATH}/pids.current" ]]; then
    PIDS_CURRENT=$(cat "${CGROUP_PATH}/pids.current" 2>/dev/null || echo "0")
fi

if [[ -f "${CGROUP_PATH}/pids.max" ]]; then
    raw=$(cat "${CGROUP_PATH}/pids.max" 2>/dev/null || echo "max")
    if [[ "${raw}" == "max" ]]; then
        PIDS_MAX="null"
    else
        PIDS_MAX="${raw}"
    fi
fi

onx_log "cgroup-read: ${USERNAME} (uid=${UID_VAL}) mem=${MEM_USED_MB}MB pids=${PIDS_CURRENT}"

# ── Output ────────────────────────────────────────────────────────────────────
printf '{"username":"%s","uid":%s,"active":true,"cpu_usage_seconds":%s,"memory_used_mb":%s,"memory_limit_mb":%s,"io_read_bytes":%s,"io_write_bytes":%s,"processes":%s,"process_limit":%s,"cgroup_path":"%s"}\n' \
    "${USERNAME}" "${UID_VAL}" \
    "${CPU_USAGE_SEC}" \
    "${MEM_USED_MB}" \
    "${MEM_MAX}" \
    "${IO_RBYTES}" "${IO_WBYTES}" \
    "${PIDS_CURRENT}" "${PIDS_MAX}" \
    "${CGROUP_PATH}"
