#!/bin/sh # bee-tui: interactive text menu for debug LiveCD operations. set -u if ! command -v dialog >/dev/null 2>&1; then echo "ERROR: dialog is required but not installed" exit 1 fi pause() { echo printf 'Press Enter to continue... ' read -r _ } header() { clear echo "==============================================" echo " bee TUI (debug)" echo "==============================================" echo } menu_choice() { title="$1" prompt="$2" shift 2 dialog --clear --stdout --title "$title" --menu "$prompt" 20 90 12 "$@" } list_ifaces() { ip -o link show \ | awk -F': ' '{print $2}' \ | grep -v '^lo$' \ | grep -vE '^(docker|virbr|veth|tun|tap|br-|bond|dummy)' \ | sort } show_network_status() { header echo "Network interfaces" echo for iface in $(list_ifaces); do state=$(ip -o link show "$iface" | awk '{print $9}') ipv4=$(ip -o -4 addr show dev "$iface" | awk '{print $4}' | paste -sd ',') [ -n "$ipv4" ] || ipv4="(no IPv4)" echo "- $iface: state=$state ip=$ipv4" done echo ip route | sed 's/^/ route: /' pause } choose_interface() { ifaces="$(list_ifaces)" if [ -z "$ifaces" ]; then echo "No physical interfaces found" return 1 fi set -- for iface in $ifaces; do set -- "$@" "$iface" "$iface" done iface=$(menu_choice "Network" "Select interface" "$@") || return 1 CHOSEN_IFACE="$iface" return 0 } network_dhcp_one() { header echo "DHCP on one interface" echo choose_interface || { pause; return; } iface="$CHOSEN_IFACE" echo echo "Starting DHCP on $iface..." ip link set "$iface" up 2>/dev/null || true udhcpc -i "$iface" -t 5 -T 3 pause } network_dhcp_all() { header echo "Restarting DHCP on all physical interfaces..." echo /usr/local/bin/bee-net-restart pause } network_static_one() { header echo "Static IPv4 setup" echo choose_interface || { pause; return; } iface="$CHOSEN_IFACE" echo printf 'IPv4 address (example 192.168.1.10): ' read -r ip if [ -z "$ip" ]; then echo "IP address is required" pause return fi printf 'Netmask (example 24 or 255.255.255.0): ' read -r mask if [ -z "$mask" ]; then echo "Netmask is required" pause return fi prefix=$(mask_to_prefix "$mask") if [ -z "$prefix" ]; then echo "Invalid netmask: $mask" pause return fi cidr="$ip/$prefix" printf 'Default gateway: ' read -r gw if [ -z "$gw" ]; then echo "Default gateway is required" pause return fi printf 'DNS server (optional): ' read -r dns ip link set "$iface" up 2>/dev/null || true ip addr flush dev "$iface" if ! ip addr add "$cidr" dev "$iface"; then echo "Failed to set IP" pause return fi if [ -n "$gw" ]; then ip route del default >/dev/null 2>&1 || true ip route add default via "$gw" dev "$iface" fi if [ -n "$dns" ]; then printf 'nameserver %s\n' "$dns" > /etc/resolv.conf fi echo echo "Static config applied to $iface" pause } mask_to_prefix() { mask="$(echo "$1" | tr -d '[:space:]')" case "$mask" in 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32) echo "$mask" return 0 ;; esac case "$mask" in 255.0.0.0) echo 8 ;; 255.128.0.0) echo 9 ;; 255.192.0.0) echo 10 ;; 255.224.0.0) echo 11 ;; 255.240.0.0) echo 12 ;; 255.248.0.0) echo 13 ;; 255.252.0.0) echo 14 ;; 255.254.0.0) echo 15 ;; 255.255.0.0) echo 16 ;; 255.255.128.0) echo 17 ;; 255.255.192.0) echo 18 ;; 255.255.224.0) echo 19 ;; 255.255.240.0) echo 20 ;; 255.255.248.0) echo 21 ;; 255.255.252.0) echo 22 ;; 255.255.254.0) echo 23 ;; 255.255.255.0) echo 24 ;; 255.255.255.128) echo 25 ;; 255.255.255.192) echo 26 ;; 255.255.255.224) echo 27 ;; 255.255.255.240) echo 28 ;; 255.255.255.248) echo 29 ;; 255.255.255.252) echo 30 ;; 255.255.255.254) echo 31 ;; 255.255.255.255) echo 32 ;; *) return 1 ;; esac } network_menu() { while true; do choice=$(menu_choice "Network" "Select action" \ "1" "Show network status" \ "2" "DHCP on all interfaces" \ "3" "DHCP on one interface" \ "4" "Set static IPv4 on one interface" \ "5" "Back") || return case "$choice" in 1) show_network_status ;; 2) network_dhcp_all ;; 3) network_dhcp_one ;; 4) network_static_one ;; 5) return ;; *) echo "Invalid choice"; pause ;; esac done } bee_services_list() { for path in /etc/init.d/bee-*; do [ -e "$path" ] || continue basename "$path" done } services_status_all() { header echo "bee service status" echo for svc in $(bee_services_list); do if rc-service "$svc" status >/dev/null 2>&1; then echo "- $svc: running" else echo "- $svc: stopped" fi done pause } choose_service() { svcs="$(bee_services_list)" if [ -z "$svcs" ]; then echo "No bee-* services found" return 1 fi set -- for svc in $svcs; do set -- "$@" "$svc" "$svc" done svc=$(menu_choice "bee Services" "Select service" "$@") || return 1 CHOSEN_SERVICE="$svc" return 0 } service_action_menu() { header echo "Service action" echo choose_service || { pause; return; } svc="$CHOSEN_SERVICE" act=$(menu_choice "Service: $svc" "Select action" \ "1" "status" \ "2" "restart" \ "3" "start" \ "4" "stop" \ "5" "toggle start/stop" \ "6" "Back") || return case "$act" in 1) rc-service "$svc" status || true ;; 2) rc-service "$svc" restart || true ;; 3) rc-service "$svc" start || true ;; 4) rc-service "$svc" stop || true ;; 5) if rc-service "$svc" status >/dev/null 2>&1; then rc-service "$svc" stop || true else rc-service "$svc" start || true fi ;; 6) return ;; *) echo "Invalid action" ;; esac pause } services_menu() { while true; do choice=$(menu_choice "bee Services" "Select action" \ "1" "Status of all bee-* services" \ "2" "Manage one service (status/restart/start/stop/toggle)" \ "3" "Back") || return case "$choice" in 1) services_status_all ;; 2) service_action_menu ;; 3) return ;; *) echo "Invalid choice"; pause ;; esac done } confirm_phrase() { phrase="$1" prompt="$2" echo printf '%s (%s): ' "$prompt" "$phrase" read -r value [ "$value" = "$phrase" ] } shutdown_menu() { while true; do choice=$(menu_choice "Shutdown/Reboot Tests" "Select action" \ "1" "Reboot now" \ "2" "Power off now" \ "3" "Schedule poweroff in 60s" \ "4" "Cancel scheduled shutdown" \ "5" "IPMI chassis power status" \ "6" "IPMI chassis power soft" \ "7" "IPMI chassis power cycle" \ "8" "Back") || return case "$choice" in 1) confirm_phrase "REBOOT" "Type confirmation" || { echo "Canceled"; pause; continue; } reboot ;; 2) confirm_phrase "POWEROFF" "Type confirmation" || { echo "Canceled"; pause; continue; } poweroff ;; 3) confirm_phrase "SCHEDULE" "Type confirmation" || { echo "Canceled"; pause; continue; } shutdown -P +1 "bee test: scheduled poweroff in 60 seconds" echo "Scheduled" pause ;; 4) shutdown -c || true echo "Canceled (if any schedule existed)" pause ;; 5) ipmitool chassis power status || echo "ipmitool power status failed" pause ;; 6) confirm_phrase "IPMI-SOFT" "Type confirmation" || { echo "Canceled"; pause; continue; } ipmitool chassis power soft || echo "ipmitool soft power failed" pause ;; 7) confirm_phrase "IPMI-CYCLE" "Type confirmation" || { echo "Canceled"; pause; continue; } ipmitool chassis power cycle || echo "ipmitool power cycle failed" pause ;; 8) return ;; *) echo "Invalid choice" pause ;; esac done } gpu_burn_10m() { header echo "GPU Burn (10 minutes)" echo if ! command -v gpu_burn >/dev/null 2>&1; then echo "gpu_burn binary not found in PATH" echo "Expected command: gpu_burn" pause return fi if ! command -v nvidia-smi >/dev/null 2>&1 || ! nvidia-smi -L >/dev/null 2>&1; then echo "NVIDIA driver/GPU not ready (nvidia-smi failed)" pause return fi confirm_phrase "GPU-BURN" "Type confirmation to start benchmark" || { echo "Canceled"; pause; return; } echo "Running: gpu_burn 600" echo "Log: /var/log/bee-gpuburn.log" gpu_burn 600 2>&1 | tee /var/log/bee-gpuburn.log echo echo "GPU Burn finished" pause } gpu_benchmarks_menu() { while true; do choice=$(menu_choice "Benchmarks -> GPU" "Select action" \ "1" "GPU Burn (10 minutes)" \ "2" "Back") || return case "$choice" in 1) gpu_burn_10m ;; 2) return ;; *) echo "Invalid choice"; pause ;; esac done } benchmarks_menu() { while true; do choice=$(menu_choice "Benchmarks" "Select category" \ "1" "GPU" \ "2" "Back") || return case "$choice" in 1) gpu_benchmarks_menu ;; 2) return ;; *) echo "Invalid choice"; pause ;; esac done } run_cmd_log() { label="$1" cmd="$2" log_file="$3" { echo "=== $label ===" echo "time: $(date -u '+%Y-%m-%dT%H:%M:%SZ')" echo "cmd: $cmd" echo sh -c "$cmd" } >"$log_file" 2>&1 return $? } run_gpu_nvidia_acceptance_test() { header echo "System acceptance tests -> GPU NVIDIA" echo confirm_phrase "SAT-GPU" "Type confirmation to start tests" || { echo "Canceled"; pause; return; } ts="$(date -u '+%Y%m%d-%H%M%S')" base_dir="/var/log/bee-sat" run_dir="$base_dir/gpu-nvidia-$ts" archive="$base_dir/gpu-nvidia-$ts.tar.gz" mkdir -p "$run_dir" summary="$run_dir/summary.txt" : >"$summary" echo "Running acceptance commands..." echo "Logs directory: $run_dir" echo "Archive target: $archive" echo c1="nvidia-smi -q" c2="dmidecode -t baseboard" c3="dmidecode -t system" c4="nvidia-bug-report.sh" run_cmd_log "nvidia_smi_q" "$c1" "$run_dir/01-nvidia-smi-q.log"; rc1=$? run_cmd_log "dmidecode_baseboard" "$c2" "$run_dir/02-dmidecode-baseboard.log"; rc2=$? run_cmd_log "dmidecode_system" "$c3" "$run_dir/03-dmidecode-system.log"; rc3=$? run_cmd_log "nvidia_bug_report" "$c4" "$run_dir/04-nvidia-bug-report.log"; rc4=$? bug_report="$(ls -1 nvidia-bug-report.log.gz 2>/dev/null | head -n1 || true)" if [ -n "$bug_report" ] && [ -f "$bug_report" ]; then cp -f "$bug_report" "$run_dir/" fi { echo "run_at_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" echo "cmd_nvidia_smi_q_rc=$rc1" echo "cmd_dmidecode_baseboard_rc=$rc2" echo "cmd_dmidecode_system_rc=$rc3" echo "cmd_nvidia_bug_report_rc=$rc4" } >>"$summary" tar -czf "$archive" -C "$base_dir" "gpu-nvidia-$ts" tar_rc=$? echo "archive_rc=$tar_rc" >>"$summary" echo echo "Done." echo "- Logs: $run_dir" echo "- Archive: $archive (rc=$tar_rc)" pause } gpu_nvidia_sat_menu() { while true; do choice=$(menu_choice "System acceptance tests -> GPU NVIDIA" "Select action" \ "1" "Run command pack" \ "2" "Back") || return case "$choice" in 1) run_gpu_nvidia_acceptance_test ;; 2) return ;; *) echo "Invalid choice"; pause ;; esac done } system_acceptance_tests_menu() { while true; do choice=$(menu_choice "System acceptance tests" "Select category" \ "1" "GPU NVIDIA" \ "2" "Back") || return case "$choice" in 1) gpu_nvidia_sat_menu ;; 2) return ;; *) echo "Invalid choice"; pause ;; esac done } run_audit_now() { header echo "Run audit now" echo /usr/local/bin/audit --output stdout > /var/log/bee-audit.json 2>/var/log/bee-audit.log rc=$? if [ "$rc" -eq 0 ]; then echo "Audit completed successfully" else echo "Audit finished with errors (rc=$rc)" fi echo "Logs: /var/log/bee-audit.log, /var/log/bee-audit.json" pause } check_required_tools() { header echo "Required tools check" echo for tool in dmidecode smartctl nvme ipmitool lspci audit nvidia-smi gpu_burn dialog; do if command -v "$tool" >/dev/null 2>&1; then echo "- $tool: OK ($(command -v "$tool"))" else echo "- $tool: MISSING" fi done pause } main_menu() { while true; do choice=$(menu_choice "Bee TUI (debug)" "Select action" \ "1" "Network setup" \ "2" "bee service management" \ "3" "Shutdown/reboot tests" \ "4" "Benchmarks" \ "5" "System acceptance tests" \ "6" "Run audit now" \ "7" "Check required tools" \ "8" "Show last audit log tail" \ "9" "Exit to console") || exit 0 case "$choice" in 1) network_menu ;; 2) services_menu ;; 3) shutdown_menu ;; 4) benchmarks_menu ;; 5) system_acceptance_tests_menu ;; 6) run_audit_now ;; 7) check_required_tools ;; 8) header tail -n 40 /var/log/bee-audit.log 2>/dev/null || echo "No /var/log/bee-audit.log" echo tail -n 20 /var/log/bee-audit.json 2>/dev/null || true pause ;; 9) exit 0 ;; *) echo "Invalid choice"; pause ;; esac done } main_menu