Files
infmap/app/gather_info.sh
j 58f542b96f
All checks were successful
Build-Publish / build (linux/amd64) (push) Successful in 4s
Build-Publish / build (linux/arm64) (push) Successful in 12s
Build-Publish / create-manifest (push) Successful in 2s
Build-Publish / publish-template (push) Successful in 8s
Add sudo support for container/VM hypervisor commands
2026-03-08 13:51:55 +13:00

280 lines
9.7 KiB
Bash
Executable File

#!/bin/bash
# Gather system information from a remote server
# Output format: [section] headers followed by key=value pairs
echo "[system]"
echo "hostname=$(hostname)"
if [ -f /etc/os-release ]; then
. /etc/os-release
echo "os_name=$NAME"
echo "os_version=$VERSION_ID"
echo "os_pretty=$PRETTY_NAME"
fi
echo "kernel=$(uname -r)"
echo "arch=$(uname -m)"
echo "uptime_seconds=$(cut -d' ' -f1 /proc/uptime 2>/dev/null | cut -d. -f1)"
# Motherboard (readable without root on most systems)
echo "board_vendor=$(cat /sys/class/dmi/id/board_vendor 2>/dev/null || echo 'Unknown')"
echo "board_name=$(cat /sys/class/dmi/id/board_name 2>/dev/null || echo 'Unknown')"
echo "board_version=$(cat /sys/class/dmi/id/board_version 2>/dev/null || echo 'Unknown')"
echo "bios_version=$(cat /sys/class/dmi/id/bios_version 2>/dev/null || echo 'Unknown')"
echo "bios_date=$(cat /sys/class/dmi/id/bios_date 2>/dev/null || echo 'Unknown')"
echo "[cpu]"
echo "model=$(lscpu 2>/dev/null | grep 'Model name' | sed 's/Model name:[[:space:]]*//')"
echo "cores=$(nproc 2>/dev/null || echo 0)"
echo "sockets=$(lscpu 2>/dev/null | grep 'Socket(s)' | awk '{print $2}')"
echo "threads_per_core=$(lscpu 2>/dev/null | grep 'Thread(s) per core' | awk '{print $2}')"
# CPU usage - sample /proc/stat with 1 second interval
read -r label user1 nice1 system1 idle1 iowait1 irq1 softirq1 steal1 < /proc/stat
sleep 1
read -r label user2 nice2 system2 idle2 iowait2 irq2 softirq2 steal2 < /proc/stat
total1=$((user1 + nice1 + system1 + idle1 + iowait1 + irq1 + softirq1 + steal1))
total2=$((user2 + nice2 + system2 + idle2 + iowait2 + irq2 + softirq2 + steal2))
diff_total=$((total2 - total1))
diff_idle=$((idle2 - idle1))
if [ $diff_total -gt 0 ]; then
usage_x10=$(( (diff_total - diff_idle) * 1000 / diff_total ))
whole=$((usage_x10 / 10))
frac=$((usage_x10 % 10))
echo "usage_percent=${whole}.${frac}"
else
echo "usage_percent=0.0"
fi
# Temperatures - try sensors (lm-sensors), fall back to thermal zones
echo "[temperatures]"
if command -v sensors &>/dev/null; then
sensors 2>/dev/null | awk -F'[: ]+' '
/^[^ ]/ { chip=$1 }
/°C/ {
label=$1
gsub(/^ +| +$/, "", label)
match($0, /[+-]([0-9]+\.[0-9]+)°C/, m)
if (m[1]+0 > 0) print chip "/" label "=" m[1]
}
'
else
for tz in /sys/class/thermal/thermal_zone*; do
[ -f "$tz/temp" ] || continue
type=$(cat "$tz/type" 2>/dev/null || echo "unknown")
temp_mc=$(cat "$tz/temp" 2>/dev/null || echo 0)
temp_c=$((temp_mc / 1000))
temp_frac=$(( (temp_mc % 1000) / 100 ))
[ "$temp_c" -gt 0 ] 2>/dev/null && echo "${type}=${temp_c}.${temp_frac}"
done
fi
echo "[memory]"
total_kb=$(grep MemTotal /proc/meminfo 2>/dev/null | awk '{print $2}')
available_kb=$(grep MemAvailable /proc/meminfo 2>/dev/null | awk '{print $2}')
if [ -n "$total_kb" ] && [ -n "$available_kb" ]; then
used_kb=$((total_kb - available_kb))
total_mb=$((total_kb / 1024))
used_mb=$((used_kb / 1024))
available_mb=$((available_kb / 1024))
if [ "$total_kb" -gt 0 ]; then
usage_x10=$((used_kb * 1000 / total_kb))
whole=$((usage_x10 / 10))
frac=$((usage_x10 % 10))
echo "usage_percent=${whole}.${frac}"
else
echo "usage_percent=0.0"
fi
echo "total_mb=$total_mb"
echo "used_mb=$used_mb"
echo "available_mb=$available_mb"
fi
# Physical disks
lsblk -b -n -o NAME,SIZE,TYPE 2>/dev/null | while read -r name size type; do
if [ "$type" = "disk" ]; then
echo "[disk:$name]"
echo "name=$name"
echo "size_bytes=$size"
fi
done
# Mounted filesystem usage
df -B1 --output=target,size,used,avail,pcent 2>/dev/null | tail -n +2 | while read -r mount total used avail percent; do
case "$mount" in
/|/home|/var|/tmp|/boot|/data*|/mnt*|/srv*|/opt*)
safename=$(echo "$mount" | tr '/' '_')
echo "[disk_usage:${safename}]"
echo "mount=$mount"
echo "total_bytes=$total"
echo "used_bytes=$used"
echo "available_bytes=$avail"
echo "usage_percent=${percent%\%}"
;;
esac
done
# GPUs
gpu_idx=0
while read -r line; do
echo "[gpu:$gpu_idx]"
echo "description=$line"
gpu_idx=$((gpu_idx + 1))
done < <(lspci 2>/dev/null | grep -iE 'vga|3d|display')
# Network interfaces
for iface in $(ls /sys/class/net/ 2>/dev/null); do
[ "$iface" = "lo" ] && continue
echo "[net:$iface]"
echo "name=$iface"
echo "mac=$(cat /sys/class/net/$iface/address 2>/dev/null)"
echo "ipv4=$(ip -4 addr show "$iface" 2>/dev/null | grep -oP 'inet \K[0-9.]+' | head -1)"
echo "ipv6=$(ip -6 addr show "$iface" 2>/dev/null | grep -oP 'inet6 \K[0-9a-f:]+' | grep -v '^fe80' | head -1)"
echo "state=$(cat /sys/class/net/$iface/operstate 2>/dev/null)"
speed=$(cat /sys/class/net/$iface/speed 2>/dev/null)
[ -n "$speed" ] && [ "$speed" != "-1" ] && echo "speed_mbps=$speed"
driver=$(readlink /sys/class/net/$iface/device/driver 2>/dev/null | xargs basename 2>/dev/null)
[ -n "$driver" ] && echo "driver=$driver"
done
echo "[routing]"
default_line=$(ip route 2>/dev/null | grep default | head -1)
echo "gateway=$(echo "$default_line" | awk '{print $3}')"
echo "interface=$(echo "$default_line" | awk '{print $5}')"
echo "[dns]"
grep -E '^nameserver' /etc/resolv.conf 2>/dev/null | awk '{print "server=" $2}'
echo "[tailscale]"
if command -v tailscale &>/dev/null; then
echo "installed=true"
echo "ipv4=$(tailscale ip -4 2>/dev/null)"
echo "hostname=$(tailscale status --self --json 2>/dev/null | grep -o '"DNSName":"[^"]*"' | head -1 | cut -d'"' -f4)"
else
echo "installed=false"
fi
# --- Container / VM autodiscovery ---
# Helper: collect basic stats by execing into a container
# Usage: gather_container_stats <exec_prefix>
# e.g. gather_container_stats "pct exec 100 --"
gather_container_stats() {
local exec_cmd="$1"
# Memory
local mem_total mem_avail mem_used mem_pct
mem_total=$($exec_cmd cat /proc/meminfo 2>/dev/null | awk '/MemTotal/{print int($2/1024)}')
mem_avail=$($exec_cmd cat /proc/meminfo 2>/dev/null | awk '/MemAvailable/{print int($2/1024)}')
if [ -n "$mem_total" ] && [ -n "$mem_avail" ] && [ "$mem_total" -gt 0 ] 2>/dev/null; then
mem_used=$((mem_total - mem_avail))
mem_pct=$((mem_used * 100 / mem_total))
echo "mem_total_mb=$mem_total"
echo "mem_used_mb=$mem_used"
echo "mem_percent=$mem_pct"
fi
# Disk (rootfs)
local disk_info
disk_info=$($exec_cmd df -B1 / 2>/dev/null | tail -1)
if [ -n "$disk_info" ]; then
echo "disk_total=$(echo "$disk_info" | awk '{print $2}')"
echo "disk_used=$(echo "$disk_info" | awk '{print $3}')"
echo "disk_percent=$(echo "$disk_info" | awk '{gsub(/%/,""); print $5}')"
fi
# IP
local ip
ip=$($exec_cmd hostname -I 2>/dev/null | awk '{print $1}')
[ -n "$ip" ] && echo "ip=$ip"
# Uptime
local uptime_s
uptime_s=$($exec_cmd cut -d' ' -f1 /proc/uptime 2>/dev/null | cut -d. -f1)
[ -n "$uptime_s" ] && echo "uptime_seconds=$uptime_s"
}
# Use sudo if available and needed (infmap user won't have direct access)
_sudo() {
if [ "$(id -u)" -eq 0 ]; then
"$@"
else
sudo -n "$@" 2>/dev/null
fi
}
# Proxmox LXC (pct)
if command -v pct &>/dev/null; then
_sudo pct list 2>/dev/null | tail -n +2 | while read -r vmid status _ name _; do
[ -z "$vmid" ] && continue
echo "[container:pct-${vmid}]"
echo "type=lxc"
echo "platform=proxmox"
echo "id=$vmid"
echo "name=${name:-$vmid}"
echo "status=$status"
if [ "$status" = "running" ]; then
gather_container_stats "_sudo pct exec $vmid --"
fi
done
fi
# Proxmox VMs (qm)
if command -v qm &>/dev/null; then
_sudo qm list 2>/dev/null | tail -n +2 | while read -r vmid name status _ mem _; do
[ -z "$vmid" ] && continue
echo "[container:qm-${vmid}]"
echo "type=vm"
echo "platform=proxmox"
echo "id=$vmid"
echo "name=${name:-$vmid}"
echo "status=$status"
[ -n "$mem" ] && echo "mem_allocated_mb=$mem"
# VM stats require guest agent - best effort
if [ "$status" = "running" ]; then
agent_test=$(_sudo qm guest exec "$vmid" -- cat /proc/meminfo 2>/dev/null)
if [ -n "$agent_test" ]; then
gather_container_stats "_sudo qm guest exec $vmid --"
fi
fi
done
fi
# Plain LXC (lxc/lxd)
if command -v lxc &>/dev/null && ! command -v pct &>/dev/null; then
_sudo lxc list --format csv -c nsN 2>/dev/null | while IFS=',' read -r name status network; do
[ -z "$name" ] && continue
echo "[container:lxc-${name}]"
echo "type=lxc"
echo "platform=lxd"
echo "name=$name"
echo "status=$status"
if [ "$status" = "RUNNING" ]; then
gather_container_stats "_sudo lxc exec $name --"
lxd_ip=$(echo "$network" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)
[ -n "$lxd_ip" ] && echo "ip=$lxd_ip"
fi
done
fi
# libvirt VMs (virsh)
if command -v virsh &>/dev/null; then
_sudo virsh list --all --name 2>/dev/null | while read -r name; do
[ -z "$name" ] && continue
state=$(_sudo virsh domstate "$name" 2>/dev/null | head -1)
echo "[container:virsh-${name}]"
echo "type=vm"
echo "platform=libvirt"
echo "name=$name"
echo "status=$state"
if [ "$state" = "running" ]; then
virsh_ip=$(_sudo virsh domifaddr "$name" --source agent 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)
[ -n "$virsh_ip" ] && echo "ip=$virsh_ip"
fi
done
fi
echo "[end]"